diff --git a/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/support/collections/RedisProperties.java b/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/support/collections/RedisProperties.java
new file mode 100644
index 000000000..22b44ddc3
--- /dev/null
+++ b/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/support/collections/RedisProperties.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2011 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.support.collections;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.Map.Entry;
+import java.util.concurrent.TimeUnit;
+
+import org.springframework.data.keyvalue.redis.connection.DataType;
+import org.springframework.data.keyvalue.redis.core.BoundHashOperations;
+import org.springframework.data.keyvalue.redis.core.RedisOperations;
+
+/**
+ * {@link Properties} extension for a Redis back-store. Useful for reading (and storing) properties
+ * inside a Redis hash. Particularly useful inside a Spring container for hooking into Spring's property
+ * placeholder or {@link org.springframework.beans.factory.config.PropertiesFactoryBean}.
+ *
+ * Note that this implementation only accepts Strings - objects of other type are not supported.
+ *
+ * @see Properties
+ * @see org.springframework.core.io.support.PropertiesLoaderSupport
+ * @author Costin Leau
+ */
+public class RedisProperties extends Properties implements RedisMap {
+
+ private final BoundHashOperations hashOps;
+ private final RedisMap delegate;
+
+ /**
+ * Constructs a new RedisProperties instance.
+ *
+ */
+ public RedisProperties(BoundHashOperations boundOps) {
+ this(null, boundOps);
+ }
+
+ /**
+ * Constructs a new RedisProperties instance.
+ *
+ * @param boundOps
+ */
+ public RedisProperties(String key, RedisOperations operations) {
+ this(null, operations. boundHashOps(key));
+ }
+
+ /**
+ * Constructs a new RedisProperties instance.
+ *
+ * @param defaults
+ */
+ public RedisProperties(Properties defaults, BoundHashOperations boundOps) {
+ super(defaults);
+ this.hashOps = boundOps;
+ this.delegate = new DefaultRedisMap(boundOps);
+ }
+
+ /**
+ * Constructs a new RedisProperties instance.
+ *
+ * @param defaults
+ * @param boundOps
+ */
+ public RedisProperties(Properties defaults, String key, RedisOperations operations) {
+ this(defaults, operations. boundHashOps(key));
+ }
+
+ @Override
+ public synchronized Object get(Object key) {
+ return delegate.get(key);
+ }
+
+ @Override
+ public synchronized Object put(Object key, Object value) {
+ return delegate.put((String) key, (String) value);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public synchronized void putAll(Map extends Object, ? extends Object> t) {
+ delegate.putAll((Map extends String, ? extends String>) t);
+ }
+
+ @Override
+ public Enumeration> propertyNames() {
+ Set keys = new LinkedHashSet(delegate.keySet());
+ keys.addAll(defaults.stringPropertyNames());
+ return Collections.enumeration(keys);
+ }
+
+ @Override
+ public synchronized void clear() {
+ delegate.clear();
+ }
+
+ @Override
+ public synchronized Object clone() {
+ return new RedisProperties(defaults, hashOps);
+ }
+
+ @Override
+ public synchronized boolean contains(Object value) {
+ return containsValue(value);
+ }
+
+ @Override
+ public synchronized boolean containsKey(Object key) {
+ return delegate.containsKey(key);
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ return delegate.containsValue(value);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public synchronized Enumeration elements() {
+ Collection values = delegate.values();
+ return Collections.enumeration(values);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Set> entrySet() {
+ Set entries = delegate.entrySet();
+ return entries;
+ }
+
+ @Override
+ public synchronized boolean equals(Object o) {
+ if (o == this)
+ return true;
+
+ if (o instanceof RedisProperties) {
+ return o.hashCode() == hashCode();
+ }
+ return false;
+ }
+
+ @Override
+ public synchronized int hashCode() {
+ int hash = RedisProperties.class.hashCode();
+ return hash * 17 + delegate.hashCode();
+ }
+
+ @Override
+ public synchronized boolean isEmpty() {
+ return delegate.isEmpty();
+ }
+
+ @Override
+ public synchronized Enumeration keys() {
+ Set keys = keySet();
+ return Collections.enumeration(keys);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Set keySet() {
+ Set keys = delegate.keySet();
+ return keys;
+ }
+
+ @Override
+ public synchronized Object remove(Object key) {
+ return delegate.remove(key);
+ }
+
+ @Override
+ public synchronized int size() {
+ return delegate.size();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Collection values() {
+ Collection vals = delegate.values();
+ return vals;
+ }
+
+ @Override
+ public Long increment(Object key, long delta) {
+ return hashOps.increment((String) key, delta);
+ }
+
+ @Override
+ public RedisOperations getOperations() {
+ return hashOps.getOperations();
+ }
+
+ @Override
+ public Boolean expire(long timeout, TimeUnit unit) {
+ return hashOps.expire(timeout, unit);
+ }
+
+ @Override
+ public Boolean expireAt(Date date) {
+ return hashOps.expireAt(date);
+ }
+
+ @Override
+ public Long getExpire() {
+ return hashOps.getExpire();
+ }
+
+ @Override
+ public String getKey() {
+ return hashOps.getKey();
+ }
+
+ @Override
+ public DataType getType() {
+ return hashOps.getType();
+ }
+
+ @Override
+ public Boolean persist() {
+ return hashOps.persist();
+ }
+
+ @Override
+ public void rename(String newKey) {
+ hashOps.rename(newKey);
+ }
+
+ @Override
+ public Object putIfAbsent(Object key, Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean remove(Object key, Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean replace(Object key, Object oldValue, Object newValue) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object replace(Object key, Object value) {
+ throw new UnsupportedOperationException();
+ }
+}
\ No newline at end of file
diff --git a/spring-data-redis/src/test/java/org/springframework/data/keyvalue/redis/support/collections/AbstractRedisMapTests.java b/spring-data-redis/src/test/java/org/springframework/data/keyvalue/redis/support/collections/AbstractRedisMapTests.java
index 1218c2e68..f9d266355 100644
--- a/spring-data-redis/src/test/java/org/springframework/data/keyvalue/redis/support/collections/AbstractRedisMapTests.java
+++ b/spring-data-redis/src/test/java/org/springframework/data/keyvalue/redis/support/collections/AbstractRedisMapTests.java
@@ -162,7 +162,7 @@ public abstract class AbstractRedisMapTests {
K k1 = getKey();
V v1 = getValue();
- assertNull(map.get(UUID.randomUUID()));
+ assertNull(map.get(UUID.randomUUID().toString()));
assertNull(map.get(k1));
map.put(k1, v1);
assertEquals(v1, map.get(k1));
diff --git a/spring-data-redis/src/test/java/org/springframework/data/keyvalue/redis/support/collections/RedisPropertiesTest.java b/spring-data-redis/src/test/java/org/springframework/data/keyvalue/redis/support/collections/RedisPropertiesTest.java
new file mode 100644
index 000000000..2075d788a
--- /dev/null
+++ b/spring-data-redis/src/test/java/org/springframework/data/keyvalue/redis/support/collections/RedisPropertiesTest.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2011 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.support.collections;
+
+import static org.junit.Assert.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.LinkedHashSet;
+import java.util.Properties;
+import java.util.Set;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runners.Parameterized.Parameters;
+import org.springframework.data.keyvalue.redis.Person;
+import org.springframework.data.keyvalue.redis.SettingsUtils;
+import org.springframework.data.keyvalue.redis.connection.jedis.JedisConnectionFactory;
+import org.springframework.data.keyvalue.redis.connection.jredis.JredisConnectionFactory;
+import org.springframework.data.keyvalue.redis.connection.rjc.RjcConnectionFactory;
+import org.springframework.data.keyvalue.redis.core.RedisTemplate;
+import org.springframework.data.keyvalue.redis.core.StringRedisTemplate;
+import org.springframework.data.keyvalue.redis.serializer.JacksonJsonRedisSerializer;
+import org.springframework.data.keyvalue.redis.serializer.OxmSerializer;
+import org.springframework.oxm.xstream.XStreamMarshaller;
+
+/**
+ * @author Costin Leau
+ */
+public class RedisPropertiesTest extends RedisMapTests {
+
+ protected Properties defaults = new Properties();
+ protected RedisProperties props;
+
+ /**
+ * Constructs a new RedisPropertiesTest instance.
+ *
+ * @param keyFactory
+ * @param valueFactory
+ * @param template
+ */
+ public RedisPropertiesTest(ObjectFactory keyFactory, ObjectFactory valueFactory,
+ RedisTemplate template) {
+ super(keyFactory, valueFactory, template);
+ }
+
+ @Override
+ RedisMap createMap() {
+ String redisName = getClass().getSimpleName();
+ props = new RedisProperties(defaults, redisName, new StringRedisTemplate(template.getConnectionFactory()));
+ return props;
+ }
+
+ @Override
+ protected RedisStore copyStore(RedisStore store) {
+ return new RedisProperties(store.getKey(), store.getOperations());
+ }
+
+ @Test
+ public void testGetOperations() {
+ assertTrue(map.getOperations() instanceof StringRedisTemplate);
+ }
+
+ @Test
+ public void testPropertiesLoad() throws Exception {
+ InputStream stream = getClass().getResourceAsStream(
+ "/org/springframework/data/keyvalue/redis/support/collections/props.properties");
+
+ assertNotNull(stream);
+
+ int size = props.size();
+
+ try {
+ props.load(stream);
+ } finally {
+ stream.close();
+ }
+
+ assertEquals("bar", props.get("foo"));
+ assertEquals("head", props.get("bucket"));
+ assertEquals("island", props.get("lotus"));
+ assertEquals(size + 3, props.size());
+ }
+
+ @Test
+ @Ignore
+ public void testPropertiesLoadXml() throws Exception {
+ InputStream stream = getClass().getResourceAsStream(
+ "/org/springframework/data/keyvalue/redis/support/collections/props.properties");
+
+ assertNotNull(stream);
+
+ int size = props.size();
+
+ try {
+ props.loadFromXML(stream);
+ } finally {
+ stream.close();
+ }
+
+ assertEquals("bar", props.get("foo"));
+ assertEquals("head", props.get("bucket"));
+ assertEquals("island", props.get("lotus"));
+ assertEquals(size + 3, props.size());
+ }
+
+ @Test
+ public void testPropertiesSave() throws Exception {
+ props.setProperty("x", "y");
+ props.setProperty("a", "b");
+
+ StringWriter writer = new StringWriter();
+ props.store(writer, "no-comment");
+ System.out.println(writer.toString());
+ }
+
+ @Test
+ @Ignore
+ public void testPropertiesSaveXml() throws Exception {
+ props.setProperty("x", "y");
+ props.setProperty("a", "b");
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ props.storeToXML(bos, "comment");
+ System.out.println(bos.toString());
+ }
+
+ @Test
+ public void testGetProperty() throws Exception {
+ String property = props.getProperty("a");
+ assertNull(property);
+ defaults.put("a", "x");
+ assertEquals("x", props.getProperty("a"));
+ }
+
+ @Test
+ public void testGetPropertyDefault() throws Exception {
+ assertEquals("x", props.getProperty("a", "x"));
+ }
+
+ @Test
+ public void testSetProperty() throws Exception {
+ assertNull(props.getProperty("a"));
+ defaults.setProperty("a", "x");
+ assertEquals("x", props.getProperty("a"));
+ }
+
+ @Test
+ public void testPropertiesList() throws Exception {
+ defaults.setProperty("a", "b");
+ props.setProperty("x", "y");
+ props.list(System.out);
+ }
+
+ @Test
+ public void testPropertyNames() throws Exception {
+ String key1="foo";
+ String key2="x";
+ String key3 = "d";
+
+ String val ="o";
+
+ defaults.setProperty(key3, val);
+ props.setProperty(key1, val);
+ props.setProperty(key2, val);
+
+ Enumeration> names = props.propertyNames();
+ Set keys = new LinkedHashSet();
+ keys.add(names.nextElement());
+ keys.add(names.nextElement());
+ keys.add(names.nextElement());
+
+ assertFalse(names.hasMoreElements());
+ }
+
+ @Test
+ public void testStringPropertyNames() throws Exception {
+ String key1 = "foo";
+ String key2 = "x";
+ String key3 = "d";
+
+ String val = "o";
+
+ defaults.setProperty(key3, val);
+ props.setProperty(key1, val);
+ props.setProperty(key2, val);
+
+ Set keys = props.stringPropertyNames();
+ assertTrue(keys.contains(key1));
+ assertTrue(keys.contains(key2));
+ assertTrue(keys.contains(key3));
+ }
+
+ @Parameters
+ public static Collection testParams() {
+ // XStream serializer
+ XStreamMarshaller xstream = new XStreamMarshaller();
+ try {
+ xstream.afterPropertiesSet();
+ } catch (Exception ex) {
+ throw new RuntimeException("Cannot init XStream", ex);
+ }
+ OxmSerializer serializer = new OxmSerializer(xstream, xstream);
+ JacksonJsonRedisSerializer jsonSerializer = new JacksonJsonRedisSerializer(Person.class);
+ JacksonJsonRedisSerializer jsonStringSerializer = new JacksonJsonRedisSerializer(String.class);
+
+ // create Jedis Factory
+ ObjectFactory stringFactory = new StringObjectFactory();
+
+ JedisConnectionFactory jedisConnFactory = new JedisConnectionFactory();
+ jedisConnFactory.setUsePool(false);
+
+ jedisConnFactory.setPort(SettingsUtils.getPort());
+ jedisConnFactory.setHostName(SettingsUtils.getHost());
+
+ jedisConnFactory.afterPropertiesSet();
+
+ RedisTemplate genericTemplate = new RedisTemplate(jedisConnFactory);
+
+ RedisTemplate xstreamGenericTemplate = new RedisTemplate();
+ xstreamGenericTemplate.setConnectionFactory(jedisConnFactory);
+ xstreamGenericTemplate.setDefaultSerializer(serializer);
+ xstreamGenericTemplate.afterPropertiesSet();
+
+ RedisTemplate jsonPersonTemplate = new RedisTemplate();
+ jsonPersonTemplate.setConnectionFactory(jedisConnFactory);
+ jsonPersonTemplate.setDefaultSerializer(jsonSerializer);
+ jsonPersonTemplate.setHashKeySerializer(jsonSerializer);
+ jsonPersonTemplate.setHashValueSerializer(jsonStringSerializer);
+ jsonPersonTemplate.afterPropertiesSet();
+
+ // JRedis
+ JredisConnectionFactory jredisConnFactory = new JredisConnectionFactory();
+ jredisConnFactory.setUsePool(true);
+ jredisConnFactory.setPort(SettingsUtils.getPort());
+ jredisConnFactory.setHostName(SettingsUtils.getHost());
+ jredisConnFactory.afterPropertiesSet();
+
+ RedisTemplate genericTemplateJR = new RedisTemplate(jredisConnFactory);
+ RedisTemplate xGenericTemplateJR = new RedisTemplate();
+ xGenericTemplateJR.setConnectionFactory(jredisConnFactory);
+ xGenericTemplateJR.setDefaultSerializer(serializer);
+ xGenericTemplateJR.afterPropertiesSet();
+
+ RedisTemplate jsonPersonTemplateJR = new RedisTemplate();
+ jsonPersonTemplateJR.setConnectionFactory(jredisConnFactory);
+ jsonPersonTemplateJR.setDefaultSerializer(jsonSerializer);
+ jsonPersonTemplateJR.setHashKeySerializer(jsonSerializer);
+ jsonPersonTemplateJR.setHashValueSerializer(jsonStringSerializer);
+ jsonPersonTemplateJR.afterPropertiesSet();
+
+ // RJC
+
+ // rjc
+ RjcConnectionFactory rjcConnFactory = new RjcConnectionFactory();
+ rjcConnFactory.setUsePool(true);
+ rjcConnFactory.setPort(SettingsUtils.getPort());
+ rjcConnFactory.setHostName(SettingsUtils.getHost());
+ rjcConnFactory.afterPropertiesSet();
+
+ RedisTemplate genericTemplateRJC = new RedisTemplate(jredisConnFactory);
+ RedisTemplate xGenericTemplateRJC = new RedisTemplate();
+ xGenericTemplateRJC.setConnectionFactory(rjcConnFactory);
+ xGenericTemplateRJC.setDefaultSerializer(serializer);
+ xGenericTemplateRJC.afterPropertiesSet();
+
+ RedisTemplate jsonPersonTemplateRJC = new RedisTemplate();
+ jsonPersonTemplateRJC.setConnectionFactory(rjcConnFactory);
+ jsonPersonTemplateRJC.setDefaultSerializer(jsonSerializer);
+ jsonPersonTemplateRJC.setHashKeySerializer(jsonSerializer);
+ jsonPersonTemplateRJC.setHashValueSerializer(jsonStringSerializer);
+ jsonPersonTemplateRJC.afterPropertiesSet();
+
+
+ return Arrays.asList(new Object[][] { { stringFactory, stringFactory, genericTemplate },
+ { stringFactory, stringFactory, genericTemplate }, { stringFactory, stringFactory, genericTemplate },
+ { stringFactory, stringFactory, genericTemplate },
+ { stringFactory, stringFactory, xstreamGenericTemplate },
+ { stringFactory, stringFactory, genericTemplateJR },
+ { stringFactory, stringFactory, genericTemplateJR },
+ { stringFactory, stringFactory, genericTemplateJR },
+ { stringFactory, stringFactory, genericTemplateJR },
+ { stringFactory, stringFactory, xGenericTemplateJR },
+ { stringFactory, stringFactory, jsonPersonTemplate },
+ { stringFactory, stringFactory, jsonPersonTemplateJR },
+ { stringFactory, stringFactory, genericTemplateRJC },
+ { stringFactory, stringFactory, genericTemplateRJC },
+ { stringFactory, stringFactory, genericTemplateRJC },
+ { stringFactory, stringFactory, genericTemplateRJC },
+ { stringFactory, stringFactory, xGenericTemplateRJC },
+ { stringFactory, stringFactory, jsonPersonTemplateRJC } });
+ }
+}
\ No newline at end of file
diff --git a/spring-data-redis/src/test/resources/org/springframework/data/keyvalue/redis/support/collections/props.properties b/spring-data-redis/src/test/resources/org/springframework/data/keyvalue/redis/support/collections/props.properties
new file mode 100644
index 000000000..aad78142d
--- /dev/null
+++ b/spring-data-redis/src/test/resources/org/springframework/data/keyvalue/redis/support/collections/props.properties
@@ -0,0 +1,4 @@
+# redis connection properties
+foo=bar
+bucket=head
+lotus=island
\ No newline at end of file
diff --git a/spring-data-redis/src/test/resources/org/springframework/data/keyvalue/redis/support/collections/props.xml b/spring-data-redis/src/test/resources/org/springframework/data/keyvalue/redis/support/collections/props.xml
new file mode 100644
index 000000000..2e49de5b7
--- /dev/null
+++ b/spring-data-redis/src/test/resources/org/springframework/data/keyvalue/redis/support/collections/props.xml
@@ -0,0 +1,6 @@
+
+Hi
+bar
+head
+island
+
\ No newline at end of file