diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/handler/MessageHandlerChain.java b/org.springframework.integration/src/main/java/org/springframework/integration/handler/MessageHandlerChain.java index 72f944c305..0803ae8a51 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/handler/MessageHandlerChain.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/handler/MessageHandlerChain.java @@ -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. + *

+ * Each of the handlers has to implement + * public void setOutputChannel(MessageChannel outputChannel);. 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. + *

+ * 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}. + *

+ * This component can be used from the namespace to improve the readability of + * the configuration by removing channels that can be created implicitly. + *

+ *

+ * <chain>
+ *     <filter ref="someFilter"/>
+ *     <bean class="SomeMessageHandlerImplementation"/>
+ *     <transformer ref="someTransformer"/>
+ *     <aggregator ... />
+ * </chain>
+ * 
* * @author Mark Fisher + * @author Iwein Fuld */ public class MessageHandlerChain extends IntegrationObjectSupport implements MessageHandler { + private static final String OUTPUT_CHANNEL_PROPERTY = "outputChannel"; + private volatile List 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 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); } } - } diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/handler/MessageHandlerChainTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/handler/MessageHandlerChainTests.java index a569d6fb98..8f1bed943e 100644 --- a/org.springframework.integration/src/test/java/org/springframework/integration/handler/MessageHandlerChainTests.java +++ b/org.springframework.integration/src/test/java/org/springframework/integration/handler/MessageHandlerChainTests.java @@ -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 handlers = new ArrayList(); 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 handlers = new ArrayList(); handlers.add(producer); @@ -113,8 +113,7 @@ public class MessageHandlerChainTests { Message 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); -// } -// } }