INT-2856: Add Management for RecipientListRouter

JIRA: https://jira.spring.io/browse/INT-2856

INT-2856:Add support for adding/removing individual recipients to the RecipientListRouter

INT-2856: allow recipient channel null on init

INT-2856: Polishing
This commit is contained in:
David Liu
2014-08-19 11:28:16 +03:00
committed by Artem Bilan
parent 59c7c0edc1
commit 84421fd91c
10 changed files with 548 additions and 78 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 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.
@@ -28,7 +28,6 @@ import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.filter.ExpressionEvaluatingSelector;
import org.springframework.integration.router.RecipientListRouter;
import org.springframework.integration.router.RecipientListRouter.Recipient;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
@@ -43,13 +42,11 @@ import org.springframework.util.xml.DomUtils;
public class RecipientListRouterParser extends AbstractRouterParser {
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
protected BeanDefinition doParseRouter(Element element, ParserContext parserContext) {
BeanDefinitionBuilder recipientListRouterBuilder = BeanDefinitionBuilder.genericBeanDefinition(RecipientListRouter.class);
BeanDefinitionBuilder recipientListRouterBuilder =
BeanDefinitionBuilder.genericBeanDefinition(RecipientListRouter.class);
List<Element> childElements = DomUtils.getChildElementsByTagName(element, "recipient");
Assert.notEmpty(childElements,
"At least one recipient channel must be defined (e.g., <recipient channel=\"channel1\"/>).");
ManagedList recipientList = new ManagedList();
ManagedList<BeanDefinition> recipientList = new ManagedList<BeanDefinition>();
for (Element childElement : childElements) {
BeanDefinitionBuilder recipientBuilder = BeanDefinitionBuilder.genericBeanDefinition(Recipient.class);
recipientBuilder.addConstructorArgReference(childElement.getAttribute("channel"));
@@ -62,7 +59,9 @@ public class RecipientListRouterParser extends AbstractRouterParser {
}
recipientList.add(recipientBuilder.getBeanDefinition());
}
recipientListRouterBuilder.addPropertyValue("recipients", recipientList);
if(recipientList.size() > 0) {
recipientListRouterBuilder.addPropertyValue("recipients", recipientList);
}
return recipientListRouterBuilder.getBeanDefinition();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2014 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.
@@ -26,21 +26,36 @@ import org.springframework.integration.handler.ExpressionEvaluatingMessageProces
/**
* A {@link MessageSelector} implementation that evaluates a SpEL expression.
* The evaluation result of the expression must be a boolean value.
*
*
* @author Mark Fisher
* @author Liujiong
* @since 2.0
*/
public class ExpressionEvaluatingSelector extends AbstractMessageProcessingSelector {
private static final ExpressionParser expressionParser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
private static final ExpressionParser expressionParser =
new SpelExpressionParser(new SpelParserConfiguration(true, true));
private final String expressionString;
public ExpressionEvaluatingSelector(String expressionString) {
super(new ExpressionEvaluatingMessageProcessor<Boolean>(expressionParser.parseExpression(expressionString), Boolean.class));
super(new ExpressionEvaluatingMessageProcessor<Boolean>(expressionParser.parseExpression(expressionString),
Boolean.class));
this.expressionString = expressionString;
}
public ExpressionEvaluatingSelector(Expression expression) {
super(new ExpressionEvaluatingMessageProcessor<Boolean>(expression, Boolean.class));
this.expressionString = expression.getExpressionString();
}
public String getExpressionString() {
return expressionString;
}
@Override
public String toString() {
return "ExpressionEvaluatingSelector for: [" + this.expressionString + "]";
}
}

View File

@@ -18,13 +18,24 @@ package org.springframework.integration.router;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.integration.core.MessageSelector;
import org.springframework.integration.filter.ExpressionEvaluatingSelector;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* <pre class="code">
@@ -55,11 +66,12 @@ import org.springframework.util.Assert;
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Artem Bilan
* @author Liujiong
*/
public class RecipientListRouter extends AbstractMessageRouter implements InitializingBean {
private volatile List<Recipient> recipients;
public class RecipientListRouter extends AbstractMessageRouter
implements InitializingBean, RecipientListRouterManagement {
private final ConcurrentLinkedQueue<Recipient> recipients = new ConcurrentLinkedQueue<Recipient>();
/**
* Set the channels for this router. Either call this method or
@@ -82,7 +94,36 @@ public class RecipientListRouter extends AbstractMessageRouter implements Initia
*/
public void setRecipients(List<Recipient> recipients) {
Assert.notEmpty(recipients, "recipients must not be empty");
this.recipients = recipients;
ConcurrentLinkedQueue<Recipient> originalRecipients = this.recipients;
this.recipients.clear();
this.recipients.addAll(recipients);
if (logger.isDebugEnabled()) {
logger.debug("Channel Recipients:" + originalRecipients + " replaced with:" + this.recipients);
}
}
/**
* Set the recipients for this router.
* @param recipientMappings, map contains channelName and expression
*/
@Override
@ManagedAttribute
public void setRecipientMappings(Map<String, String> recipientMappings) {
Assert.notEmpty(recipientMappings, "recipientMappings must not be empty");
Assert.noNullElements(recipientMappings.keySet().toArray(), "'recipientMappings' cannot have null keys.");
ConcurrentLinkedQueue<Recipient> originalRecipients = this.recipients;
this.recipients.clear();
for (Entry<String, String> next : recipientMappings.entrySet()) {
if (StringUtils.hasText(next.getValue())) {
this.addRecipient(next.getKey(), next.getValue());
}
else {
this.addRecipient(next.getKey());
}
}
if (logger.isDebugEnabled()) {
logger.debug("Channel Recipients:" + originalRecipients + " replaced with:" + this.recipients);
}
}
@Override
@@ -90,17 +131,11 @@ public class RecipientListRouter extends AbstractMessageRouter implements Initia
return "recipient-list-router";
}
@Override
public void onInit() throws Exception {
Assert.notEmpty(this.recipients, "recipient list must not be empty");
super.onInit();
}
@Override
protected Collection<MessageChannel> determineTargetChannels(Message<?> message) {
List<MessageChannel> channels = new ArrayList<MessageChannel>();
List<Recipient> recipientList = this.recipients;
for (Recipient recipient : recipientList) {
for (Recipient recipient : this.recipients) {
if (recipient.accept(message)) {
channels.add(recipient.getChannel());
}
@@ -108,6 +143,85 @@ public class RecipientListRouter extends AbstractMessageRouter implements Initia
return channels;
}
@Override
@ManagedOperation
public void addRecipient(String channelName, String selectorExpression) {
Assert.hasText(channelName, "'channelName' must not be empty.");
Assert.hasText(selectorExpression, "'selectorExpression' must not be empty.");
MessageChannel channel = this.getBeanFactory().getBean(channelName, MessageChannel.class);
ExpressionEvaluatingSelector expressionEvaluatingSelector = new ExpressionEvaluatingSelector(selectorExpression);
expressionEvaluatingSelector.setBeanFactory(this.getBeanFactory());
this.recipients.add(new Recipient(channel, expressionEvaluatingSelector));
}
@Override
@ManagedOperation
public void addRecipient(String channelName) {
Assert.hasText(channelName, "'channelName' must not be empty.");
MessageChannel channel = this.getBeanFactory().getBean(channelName, MessageChannel.class);
this.recipients.add(new Recipient(channel));
}
@Override
@ManagedOperation
public int removeRecipient(String channelName) {
int counter = 0;
MessageChannel channel = this.getBeanFactory().getBean(channelName, MessageChannel.class);
for (Iterator<Recipient> it = this.recipients.iterator(); it.hasNext(); ) {
if (it.next().getChannel() == channel) {
it.remove();
counter++;
}
}
return counter;
}
@Override
@ManagedOperation
public int removeRecipient(String channelName, String selectorExpression) {
int counter = 0;
MessageChannel targetChannel = this.getBeanFactory().getBean(channelName, MessageChannel.class);
for (Iterator<Recipient> it = this.recipients.iterator(); it.hasNext(); ) {
Recipient next = it.next();
MessageSelector selector = next.getSelector();
MessageChannel channel = next.getChannel();
if (selector instanceof ExpressionEvaluatingSelector &&
channel == targetChannel &&
((ExpressionEvaluatingSelector) selector).getExpressionString().equals(selectorExpression)) {
it.remove();
counter++;
}
}
return counter;
}
@Override
@ManagedAttribute
public Collection<Recipient> getRecipients() {
return Collections.unmodifiableCollection(this.recipients);
}
@Override
@ManagedOperation
public void replaceRecipients(Properties recipientMappings) {
Assert.notEmpty(recipientMappings, "'recipientMappings' must not be empty");
Set<String> keys = recipientMappings.stringPropertyNames();
ConcurrentLinkedQueue<Recipient> originalRecipients = this.recipients;
this.recipients.clear();
for (String key : keys) {
Assert.notNull(key, "channelName can't be null.");
if (StringUtils.hasText(recipientMappings.getProperty(key))) {
this.addRecipient(key, recipientMappings.getProperty(key));
}
else {
this.addRecipient(key);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Channel Recipients:" + originalRecipients + " replaced with:" + this.recipients);
}
}
public static class Recipient {
@@ -125,6 +239,9 @@ public class RecipientListRouter extends AbstractMessageRouter implements Initia
this.selector = selector;
}
private MessageSelector getSelector() {
return selector;
}
public MessageChannel getChannel() {
return this.channel;
@@ -133,6 +250,7 @@ public class RecipientListRouter extends AbstractMessageRouter implements Initia
public boolean accept(Message<?> message) {
return (this.selector == null || this.selector.accept(message));
}
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright 2014 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.integration.router;
import java.util.Collection;
import java.util.Map;
import java.util.Properties;
import org.springframework.integration.router.RecipientListRouter.Recipient;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
/**
* Exposes adding/removing individual recipients operations for
* RecipientListRouter. This can be used with a control-bus and JMX.
*
* @author Liujiong
* @since 4.1
*
*/
@ManagedResource
public interface RecipientListRouterManagement {
/**
* Add a recipient with channelName and expression.
* @param channelName The channel name.
* @param selectorExpression The expression to filter the incoming message.
*/
@ManagedOperation
void addRecipient(String channelName, String selectorExpression);
/**
* Add a recipient with channelName.
* @param channelName The channel name.
*/
@ManagedOperation
void addRecipient(String channelName);
/**
* Remove all recipients that match the channelName.
* @param channelName The channel name.
*/
@ManagedOperation
int removeRecipient(String channelName);
/**
* Remove all recipients that match the channelName and expression.
* @param channelName The channel name.
* @param selectorExpression The expression to filter the incoming message
*/
@ManagedOperation
int removeRecipient(String channelName, String selectorExpression);
/**
* @return an unmodifiable collection of recipients.
*/
@ManagedAttribute
Collection<Recipient> getRecipients();
/**
* Replace recipient.
* @param recipientMappings contain channelName and expression.
*/
@ManagedOperation
void replaceRecipients(Properties recipientMappings);
/**
* Set recipients.
* @param recipientMappings contain channelName and expression.
*/
@ManagedAttribute
void setRecipientMappings(Map<String, String> recipientMappings);
}

View File

@@ -1685,7 +1685,15 @@
<xsd:element name="resequencer" type="resequencer-type"/>
<xsd:element name="router" type="routerTypeChain"/>
<xsd:element name="payload-type-router" type="payloadTypeRouterTypeChain"/>
<xsd:element name="recipient-list-router" type="recipientListRouterTypeChain"/>
<xsd:element name="recipient-list-router">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="abstractRouterType">
<xsd:group ref="commonRecipientListRouterGroup"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="exception-type-router" type="exceptionTypeRouterTypeChain"/>
<xsd:element name="header-value-router" type="headerValueRouterTypeChain"/>
<xsd:element name="delayer" type="delayer-type"/>
@@ -2835,7 +2843,7 @@
<xsd:complexContent>
<xsd:extension base="commonRouterType">
<xsd:sequence>
<xsd:element ref="poller" minOccurs="0" maxOccurs="1"/>
<xsd:element ref="poller" minOccurs="0" maxOccurs="1"/>
<xsd:group ref="routerCommonGroup" />
</xsd:sequence>
<xsd:attributeGroup ref="topLevelRouterAttributeGroup"/>
@@ -2943,61 +2951,40 @@
<!-- Type definitions used by the Recipient List Router -->
<xsd:complexType name="commonRecipientListRouterType" abstract="true">
<xsd:complexContent>
<xsd:extension base="abstractRouterType">
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:group name="commonRecipientListRouterGroup">
<xsd:sequence>
<xsd:element name="recipient" type="recipientSelectorExpressionChannelType" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>
An expression to be evaluated to determine if this recipient
should be included in the recipient list for a given input
Message. The evaluation result of the expression must be a boolean.
If this attribute is not defined, the channel will always be
among the list of recipients.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:group>
<xsd:complexType name="recipientListRouterType">
<xsd:complexContent>
<xsd:extension base="commonRecipientListRouterType">
<xsd:sequence>
<xsd:element ref="poller" minOccurs="0" maxOccurs="1"/>
<xsd:element name="recipient" type="recipientSelectorExpressionChannelType" minOccurs="1" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>
An expression to be evaluated to determine if this recipient
should be included in the recipient list for a given input
Message. The evaluation result of the expression must be a boolean.
If this attribute is not defined, the channel will always be
among the list of recipients.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attributeGroup ref="topLevelRouterAttributeGroup"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="recipientListRouterType">
<xsd:complexContent>
<xsd:extension base="abstractRouterType">
<xsd:sequence>
<xsd:element ref="poller" minOccurs="0" maxOccurs="1"/>
<xsd:group ref="commonRecipientListRouterGroup"/>
</xsd:sequence>
<xsd:attributeGroup ref="topLevelRouterAttributeGroup"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="recipientListRouterTypeChain">
<xsd:complexContent>
<xsd:extension base="commonRecipientListRouterType">
<xsd:sequence>
<xsd:element name="recipient" type="recipientSelectorExpressionChannelType" minOccurs="1" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>
An expression to be evaluated to determine if this recipient
should be included in the recipient list for a given input
Message. The evaluation result of the expression must be a boolean.
If this attribute is not defined, the channel will always be
among the list of recipients.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<!-- Type definitions used by the Payload Type Router -->
<xsd:complexType name="commonPayloadTypeRouterType" abstract="true">
<xsd:complexContent>
<xsd:extension base="abstractRouterType">
</xsd:extension>
<xsd:extension base="abstractRouterType"/>
</xsd:complexContent>
</xsd:complexType>

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<channel id="output">
<queue/>
</channel>
<control-bus input-channel="input" output-channel="output" send-timeout="100" order="1" auto-startup="false"/>
<recipient-list-router id="simpleRouter" input-channel="routingChannelA"/>
<channel id="channel1">
<queue capacity="1" />
</channel>
<channel id="channel2">
<queue capacity="1" />
</channel>
<channel id="channel3">
<queue capacity="1" />
</channel>
<channel id="channel4">
<queue capacity="1" />
</channel>
<channel id="channel5">
<queue capacity="1" />
</channel>
<channel id="channel6">
<queue capacity="1" />
</channel>
<channel id="channel7">
<queue capacity="1" />
</channel>
</beans:beans>

View File

@@ -0,0 +1,166 @@
/*
* Copyright 2002-2014 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.integration.config.xml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.router.RecipientListRouter.Recipient;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Liujiong
* @since 4.1
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class ControlBusRecipientListRouterTests {
@Autowired
private MessageChannel input;
@Autowired
private PollableChannel output;
@Autowired
private ConfigurableApplicationContext context;
@Autowired
@Qualifier("routingChannelA")
private MessageChannel channel;
@Before
public void aa(){
context.start();
}
@Test
public void testAddRecipient() {
MessagingTemplate messagingTemplate = new MessagingTemplate();
messagingTemplate.setReceiveTimeout(1000);
messagingTemplate.convertAndSend(input, "@'simpleRouter.handler'.addRecipient('channel2','true')");
Message<?> message = new GenericMessage<Integer>(1);
channel.send(message);
PollableChannel chanel2 = (PollableChannel) context.getBean("channel2");
assertTrue(chanel2.receive(0).getPayload().equals(1));
}
@Test
public void testAddRecipientWithNullExpression() {
MessagingTemplate messagingTemplate = new MessagingTemplate();
messagingTemplate.setReceiveTimeout(1000);
messagingTemplate.convertAndSend(input, "@'simpleRouter.handler'.addRecipient('channel3')");
Message<?> message = new GenericMessage<Integer>(1);
channel.send(message);
PollableChannel chanel3 = (PollableChannel) context.getBean("channel3");
assertTrue(chanel3.receive(0).getPayload().equals(1));
}
@Test
public void testRemoveRecipient() {
MessagingTemplate messagingTemplate = new MessagingTemplate();
messagingTemplate.setReceiveTimeout(1000);
messagingTemplate.convertAndSend(input, "@'simpleRouter.handler'.addRecipient('channel1')");
messagingTemplate.convertAndSend(input, "@'simpleRouter.handler'.addRecipient('channel4')");
messagingTemplate.convertAndSend(input, "@'simpleRouter.handler'.removeRecipient('channel4')");
Message<?> message = new GenericMessage<Integer>(1);
channel.send(message);
PollableChannel chanel1 = (PollableChannel) context.getBean("channel1");
PollableChannel chanel4 = (PollableChannel) context.getBean("channel4");
assertTrue(chanel1.receive(0).getPayload().equals(1));
assertNull(chanel4.receive(0));
}
@Test
public void testRemoveRecipientWithExpression() {
MessagingTemplate messagingTemplate = new MessagingTemplate();
messagingTemplate.setReceiveTimeout(1000);
messagingTemplate.convertAndSend(input, "@'simpleRouter.handler'.addRecipient('channel1','true')");
messagingTemplate.convertAndSend(input, "@'simpleRouter.handler'.addRecipient('channel5','true')");
messagingTemplate.convertAndSend(input, "@'simpleRouter.handler'.removeRecipient('channel5','true')");
Message<?> message = new GenericMessage<Integer>(1);
channel.send(message);
PollableChannel chanel1 = (PollableChannel) context.getBean("channel1");
PollableChannel chanel5 = (PollableChannel) context.getBean("channel5");
assertTrue(chanel1.receive(0).getPayload().equals(1));
assertNull(chanel5.receive(0));
}
@Test
@SuppressWarnings("unchecked")
public void testGetRecipients() {
MessagingTemplate messagingTemplate = new MessagingTemplate();
messagingTemplate.setReceiveTimeout(1000);
messagingTemplate.convertAndSend(input, "@'simpleRouter.handler'.addRecipient('channel1')");
messagingTemplate.convertAndSend(input, "@'simpleRouter.handler'.getRecipients()");
PollableChannel chanel1 = (PollableChannel) context.getBean("channel1");
Message<?> result = this.output.receive(0);
Collection<Recipient> mappings = (Collection<Recipient>) result.getPayload();
assertEquals(context.getBean("channel1"), mappings.iterator().next().getChannel());
}
@Test
public void testSetRecipients() {
MessagingTemplate messagingTemplate = new MessagingTemplate();
messagingTemplate.setReceiveTimeout(1000);
Map<String,String> map = new HashMap<String,String>();map.put("channel6","true");
Message<?> message = MessageBuilder.withPayload("@'simpleRouter.handler'.setRecipientMappings(headers.recipientMap)").setHeader("recipientMap", map).build();
this.input.send(message);
message = new GenericMessage<Integer>(1);
channel.send(message);
PollableChannel chanel6 = (PollableChannel) context.getBean("channel6");
assertTrue(chanel6.receive(0).getPayload().equals(1));
}
@Test
public void testReplaceRecipients() {
MessagingTemplate messagingTemplate = new MessagingTemplate();
messagingTemplate.setReceiveTimeout(1000);
messagingTemplate.convertAndSend(input, "@'simpleRouter.handler'.replaceRecipients('channel7=true')");
Message<?> message = new GenericMessage<Integer>(1);
channel.send(message);
PollableChannel chanel7 = (PollableChannel) context.getBean("channel7");
assertTrue(chanel7.receive(0).getPayload().equals(1));
}
}

View File

@@ -26,6 +26,7 @@ import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.junit.Test;
import org.mockito.Mockito;
@@ -62,11 +63,11 @@ public class RecipientListRouterTests {
router.setChannels(channels);
router.setBeanFactory(mock(BeanFactory.class));
router.afterPropertiesSet();
List<Recipient> recipients = (List<Recipient>)
ConcurrentLinkedQueue<Recipient> recipients = (ConcurrentLinkedQueue<Recipient>)
new DirectFieldAccessor(router).getPropertyValue("recipients");
assertEquals(2, recipients.size());
assertEquals(channel1, new DirectFieldAccessor(recipients.get(0)).getPropertyValue("channel"));
assertEquals(channel2, new DirectFieldAccessor(recipients.get(1)).getPropertyValue("channel"));
assertEquals(channel1, new DirectFieldAccessor(recipients.poll()).getPropertyValue("channel"));
assertEquals(channel2, new DirectFieldAccessor(recipients.poll()).getPropertyValue("channel"));
}
@Test
@@ -359,11 +360,16 @@ public class RecipientListRouterTests {
router.setChannels(channels);
}
@Test(expected = IllegalArgumentException.class)
public void noChannelListFailsInitialization() {
@Test
public void noChannelListPassInitialization() {
RecipientListRouter router = new RecipientListRouter();
router.setBeanFactory(mock(BeanFactory.class));
QueueChannel defaultOutputChannel = new QueueChannel();
router.setDefaultOutputChannel(defaultOutputChannel);
router.afterPropertiesSet();
router.handleMessage(new GenericMessage<String>("foo"));
Message<?> receive = defaultOutputChannel.receive(1000);
assertNotNull(receive);
}
@Test

View File

@@ -559,6 +559,42 @@
</para>
</section>
<section id="recipient-list-router-management">
<title>RecipientListRouterManagement</title>
<para>
Starting with <emphasis>version 4.1</emphasis>, the <classname>RecipientListRouter</classname> provides
several operation to manipulate with <emphasis>recipients</emphasis> dynamically at runtime. These
management operations are presented by <interfacename>RecipientListRouterManagement</interfacename>
<classname>@ManagedResource</classname>. They are available using <xref linkend="control-bus"/>
as well as via JMX:
<programlisting
language="xml"><![CDATA[<control-bus input-channel="controlBus"/>
<recipient-list-router id="simpleRouter" input-channel="routingChannelA">
<recipient channel="channel1"/>
</recipient-list-router>
<channel id="channel2"/>]]></programlisting>
<programlisting language="java"><![CDATA[
messagingTemplate.convertAndSend(controlBus,
"@'simpleRouter.handler'.addRecipient('channel2')");
]]></programlisting>
From the application start up the <code>simpleRouter</code> will have only one <code>channel1</code>
recipient. But after the <code>addRecipient</code> command above the new <code>channel2</code> recipient
will be added. It is a "registering an interest in something that is part of the Message" use case, when
we may be interested in messages from the <emphasis>router</emphasis> at some time period, so we are
<emphasis>subscribing</emphasis> to the the <code>recipient-list-router</code> and in some point decide to
<emphasis>unsubscribe</emphasis> our interest.
</para>
<para>
Having the runtime management operation for the <code>&lt;recipient-list-router&gt;</code>, it can be configured
without any <code>&lt;recipient&gt;</code> from the start. In this case the behaviour of
<classname>RecipientListRouter</classname> is the same, when there is no one matching recipient for the
message: if <code>defaultOutputChannel</code> is configured, the message will be sent there, otherwise
the <classname>MessageDeliveryException</classname> is thrown.
</para>
</section>
<section id="router-implementations-xpath-router">
<title>XPath Router</title>
<para>The XPath Router is part of the XML Module. As such, please read chapter

View File

@@ -136,5 +136,15 @@
See <xref linkend="jmx-shutdown"/> for more information.
</para>
</section>
<section id="4.1-recipientListRouter">
<title>Management for RecipientListRouter</title>
<para>
The <classname>RecipientListRouter</classname> provides now several <emphasis>management</emphasis>
operations to configure <emphasis>recipients</emphasis> at runtime.
With that the <code>&lt;recipient-list-router&gt;</code> can now be configured without any
<code>&lt;recipient&gt;</code> from the start.
See <xref linkend="recipient-list-router-management"/> for more information.
</para>
</section>
</section>
</chapter>