Skip to content

Commit 84421fd

Browse files
liujiong1982artembilan
authored andcommitted
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
1 parent 59c7c0e commit 84421fd

File tree

10 files changed

+548
-78
lines changed

10 files changed

+548
-78
lines changed

spring-integration-core/src/main/java/org/springframework/integration/config/xml/RecipientListRouterParser.java

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -28,7 +28,6 @@
2828
import org.springframework.integration.filter.ExpressionEvaluatingSelector;
2929
import org.springframework.integration.router.RecipientListRouter;
3030
import org.springframework.integration.router.RecipientListRouter.Recipient;
31-
import org.springframework.util.Assert;
3231
import org.springframework.util.StringUtils;
3332
import org.springframework.util.xml.DomUtils;
3433

@@ -43,13 +42,11 @@
4342
public class RecipientListRouterParser extends AbstractRouterParser {
4443

4544
@Override
46-
@SuppressWarnings({ "unchecked", "rawtypes" })
4745
protected BeanDefinition doParseRouter(Element element, ParserContext parserContext) {
48-
BeanDefinitionBuilder recipientListRouterBuilder = BeanDefinitionBuilder.genericBeanDefinition(RecipientListRouter.class);
46+
BeanDefinitionBuilder recipientListRouterBuilder =
47+
BeanDefinitionBuilder.genericBeanDefinition(RecipientListRouter.class);
4948
List<Element> childElements = DomUtils.getChildElementsByTagName(element, "recipient");
50-
Assert.notEmpty(childElements,
51-
"At least one recipient channel must be defined (e.g., <recipient channel=\"channel1\"/>).");
52-
ManagedList recipientList = new ManagedList();
49+
ManagedList<BeanDefinition> recipientList = new ManagedList<BeanDefinition>();
5350
for (Element childElement : childElements) {
5451
BeanDefinitionBuilder recipientBuilder = BeanDefinitionBuilder.genericBeanDefinition(Recipient.class);
5552
recipientBuilder.addConstructorArgReference(childElement.getAttribute("channel"));
@@ -62,7 +59,9 @@ protected BeanDefinition doParseRouter(Element element, ParserContext parserCont
6259
}
6360
recipientList.add(recipientBuilder.getBeanDefinition());
6461
}
65-
recipientListRouterBuilder.addPropertyValue("recipients", recipientList);
62+
if(recipientList.size() > 0) {
63+
recipientListRouterBuilder.addPropertyValue("recipients", recipientList);
64+
}
6665
return recipientListRouterBuilder.getBeanDefinition();
6766
}
6867

spring-integration-core/src/main/java/org/springframework/integration/filter/ExpressionEvaluatingSelector.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2010 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,21 +26,36 @@
2626
/**
2727
* A {@link MessageSelector} implementation that evaluates a SpEL expression.
2828
* The evaluation result of the expression must be a boolean value.
29-
*
29+
*
3030
* @author Mark Fisher
31+
* @author Liujiong
3132
* @since 2.0
3233
*/
3334
public class ExpressionEvaluatingSelector extends AbstractMessageProcessingSelector {
3435

35-
private static final ExpressionParser expressionParser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
36+
private static final ExpressionParser expressionParser =
37+
new SpelExpressionParser(new SpelParserConfiguration(true, true));
3638

39+
private final String expressionString;
3740

3841
public ExpressionEvaluatingSelector(String expressionString) {
39-
super(new ExpressionEvaluatingMessageProcessor<Boolean>(expressionParser.parseExpression(expressionString), Boolean.class));
42+
super(new ExpressionEvaluatingMessageProcessor<Boolean>(expressionParser.parseExpression(expressionString),
43+
Boolean.class));
44+
this.expressionString = expressionString;
4045
}
4146

4247
public ExpressionEvaluatingSelector(Expression expression) {
4348
super(new ExpressionEvaluatingMessageProcessor<Boolean>(expression, Boolean.class));
49+
this.expressionString = expression.getExpressionString();
50+
}
51+
52+
public String getExpressionString() {
53+
return expressionString;
54+
}
55+
56+
@Override
57+
public String toString() {
58+
return "ExpressionEvaluatingSelector for: [" + this.expressionString + "]";
4459
}
4560

4661
}

spring-integration-core/src/main/java/org/springframework/integration/router/RecipientListRouter.java

Lines changed: 129 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,24 @@
1818

1919
import java.util.ArrayList;
2020
import java.util.Collection;
21+
import java.util.Collections;
22+
import java.util.Iterator;
2123
import java.util.List;
24+
import java.util.Map;
25+
import java.util.Map.Entry;
26+
import java.util.Properties;
27+
import java.util.Set;
28+
import java.util.concurrent.ConcurrentLinkedQueue;
2229

2330
import org.springframework.beans.factory.InitializingBean;
2431
import org.springframework.integration.core.MessageSelector;
32+
import org.springframework.integration.filter.ExpressionEvaluatingSelector;
33+
import org.springframework.jmx.export.annotation.ManagedAttribute;
34+
import org.springframework.jmx.export.annotation.ManagedOperation;
2535
import org.springframework.messaging.Message;
2636
import org.springframework.messaging.MessageChannel;
2737
import org.springframework.util.Assert;
38+
import org.springframework.util.StringUtils;
2839

2940
/**
3041
* <pre class="code">
@@ -55,11 +66,12 @@
5566
* @author Mark Fisher
5667
* @author Oleg Zhurakousky
5768
* @author Artem Bilan
69+
* @author Liujiong
5870
*/
59-
public class RecipientListRouter extends AbstractMessageRouter implements InitializingBean {
60-
61-
private volatile List<Recipient> recipients;
71+
public class RecipientListRouter extends AbstractMessageRouter
72+
implements InitializingBean, RecipientListRouterManagement {
6273

74+
private final ConcurrentLinkedQueue<Recipient> recipients = new ConcurrentLinkedQueue<Recipient>();
6375

6476
/**
6577
* Set the channels for this router. Either call this method or
@@ -82,32 +94,134 @@ public void setChannels(List<MessageChannel> channels) {
8294
*/
8395
public void setRecipients(List<Recipient> recipients) {
8496
Assert.notEmpty(recipients, "recipients must not be empty");
85-
this.recipients = recipients;
97+
ConcurrentLinkedQueue<Recipient> originalRecipients = this.recipients;
98+
this.recipients.clear();
99+
this.recipients.addAll(recipients);
100+
if (logger.isDebugEnabled()) {
101+
logger.debug("Channel Recipients:" + originalRecipients + " replaced with:" + this.recipients);
102+
}
86103
}
87104

105+
/**
106+
* Set the recipients for this router.
107+
* @param recipientMappings, map contains channelName and expression
108+
*/
88109
@Override
89-
public String getComponentType() {
90-
return "recipient-list-router";
110+
@ManagedAttribute
111+
public void setRecipientMappings(Map<String, String> recipientMappings) {
112+
Assert.notEmpty(recipientMappings, "recipientMappings must not be empty");
113+
Assert.noNullElements(recipientMappings.keySet().toArray(), "'recipientMappings' cannot have null keys.");
114+
ConcurrentLinkedQueue<Recipient> originalRecipients = this.recipients;
115+
this.recipients.clear();
116+
for (Entry<String, String> next : recipientMappings.entrySet()) {
117+
if (StringUtils.hasText(next.getValue())) {
118+
this.addRecipient(next.getKey(), next.getValue());
119+
}
120+
else {
121+
this.addRecipient(next.getKey());
122+
}
123+
}
124+
if (logger.isDebugEnabled()) {
125+
logger.debug("Channel Recipients:" + originalRecipients + " replaced with:" + this.recipients);
126+
}
91127
}
92128

93129
@Override
94-
public void onInit() throws Exception {
95-
Assert.notEmpty(this.recipients, "recipient list must not be empty");
96-
super.onInit();
130+
public String getComponentType() {
131+
return "recipient-list-router";
97132
}
98133

134+
99135
@Override
100136
protected Collection<MessageChannel> determineTargetChannels(Message<?> message) {
101137
List<MessageChannel> channels = new ArrayList<MessageChannel>();
102-
List<Recipient> recipientList = this.recipients;
103-
for (Recipient recipient : recipientList) {
138+
for (Recipient recipient : this.recipients) {
104139
if (recipient.accept(message)) {
105140
channels.add(recipient.getChannel());
106141
}
107142
}
108143
return channels;
109144
}
110145

146+
@Override
147+
@ManagedOperation
148+
public void addRecipient(String channelName, String selectorExpression) {
149+
Assert.hasText(channelName, "'channelName' must not be empty.");
150+
Assert.hasText(selectorExpression, "'selectorExpression' must not be empty.");
151+
MessageChannel channel = this.getBeanFactory().getBean(channelName, MessageChannel.class);
152+
ExpressionEvaluatingSelector expressionEvaluatingSelector = new ExpressionEvaluatingSelector(selectorExpression);
153+
expressionEvaluatingSelector.setBeanFactory(this.getBeanFactory());
154+
this.recipients.add(new Recipient(channel, expressionEvaluatingSelector));
155+
}
156+
157+
@Override
158+
@ManagedOperation
159+
public void addRecipient(String channelName) {
160+
Assert.hasText(channelName, "'channelName' must not be empty.");
161+
MessageChannel channel = this.getBeanFactory().getBean(channelName, MessageChannel.class);
162+
this.recipients.add(new Recipient(channel));
163+
}
164+
165+
@Override
166+
@ManagedOperation
167+
public int removeRecipient(String channelName) {
168+
int counter = 0;
169+
MessageChannel channel = this.getBeanFactory().getBean(channelName, MessageChannel.class);
170+
for (Iterator<Recipient> it = this.recipients.iterator(); it.hasNext(); ) {
171+
if (it.next().getChannel() == channel) {
172+
it.remove();
173+
counter++;
174+
}
175+
}
176+
return counter;
177+
}
178+
179+
@Override
180+
@ManagedOperation
181+
public int removeRecipient(String channelName, String selectorExpression) {
182+
int counter = 0;
183+
MessageChannel targetChannel = this.getBeanFactory().getBean(channelName, MessageChannel.class);
184+
for (Iterator<Recipient> it = this.recipients.iterator(); it.hasNext(); ) {
185+
Recipient next = it.next();
186+
MessageSelector selector = next.getSelector();
187+
MessageChannel channel = next.getChannel();
188+
if (selector instanceof ExpressionEvaluatingSelector &&
189+
channel == targetChannel &&
190+
((ExpressionEvaluatingSelector) selector).getExpressionString().equals(selectorExpression)) {
191+
it.remove();
192+
counter++;
193+
}
194+
}
195+
return counter;
196+
}
197+
198+
@Override
199+
@ManagedAttribute
200+
public Collection<Recipient> getRecipients() {
201+
return Collections.unmodifiableCollection(this.recipients);
202+
}
203+
204+
@Override
205+
@ManagedOperation
206+
public void replaceRecipients(Properties recipientMappings) {
207+
Assert.notEmpty(recipientMappings, "'recipientMappings' must not be empty");
208+
Set<String> keys = recipientMappings.stringPropertyNames();
209+
ConcurrentLinkedQueue<Recipient> originalRecipients = this.recipients;
210+
this.recipients.clear();
211+
for (String key : keys) {
212+
Assert.notNull(key, "channelName can't be null.");
213+
if (StringUtils.hasText(recipientMappings.getProperty(key))) {
214+
this.addRecipient(key, recipientMappings.getProperty(key));
215+
}
216+
else {
217+
this.addRecipient(key);
218+
}
219+
}
220+
if (logger.isDebugEnabled()) {
221+
logger.debug("Channel Recipients:" + originalRecipients + " replaced with:" + this.recipients);
222+
}
223+
}
224+
111225

112226
public static class Recipient {
113227

@@ -125,6 +239,9 @@ public Recipient(MessageChannel channel, MessageSelector selector) {
125239
this.selector = selector;
126240
}
127241

242+
private MessageSelector getSelector() {
243+
return selector;
244+
}
128245

129246
public MessageChannel getChannel() {
130247
return this.channel;
@@ -133,6 +250,7 @@ public MessageChannel getChannel() {
133250
public boolean accept(Message<?> message) {
134251
return (this.selector == null || this.selector.accept(message));
135252
}
253+
136254
}
137255

138256
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright 2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.integration.router;
17+
18+
import java.util.Collection;
19+
import java.util.Map;
20+
import java.util.Properties;
21+
22+
import org.springframework.integration.router.RecipientListRouter.Recipient;
23+
import org.springframework.jmx.export.annotation.ManagedAttribute;
24+
import org.springframework.jmx.export.annotation.ManagedOperation;
25+
import org.springframework.jmx.export.annotation.ManagedResource;
26+
27+
/**
28+
* Exposes adding/removing individual recipients operations for
29+
* RecipientListRouter. This can be used with a control-bus and JMX.
30+
*
31+
* @author Liujiong
32+
* @since 4.1
33+
*
34+
*/
35+
@ManagedResource
36+
public interface RecipientListRouterManagement {
37+
38+
/**
39+
* Add a recipient with channelName and expression.
40+
* @param channelName The channel name.
41+
* @param selectorExpression The expression to filter the incoming message.
42+
*/
43+
@ManagedOperation
44+
void addRecipient(String channelName, String selectorExpression);
45+
46+
/**
47+
* Add a recipient with channelName.
48+
* @param channelName The channel name.
49+
*/
50+
@ManagedOperation
51+
void addRecipient(String channelName);
52+
53+
/**
54+
* Remove all recipients that match the channelName.
55+
* @param channelName The channel name.
56+
*/
57+
@ManagedOperation
58+
int removeRecipient(String channelName);
59+
60+
/**
61+
* Remove all recipients that match the channelName and expression.
62+
* @param channelName The channel name.
63+
* @param selectorExpression The expression to filter the incoming message
64+
*/
65+
@ManagedOperation
66+
int removeRecipient(String channelName, String selectorExpression);
67+
68+
/**
69+
* @return an unmodifiable collection of recipients.
70+
*/
71+
@ManagedAttribute
72+
Collection<Recipient> getRecipients();
73+
74+
/**
75+
* Replace recipient.
76+
* @param recipientMappings contain channelName and expression.
77+
*/
78+
@ManagedOperation
79+
void replaceRecipients(Properties recipientMappings);
80+
81+
/**
82+
* Set recipients.
83+
* @param recipientMappings contain channelName and expression.
84+
*/
85+
@ManagedAttribute
86+
void setRecipientMappings(Map<String, String> recipientMappings);
87+
88+
}

0 commit comments

Comments
 (0)