DATAKV-25
+ add namespace for Redis pubsub
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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 java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.data.keyvalue.redis.listener.ChannelTopic;
|
||||
import org.springframework.data.keyvalue.redis.listener.PatternTopic;
|
||||
import org.springframework.data.keyvalue.redis.listener.RedisMessageListenerContainer;
|
||||
import org.springframework.data.keyvalue.redis.listener.Topic;
|
||||
import org.springframework.data.keyvalue.redis.listener.adapter.MessageListenerAdapter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Attr;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NamedNodeMap;
|
||||
|
||||
/**
|
||||
* Parser for the JMS <code><listener-container></code> element.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class RedisListenerContainerParser extends AbstractSimpleBeanDefinitionParser {
|
||||
|
||||
@Override
|
||||
protected Class<RedisMessageListenerContainer> getBeanClass(Element element) {
|
||||
return RedisMessageListenerContainer.class;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
// parse attributes (but replace the value assignment with references)
|
||||
NamedNodeMap attributes = element.getAttributes();
|
||||
|
||||
for (int x = 0; x < attributes.getLength(); x++) {
|
||||
Attr attribute = (Attr) attributes.item(x);
|
||||
if (isEligibleAttribute(attribute, parserContext)) {
|
||||
String propertyName = extractPropertyName(attribute.getLocalName());
|
||||
Assert.state(StringUtils.hasText(propertyName),
|
||||
"Illegal property name returned from 'extractPropertyName(String)': cannot be null or empty.");
|
||||
builder.addPropertyReference(propertyName, attribute.getValue());
|
||||
}
|
||||
}
|
||||
postProcess(builder, element);
|
||||
|
||||
// parse nested listeners
|
||||
List<Element> listDefs = DomUtils.getChildElementsByTagName(element, "listener");
|
||||
|
||||
if (!listDefs.isEmpty()) {
|
||||
ManagedMap<BeanDefinition, Collection<? extends BeanDefinition>> listeners = new ManagedMap<BeanDefinition, Collection<? extends BeanDefinition>>(
|
||||
listDefs.size());
|
||||
for (Element listElement : listDefs) {
|
||||
Object[] listenerDefinition = parseListener(listElement);
|
||||
listeners.put((BeanDefinition) listenerDefinition[0],
|
||||
(Collection<? extends BeanDefinition>) listenerDefinition[1]);
|
||||
}
|
||||
|
||||
builder.addPropertyValue("messageListeners", listeners);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a listener definition. Returns the listener bean reference definition (as the array first entry) and its associated topics (also as bean definitions).
|
||||
*
|
||||
* @param element
|
||||
* @return
|
||||
*/
|
||||
private Object[] parseListener(Element element) {
|
||||
Object[] ret = new Object[2];
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MessageListenerAdapter.class);
|
||||
builder.addConstructorArgReference(element.getAttribute("ref"));
|
||||
|
||||
String method = element.getAttribute("method");
|
||||
if (StringUtils.hasText(method)){
|
||||
builder.addPropertyValue("defaultListenerMethod", method);
|
||||
}
|
||||
|
||||
String serializer = element.getAttribute("serializer");
|
||||
if (StringUtils.hasText(serializer)){
|
||||
builder.addPropertyReference("serializer", serializer);
|
||||
}
|
||||
|
||||
// assemble topics
|
||||
Collection<Topic> topics = new ArrayList<Topic>();
|
||||
|
||||
// get channels
|
||||
String channels = element.getAttribute("channel");
|
||||
if (StringUtils.hasText(channels)) {
|
||||
String[] array = StringUtils.delimitedListToStringArray(channels, " ");
|
||||
|
||||
for (String string : array) {
|
||||
topics.add(new ChannelTopic(string));
|
||||
}
|
||||
}
|
||||
|
||||
// get patterns
|
||||
String patterns = element.getAttribute("pattern");
|
||||
if (StringUtils.hasText(patterns)) {
|
||||
String[] array = StringUtils.delimitedListToStringArray(patterns, " ");
|
||||
|
||||
for (String string : array) {
|
||||
topics.add(new PatternTopic(string));
|
||||
}
|
||||
}
|
||||
|
||||
ret[0] = builder.getBeanDefinition();
|
||||
ret[1] = topics;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldGenerateId() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.xml.NamespaceHandler;
|
||||
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
||||
|
||||
/**
|
||||
* {@link NamespaceHandler} for Spring Data Redis namespace.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class RedisNamespaceHandler extends NamespaceHandlerSupport {
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
registerBeanDefinitionParser("listener-container", new RedisListenerContainerParser());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
http\://www.springframework.org/schema/redis=org.springframework.data.keyvalue.redis.config.RedisNamespaceHandler
|
||||
@@ -0,0 +1,2 @@
|
||||
http\://www.springframework.org/schema/redis/spring-redis-1.0.xsd=org/springframework/data/keyvalue/redis/config/spring-redis-1.0.xsd
|
||||
http\://www.springframework.org/schema/redis/spring-redis.xsd=org/springframework/data/keyvalue/redis/config/spring-redis-1.0.xsd
|
||||
@@ -0,0 +1,4 @@
|
||||
# Tooling related information for the jms namespace
|
||||
http\://www.springframework.org/schema/redis@name=redis Namespace
|
||||
http\://www.springframework.org/schema/redis@prefix=redis
|
||||
http\://www.springframework.org/schema/redis@icon=org/springframework/data/keyvalue/redis/config/spring-redis.gif
|
||||
@@ -0,0 +1,165 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<xsd:schema xmlns="http://www.springframework.org/schema/redis"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:tool="http://www.springframework.org/schema/tool"
|
||||
targetNamespace="http://www.springframework.org/schema/redis"
|
||||
elementFormDefault="qualified"
|
||||
attributeFormDefault="unqualified">
|
||||
|
||||
<xsd:import namespace="http://www.springframework.org/schema/tool" schemaLocation="http://www.springframework.org/schema/tool/spring-tool.xsd"/>
|
||||
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Defines the configuration elements for the Spring Data Redis support.
|
||||
Allows for configuring Redis listener containers in XML 'shortcut' style.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<xsd:element name="listener-container">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Container of Redis listeners. All listeners will be hosted by the same container.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:exports type="org.springframework.data.keyvalue.redis.listener.RedisMessageListenerContainer"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="listener" type="listenerType" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="connection-factory" type="xsd:string" default="redisConnectionFactory">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A reference to the Redis ConnectionFactory bean.
|
||||
Default is "redisConnectionFactory".
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.data.keyvalue.redis.connection.ConnectionFactory"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="task-executor" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A reference to a Spring TaskExecutor (or standard JDK 1.5 Executor) for executing
|
||||
Redis listener invokers. Default is a SimpleAsyncTaskExecutor.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="java.util.concurrent.Executor"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="subscription-task-executor" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A reference to a Spring TaskExecutor (or standard JDK 1.5 Executor) for listening
|
||||
to Redis messages. By default reuses the 'task-executor' value.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="java.util.concurrent.Executor"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="topic-serializer" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A reference to the RedisSerializer strategy for converting Redis channels/patterns to
|
||||
serialized format. Default is a StringRedisSerializer.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.data.keyvalue.redis.serializer.RedisSerializer"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="error-handler" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A reference to an ErrorHandler strategy for handling any uncaught Exceptions
|
||||
that may occur during the execution of the MessageListener.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.util.ErrorHandler"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="phase" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The lifecycle phase within which this container should start and stop. The lower
|
||||
the value the earlier this container will start and the later it will stop. The
|
||||
default is Integer.MAX_VALUE meaning the container will start as late as possible
|
||||
and stop as soon as possible.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:complexType name="listenerType">
|
||||
<xsd:attribute name="ref" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The bean name of the listener object, implementing
|
||||
the MessageListener interface or defining the specified listener method.
|
||||
Required.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref"/>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="channel" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The channel(s) to which the listener is subscribed. Multiple values can be specified
|
||||
by separating them with spaces.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="pattern" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The pattern(s) matching the channels to which the listener is subscribed. Multiple values can be specified
|
||||
by separating them with spaces.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="method" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The name of the listener method to invoke. If not specified,
|
||||
the target bean is supposed to implement the MessageListener
|
||||
interface or provide a method named 'handleMessage'.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="serializer" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A reference to the RedisSerializer strategy for converting Redis Messages to
|
||||
listener method arguments. Default is a StringRedisSerializer.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.data.keyvalue.redis.serializer.RedisSerializer"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:schema>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 581 B |
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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 static org.junit.Assert.*;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.support.GenericXmlApplicationContext;
|
||||
import org.springframework.data.keyvalue.redis.listener.RedisMessageListenerContainer;
|
||||
|
||||
/**
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class NamespaceTest {
|
||||
|
||||
private GenericXmlApplicationContext ctx;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
ctx = new GenericXmlApplicationContext("/org/springframework/data/keyvalue/redis/config/namespace.xml");
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
if (ctx != null)
|
||||
ctx.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSanityTest() throws Exception {
|
||||
RedisMessageListenerContainer container = ctx.getBean(RedisMessageListenerContainer.class);
|
||||
assertTrue(container.isRunning());
|
||||
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,11 @@ package org.springframework.data.keyvalue.redis.listener.adapter;
|
||||
*/
|
||||
public class RedisMDP {
|
||||
|
||||
public void handle(String message) {
|
||||
public void handleMessage(String message) {
|
||||
System.out.println("Received message " + message);
|
||||
}
|
||||
}
|
||||
|
||||
public void anotherHandle(String message) {
|
||||
System.out.println("[*] Received message " + message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?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:redis="http://www.springframework.org/schema/redis"
|
||||
xmlns:task="http://www.springframework.org/schema/task"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd
|
||||
http://www.springframework.org/schema/redis http://www.springframework.org/schema/redis/spring-redis.xsd">
|
||||
|
||||
<!-- the default ConnectionFactory -->
|
||||
<bean id="redisConnectionFactory" class="org.springframework.data.keyvalue.redis.connection.jedis.JedisConnectionFactory"/>
|
||||
|
||||
<task:executor id="testTaskExecutor" />
|
||||
|
||||
<redis:listener-container task-executor="testTaskExecutor">
|
||||
<!-- default handle method -->
|
||||
<redis:listener ref="testBean1" channel="z1 z2" pattern="x*"/>
|
||||
<!-- channel subscription only -->
|
||||
<redis:listener ref="testBean1" method="anotherHandle" channel="x1" serializer="serializer"/>
|
||||
</redis:listener-container>
|
||||
|
||||
<bean id="testBean1" class="org.springframework.data.keyvalue.redis.listener.adapter.RedisMDP"/>
|
||||
|
||||
<bean id="serializer" class="org.springframework.data.keyvalue.redis.serializer.StringRedisSerializer"/>
|
||||
|
||||
</beans>
|
||||
Reference in New Issue
Block a user