INT-267: Implement Routing Slip Pattern

JIRA: https://jira.spring.io/browse/INT-267

The implementation looks like:
* There is the `ROUTING_SLIP` header to keep the list of bean ids;
* The `ROUTING_SLIP_INDEX` header keeps track of current `index` in the `ROUTING_SLIP` header;
* `ROUTING_SLIP` List can contain channel names or bean references for the `RoutingSlip` strategy implementations.
They are differentiated with `@` prefix;
* The `<header-enricher>` adds `<routing-slip>` sub-element to specify the comma-delimited value for desired `ROUTING_SLIP` for the downstream flow;
* The `AbstractReplyProducingMessageHandler` adds the logic to get deal with `ROUTING_SLIP` List and the algorithm is:
    - If `ROUTING_SLIP` isn't `null` we build `AtomicInteger` for the current `routingSlipIndex`;
    - the recursive `getReplyChannelFromRoutingSlip` should return a channel name or `null`;
    - if current `ROUTING_SLIP_INDEX` if for the `RoutingSlip` strategy, we check its result for `null` and `incrementAndGet()` the current index or not;
    - for the simple channel name value from `ROUTING_SLIP` list we just `incrementAndGet()` the current index and return the value;
    - the new `ROUTING_SLIP_INDEX` is populated to the headers of new reply message.
* Polishing for `AbstractMessageSplitter`

**TODO**: Docs and applying `RoutingSlip` algorithm for the `AbstractCorrelatingMessageHandler`

INT-267: Move `replyProducing` logic `ARPMH` -> `AMPH`

* Rework `AbstractCorrelatingMessageHandler` to use methods from super class
* Rework `MessageHandlerChain.ReplyForwardingMessageChannel` to use `produceReply`
* Rename `RoutingSlip` -> `RoutingSlipRouteStrategy`
* Add `ExpressionEvaluationRoutingSlipRouteStrategy`

Now `routingSlip` header can be configured like:

```
<routing-slip value="channel1; #{@routingSlipRoutingPojo.get(request, reply)}; @routingSlipRoutingStrategy; #{request.headers[myRoutingSlipChannel]}; channel6"/>
```

Where `;` is used as delimiter, because of `,` in the method invocation from SpEL.
The simple literal (`channel1`) is just a `MessageChannel` `id`.
`@` is used for `RoutingSlipRouteStrategy` bean reference.
`#{...}` used for SpEL.
The `HeaderEnricherParserSupport` parses this `value` to the `List<String>` - a set of bean names, where any SpEL is wrapped
to the `ExpressionEvaluationRoutingSlipRouteStrategy` bean definition

INT-267: Introduce `RoutingSlip` Domain class

Rename `AbstractMessageProducingHandler` methods: `*reply` -> `*output`

Conflicts:
	src/reference/docbook/whats-new.xml

INT-267: Rework `RoutingSlip` -> `Map<List<String>, Integer>`

Since `RoutingSlip` POJO isn't scalable in the distributed multi-language environment,
it would be better to use some Java generic type for this `ROUTING_SLIP` header.

The `Collections.singletonMap(Collections.unmodifiableList(routingSlipPath), 0)` is the best candidate to be convertible to other systems
and allow to have thread-safety.

The `ROUTING_SLIP` header is recalculated now on each `nextPath`

INT-267: Introduce `RoutingSlipHeaderValueMessageProcessor`

* Rework `HeaderEnricherParserSupport` logic to get rid of SpEL parsing
and change `routingSlipPath` to the `ManagedList<String>` to get gain of `property-placehoder`
* Add `<context:property-placeholder>` stuff to the `RoutingSlipTests`

INT-267: Move inline expressions to the implicit `EERSRS` from the `RoutingSlipHeaderValueMessageProcessor`

INT-267: Fix up JavaDocs and add JDBC test-case

INT-267: Address PR comments

INT-267: Polishing according PR comments

Polishing
This commit is contained in:
Artem Bilan
2014-09-16 15:50:10 +03:00
committed by Gary Russell
parent c7286cc991
commit fa03c6f268
24 changed files with 793 additions and 265 deletions

View File

@@ -8,7 +8,7 @@
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/jdbc http://www.springframework.org/schema/integration/jdbc/spring-integration-jdbc.xsd
http://www.springframework.org/schema/integration/jdbc http://www.springframework.org/schema/integration/jdbc/spring-integration-jdbc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
@@ -36,9 +36,9 @@
<int:logging-channel-adapter channel="output" />
<service-activator id="service-activator"
input-channel="input" output-channel="output"
input-channel="input" output-channel="output"
xmlns="http://www.springframework.org/schema/integration">
<beans:bean
<beans:bean
class="org.springframework.integration.jdbc.JdbcMessageStoreChannelIntegrationTests$Service" />
<poller fixed-rate="200">
<advice-chain>
@@ -69,4 +69,8 @@
<property name="dataSource" ref="dataSource" />
</bean>
<int:header-enricher input-channel="routingSlip" output-channel="input">
<int:routing-slip value="@myService.nextPath(request, reply)"/>
</int:header-enricher>
</beans>

View File

@@ -13,12 +13,15 @@
package org.springframework.integration.jdbc;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.NotSerializableException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
@@ -31,8 +34,11 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.serializer.support.SerializationFailedException;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.store.MessageGroup;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageDeliveryException;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.Repeat;
@@ -71,6 +77,9 @@ public class JdbcMessageStoreChannelIntegrationTests {
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
private MessageChannel routingSlip;
@Before
public void clear() {
Service.reset(1);
@@ -220,6 +229,21 @@ public class JdbcMessageStoreChannelIntegrationTests {
}
@Test
public void testWithRoutingSlip() {
try {
this.routingSlip.send(new GenericMessage<String>("foo"));
fail("MessageDeliveryException expected");
}
catch (Exception e) {
assertThat(e, instanceOf(MessageDeliveryException.class));
assertThat(e.getCause(), instanceOf(SerializationFailedException.class));
assertThat(e.getCause().getCause(), instanceOf(NotSerializableException.class));
assertThat(e.getMessage(),
containsString("org.springframework.integration.routingslip.ExpressionEvaluatingRoutingSlipRouteStrategy"));
}
}
public static class Service {
private static boolean fail = false;