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:
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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 + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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><recipient-list-router></code>, it can be configured
|
||||
without any <code><recipient></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
|
||||
|
||||
@@ -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><recipient-list-router></code> can now be configured without any
|
||||
<code><recipient></code> from the start.
|
||||
See <xref linkend="recipient-list-router-management"/> for more information.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
Reference in New Issue
Block a user