IN PROGRESS - issue INT-574: Chain element should support aggregators mid chain as well as handlers extending AbstractReplyProducingMessageHandler
http://jira.springframework.org/browse/INT-574 All MessageHandlers with a setOutputChannel will now be accepted in the chain, the existing functionality has remained identical.
This commit is contained in:
@@ -18,11 +18,14 @@ package org.springframework.integration.handler;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.BeanWrapperImpl;
|
||||
import org.springframework.beans.PropertyAccessor;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.context.IntegrationObjectSupport;
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.filter.MessageFilter;
|
||||
import org.springframework.integration.message.MessageHandler;
|
||||
import org.springframework.integration.message.MessageHandlingException;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -30,11 +33,37 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* A composite {@link MessageHandler} implementation that invokes a chain of
|
||||
* MessageHandler instances in order.
|
||||
* <p/>
|
||||
* Each of the handlers has to implement
|
||||
* <code>public void setOutputChannel(MessageChannel outputChannel);</code>. An
|
||||
* exception is made for the last handler in the case that the chain itself does
|
||||
* not have an output channel. No other assumptions about the type of handler
|
||||
* are made.
|
||||
* <p/>
|
||||
* It is expected that each handler will produce reply messages and send them to
|
||||
* its output channel, although this is not enforced. It is possible to filter
|
||||
* messages in the middle of the chain, for example using a
|
||||
* {@link MessageFilter}.
|
||||
* <p/>
|
||||
* This component can be used from the namespace to improve the readability of
|
||||
* the configuration by removing channels that can be created implicitly.
|
||||
* <p/>
|
||||
* <pre>
|
||||
* <chain>
|
||||
* <filter ref="someFilter"/>
|
||||
* <bean class="SomeMessageHandlerImplementation"/>
|
||||
* <transformer ref="someTransformer"/>
|
||||
* <aggregator ... />
|
||||
* </chain>
|
||||
* </pre>
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Iwein Fuld
|
||||
*/
|
||||
public class MessageHandlerChain extends IntegrationObjectSupport implements MessageHandler {
|
||||
|
||||
private static final String OUTPUT_CHANNEL_PROPERTY = "outputChannel";
|
||||
|
||||
private volatile List<MessageHandler> handlers;
|
||||
|
||||
private volatile MessageChannel outputChannel;
|
||||
@@ -43,7 +72,6 @@ public class MessageHandlerChain extends IntegrationObjectSupport implements Mes
|
||||
|
||||
private final Object initializationMonitor = new Object();
|
||||
|
||||
|
||||
public void setHandlers(List<MessageHandler> handlers) {
|
||||
this.handlers = handlers;
|
||||
}
|
||||
@@ -76,32 +104,32 @@ public class MessageHandlerChain extends IntegrationObjectSupport implements Mes
|
||||
boolean first = (i == 0);
|
||||
boolean last = (i == handlers.size() - 1);
|
||||
MessageHandler handler = handlers.get(i);
|
||||
PropertyAccessor accessor = new BeanWrapperImpl(handler);
|
||||
if (!first) {
|
||||
EventDrivenConsumer consumer = new EventDrivenConsumer(channel, handler);
|
||||
consumer.start();
|
||||
}
|
||||
if (!last) {
|
||||
Assert.isInstanceOf(AbstractReplyProducingMessageHandler.class, handler,
|
||||
"All handlers except for the last one in the chain must implement "
|
||||
+ AbstractReplyProducingMessageHandler.class.getName());
|
||||
channel = new DirectChannel();
|
||||
channel.setBeanName("_" + this + ".channel#" + i);
|
||||
((AbstractReplyProducingMessageHandler) handler).setOutputChannel(channel);
|
||||
Assert.notNull(accessor.getPropertyType(OUTPUT_CHANNEL_PROPERTY),
|
||||
"All handlers except for the last one in the chain must implement property '"
|
||||
+ OUTPUT_CHANNEL_PROPERTY + "' of type 'MessageChannel'");
|
||||
accessor.setPropertyValue(OUTPUT_CHANNEL_PROPERTY, channel);
|
||||
}
|
||||
else if (handler instanceof AbstractReplyProducingMessageHandler) {
|
||||
else if (accessor.getPropertyType(OUTPUT_CHANNEL_PROPERTY) != null) {
|
||||
MessageChannel replyChannel = (this.outputChannel != null) ? this.outputChannel
|
||||
: new ReplyForwardingMessageChannel();
|
||||
((AbstractReplyProducingMessageHandler) handler).setOutputChannel(replyChannel);
|
||||
accessor.setPropertyValue(OUTPUT_CHANNEL_PROPERTY, replyChannel);
|
||||
}
|
||||
else {
|
||||
Assert.isNull(this.outputChannel,
|
||||
"An output channel was provided, but the final handler in the chain is not an "
|
||||
+ "instance of [" + AbstractReplyProducingMessageHandler.class.getName() + "]");
|
||||
"An output channel was provided, but the final handler in the chain does not implement property '"
|
||||
+ OUTPUT_CHANNEL_PROPERTY + "' of type 'MessageChannel'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class ReplyForwardingMessageChannel implements MessageChannel {
|
||||
|
||||
public String getName() {
|
||||
@@ -126,10 +154,10 @@ public class MessageHandlerChain extends IntegrationObjectSupport implements Mes
|
||||
replyChannel = getChannelResolver().resolveChannelName((String) replyChannelHeader);
|
||||
}
|
||||
else {
|
||||
throw new MessageHandlingException(message, "invalid replyChannel type [" + replyChannelHeader.getClass() + "]");
|
||||
throw new MessageHandlingException(message, "invalid replyChannel type ["
|
||||
+ replyChannelHeader.getClass() + "]");
|
||||
}
|
||||
return (timeout >= 0) ? replyChannel.send(message, timeout) : replyChannel.send(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public class MessageHandlerChainTests {
|
||||
public void chainWithOutputChannel() {
|
||||
handler.handleMessage(message);
|
||||
expectLastCall().times(3);
|
||||
expect(outputChannel.send(eq(message), anyLong())).andReturn(true);
|
||||
expect(outputChannel.send(eq(message))).andReturn(true);
|
||||
replay(allMocks);
|
||||
List<MessageHandler> handlers = new ArrayList<MessageHandler>();
|
||||
handlers.add(producer);
|
||||
@@ -96,7 +96,7 @@ public class MessageHandlerChainTests {
|
||||
handler.handleMessage(message);
|
||||
expectLastCall().times(3);
|
||||
//equality is lost when recreating the message
|
||||
expect(outputChannel.send(isA(Message.class), anyLong())).andReturn(true);
|
||||
expect(outputChannel.send(isA(Message.class))).andReturn(true);
|
||||
replay(allMocks);
|
||||
List<MessageHandler> handlers = new ArrayList<MessageHandler>();
|
||||
handlers.add(producer);
|
||||
@@ -113,8 +113,7 @@ public class MessageHandlerChainTests {
|
||||
Message<String> message = MessageBuilder.withPayload("test").setReplyChannelName("testChannel").build();
|
||||
handler.handleMessage(message);
|
||||
expectLastCall().times(3);
|
||||
//equality is lost when recreating the message
|
||||
expect(outputChannel.send(isA(Message.class), anyLong())).andReturn(true);
|
||||
expect(outputChannel.send(eq(message))).andReturn(true);
|
||||
replay(allMocks);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("testChannel", outputChannel);
|
||||
@@ -129,39 +128,24 @@ public class MessageHandlerChainTests {
|
||||
chain.handleMessage(message);
|
||||
}
|
||||
|
||||
private class ProducingHandlerStub extends AbstractReplyProducingMessageHandler {
|
||||
|
||||
private class ProducingHandlerStub implements MessageHandler {
|
||||
private MessageChannel output;
|
||||
|
||||
private final MessageHandler messageHandler;
|
||||
|
||||
|
||||
public ProducingHandlerStub(MessageHandler handler) {
|
||||
messageHandler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleRequestMessage(Message<?> requestMessage, ReplyMessageHolder replyMessageHolder) {
|
||||
messageHandler.handleMessage(requestMessage);
|
||||
replyMessageHolder.add(requestMessage);
|
||||
|
||||
public void setOutputChannel(MessageChannel channel) {
|
||||
this.output = channel;
|
||||
|
||||
}
|
||||
|
||||
public void handleMessage(Message<?> message) {
|
||||
messageHandler.handleMessage(message);
|
||||
output.send(message);
|
||||
}
|
||||
|
||||
}
|
||||
// private class ProducingHandlerStub implements MessageHandler {
|
||||
// private MessageChannel output;
|
||||
//
|
||||
// private final MessageHandler messageHandler;
|
||||
//
|
||||
// public ProducingHandlerStub(MessageHandler handler) {
|
||||
// messageHandler = handler;
|
||||
// }
|
||||
//
|
||||
// void setOutputChannel(MessageChannel channel) {
|
||||
// this.output = channel;
|
||||
//
|
||||
// }
|
||||
//
|
||||
// public void handleMessage(Message<?> message) {
|
||||
// messageHandler.handleMessage(message);
|
||||
// output.send(message);
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user