From 5fc53cec9c9d24f38f695d4638b43f97e53c7063 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Thu, 9 Dec 2010 13:37:18 +0200 Subject: [PATCH] + rename SimpleRedisSerializer to JdkSerializationRedisSerializer + add GenericToStringSerializer (cannot make it the default since the Class is required) + add dedicated StringRedisTemplate --- .../keyvalue/redis/core/RedisTemplate.java | 29 ++--- .../redis/core/StringRedisTemplate.java | 37 ++++++ .../serializer/GenericToStringSerializer.java | 108 ++++++++++++++++++ ...a => JdkSerializationRedisSerializer.java} | 5 +- .../serializer/StringRedisSerializer.java | 18 +-- .../AbstractConnectionIntegrationTests.java | 4 +- .../SimpleRedisSerializerTests.java | 4 +- 7 files changed, 178 insertions(+), 27 deletions(-) create mode 100644 spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/core/StringRedisTemplate.java create mode 100644 spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/serializer/GenericToStringSerializer.java rename spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/serializer/{SimpleRedisSerializer.java => JdkSerializationRedisSerializer.java} (89%) diff --git a/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/core/RedisTemplate.java b/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/core/RedisTemplate.java index 314dfdc65..863d0821f 100644 --- a/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/core/RedisTemplate.java +++ b/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/core/RedisTemplate.java @@ -35,8 +35,8 @@ import org.springframework.data.keyvalue.redis.connection.DataType; import org.springframework.data.keyvalue.redis.connection.RedisConnection; import org.springframework.data.keyvalue.redis.connection.RedisConnectionFactory; import org.springframework.data.keyvalue.redis.connection.SortParameters; +import org.springframework.data.keyvalue.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.data.keyvalue.redis.serializer.RedisSerializer; -import org.springframework.data.keyvalue.redis.serializer.SimpleRedisSerializer; import org.springframework.data.keyvalue.redis.serializer.StringRedisSerializer; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.util.Assert; @@ -46,6 +46,8 @@ import org.springframework.util.ClassUtils; * Helper class that simplifies Redis data access code. *

* Performs automatic serialization/deserialization between the given objects and the underlying binary data in the Redis store. + * By default, it uses Java serialization for its objects (through {@link JdkSerializationRedisSerializer}). For String intensive + * operations consider the dedicated {@link StringRedisTemplate}. *

* The central method is execute, supporting Redis access code implementing the {@link RedisCallback} interface. * It provides {@link RedisConnection} handling such that neither the {@link RedisCallback} implementation nor @@ -55,23 +57,23 @@ import org.springframework.util.ClassUtils; * Once configured, this class is thread-safe. * *

Note that while the template is generified, it is up to the serializers/deserializers to properly convert the given Objects - * to and from binary data. When using a generic serialization mechanism (such as Java serialization or JSON) the types lose their - * importance and can be skipped or only used as syntactic sugar. + * to and from binary data. *

