From a3bd816cf8f56b28b92285e5c900e15311fcf724 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 25 Apr 2016 14:38:44 +0200 Subject: [PATCH 1/2] DATAREDIS-501 - RedisTemplate's default serializer assumes that its ClassLoader can load application types. Prepare issue branch. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e6d868411f..6a0be5760d 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 1.8.0.BUILD-SNAPSHOT + 1.8.0.DATAREDIS-501-SNAPSHOT Spring Data Redis From 97da37777eacc5d137e295da49211a45d5fa5607 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 26 Apr 2016 08:39:17 +0200 Subject: [PATCH 2/2] DATAREDIS-501 - Use application context ClassLoader as default for JdkSerializationRedisSerializer in RedisTemplate. We now pick up the ClassLoader from the ApplicationContext and use the latter as default in RedisTemplate for initializing the JdkSerializationRedisSerializer. We only do this in case the default serializer has not been set explicitly. --- .../data/redis/core/RedisTemplate.java | 29 ++++++++++++-- .../redis/core/RedisTemplateUnitTests.java | 38 +++++++++++++++++-- 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/core/RedisTemplate.java b/src/main/java/org/springframework/data/redis/core/RedisTemplate.java index 7a9dcb9ade..f2b9677ce9 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisTemplate.java +++ b/src/main/java/org/springframework/data/redis/core/RedisTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2015 the original author or authors. + * Copyright 2011-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.dao.DataAccessException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.redis.RedisSystemException; @@ -75,13 +76,14 @@ * @param the Redis value type against which the template works * @see StringRedisTemplate */ -public class RedisTemplate extends RedisAccessor implements RedisOperations { +public class RedisTemplate extends RedisAccessor implements RedisOperations, BeanClassLoaderAware { private boolean enableTransactionSupport = false; private boolean exposeConnection = false; private boolean initialized = false; private boolean enableDefaultSerializer = true; - private RedisSerializer defaultSerializer = new JdkSerializationRedisSerializer(); + private RedisSerializer defaultSerializer; + private ClassLoader classLoader; private RedisSerializer keySerializer = null; private RedisSerializer valueSerializer = null; @@ -104,10 +106,19 @@ public class RedisTemplate extends RedisAccessor implements RedisOperation public RedisTemplate() {} public void afterPropertiesSet() { + super.afterPropertiesSet(); + boolean defaultUsed = false; + if (defaultSerializer == null) { + + defaultSerializer = new JdkSerializationRedisSerializer( + classLoader != null ? classLoader : this.getClass().getClassLoader()); + } + if (enableDefaultSerializer) { + if (keySerializer == null) { keySerializer = defaultSerializer; defaultUsed = true; @@ -1096,4 +1107,16 @@ public void setEnableTransactionSupport(boolean enableTransactionSupport) { this.enableTransactionSupport = enableTransactionSupport; } + /** + * Set the {@link ClassLoader} to be used for the default {@link JdkSerializationRedisSerializer} in case no other + * {@link RedisSerializer} is explicitly set as the default one. + * + * @param resourceLoader can be {@literal null}. + * @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader + * @since 1.8 + */ + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } } diff --git a/src/test/java/org/springframework/data/redis/core/RedisTemplateUnitTests.java b/src/test/java/org/springframework/data/redis/core/RedisTemplateUnitTests.java index 9102f6ef42..9806bcaa11 100644 --- a/src/test/java/org/springframework/data/redis/core/RedisTemplateUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/RedisTemplateUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,9 +15,14 @@ */ package org.springframework.data.redis.core; +import static org.hamcrest.core.Is.*; +import static org.hamcrest.core.IsNull.*; +import static org.junit.Assert.*; import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; +import java.io.Serializable; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -25,6 +30,8 @@ import org.mockito.runners.MockitoJUnitRunner; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; +import org.springframework.instrument.classloading.ShadowingClassLoader; /** * @author Christoph Strobl @@ -32,14 +39,14 @@ @RunWith(MockitoJUnitRunner.class) public class RedisTemplateUnitTests { - private RedisTemplate template; + private RedisTemplate template; private @Mock RedisConnectionFactory connectionFactoryMock; private @Mock RedisConnection redisConnectionMock; @Before public void setUp() { - template = new RedisTemplate(); + template = new RedisTemplate(); template.setConnectionFactory(connectionFactoryMock); when(connectionFactoryMock.getConnection()).thenReturn(redisConnectionMock); @@ -66,4 +73,29 @@ public void slaveOfNoOneIsDelegatedToConnectionCorrectly() { verify(redisConnectionMock, times(1)).slaveOfNoOne(); } + /** + * @see DATAREDIS-501 + */ + @Test + public void templateShouldPassOnAndUseResoureLoaderClassLoaderToDefaultJdkSerializerWhenNotAlreadySet() { + + ShadowingClassLoader scl = new ShadowingClassLoader(ClassLoader.getSystemClassLoader()); + + template = new RedisTemplate(); + template.setConnectionFactory(connectionFactoryMock); + template.setBeanClassLoader(scl); + template.afterPropertiesSet(); + + when(redisConnectionMock.get(any(byte[].class))) + .thenReturn(new JdkSerializationRedisSerializer().serialize(new SomeArbitrarySeriaizableObject())); + + Object deserialized = template.opsForValue().get("spring"); + assertThat(deserialized, notNullValue()); + assertThat(deserialized.getClass().getClassLoader(), is((ClassLoader) scl)); + } + + static class SomeArbitrarySeriaizableObject implements Serializable { + private static final long serialVersionUID = -5973659324040506423L; + } + }