This commit is contained in:
Iwein Fuld
2009-04-14 17:20:51 +00:00
parent 33787588ab
commit bde9178a1f
2 changed files with 54 additions and 23 deletions

View File

@@ -43,16 +43,18 @@ import org.springframework.util.Assert;
* 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}.
* {@link MessageFilter}. A {@link MessageHandler} returning null will have the
* same effect, although this option is less expressive.
* <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>
* &lt;chain&gt;
* &lt;filter ref="someFilter"/&gt;
* &lt;bean class="SomeMessageHandlerImplementation"/&gt;
* &lt;transformer ref="someTransformer"/&gt;
* &lt;filter ref=&quot;someFilter&quot;/&gt;
* &lt;bean class=&quot;SomeMessageHandlerImplementation&quot;/&gt;
* &lt;transformer ref=&quot;someTransformer&quot;/&gt;
* &lt;aggregator ... /&gt;
* &lt;/chain&gt;
* </pre>
@@ -60,7 +62,8 @@ import org.springframework.util.Assert;
* @author Mark Fisher
* @author Iwein Fuld
*/
public class MessageHandlerChain extends IntegrationObjectSupport implements MessageHandler {
public class MessageHandlerChain extends IntegrationObjectSupport implements
MessageHandler {
private static final String OUTPUT_CHANNEL_PROPERTY = "outputChannel";
@@ -83,7 +86,9 @@ public class MessageHandlerChain extends IntegrationObjectSupport implements Mes
public final void afterPropertiesSet() {
synchronized (this.initializationMonitor) {
if (!this.initialized) {
Assert.notEmpty(this.handlers, "handler list must not be empty");
Assert
.notEmpty(this.handlers,
"handler list must not be empty");
this.configureChain();
this.initialized = true;
}
@@ -106,26 +111,33 @@ public class MessageHandlerChain extends IntegrationObjectSupport implements Mes
MessageHandler handler = handlers.get(i);
PropertyAccessor accessor = new BeanWrapperImpl(handler);
if (!first) {
EventDrivenConsumer consumer = new EventDrivenConsumer(channel, handler);
EventDrivenConsumer consumer = new EventDrivenConsumer(channel,
handler);
consumer.start();
}
if (!last) {
channel = new DirectChannel();
channel.setBeanName("_" + this + ".channel#" + i);
Assert.notNull(accessor.getPropertyType(OUTPUT_CHANNEL_PROPERTY),
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'");
+ OUTPUT_CHANNEL_PROPERTY
+ "' of type 'MessageChannel'");
accessor.setPropertyValue(OUTPUT_CHANNEL_PROPERTY, channel);
}
else if (accessor.getPropertyType(OUTPUT_CHANNEL_PROPERTY) != null) {
MessageChannel replyChannel = (this.outputChannel != null) ? this.outputChannel
: new ReplyForwardingMessageChannel();
accessor.setPropertyValue(OUTPUT_CHANNEL_PROPERTY, replyChannel);
accessor
.setPropertyValue(OUTPUT_CHANNEL_PROPERTY, replyChannel);
}
else {
Assert.isNull(this.outputChannel,
"An output channel was provided, but the final handler in the chain does not implement property '"
+ OUTPUT_CHANNEL_PROPERTY + "' of type 'MessageChannel'");
Assert
.isNull(
this.outputChannel,
"An output channel was provided, but the final handler in the chain does not implement property '"
+ OUTPUT_CHANNEL_PROPERTY
+ "' of type 'MessageChannel'");
}
}
}
@@ -143,21 +155,26 @@ public class MessageHandlerChain extends IntegrationObjectSupport implements Mes
public boolean send(Message<?> message, long timeout) {
Object replyChannelHeader = message.getHeaders().getReplyChannel();
if (replyChannelHeader == null) {
throw new MessageHandlingException(message, "no replyChannel header available");
throw new MessageHandlingException(message,
"no replyChannel header available");
}
MessageChannel replyChannel = null;
if (replyChannelHeader instanceof MessageChannel) {
replyChannel = (MessageChannel) replyChannelHeader;
}
else if (replyChannelHeader instanceof String) {
Assert.notNull(getChannelResolver(), "ChannelResolver is required");
replyChannel = getChannelResolver().resolveChannelName((String) replyChannelHeader);
Assert.notNull(getChannelResolver(),
"ChannelResolver is required");
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);
return (timeout >= 0) ? replyChannel.send(message, timeout)
: replyChannel.send(message);
}
}
}

View File

@@ -26,8 +26,20 @@
</tip>
</para>
<para>
The handler chain simplifies configuration while internally maintaining the same degree of loose coupling between
components, and it is trivial to modify the configuration if at some point a non-linear arrangement is required.
The handler chain simplifies configuration while internally maintaining the same degree of loose coupling between
components, and it is trivial to modify the configuration if at some point a non-linear arrangement is required.
The chain will be expanded into a linear setup of the listed endpoints, separated by direct
channels. The reply channel header will not be taken into account within the chain: only
after the last handler is invoked will the resulting message be forwarded onto the reply channel or the chain's output
channel. Because of this setup all handlers except the last require a <methodname>setOutputChannel</methodname>
implementation. The last handler only needs an output channel if the outputChannel on the MessageHandlerChain is set.
If this is not the case, the "replyChannel" header needs to be set on each message, so that it is clear where the
message needs to go.
</para>
<para>
In most cases there is no need to implement MessageHandlers yourself. The next section will focus on namespace
support for the chain element. Most Spring Integration enpoints like Service Activators and Transformers are
suitable for use within a <classname>MessageHandlerChain</classname>.
</para>
</section>
@@ -36,7 +48,7 @@
<para>
The &lt;chain&gt; element provides an 'input-channel' attribute, and if the last element in the chain is capable
of producing reply messages, it may optionally provide an 'output-channel' attribute. The sub-elements are then
filters, transformers, splitters, and service-activators. The last element may be a router.
filters, transformers, splitters, and service-activators. The last element may also be a router or aggregator.
<programlisting language="xml"><![CDATA[ <chain input-channel="input" output-channel="output">
<filter ref="someSelector" throw-exception-on-rejection="true"/>
<header-enricher error-channel="customErrorChannel">
@@ -45,7 +57,9 @@
<service-activator ref="someService" method="someMethod"/>
</chain>]]></programlisting>
</para>
The &lt;header-enricher&gt; used in the above example will set a message header with name "foo" and value "bar" on the message.
The &lt;header-enricher&gt; element used in the above example will set a message header with name "foo" and value "bar" on the message.
A header enricher is a specialization of Transformer that touches only header values. You could obtain the same result by
implementing a MessageHandler that did the header modifications and wiring that as a bean.
</section>
</chapter>