INT-3538: Docs for Routing Slip
JIRA: https://jira.spring.io/browse/INT-3538 INT-3538-2: `RoutingSlipHeaderValueMessageProcessor` improvement Since `HeaderEnricher` populates `beanFactory` to its `HeaderValueMessageProcessor`s, there is no reason to specify `RoutingSlipHeaderValueMessageProcessor` as a separate bean. Therefore the `IntegrationEvaluationContextAware` may not be reached. Remove its support from `RoutingSlipHeaderValueMessageProcessor` and just rely on the `setBeanFactory` RS Doc Polishing
This commit is contained in:
committed by
Gary Russell
parent
af19bbfa70
commit
efe3b11fc3
@@ -157,12 +157,14 @@ public class HeaderEnricher extends IntegrationObjectSupport implements Transfor
|
||||
if (processor instanceof BeanFactoryAware && this.getBeanFactory() != null) {
|
||||
((BeanFactoryAware) processor).setBeanFactory(this.getBeanFactory());
|
||||
}
|
||||
Boolean processerOverwrite = processor.isOverwrite();
|
||||
if (processerOverwrite != null) {
|
||||
shouldOverwrite |= processerOverwrite;
|
||||
Boolean processorOverwrite = processor.isOverwrite();
|
||||
if (processorOverwrite != null) {
|
||||
shouldOverwrite |= processorOverwrite;
|
||||
}
|
||||
}
|
||||
if (this.messageProcessor != null && this.messageProcessor instanceof BeanFactoryAware && this.getBeanFactory() != null) {
|
||||
if (this.messageProcessor != null
|
||||
&& this.messageProcessor instanceof BeanFactoryAware
|
||||
&& this.getBeanFactory() != null) {
|
||||
((BeanFactoryAware) this.messageProcessor).setBeanFactory(this.getBeanFactory());
|
||||
}
|
||||
if (!shouldOverwrite && !this.shouldSkipNulls) {
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.integration.expression.IntegrationEvaluationContextAware;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.integration.routingslip.ExpressionEvaluatingRoutingSlipRouteStrategy;
|
||||
import org.springframework.integration.routingslip.RoutingSlipRouteStrategy;
|
||||
import org.springframework.messaging.Message;
|
||||
@@ -46,7 +46,7 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class RoutingSlipHeaderValueMessageProcessor
|
||||
extends AbstractHeaderValueMessageProcessor<Map<List<Object>, Integer>>
|
||||
implements BeanFactoryAware, IntegrationEvaluationContextAware {
|
||||
implements BeanFactoryAware {
|
||||
|
||||
private final List<String> routingSlipPath;
|
||||
|
||||
@@ -64,12 +64,9 @@ public class RoutingSlipHeaderValueMessageProcessor
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIntegrationEvaluationContext(EvaluationContext evaluationContext) {
|
||||
this.evaluationContext = evaluationContext;
|
||||
this.evaluationContext = IntegrationContextUtils.getEvaluationContext(beanFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -985,7 +985,7 @@ public List<String> route(@Header("orderStatus") OrderStatus status)]]></program
|
||||
<para>
|
||||
Note that these methods can be used for simple changes (updating a single route or adding/removing a
|
||||
route). However, if you want to remove one route and add another, the updates are not atomic. This
|
||||
means the routing table may be in an indeterminate state betweent the updates. Starting with
|
||||
means the routing table may be in an indeterminate state between the updates. Starting with
|
||||
<emphasis>version 4.0</emphasis>, you can now use the control bus to update the entire routing
|
||||
table atomically.
|
||||
</para>
|
||||
@@ -1002,10 +1002,10 @@ public List<String> route(@Header("orderStatus") OrderStatus status)]]></program
|
||||
Notice that the parameter is a properties object; this allows the use of the inbuilt
|
||||
<code>StringToPropertiesConverter</code> by a control bus command, for example:
|
||||
<programlisting>"@'router.handler'.replaceChannelMappings('foo=qux \n baz=bar')"</programlisting>
|
||||
- note that each mapping is separted by a newline character (<code>\n</code>). For programmatic
|
||||
- note that each mapping is separated by a newline character (<code>\n</code>). For programmatic
|
||||
changes to the map, it is recommended that the <code>setChannelMappings</code> method
|
||||
is used instead, for type-safety. Any non-String keys or values passed into
|
||||
<code>replaceChannelMappings</code> are ingnored.
|
||||
<code>replaceChannelMappings</code> are ignored.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
@@ -1018,15 +1018,183 @@ public List<String> route(@Header("orderStatus") OrderStatus status)]]></program
|
||||
</para>
|
||||
<note>
|
||||
<para>
|
||||
For more information about Spring Integration's JMX suppor, please see chapter <emphasis><xref linkend="jmx" endterm="jmx.title"/></emphasis>.
|
||||
For more information about Spring Integration's JMX support, please see chapter
|
||||
<emphasis><xref linkend="jmx" endterm="jmx.title"/></emphasis>.
|
||||
</para>
|
||||
</note>
|
||||
</section>
|
||||
<section id="routing-slip">
|
||||
<title>Routing Slip</title>
|
||||
<para>
|
||||
TBD
|
||||
Starting with <emphasis>version 4.1</emphasis>, Spring Integration provides an implementation
|
||||
of the <ulink url="http://www.eaipatterns.com/RoutingTable.html">Routing Slip</ulink>
|
||||
Enterprise Integration Pattern. It is implemented as a <code>routingSlip</code> message header
|
||||
which is used to determine the next channel in
|
||||
<classname>AbstractMessageProducingHandler</classname>s, when an <code>outputChannel</code> isn't
|
||||
specified for the endpoint. This pattern is useful in complex, dynamic, cases when it can become
|
||||
difficult to configure multiple routers to determine message flow. When a message arrives at an
|
||||
endpoint that has no <code>output-channel</code>, the <code>routingSlip</code> is consulted
|
||||
to determine the next channel to which the message will be sent. When the routing slip is exhausted,
|
||||
normal <code>replyChannel</code> processing resumes.
|
||||
</para>
|
||||
<para>
|
||||
Configuration for the <emphasis>Routing Slip</emphasis> is presented as a
|
||||
<code>HeaderEnricher</code> option - a <emphasis>semicolon-separated</emphasis> Routing Slip
|
||||
<code>path</code> entries:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<util:properties id="properties">
|
||||
<beans:prop key="myRoutePath1">channel1</beans:prop>
|
||||
<beans:prop key="myRoutePath2">request.headers[myRoutingSlipChannel]</beans:prop>
|
||||
</util:properties>
|
||||
|
||||
<context:property-placeholder properties-ref="properties"/>
|
||||
|
||||
<header-enricher input-channel="input" output-channel="process">
|
||||
<routing-slip
|
||||
value="${myRoutePath1}; @routingSlipRoutingPojo.get(request, reply);
|
||||
routingSlipRoutingStrategy; ${myRoutePath2}; finishChannel"/>
|
||||
</header-enricher>]]></programlisting>
|
||||
<para>
|
||||
In this sample we have:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
A <code><context:property-placeholder></code> configuration to demonstrate that the
|
||||
entries in the Routing Slip <code>path</code> can be specified as resolvable keys.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
The <code><header-enricher></code> <code><routing-slip></code> sub-element is
|
||||
used to populate the <classname>RoutingSlipHeaderValueMessageProcessor</classname> to the
|
||||
<classname>HeaderEnricher</classname> handler.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
The <classname>RoutingSlipHeaderValueMessageProcessor</classname> accepts a String array of
|
||||
resolved Routing Slip <code>path</code> entries and returns (from <code>processMessage()</code>)
|
||||
a <code>singletonMap</code> with the <code>path</code> as <code>key</code> and <code>0</code>
|
||||
as initial <code>routingSlipIndex</code>.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
Routing Slip <code>path</code> entries can contain <interfacename>MessageChannel</interfacename> bean
|
||||
names, <interfacename>RoutingSlipRouteStrategy</interfacename> bean names and also Spring
|
||||
expressions (SpEL). The <classname>RoutingSlipHeaderValueMessageProcessor</classname> checks each
|
||||
Routing Slip <code>path</code> entry against the <interfacename>BeanFactory</interfacename> on the
|
||||
first <code>processMessage</code> invocation. It converts entries, which aren't bean names in the
|
||||
application context, to <classname>ExpressionEvaluatingRoutingSlipRouteStrategy</classname> instances.
|
||||
<interfacename>RoutingSlipRouteStrategy</interfacename> entries are invoked multiple times, until
|
||||
they return null, or an empty String.
|
||||
</para>
|
||||
<para>
|
||||
Since the <emphasis>Routing Slip</emphasis> is involved in the <code>getOutputChannel</code> process we
|
||||
have a <emphasis>request-reply</emphasis> context. The
|
||||
<interfacename>RoutingSlipRouteStrategy</interfacename> has been introduced to determine the next
|
||||
<code>outputChannel</code> using the <code>requestMessage</code>, as well as the <code>reply</code>
|
||||
object. An implementation of this strategy should be registered as a bean in the application context
|
||||
and its bean name is used in the Routing Slip <code>path</code>. The
|
||||
<classname>ExpressionEvaluatingRoutingSlipRouteStrategy</classname> implementation is provided. It
|
||||
accepts a SpEL expression, and an internal
|
||||
<classname>ExpressionEvaluatingRoutingSlipRouteStrategy.RequestAndReply</classname> object is used as
|
||||
the <emphasis>root object</emphasis> of the evaluation context. This is to avoid the overhead of
|
||||
<interfacename>EvaluationContext</interfacename> creation for each
|
||||
<code>ExpressionEvaluatingRoutingSlipRouteStrategy.getNextPath()</code> invocation. It is a simple
|
||||
Java Bean with two properties - <code>Message<?> request</code> and <code>Object reply</code>.
|
||||
With this expression implementation, we can specify Routing Slip <code>path</code> entries using SpEL
|
||||
(<code>@routingSlipRoutingPojo.get(request, reply)</code>, <code>request.headers[myRoutingSlipChannel]</code>)
|
||||
avoiding a bean definition for the <interfacename>RoutingSlipRouteStrategy</interfacename>.
|
||||
</para>
|
||||
<important>
|
||||
<para>
|
||||
If a <emphasis>Routing Slip</emphasis> is involved in a distributed environment - cross-JVM
|
||||
application, <code>request-reply</code> through a Message Broker (e.g. <xref linkend="amqp"/>,
|
||||
<xref linkend="jms"/>), or persistence <interfacename>MessageStore</interfacename>
|
||||
(<xref linkend="message-store"/>) is used in the integration flow, etc., - it is recommended
|
||||
to <emphasis role="bold">not</emphasis>
|
||||
use <emphasis>inline</emphasis> expressions for the Routing Slip <code>path</code>. The framework
|
||||
(<classname>RoutingSlipHeaderValueMessageProcessor</classname>) converts them to
|
||||
<classname>ExpressionEvaluatingRoutingSlipRouteStrategy</classname> objects and they are used in
|
||||
the <code>routingSlip</code> message header. Since this class isn't
|
||||
<interfacename>Serializable</interfacename> (and it can't be, because it depends on the
|
||||
<interfacename>BeanFactory</interfacename>) the entire Message becomes non-serializable and in
|
||||
any distributed operation we end up with <classname>NotSerializableException</classname>.
|
||||
To overcome this limitation, register an
|
||||
<classname>ExpressionEvaluatingRoutingSlipRouteStrategy</classname> bean with the desired SpEL and
|
||||
use its bean name in the Routing Slip <code>path</code> configuration.
|
||||
</para>
|
||||
</important>
|
||||
<para>
|
||||
For Java configuration, simply add a
|
||||
<classname>RoutingSlipHeaderValueMessageProcessor</classname> instance to the
|
||||
<classname>HeaderEnricher</classname> bean definition:
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[@Bean
|
||||
@Transformer(inputChannel = "routingSlipHeaderChannel")
|
||||
public HeaderEnricher headerEnricher() {
|
||||
return new HeaderEnricher(Collections.singletonMap(IntegrationMessageHeaderAccessor.ROUTING_SLIP,
|
||||
new RoutingSlipHeaderValueMessageProcessor("myRoutePath1",
|
||||
"@routingSlipRoutingPojo.get(request, reply)",
|
||||
"routingSlipRoutingStrategy",
|
||||
"request.headers[myRoutingSlipChannel]",
|
||||
"finishChannel")));
|
||||
}]]></programlisting>
|
||||
<para>
|
||||
The <emphasis>Routing Slip</emphasis> algorithm works as follows when an endpoint produces
|
||||
a reply and there is no <code>outputChannel</code> defined:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
The <code>routingSlipIndex</code> is used to get a value from the Routing Slip
|
||||
<code>path</code> list.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
If the value by <code>routingSlipIndex</code> is <classname>String</classname>, it
|
||||
is used to get a bean from <interfacename>BeanFactory</interfacename>.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
If a returned bean is an instance of <interfacename>MessageChannel</interfacename>, it is used
|
||||
as the next <code>outputChannel</code> and the <code>routingSlipIndex</code> is incremented
|
||||
in the reply message header (the Routing Slip <code>path</code> entries remain unchanged).
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
If a returned bean is an instance of <interfacename>RoutingSlipRouteStrategy</interfacename> and its
|
||||
<code>getNextPath</code> doesn't return an empty String, that result is used a bean name for
|
||||
the next <code>outputChannel</code>. The <code>routingSlipIndex</code> remains unchanged.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
If <code>RoutingSlipRouteStrategy.getNextPath</code> returns an empty String,
|
||||
the <code>routingSlipIndex</code> is incremented and the
|
||||
<code>getOutputChannelFromRoutingSlip</code> is invoked recursively for the next Routing Slip
|
||||
<code>path</code> item;
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
If the next Routing Slip <code>path</code> entry isn't a String it must be an instance of
|
||||
<interfacename>RoutingSlipRouteStrategy</interfacename>;
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
When the <code>routingSlipIndex</code> exceeds the size of the Routing Slip <code>path</code>
|
||||
list, the algorithm moves to the default behavior for the standard <code>replyChannel</code>
|
||||
header.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user