INT-1552 doc polishing, and added @ManagedResource/@ManagedOperation on the AbstractMessageRouter (for dynamic control over mappings)

This commit is contained in:
Mark Fisher
2010-11-22 15:10:40 -05:00
parent 5dc6763735
commit a3c4099c6c
2 changed files with 55 additions and 46 deletions

View File

@@ -17,7 +17,7 @@
A <classname>PayloadTypeRouter</classname> will send Messages to the channel as defined by payload-type
mappings.
<programlisting language="xml"><![CDATA[<bean id="payloadTypeRouter" class="org.springframework.integration.router.PayloadTypeRouter">
<property name="payloadTypeChannelMap">
<property name="channelIdentifierMap">
<map>
<entry key="java.lang.String" value-ref="stringChannel"/>
<entry key="java.lang.Integer" value-ref="integerChannel"/>
@@ -26,10 +26,11 @@
</bean>]]></programlisting>
</para>
<para>
Configuration of <classname>PayloadTypeRouter</classname> is also supported via the namespace provided by Spring Integration (see <xref linkend="configuration-namespace"/>),
which essentially simplifies configuration by combining <code>&lt;router/&gt;</code> configuration and its corresponding implementation defined using <code>&lt;bean/&gt;</code> element
Configuration of the <classname>PayloadTypeRouter</classname> is also supported via the namespace provided by Spring Integration (see <xref linkend="configuration-namespace"/>),
which essentially simplifies configuration by combining the <code>&lt;router/&gt;</code> configuration and its corresponding implementation
defined using a <code>&lt;bean/&gt;</code> element
into a single and more concise configuration element.
The example below demonstrates a <classname>PayloadTypeRouter</classname> configuration which is equivalent to the one above using Spring Integration's namespace support:
The example below demonstrates a <classname>PayloadTypeRouter</classname> configuration which is equivalent to the one above using the namespace support:
</para>
<para>
<programlisting language="xml"><![CDATA[<payload-type-router input-channel="routingChannel">
@@ -42,7 +43,7 @@
<title>HeaderValueRouter</title>
<para>
A <classname>HeaderValueRouter</classname> will send Messages to the channel based on the individual header value mappings.
When <code>HeaderValueRouter</code> is created it is initialized with the <emphasis>name</emphasis> of the header to be evaluated.
When a <code>HeaderValueRouter</code> is created it is initialized with the <emphasis>name</emphasis> of the header to be evaluated.
The <emphasis>value</emphasis> of the header could be one of two things:</para>
<para>
1. Arbitrary value
@@ -51,11 +52,11 @@
2. Channel name
</para>
<para>
If arbitrary then additional mapping of these header values to channel names is required, otherwise no additional configuration is needed.
If arbitrary then additional mappings for these header values to channel names is required, otherwise no additional configuration is needed.
</para>
<para>
Spring Integration provides a simple namespace-based XML configuration to configure <classname>HeaderValueRouter</classname>.
The example below demonstrates two types of namespace-based configuration of <classname>HeaderValueRouter</classname>.
Spring Integration provides a simple namespace-based XML configuration to configure a <classname>HeaderValueRouter</classname>.
The example below demonstrates two types of namespace-based configuration for the <classname>HeaderValueRouter</classname>.
</para>
<para><emphasis>1. Configuration where mapping of header values to channels is required</emphasis> </para>
<para>
@@ -69,7 +70,8 @@
exception. If you want to suppress such exceptions and send unresolved messages to the default output channel
(identified with the <code>default-output-channel</code> attribute) set <code>ignore-channel-name-resolution-failures</code> to true.
Normally, messages for which the header value is not explicitly mapped to a channel will be sent to the <code>default-output-channel</code>.
However, in cases where the header value is mapped to a channel name but the channel cannot be resolved, setting <code>ignore-channel-name-resolution-failures</code>
However, in cases where the header value is mapped to a channel name but the channel cannot be resolved, setting
the <code>ignore-channel-name-resolution-failures</code>
attribute to true will result in routing such messages to the <code>default-output-channel</code>.
</para>
@@ -106,7 +108,7 @@
</para>
</section>
<para>
Spring Integration also provides namespace support for <classname>RecipientListRouter</classname> configuration (see <xref linkend="configuration-namespace"/>)
Spring Integration also provides namespace support for the <classname>RecipientListRouter</classname> configuration (see <xref linkend="configuration-namespace"/>)
as the example below demonstrates.
</para>
<para>
@@ -120,12 +122,14 @@
</para>
<note>
The 'apply-sequence' flag here has the same effect as it does for a publish-subscribe-channel,
and like publish-subscribe-channel, it is disabled by default on the recipient-list-router. Refer to
and like a publish-subscribe-channel, it is disabled by default on the recipient-list-router. Refer to
<xref linkend="channel-configuration-pubsubchannel"/> for more information.
</note>
<para>
Another convenient option to configure <classname>RecipientListRouter</classname> is to use Spring Expression Language (SpEL) support
Another convenient option when configuring a <classname>RecipientListRouter</classname> is to use Spring Expression Language (SpEL) support
as selectors for individual recipient channels. This is similar to using a Filter at the beginning of 'chain' to act as a "Selective Consumer".
However, in this case, it's all combined rather concisely into the router's configuration.
<programlisting language="xml"><![CDATA[<int:recipient-list-router id="customRouter" input-channel="routingChannel">
<int:recipient channel="channel1" selector-expression="payload.equals('foo')"/>
@@ -142,7 +146,7 @@
<section id="router-namespace">
<title>Configuring Router</title>
<section>
<title>Configuring Router with XML</title>
<title>Configuring a Content Based Router with XML</title>
<para>
The "router" element provides a simple way to connect a router to an input channel and also accepts the
optional <code>default-output-channel</code> attribute. The <code>ref</code> attribute references the bean name of a custom Router implementation
@@ -173,17 +177,17 @@
</para>
</note>
<para>
<emphasis>Routers and Spring Expression Language (SpEL)</emphasis>
<emphasis>Routers and the Spring Expression Language (SpEL)</emphasis>
</para>
<para>
Sometimes the routing logic may be simple and writing a separate class for it and configuring it as a bean may seem
like overkill. Since Spring Integration 2.0 we offer an alternative where you can now use
like overkill. As of Spring Integration 2.0 we offer an alternative where you can now use
<ulink url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.htm">SpEL</ulink>
to implement simple computations that previously required a custom POJO router.
<programlisting language="xml"><![CDATA[<int:router input-channel="inChannel" expression="payload + 'Channel'"/>]]></programlisting>
In the above configuration the result channel will be computed by the SpEL expression which simply concatenates the value
of the <code>payload</code> with the literal 'Channel'
of the <code>payload</code> with the literal String 'Channel'.
</para>
<para>
@@ -203,7 +207,7 @@
</section>
<section id="router-annotation">
<title>Configuring Router with Annotations</title>
<title>Configuring a Router with Annotations</title>
<para>
When using <interfacename>@Router</interfacename> to annotate a method, the method may return either a
<interfacename>MessageChannel</interfacename> or <classname>String</classname> type. In the latter case,
@@ -363,7 +367,7 @@ public List&lt;String&gt; route(@Header("orderStatus") OrderStatus status)</prog
However if we look at the alternate configuration of the <classname>HeaderValueRouter</classname> we clearly see that
there is no <code>mapping</code> sub element:
<programlisting language="xml"><![CDATA[<header-value-router input-channel="inputChannel" header-name="testHeader">]]></programlisting>
But the configuration is still perfectly valid. So the natural question is what about the maping in the Step 2?
But the configuration is still perfectly valid. So the natural question is what about the mapping in the Step 2?
</para>
<para>
What this means is that Step 2 is now an optional step. If mapping is not defined then the <code>channel identifier</code>
@@ -376,53 +380,54 @@ public List&lt;String&gt; route(@Header("orderStatus") OrderStatus status)</prog
</para>
<para><emphasis>For Example:</emphasis> </para>
<para>
In the above configuration lets assume that the <code>testHeader</code> value is 'kermit' which is now a <code>channel identifier</code>
In the above configuration let's assume that the <code>testHeader</code> value is 'kermit' which is now a <code>channel identifier</code>
(Step 1). Since there is no mapping in this router, resolving this <code>channel identifier</code> to a <code>channel name</code>
(Step 2) is impossible and this <code>channel identifier</code> is now treated as <code>channel name</code>. However what if
there was a mapping but for a different value, the end result would still be the same and that is:
<emphasis>if new value can not be determined through the process of resolving 'channel identifier' to a 'channel name',
such 'channel identifier' becomes 'channel name'</emphasis>
there was a mapping but for a different value? The end result would still be the same and that is:
<emphasis>if new value cannot be determined through the process of resolving the 'channel identifier' to a 'channel name',
such 'channel identifier' becomes 'channel name'.</emphasis>
</para>
<para>
So all that is left is for Step 3 to resolve <code>channel name</code> ('kermit') to an actual instance of the
<classname>MessageChannel</classname> identified by this name. That will be done via a default
<interface>ChannelResolver</interface> implementation which is a <classname>BeanFactoryChannelResolver</classname> which
basically does a bean lookup by the name provided. So now all messages which contain the header/value pair as <code>testHeader=kermit</code>
are going to be routed to a 'kermit' <classname>MessageChannel</classname>.
So all that is left is for Step 3 to resolve the <code>channel name</code> ('kermit') to an actual instance of the
<classname>MessageChannel</classname> identified by this name. That will be done via the default
<interface>ChannelResolver</interface> implementation which is a <classname>BeanFactoryChannelResolver</classname>. It
basically does a bean lookup for the name provided. So now all messages which contain the header/value pair as <code>testHeader=kermit</code>
are going to be routed to a <classname>MessageChannel</classname> whose bean name (id) is 'kermit'.
</para>
<para>
But what if you want to route these messages to 'simpson' channel? Obviously changing a static configuration will work,
But what if you want to route these messages to the 'simpson' channel? Obviously changing a static configuration will work,
but will also require bringing your system down. However if you had access to the <code>channel identifier</code> map, then you
could just introduce a new mapping where header/value pair is now <code>kermit=simpson</code>, thus allowing Step 2 to treat
'kermit' as <code>channel identifier</code> while resolving it to 'simpson' as <code>channel name</code> .
could just introduce a new mapping where the header/value pair is now <code>kermit=simpson</code>, thus allowing Step 2 to treat
'kermit' as a <code>channel identifier</code> while resolving it to 'simpson' as the <code>channel name</code> .
</para>
<para>
The same obviously applies for <classname>PayloadTypeRouter</classname> where you can now remap or remove a particular <emphasis>payload type
mapping</emphasis>, and every other router including <emphasis>expression-based</emphasis> routers since their computed value
will now have a chance to go through Step 2 to be aditionally resolved to the actual <code>channel name</code>.
mapping</emphasis>. In fact, it applies to every other router including <emphasis>expression-based</emphasis> routers since their computed values
will now have a chance to go through Step 2 to be additionally resolved to the actual <code>channel name</code>.
</para>
<para>
In Spring Integration 2.0 the routers hierarchy underwent major refactoring and now any router that is a subclass of the
In Spring Integration 2.0 the routers hierarchy underwent significant refactoring so that now any router that is a subclass of the
<classname>AbstractMessageRouter</classname> (which includes all framework defined routers) is a Dynamic Router simply because the
<code>channelIdentiferMap</code> is defined at the <classname>AbstractMessageRouter</classname> with convenient accessors
and modifiers exposed as public methods allowing you to change/add/remove router mapping at runtime via JMX (see <xref linkend="jmx"/>) or
the ControlBus (see <xref linkend="control-bus"/>) functionality. 
<code>channelIdentiferMap</code> is defined at the <classname>AbstractMessageRouter</classname> level. That map's setter method is
exposed as a public method along with 'setChannelMapping' and 'removeChannelMapping' methods. These allow you to change/add/remove
router mappings at runtime as long as you have a reference to the router itself. It also means that you could expose these same
configuration options via JMX (see <xref linkend="jmx"/>) or the Spring Integration ControlBus (see <xref linkend="control-bus"/>) functionality. 
</para>
<para>
<emphasis>Control Bus</emphasis>
</para>
<para>
One of the way to manage the router mappings is through the <ulink url="http://www.eaipatterns.com/ControlBus.html">Control Bus</ulink>
One way to manage the router mappings is through the <ulink url="http://www.eaipatterns.com/ControlBus.html">Control Bus</ulink>
pattern which exposes a Control Channel where you can send
control messages to manage and monitor Spring Integration components, including routers.
For more information about the Control Bus see <xref linkend="control-bus"/>. Typically you would send a control message asking to invoke a
particular JMX operation on a particular managed component (e.g., router). The two managed operations (methods) that are
specific to changing router resolution process are:
particular operation on a particular managed component (e.g., router). The two managed operations (methods) that are
specific to changing the router resolution process are:
<itemizedlist>
<listitem>
<para><code>public void setChannelMapping(String channelIdentifier, String channelName)</code> -
will allow you to add new or modify existing mapping of <code>channel identifier</code> to <code>channel name</code></para>
will allow you to add a new or modify an existing mapping between <code>channel identifier</code> and <code>channel name</code></para>
</listitem>
<listitem>
<para><code>public void removeChannelMapping(String channelIdentifier)</code> -
@@ -430,12 +435,11 @@ public List&lt;String&gt; route(@Header("orderStatus") OrderStatus status)</prog
<code>channel identifier</code> and <code>channel name</code> </para>
</listitem>
</itemizedlist>
For additional managed operations please refer to an <classname>AbstractMessageRouter</classname> for more detail
</para>
<para>
You can also use your favorite JMX client (e.g., JConsole) and use those operations (methods) to change
router configuration. For more information on Spring Integration management and monitoring please visit
<xref linkend="jmx"/>.
You can also expose a router instance with Spring's JMS support and then use your favorite JMX client (e.g., JConsole) to
manage those operations (methods) for changing the router's configuration. For more information on Spring Integration
management and monitoring please visit <xref linkend="jmx"/>.
</para>
</section>

View File

@@ -36,6 +36,8 @@ import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.support.channel.BeanFactoryChannelResolver;
import org.springframework.integration.support.channel.ChannelResolutionException;
import org.springframework.integration.support.channel.ChannelResolver;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
@@ -46,6 +48,7 @@ import org.springframework.util.StringUtils;
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
@ManagedResource
public abstract class AbstractMessageRouter extends AbstractMessageHandler {
private volatile MessageChannel defaultOutputChannel;
@@ -102,7 +105,8 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler {
this.channelIdentifierMap.putAll(channelIdentifierMap);
}
public void setChannelMapping(String channelIdentifier, String channelName){
@ManagedOperation
public void setChannelMapping(String channelIdentifier, String channelName) {
this.channelIdentifierMap.put(channelIdentifier, channelName);
}
@@ -110,7 +114,8 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler {
* Removes channel mapping for a give channel identifier
* @param channelIdentifier
*/
public void removeChannelMapping(String channelIdentifier){
@ManagedOperation
public void removeChannelMapping(String channelIdentifier) {
this.channelIdentifierMap.remove(channelIdentifier);
}