INT-3550: RoutingSlip Improvements

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

* Make `RoutingSlipRouteStrategy#getNextPath` as `Object` return type. to allow to produce `MessageChannel` result, not only `beanName`
* Make `RoutingSlipHeaderValueMessageProcessor` ctor to accept `Object... routingSlipPath` instead of just String.
It is useful from JavaConfig, when we can use `RoutingSlipRouteStrategy` `@Bean` reference.
* Add JavaConfig test case to demonstrate how `RoutingSlipRouteStrategy` can get deal with inline `FixedSubscriberChannel`
and Lambdas together with Reactor Streams.

INT-3550: Polishing according PR comments

* Fix `AbstractMessageProducingHandler` to check if `nextPath` isn't empty String
* Add `RoutingSlipHeaderValueMessageProcessor` ctor check for the `routingSlipPath` entries types
* Add `RoutingSlipRouteStrategy` JavaDocs regarding the loop of strategy invocation
* Add `Process Manager` doc

Doc Polishing.
This commit is contained in:
Artem Bilan
2014-11-06 16:20:34 +02:00
committed by Gary Russell
parent cb50b1565c
commit 9f77fcd763
7 changed files with 222 additions and 19 deletions

View File

@@ -61,4 +61,6 @@
<aggregator input-channel="aggregate" expression="new java.util.ArrayList(#root)"/>
<beans:bean class="org.springframework.integration.routingslip.RoutingSlipTests$RoutingSlipConfiguration"/>
</beans:beans>

View File

@@ -16,11 +16,17 @@
package org.springframework.integration.routingslip;
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.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@@ -30,27 +36,55 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.IntegrationMessageHeaderAccessor;
import org.springframework.integration.annotation.BridgeTo;
import org.springframework.integration.annotation.Transformer;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.FixedSubscriberChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.history.MessageHistory;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.transformer.HeaderEnricher;
import org.springframework.integration.transformer.support.RoutingSlipHeaderValueMessageProcessor;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.GenericXmlContextLoader;
import reactor.core.Environment;
import reactor.core.composable.spec.Streams;
import reactor.spring.context.config.EnableReactor;
/**
* @author Artem Bilan
* @since 4.1
*/
@ContextConfiguration
@ContextConfiguration(loader = GenericXmlContextLoader.class)
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext
public class RoutingSlipTests {
@Autowired
private MessageChannel input;
@Autowired
private MessageChannel routingSlipHeaderChannel;
@Autowired
private PollableChannel resultsChannel;
@Autowired
private MessageChannel invalidRoutingSlipChannel;
@Test
@SuppressWarnings("unchecked")
public void testRoutingSlip() {
@@ -76,6 +110,42 @@ public class RoutingSlipTests {
}
}
@Test
public void testDynamicRoutingSlipRoutStrategy() {
this.routingSlipHeaderChannel.send(new GenericMessage<>("foo"));
Message<?> result = this.resultsChannel.receive(10000);
assertNotNull(result);
assertEquals("FOO", result.getPayload());
this.routingSlipHeaderChannel.send(new GenericMessage<>(2));
result = this.resultsChannel.receive(10000);
assertNotNull(result);
assertEquals(4, result.getPayload());
}
@Test
public void testInvalidRoutingSlipRoutStrategy() {
try {
new RoutingSlipHeaderValueMessageProcessor(new Date());
fail("IllegalArgumentException expected");
}
catch (Exception e) {
assertThat(e, instanceOf(IllegalArgumentException.class));
assertThat(e.getMessage(),
containsString("The RoutingSlip can contain " +
"only bean names of MessageChannel or RoutingSlipRouteStrategy, " +
"or MessageChannel and RoutingSlipRouteStrategy instances"));
}
try {
this.invalidRoutingSlipChannel.send(new GenericMessage<>("foo"));
fail("MessagingException expected");
}
catch (Exception e) {
assertThat(e, instanceOf(MessagingException.class));
assertThat(e.getMessage(), containsString("replyChannel must be a MessageChannel or String"));
}
}
public static class TestRoutingSlipRoutePojo {
final String[] channels = {"channel2", "channel3"};
@@ -98,10 +168,79 @@ public class RoutingSlipTests {
private AtomicBoolean invoked = new AtomicBoolean();
@Override
public String getNextPath(Message<?> requestMessage, Object reply) {
public Object getNextPath(Message<?> requestMessage, Object reply) {
return !invoked.getAndSet(true) ? "channel4" : null;
}
}
@Configuration
@EnableReactor
@EnableIntegration
public static class RoutingSlipConfiguration {
@Autowired
private Environment reactorEnv;
@Bean
public MessagingTemplate messagingTemplate() {
return new MessagingTemplate();
}
@Bean
public PollableChannel resultsChannel() {
return new QueueChannel();
}
@Bean
public RoutingSlipRouteStrategy routeStrategy() {
return (requestMessage, reply) -> requestMessage.getPayload() instanceof String
? new FixedSubscriberChannel(m ->
Streams.defer((String) m.getPayload())
.env(this.reactorEnv)
.get()
.map(String::toUpperCase)
.consume(v -> messagingTemplate().convertAndSend(resultsChannel(), v))
.flush())
: new FixedSubscriberChannel(m ->
Streams.defer((Integer) m.getPayload())
.env(this.reactorEnv)
.get()
.map(v -> v * 2)
.consume(v -> messagingTemplate().convertAndSend(resultsChannel(), v))
.flush());
}
@Bean
public MessageChannel routingSlipHeaderChannel() {
return new DirectChannel();
}
@Bean
@BridgeTo
public MessageChannel processChannel() {
return new DirectChannel();
}
@Bean
@Transformer(inputChannel = "routingSlipHeaderChannel", outputChannel = "processChannel")
public HeaderEnricher headerEnricher() {
return new HeaderEnricher(Collections.singletonMap(IntegrationMessageHeaderAccessor.ROUTING_SLIP,
new RoutingSlipHeaderValueMessageProcessor(routeStrategy())));
}
@Bean
public MessageChannel invalidRoutingSlipChannel() {
return new DirectChannel();
}
@Bean
@Transformer(inputChannel = "invalidRoutingSlipChannel", outputChannel = "processChannel")
public HeaderEnricher headerEnricher2() {
return new HeaderEnricher(Collections.singletonMap(IntegrationMessageHeaderAccessor.ROUTING_SLIP,
new RoutingSlipHeaderValueMessageProcessor((RoutingSlipRouteStrategy) (message, r) -> new Date())));
}
}
}