* This is the central class in Redis support. * * @author Costin Leau * @param the Redis key type against which the template works (usually a String) * @param the Redis value type against which the template works + * @see StringRedisTemplate */ public class RedisTemplate extends RedisAccessor implements RedisOperations { private boolean exposeConnection = false; - private RedisSerializer keySerializer = new StringRedisSerializer(); - private RedisSerializer valueSerializer = new SimpleRedisSerializer(); - private RedisSerializer hashKeySerializer = new SimpleRedisSerializer(); - private RedisSerializer hashValueSerializer = new SimpleRedisSerializer(); - private RedisSerializer stringSerializer = new StringRedisSerializer(); + private RedisSerializer keySerializer = new JdkSerializationRedisSerializer(); + private RedisSerializer valueSerializer = new JdkSerializationRedisSerializer(); + private RedisSerializer hashKeySerializer = new JdkSerializationRedisSerializer(); + private RedisSerializer hashValueSerializer = new JdkSerializationRedisSerializer(); + private RedisSerializer stringSerializer = new StringRedisSerializer(); // cache singleton objects (where possible) private final ValueOperations valueOps = new DefaultValueOperations(); @@ -171,7 +173,7 @@ public class RedisTemplate extends RedisAccessor implements RedisOperation } /** - * Sets the key serializer to be used by this template. Defaults to {@link SimpleRedisSerializer}. + * Sets the key serializer to be used by this template. Defaults to {@link JdkSerializationRedisSerializer}. * * @param serializer */ @@ -180,7 +182,7 @@ public class RedisTemplate extends RedisAccessor implements RedisOperation } /** - * Sets the value serializer to be used by this template. Defaults to {@link SimpleRedisSerializer}. + * Sets the value serializer to be used by this template. Defaults to {@link JdkSerializationRedisSerializer}. * * @param serializer */ @@ -189,7 +191,7 @@ public class RedisTemplate extends RedisAccessor implements RedisOperation } /** - * Sets the hash key (or field) serializer to be used by this template. Defaults to {@link SimpleRedisSerializer}. + * Sets the hash key (or field) serializer to be used by this template. Defaults to {@link JdkSerializationRedisSerializer}. * * @param hashKeySerializer The hashKeySerializer to set. */ @@ -198,7 +200,7 @@ public class RedisTemplate extends RedisAccessor implements RedisOperation } /** - * Sets the hash value serializer to be used by this template. Defaults to {@link SimpleRedisSerializer}. + * Sets the hash value serializer to be used by this template. Defaults to {@link JdkSerializationRedisSerializer}. * * @param hashValueSerializer The hashValueSerializer to set. */ @@ -213,7 +215,7 @@ public class RedisTemplate extends RedisAccessor implements RedisOperation * @see ValueOperations#substract(Object, int, int) * @param stringSerializer The stringValueSerializer to set. */ - public void setStringSerializer(RedisSerializer stringSerializer) { + public void setStringSerializer(RedisSerializer stringSerializer) { this.stringSerializer = stringSerializer; } @@ -261,7 +263,6 @@ public class RedisTemplate extends RedisAccessor implements RedisOperation return (key != null ? keySerializer.serialize(key) : null); } - @SuppressWarnings("unchecked") private byte[] rawString(String key) { return (key != null ? stringSerializer.serialize(key) : null); } diff --git a/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/core/StringRedisTemplate.java b/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/core/StringRedisTemplate.java new file mode 100644 index 000000000..721fd9fb2 --- /dev/null +++ b/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/core/StringRedisTemplate.java @@ -0,0 +1,37 @@ +/* + * Copyright 2010 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.keyvalue.redis.core; + +import org.springframework.data.keyvalue.redis.serializer.RedisSerializer; +import org.springframework.data.keyvalue.redis.serializer.StringRedisSerializer; + +/** + * String-focused extension of RedisTemplate. Since most operations against Redis are String based, + * this class provides a dedicated class that minimizes configuration of its more generic + * {@link template RedisTemplate} especially in terms of serializers. + * + * @author Costin Leau + */ +public class StringRedisTemplate extends RedisTemplate { + + public StringRedisTemplate() { + RedisSerializer stringSerializer = new StringRedisSerializer(); + setKeySerializer(stringSerializer); + setValueSerializer(stringSerializer); + setHashKeySerializer(stringSerializer); + setHashValueSerializer(stringSerializer); + } +} diff --git a/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/serializer/GenericToStringSerializer.java b/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/serializer/GenericToStringSerializer.java new file mode 100644 index 000000000..465a9b7ef --- /dev/null +++ b/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/serializer/GenericToStringSerializer.java @@ -0,0 +1,108 @@ +/* + * Copyright 2010 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.keyvalue.redis.serializer; + +import java.nio.charset.Charset; + +import org.springframework.beans.BeansException; +import org.springframework.beans.TypeConverter; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.core.convert.ConversionService; +import org.springframework.util.Assert; + +/** + * Generic String to byte[] (and back) serializer. Relies on the Spring {@link ConversionService} + * to transform objects into String and vice versa. The Strings are convert into bytes and vice-versa + * using the specified charset (by default UTF-8). + * + * Note: The conversion service initialization happens automatically if the class is defined + * as a Spring bean. + * + * @author Costin Leau + */ +public class GenericToStringSerializer implements RedisSerializer, BeanFactoryAware { + + private final Charset charset; + private Converter converter; + private Class type; + + public GenericToStringSerializer(Class type) { + this(type, Charset.forName("UTF8")); + } + + public GenericToStringSerializer(Class type, Charset charset) { + Assert.notNull(type); + this.type = type; + this.charset = charset; + } + + public void setConversionService(ConversionService conversionService) { + Assert.notNull(conversionService, "non null conversion service required"); + converter = new Converter(conversionService); + } + + public void setTypeConverter(TypeConverter typeConverter) { + Assert.notNull(typeConverter, "non null type converter required"); + converter = new Converter(typeConverter); + } + + @Override + public T deserialize(byte[] bytes) { + String string = new String(bytes, charset); + return converter.convert(string, type); + } + + @Override + public byte[] serialize(T object) { + String string = converter.convert(object, String.class); + return string.getBytes(charset); + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + if (converter != null && beanFactory instanceof ConfigurableBeanFactory) { + ConfigurableBeanFactory cFB = (ConfigurableBeanFactory) beanFactory; + ConversionService conversionService = cFB.getConversionService(); + + converter = (conversionService != null ? new Converter(conversionService) : new Converter( + cFB.getTypeConverter())); + } + } + + private class Converter { + private final ConversionService conversionService; + private final TypeConverter typeConverter; + + public Converter(ConversionService conversionService) { + this.conversionService = conversionService; + this.typeConverter = null; + } + + public Converter(TypeConverter typeConverter) { + this.conversionService = null; + this.typeConverter = typeConverter; + } + + T convert(Object value, Class targetType) { + if (conversionService != null) { + return conversionService.convert(value, targetType); + } + return typeConverter.convertIfNecessary(value, targetType); + } + } +} \ No newline at end of file diff --git a/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/serializer/SimpleRedisSerializer.java b/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/serializer/JdkSerializationRedisSerializer.java similarity index 89% rename from spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/serializer/SimpleRedisSerializer.java rename to spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/serializer/JdkSerializationRedisSerializer.java index 83ca83824..529337648 100644 --- a/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/serializer/SimpleRedisSerializer.java +++ b/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/serializer/JdkSerializationRedisSerializer.java @@ -21,12 +21,13 @@ import org.springframework.core.serializer.support.SerializingConverter; import org.springframework.data.keyvalue.redis.UncategorizedRedisException; /** - * Simple Redis serializer delegating to the default (Java based) serializer in Spring 3. + * Java Serialization Redis serializer. + * Delegates to the default (Java based) serializer in Spring 3. * * @author Mark Pollack * @author Costin Leau */ -public class SimpleRedisSerializer implements RedisSerializer { +public class JdkSerializationRedisSerializer implements RedisSerializer { private Converter serializer = new SerializingConverter(); private Converter deserializer = new DeserializingConverter(); diff --git a/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/serializer/StringRedisSerializer.java b/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/serializer/StringRedisSerializer.java index e36686725..a5a8c97a8 100644 --- a/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/serializer/StringRedisSerializer.java +++ b/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/serializer/StringRedisSerializer.java @@ -17,16 +17,19 @@ package org.springframework.data.keyvalue.redis.serializer; import java.nio.charset.Charset; +import org.springframework.util.Assert; + /** - * Simple String to byte[] (and back) serializer. Relies on the specified charset - * (by default UTF-8) to properly convert the String into bytes and vice-versa. - * - * Useful when the interaction with the Redis happens mainly through Strings. + * Simple String to byte[] (and back) serializer. Converts Strings into bytes and vice-versa + * using the specified charset (by default UTF-8). + *

+ * Useful when the interaction with the Redis happens mainly through Strings. * * @author Costin Leau */ public class StringRedisSerializer implements RedisSerializer { + private final static byte[] EMPTY_ARRAY = new byte[0]; private final Charset charset; public StringRedisSerializer() { @@ -34,6 +37,7 @@ public class StringRedisSerializer implements RedisSerializer { } public StringRedisSerializer(Charset charset) { + Assert.notNull(charset); this.charset = charset; } @@ -43,7 +47,7 @@ public class StringRedisSerializer implements RedisSerializer { } @Override - public byte[] serialize(String object) { - return object.getBytes(charset); + public byte[] serialize(String string) { + return (string == null ? EMPTY_ARRAY : string.getBytes(charset)); } -} +} \ No newline at end of file diff --git a/spring-data-redis/src/test/java/org/springframework/data/keyvalue/redis/connection/AbstractConnectionIntegrationTests.java b/spring-data-redis/src/test/java/org/springframework/data/keyvalue/redis/connection/AbstractConnectionIntegrationTests.java index 8f193a64c..a2efefdac 100644 --- a/spring-data-redis/src/test/java/org/springframework/data/keyvalue/redis/connection/AbstractConnectionIntegrationTests.java +++ b/spring-data-redis/src/test/java/org/springframework/data/keyvalue/redis/connection/AbstractConnectionIntegrationTests.java @@ -26,13 +26,13 @@ import org.junit.Test; import org.springframework.data.keyvalue.redis.Address; import org.springframework.data.keyvalue.redis.Person; import org.springframework.data.keyvalue.redis.serializer.RedisSerializer; -import org.springframework.data.keyvalue.redis.serializer.SimpleRedisSerializer; +import org.springframework.data.keyvalue.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.data.keyvalue.redis.serializer.StringRedisSerializer; public abstract class AbstractConnectionIntegrationTests { protected RedisConnection connection; - protected RedisSerializer serializer = new SimpleRedisSerializer(); + protected RedisSerializer serializer = new JdkSerializationRedisSerializer(); protected RedisSerializer stringSerializer = new StringRedisSerializer(); private static final String listName = "test-list"; diff --git a/spring-data-redis/src/test/java/org/springframework/data/keyvalue/redis/serializer/SimpleRedisSerializerTests.java b/spring-data-redis/src/test/java/org/springframework/data/keyvalue/redis/serializer/SimpleRedisSerializerTests.java index 7b436f6e5..e02a83a86 100644 --- a/spring-data-redis/src/test/java/org/springframework/data/keyvalue/redis/serializer/SimpleRedisSerializerTests.java +++ b/spring-data-redis/src/test/java/org/springframework/data/keyvalue/redis/serializer/SimpleRedisSerializerTests.java @@ -26,7 +26,7 @@ import org.junit.Test; import org.springframework.data.keyvalue.redis.Address; import org.springframework.data.keyvalue.redis.Person; import org.springframework.data.keyvalue.redis.serializer.RedisSerializer; -import org.springframework.data.keyvalue.redis.serializer.SimpleRedisSerializer; +import org.springframework.data.keyvalue.redis.serializer.JdkSerializationRedisSerializer; public class SimpleRedisSerializerTests { @@ -103,7 +103,7 @@ public class SimpleRedisSerializerTests { @Before public void setUp() { - serializer = new SimpleRedisSerializer(); + serializer = new JdkSerializationRedisSerializer(); } @After