From 6ca8c50cfa9c55801db3d6184ae51be0ebc79285 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 16 Mar 2016 10:58:06 +0100 Subject: [PATCH 1/2] DATAREDIS-427 - Allow injecting deserializer for JdkSerializationRedisSerializer. Prepare issue branch. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e2e0f007c6..4653a864ed 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 1.7.0.BUILD-SNAPSHOT + 1.7.0.DATAREDIS-427-SNAPSHOT Spring Data Redis From ee609f4ad5807a4937ab74ad941d25f4717ca8ef Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 16 Mar 2016 11:01:25 +0100 Subject: [PATCH 2/2] DATAREDIS-427 - Allow construction of customized JdkSerializationRedisSerializer. We now allow constructing JdkSerializationRedisSerializer using an own class loader and by specifying custom converters. Using an own class-loader: new JdkSerializationRedisSerializer(classLoader) Using own converters (Serializer/Deserializer): new JdkSerializationRedisSerializer(new SerializingConverter(), new DeserializingConverter(new DefaultDeserializer(classLoader))) --- .../JdkSerializationRedisSerializer.java | 44 +++++++++++-- .../serializer/SerializableDomainClass.java | 26 ++++++++ .../SimpleRedisSerializerTests.java | 63 ++++++++++++++++++- 3 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 src/test/java/org/springframework/data/redis/serializer/SerializableDomainClass.java diff --git a/src/main/java/org/springframework/data/redis/serializer/JdkSerializationRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/JdkSerializationRedisSerializer.java index e45612aa49..d72f21e679 100644 --- a/src/main/java/org/springframework/data/redis/serializer/JdkSerializationRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/JdkSerializationRedisSerializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2013 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. @@ -18,17 +18,53 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.core.serializer.support.DeserializingConverter; import org.springframework.core.serializer.support.SerializingConverter; +import org.springframework.util.Assert; /** - * Java Serialization Redis serializer. Delegates to the default (Java based) serializer in Spring 3. + * Java Serialization Redis serializer. Delegates to the default (Java based) {@link DefaultSerializer serializer} and + * {@link DefaultDeserializer}. This {@link RedisSerializer serializer} can be constructed with either custom + * {@link ClassLoader} or own {@link Converter converters}. * * @author Mark Pollack * @author Costin Leau + * @author Mark Paluch */ public class JdkSerializationRedisSerializer implements RedisSerializer { - private Converter serializer = new SerializingConverter(); - private Converter deserializer = new DeserializingConverter(); + private final Converter serializer; + private final Converter deserializer; + + /** + * Creates a new {@link JdkSerializationRedisSerializer} using the default class loader. + */ + public JdkSerializationRedisSerializer() { + this(new SerializingConverter(), new DeserializingConverter()); + } + + /** + * Creates a new {@link JdkSerializationRedisSerializer} using a {@link ClassLoader}. + * + * @param classLoader + */ + public JdkSerializationRedisSerializer(ClassLoader classLoader) { + this(new SerializingConverter(), new DeserializingConverter(classLoader)); + } + + /** + * Creates a new {@link JdkSerializationRedisSerializer} using a {@link Converter converters} to serialize and + * deserialize objects. + * + * @param serializer must not be {@literal null} + * @param deserializer must not be {@literal null} + */ + public JdkSerializationRedisSerializer(Converter serializer, Converter deserializer) { + + Assert.notNull("Serializer must not be null!"); + Assert.notNull("Deserializer must not be null!"); + + this.serializer = serializer; + this.deserializer = deserializer; + } public Object deserialize(byte[] bytes) { if (SerializationUtils.isEmpty(bytes)) { diff --git a/src/test/java/org/springframework/data/redis/serializer/SerializableDomainClass.java b/src/test/java/org/springframework/data/redis/serializer/SerializableDomainClass.java new file mode 100644 index 0000000000..739d235b8b --- /dev/null +++ b/src/test/java/org/springframework/data/redis/serializer/SerializableDomainClass.java @@ -0,0 +1,26 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.redis.serializer; + +import java.io.Serializable; + +/** + * @author Mark Paluch + */ +public class SerializableDomainClass implements Serializable { + +} diff --git a/src/test/java/org/springframework/data/redis/serializer/SimpleRedisSerializerTests.java b/src/test/java/org/springframework/data/redis/serializer/SimpleRedisSerializerTests.java index 8b80cc7cfe..37710d6a2f 100644 --- a/src/test/java/org/springframework/data/redis/serializer/SimpleRedisSerializerTests.java +++ b/src/test/java/org/springframework/data/redis/serializer/SimpleRedisSerializerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2013 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. @@ -15,9 +15,13 @@ */ package org.springframework.data.redis.serializer; +import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import java.io.IOException; +import java.io.InputStream; import java.io.Serializable; +import java.net.URL; import java.util.UUID; import org.junit.After; @@ -26,9 +30,11 @@ import org.springframework.data.redis.Address; import org.springframework.data.redis.Person; import org.springframework.oxm.xstream.XStreamMarshaller; +import org.springframework.util.StreamUtils; /** * @author Jennifer Hickey + * @author Mark Paluch */ public class SimpleRedisSerializerTests { @@ -119,6 +125,22 @@ private void verifySerializedObjects(Object... objects) { } } + @Test + public void jdkSerializerShouldUseCustomClassLoader() throws ClassNotFoundException { + + ClassLoader customClassLoader = new CustomClassLoader(); + + JdkSerializationRedisSerializer serializer = new JdkSerializationRedisSerializer(customClassLoader); + SerializableDomainClass domainClass = new SerializableDomainClass(); + + byte[] serialized = serializer.serialize(domainClass); + Object deserialized = serializer.deserialize(serialized); + + assertThat(deserialized.getClass().getName(), is(equalTo(SerializableDomainClass.class.getName()))); + assertThat(deserialized, is(not(instanceOf(SerializableDomainClass.class)))); + assertThat(deserialized.getClass().getClassLoader(), is(equalTo(customClassLoader))); + } + @Test public void testStringEncodedSerialization() { String value = UUID.randomUUID().toString(); @@ -157,4 +179,43 @@ public void testJsonSerializer() throws Exception { assertEquals(p1, serializer.deserialize(serializer.serialize(p1))); } + /** + * Custom class loader that loads class files from the test's class path. This {@link ClassLoader} does not delegate + * to a parent class loader to truly load classes that are defined by this class loader and not interfere with any + * parent class loader. The class loader uses simple class definition which is fine for the test but do not use this + * as sample for production class loaders. + */ + private static class CustomClassLoader extends ClassLoader { + + public CustomClassLoader() { + super(null); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + + URL resource = SimpleRedisSerializerTests.class.getResource("/" + name.replace('.', '/') + ".class"); + + InputStream is = null; + try { + + is = resource.openStream(); + byte[] bytes = StreamUtils.copyToByteArray(is); + return defineClass(name, bytes, 0, bytes.length); + } catch (IOException o_O) { + throw new ClassNotFoundException("Cannot read class file", o_O); + } finally { + + if (is != null) { + try { + is.close(); + } catch (IOException e) { + // ignore + } + } + } + + } + + } }