DATAKV-58
+ add FactoryBean for creating collections on top of Redis keys + add dedicated namespace + code + integration tests
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
|
||||
import org.springframework.data.keyvalue.redis.support.collections.RedisCollectionFactoryBean;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Parser for the Redis <code><collection></code> element.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class RedisCollectionParser extends AbstractSimpleBeanDefinitionParser {
|
||||
|
||||
@Override
|
||||
protected Class<?> getBeanClass(Element element) {
|
||||
return RedisCollectionFactoryBean.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postProcess(BeanDefinitionBuilder beanDefinition, Element element) {
|
||||
String template = element.getAttribute("template");
|
||||
if (StringUtils.hasText(template)) {
|
||||
beanDefinition.addPropertyReference("template", template);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEligibleAttribute(String attributeName) {
|
||||
return super.isEligibleAttribute(attributeName) && (!"template".equals(attributeName));
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NamedNodeMap;
|
||||
|
||||
/**
|
||||
* Parser for the JMS <code><listener-container></code> element.
|
||||
* Parser for the Redis <code><listener-container></code> element.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
|
||||
@@ -28,5 +28,6 @@ class RedisNamespaceHandler extends NamespaceHandlerSupport {
|
||||
@Override
|
||||
public void init() {
|
||||
registerBeanDefinitionParser("listener-container", new RedisListenerContainerParser());
|
||||
registerBeanDefinitionParser("collection", new RedisCollectionParser());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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 org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.data.keyvalue.redis.connection.DataType;
|
||||
import org.springframework.data.keyvalue.redis.core.RedisTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Factory bean that facilitates creation of Redis-based collections. Supports list, set, zset (or sortedSet), map (or hash) and properties.
|
||||
* Will use the key type if it exists or to create a dedicated collection (Properties vs Map).
|
||||
* Otherwise uses the provided type (default is list).
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class RedisCollectionFactoryBean implements InitializingBean, BeanNameAware, FactoryBean<RedisStore> {
|
||||
|
||||
public enum CollectionType {
|
||||
LIST {
|
||||
@Override
|
||||
public DataType dataType() {
|
||||
return DataType.LIST;
|
||||
}
|
||||
},
|
||||
SET {
|
||||
@Override
|
||||
public DataType dataType() {
|
||||
return DataType.SET;
|
||||
}
|
||||
},
|
||||
ZSET {
|
||||
@Override
|
||||
public DataType dataType() {
|
||||
return DataType.ZSET;
|
||||
}
|
||||
},
|
||||
MAP {
|
||||
@Override
|
||||
public DataType dataType() {
|
||||
return DataType.HASH;
|
||||
}
|
||||
},
|
||||
PROPERTIES {
|
||||
@Override
|
||||
public DataType dataType() {
|
||||
return DataType.HASH;
|
||||
}
|
||||
};
|
||||
|
||||
abstract DataType dataType();
|
||||
}
|
||||
|
||||
|
||||
private RedisStore store;
|
||||
private CollectionType type = null;
|
||||
private RedisTemplate<String, ?> template;
|
||||
private String key;
|
||||
private String beanName;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
if (!StringUtils.hasText(key)) {
|
||||
key = beanName;
|
||||
}
|
||||
|
||||
Assert.hasText(key, "Collection key is required - no key or bean name specified");
|
||||
Assert.notNull(template, "Redis template is required");
|
||||
|
||||
DataType dt = template.type(key);
|
||||
|
||||
// can't create store
|
||||
Assert.isTrue(!DataType.STRING.equals(dt), "Cannot create store on keys of type 'string'");
|
||||
|
||||
store = createStore(dt);
|
||||
|
||||
if (store == null) {
|
||||
if (type == null) {
|
||||
type = CollectionType.LIST;
|
||||
}
|
||||
store = createStore(type.dataType());
|
||||
}
|
||||
}
|
||||
|
||||
private RedisStore createStore(DataType dt) {
|
||||
switch (dt) {
|
||||
case LIST:
|
||||
return new DefaultRedisList(key, template);
|
||||
|
||||
case SET:
|
||||
return new DefaultRedisSet(key, template);
|
||||
|
||||
case ZSET:
|
||||
return new DefaultRedisZSet(key, template);
|
||||
|
||||
case HASH:
|
||||
if (CollectionType.PROPERTIES.equals(type)) {
|
||||
return new RedisProperties(key, template);
|
||||
}
|
||||
return new DefaultRedisMap(key, template);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisStore getObject() {
|
||||
return store;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return (store != null ? store.getClass() : RedisStore.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanName(String name) {
|
||||
this.beanName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the store type. Used if the key does not exist.
|
||||
*
|
||||
* @param type The type to set.
|
||||
*/
|
||||
public void setType(CollectionType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the template used by the resulting store.
|
||||
*
|
||||
* @param template The template to set.
|
||||
*/
|
||||
public void setTemplate(RedisTemplate<String, ?> template) {
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the key of the store.
|
||||
*
|
||||
* @param key The key to set.
|
||||
*/
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.keyvalue.redis.support.collections;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
@@ -262,4 +264,14 @@ public class RedisProperties extends Properties implements RedisMap<Object, Obje
|
||||
public Object replace(Object key, Object value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void storeToXML(OutputStream os, String comment, String encoding) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void storeToXML(OutputStream os, String comment) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -149,4 +149,61 @@ listener method arguments. Default is a StringRedisSerializer.
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:element name="collection">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Factory creating collections on top of Redis keys.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:exports type="org.springframework.data.keyvalue.redis.support.collections.RedisCollectionFactoryBean"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="id" type="xsd:ID">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The name of the Redis collection.]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="key" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Redis key of the created collection. Defaults to bean id.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="template" type="xsd:string" default="redisTemplate">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A reference to a RedisTemplate bean.Default is "redisTemplate".
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.data.keyvalue.redis.core.RedisTemplate"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="type" default="LIST" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The collection type (default is list).
|
||||
If the key exists, its type takes priority. The type is used to disambiguate the collection type (map vs properties) or
|
||||
specify one in case the key is missing.]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:restriction base="xsd:string">
|
||||
<xsd:enumeration value="LIST"/>
|
||||
<xsd:enumeration value="SET"/>
|
||||
<xsd:enumeration value="ZSET"/>
|
||||
<xsd:enumeration value="MAP"/>
|
||||
<xsd:enumeration value="PROPERTIES"/>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.keyvalue.redis.ConnectionFactoryTracker;
|
||||
import org.springframework.data.keyvalue.redis.SettingsUtils;
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisConnection;
|
||||
import org.springframework.data.keyvalue.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.data.keyvalue.redis.core.RedisCallback;
|
||||
import org.springframework.data.keyvalue.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.keyvalue.redis.support.collections.RedisCollectionFactoryBean.CollectionType;
|
||||
|
||||
/**
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class RedisCollectionFactoryBeanTests {
|
||||
|
||||
protected ObjectFactory<String> factory = new StringObjectFactory();
|
||||
protected StringRedisTemplate template;
|
||||
protected RedisStore col;
|
||||
|
||||
public RedisCollectionFactoryBeanTests() {
|
||||
JedisConnectionFactory jedisConnFactory = new JedisConnectionFactory();
|
||||
jedisConnFactory.setUsePool(true);
|
||||
|
||||
jedisConnFactory.setPort(SettingsUtils.getPort());
|
||||
jedisConnFactory.setHostName(SettingsUtils.getHost());
|
||||
|
||||
jedisConnFactory.afterPropertiesSet();
|
||||
|
||||
this.template = new StringRedisTemplate(jedisConnFactory);
|
||||
ConnectionFactoryTracker.add(jedisConnFactory);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void cleanUp() {
|
||||
ConnectionFactoryTracker.cleanUp();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
// clean up the whole db
|
||||
template.execute(new RedisCallback<Object>() {
|
||||
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) {
|
||||
connection.flushDb();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private RedisStore createCollection(String key) {
|
||||
return createCollection(key, null);
|
||||
}
|
||||
|
||||
private RedisStore createCollection(String key, CollectionType type) {
|
||||
RedisCollectionFactoryBean fb = new RedisCollectionFactoryBean();
|
||||
fb.setKey(key);
|
||||
fb.setTemplate(template);
|
||||
fb.setType(type);
|
||||
fb.afterPropertiesSet();
|
||||
|
||||
return fb.getObject();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNone() throws Exception {
|
||||
RedisStore store = createCollection("nosrt", CollectionType.PROPERTIES);
|
||||
assertThat(store, instanceOf(RedisProperties.class));
|
||||
|
||||
store = createCollection("nosrt", CollectionType.MAP);
|
||||
assertThat(store, instanceOf(DefaultRedisMap.class));
|
||||
|
||||
store = createCollection("nosrt", CollectionType.SET);
|
||||
assertThat(store, instanceOf(DefaultRedisSet.class));
|
||||
|
||||
store = createCollection("nosrt", CollectionType.LIST);
|
||||
assertThat(store, instanceOf(DefaultRedisList.class));
|
||||
|
||||
store = createCollection("nosrt");
|
||||
assertThat(store, instanceOf(DefaultRedisList.class));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testExistingCol() throws Exception {
|
||||
String key = "set";
|
||||
String val = "value";
|
||||
|
||||
template.boundSetOps(key).add(val);
|
||||
RedisStore col = createCollection(key);
|
||||
assertThat(col, is(DefaultRedisSet.class));
|
||||
|
||||
key = "map";
|
||||
template.boundHashOps(key).put(val, val);
|
||||
col = createCollection(key);
|
||||
assertThat(col, is(DefaultRedisMap.class));
|
||||
|
||||
col = createCollection(key, CollectionType.PROPERTIES);
|
||||
assertThat(col, is(RedisProperties.class));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import static org.junit.Assert.*;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -128,7 +129,7 @@ public class RedisPropertiesTests extends RedisMapTests {
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
props.store(writer, "no-comment");
|
||||
System.out.println(writer.toString());
|
||||
//System.out.println(writer.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -165,7 +166,8 @@ public class RedisPropertiesTests extends RedisMapTests {
|
||||
public void testPropertiesList() throws Exception {
|
||||
defaults.setProperty("a", "b");
|
||||
props.setProperty("x", "y");
|
||||
props.list(System.out);
|
||||
StringWriter wr = new StringWriter();
|
||||
props.list(new PrintWriter(wr));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.support.GenericXmlApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class SupportXmlTests {
|
||||
|
||||
@Test
|
||||
public void testContainerSetup() throws Exception {
|
||||
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext(
|
||||
"/org/springframework/data/keyvalue/redis/support/collections/container.xml");
|
||||
|
||||
RedisList list = ctx.getBean("non-existing", RedisList.class);
|
||||
RedisProperties props = ctx.getBean("props", RedisProperties.class);
|
||||
Map map = ctx.getBean("map", Map.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xmlns:redis="http://www.springframework.org/schema/redis"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/redis http://www.springframework.org/schema/redis/spring-redis.xsd">
|
||||
|
||||
<bean id="connectionFactory" class="org.springframework.data.keyvalue.redis.connection.jedis.JedisConnectionFactory"/>
|
||||
<bean id="redisTemplate" class="org.springframework.data.keyvalue.redis.core.StringRedisTemplate" p:connectionFactory-ref="connectionFactory"/>
|
||||
|
||||
<redis:collection id="non-existing" />
|
||||
<redis:collection id="props" key="prop-key" type="PROPERTIES"/>
|
||||
<redis:collection id="map" key="map-key" type="MAP"/>
|
||||
|
||||
</beans>
|
||||
Reference in New Issue
Block a user