diff --git a/spring-integration-core/src/main/java/org/springframework/integration/transformer/HeaderEnricher.java b/spring-integration-core/src/main/java/org/springframework/integration/transformer/HeaderEnricher.java index fc73847c9d..92bdf07de7 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/transformer/HeaderEnricher.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/transformer/HeaderEnricher.java @@ -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) { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/transformer/support/RoutingSlipHeaderValueMessageProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/transformer/support/RoutingSlipHeaderValueMessageProcessor.java index 88322a65e2..aace9f599f 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/transformer/support/RoutingSlipHeaderValueMessageProcessor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/transformer/support/RoutingSlipHeaderValueMessageProcessor.java @@ -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, Integer>> - implements BeanFactoryAware, IntegrationEvaluationContextAware { + implements BeanFactoryAware { private final List 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 diff --git a/src/reference/docbook/router.xml b/src/reference/docbook/router.xml index d4de630660..42ef5da66f 100644 --- a/src/reference/docbook/router.xml +++ b/src/reference/docbook/router.xml @@ -985,7 +985,7 @@ public List route(@Header("orderStatus") OrderStatus status)]]> 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 version 4.0, you can now use the control bus to update the entire routing table atomically. @@ -1002,10 +1002,10 @@ public List route(@Header("orderStatus") OrderStatus status)]]>StringToPropertiesConverter by a control bus command, for example: "@'router.handler'.replaceChannelMappings('foo=qux \n baz=bar')" - - note that each mapping is separted by a newline character (\n). For programmatic + - note that each mapping is separated by a newline character (\n). For programmatic changes to the map, it is recommended that the setChannelMappings method is used instead, for type-safety. Any non-String keys or values passed into - replaceChannelMappings are ingnored. + replaceChannelMappings are ignored. @@ -1018,15 +1018,183 @@ public List route(@Header("orderStatus") OrderStatus status)]]> - For more information about Spring Integration's JMX suppor, please see chapter . + For more information about Spring Integration's JMX support, please see chapter + .
Routing Slip - TBD + Starting with version 4.1, Spring Integration provides an implementation + of the Routing Slip + Enterprise Integration Pattern. It is implemented as a routingSlip message header + which is used to determine the next channel in + AbstractMessageProducingHandlers, when an outputChannel 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 output-channel, the routingSlip is consulted + to determine the next channel to which the message will be sent. When the routing slip is exhausted, + normal replyChannel processing resumes. + + Configuration for the Routing Slip is presented as a + HeaderEnricher option - a semicolon-separated Routing Slip + path entries: + + + channel1 + request.headers[myRoutingSlipChannel] + + + + + + + ]]> + + In this sample we have: + + + + + A <context:property-placeholder> configuration to demonstrate that the + entries in the Routing Slip path can be specified as resolvable keys. + + + + + The <header-enricher> <routing-slip> sub-element is + used to populate the RoutingSlipHeaderValueMessageProcessor to the + HeaderEnricher handler. + + + + + The RoutingSlipHeaderValueMessageProcessor accepts a String array of + resolved Routing Slip path entries and returns (from processMessage()) + a singletonMap with the path as key and 0 + as initial routingSlipIndex. + + + + + Routing Slip path entries can contain MessageChannel bean + names, RoutingSlipRouteStrategy bean names and also Spring + expressions (SpEL). The RoutingSlipHeaderValueMessageProcessor checks each + Routing Slip path entry against the BeanFactory on the + first processMessage invocation. It converts entries, which aren't bean names in the + application context, to ExpressionEvaluatingRoutingSlipRouteStrategy instances. + RoutingSlipRouteStrategy entries are invoked multiple times, until + they return null, or an empty String. + + + Since the Routing Slip is involved in the getOutputChannel process we + have a request-reply context. The + RoutingSlipRouteStrategy has been introduced to determine the next + outputChannel using the requestMessage, as well as the reply + 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 path. The + ExpressionEvaluatingRoutingSlipRouteStrategy implementation is provided. It + accepts a SpEL expression, and an internal + ExpressionEvaluatingRoutingSlipRouteStrategy.RequestAndReply object is used as + the root object of the evaluation context. This is to avoid the overhead of + EvaluationContext creation for each + ExpressionEvaluatingRoutingSlipRouteStrategy.getNextPath() invocation. It is a simple + Java Bean with two properties - Message<?> request and Object reply. + With this expression implementation, we can specify Routing Slip path entries using SpEL + (@routingSlipRoutingPojo.get(request, reply), request.headers[myRoutingSlipChannel]) + avoiding a bean definition for the RoutingSlipRouteStrategy. + + + + If a Routing Slip is involved in a distributed environment - cross-JVM + application, request-reply through a Message Broker (e.g. , + ), or persistence MessageStore + () is used in the integration flow, etc., - it is recommended + to not + use inline expressions for the Routing Slip path. The framework + (RoutingSlipHeaderValueMessageProcessor) converts them to + ExpressionEvaluatingRoutingSlipRouteStrategy objects and they are used in + the routingSlip message header. Since this class isn't + Serializable (and it can't be, because it depends on the + BeanFactory) the entire Message becomes non-serializable and in + any distributed operation we end up with NotSerializableException. + To overcome this limitation, register an + ExpressionEvaluatingRoutingSlipRouteStrategy bean with the desired SpEL and + use its bean name in the Routing Slip path configuration. + + + + For Java configuration, simply add a + RoutingSlipHeaderValueMessageProcessor instance to the + HeaderEnricher bean definition: + + + + The Routing Slip algorithm works as follows when an endpoint produces + a reply and there is no outputChannel defined: + + + + + The routingSlipIndex is used to get a value from the Routing Slip + path list. + + + + + If the value by routingSlipIndex is String, it + is used to get a bean from BeanFactory. + + + + + If a returned bean is an instance of MessageChannel, it is used + as the next outputChannel and the routingSlipIndex is incremented + in the reply message header (the Routing Slip path entries remain unchanged). + + + + + If a returned bean is an instance of RoutingSlipRouteStrategy and its + getNextPath doesn't return an empty String, that result is used a bean name for + the next outputChannel. The routingSlipIndex remains unchanged. + + + + + If RoutingSlipRouteStrategy.getNextPath returns an empty String, + the routingSlipIndex is incremented and the + getOutputChannelFromRoutingSlip is invoked recursively for the next Routing Slip + path item; + + + + + If the next Routing Slip path entry isn't a String it must be an instance of + RoutingSlipRouteStrategy; + + + + + When the routingSlipIndex exceeds the size of the Routing Slip path + list, the algorithm moves to the default behavior for the standard replyChannel + header. + + +