From 70652739b862c443be2d7ba963c022b335af90fc Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Fri, 17 Mar 2017 16:41:31 -0400 Subject: [PATCH] JavaConfig/DSL Docs for Routers (Part 1) Also fix DSL messing `sendTimeout` property for routers. Since other message handlers have this property on the `ConsumerEndpointSpec`, add support there instead of `RouterSpec`, for consistency. Also fix `ConsumerEndpointSpec` `sendTimeout` - can be applied to any `AbstractMessageProducingHandler`, not just `AbstractReplyProducingMessageHandler`. Ditto for `async`. Polishing --- ...stractSimpleMessageHandlerFactoryBean.java | 7 +- .../integration/dsl/ConsumerEndpointSpec.java | 33 +++-- .../AbstractMessageProducingHandler.java | 4 +- src/reference/asciidoc/router.adoc | 138 +++++++++++++++++- 4 files changed, 164 insertions(+), 18 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractSimpleMessageHandlerFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractSimpleMessageHandlerFactoryBean.java index 6526fe5278..4ba429ddd8 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractSimpleMessageHandlerFactoryBean.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractSimpleMessageHandlerFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,7 @@ import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.integration.context.IntegrationObjectSupport; import org.springframework.integration.context.Orderable; import org.springframework.integration.core.MessageProducer; +import org.springframework.integration.handler.AbstractMessageProducingHandler; import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; import org.springframework.integration.support.context.NamedComponent; import org.springframework.messaging.MessageChannel; @@ -224,8 +225,8 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean the target {@link MessageHandler} implementation type. * * @author Artem Bilan + * @author Gary Russell * * @since 5.0 */ @@ -170,15 +173,19 @@ public abstract class ConsumerEndpointSpec, /** * @param sendTimeout the send timeout. * @return the endpoint spec. - * @see AbstractReplyProducingMessageHandler#setSendTimeout(long) + * @see AbstractMessageProducingHandler#setSendTimeout(long) */ public S sendTimeout(long sendTimeout) { assertHandler(); - if (this.handler instanceof AbstractReplyProducingMessageHandler) { - ((AbstractReplyProducingMessageHandler) this.handler).setSendTimeout(sendTimeout); + if (this.handler instanceof AbstractMessageProducingHandler) { + ((AbstractMessageProducingHandler) this.handler).setSendTimeout(sendTimeout); + } + else if (this.handler instanceof AbstractMessageRouter) { + // This should probably go on the RouterSpec, but we put it here for consistency + ((AbstractMessageRouter) this.handler).setSendTimeout(sendTimeout); } else { - logger.warn("'sendTimeout' can be applied only for AbstractReplyProducingMessageHandler"); + logger.warn("'sendTimeout' can be applied only for AbstractMessageProducingHandler"); } return _this(); } @@ -200,20 +207,22 @@ public abstract class ConsumerEndpointSpec, } /** - * Allow async replies. If the handler reply is a {@code ListenableFuture} send - * the output when it is satisfied rather than sending the future as the result. - * Only subclasses that support this feature should set it. + * Allow async replies. If the handler reply is a + * {@code org.springframework.util.concurrent.ListenableFuture}, send the output when + * it is satisfied rather than sending the future as the result. Ignored for handler + * return types other than + * {@link org.springframework.util.concurrent.ListenableFuture}. * @param async true to allow. * @return the endpoint spec. - * @see AbstractReplyProducingMessageHandler#setAsync(boolean) + * @see AbstractMessageProducingHandler#setAsync(boolean) */ public S async(boolean async) { assertHandler(); - if (this.handler instanceof AbstractReplyProducingMessageHandler) { - ((AbstractReplyProducingMessageHandler) this.handler).setAsync(async); + if (this.handler instanceof AbstractMessageProducingHandler) { + ((AbstractMessageProducingHandler) this.handler).setAsync(async); } else { - logger.warn("'async' can be applied only for AbstractReplyProducingMessageHandler"); + logger.warn("'async' can be applied only for AbstractMessageProducingHandler"); } return _this(); } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/AbstractMessageProducingHandler.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/AbstractMessageProducingHandler.java index 505d6d0eb1..4a98befd39 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/handler/AbstractMessageProducingHandler.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/AbstractMessageProducingHandler.java @@ -76,9 +76,9 @@ public abstract class AbstractMessageProducingHandler extends AbstractMessageHan } /** - * Allow async replies. If the handler reply is a {@link ListenableFuture} send + * Allow async replies. If the handler reply is a {@link ListenableFuture}, send * the output when it is satisfied rather than sending the future as the result. - * Only subclasses that support this feature should set it. + * Ignored for return types other than {@link ListenableFuture}. * @param async true to allow. * @since 4.3 */ diff --git a/src/reference/asciidoc/router.adoc b/src/reference/asciidoc/router.adoc index 8a0aa8d221..9ced017867 100644 --- a/src/reference/asciidoc/router.adoc +++ b/src/reference/asciidoc/router.adoc @@ -589,6 +589,55 @@ The example below demonstrates a `PayloadTypeRouter` configuration which is equi ---- +The equivalent router, using Java configuration: + +[source, java] +---- +@ServiceActivator(inputChannel = "routingChannel") +@Bean +public PayloadTypeRouter router() { + PayloadTypeRouter router = new PayloadTypeRouter(); + router.setChannelMapping(String.class.getName(), "stringChannel"); + router.setChannelMapping(Integer.class.getName(), "integerChannel"); + return router; +} +---- + +When using the Java DSL, there are two options; 1) define the router object as above... + +[source, java] +---- +@Bean +public IntegrationFlow routerFlow1() { + return IntegrationFlows.from("routingChannel") + .route(router()) + .get(); +} + +public PayloadTypeRouter router() { + PayloadTypeRouter router = new PayloadTypeRouter(); + router.setChannelMapping(String.class.getName(), "stringChannel"); + router.setChannelMapping(Integer.class.getName(), "integerChannel"); + return router; +} +---- + +Note that the router can be, but doesn't have to be, a `@Bean` - the flow will register it if it is not. + +2) define the routing function within the DSL flow itself... + +[source, java] +---- +@Bean +public IntegrationFlow routerFlow2() { + return IntegrationFlows.from("routingChannel") + .>route(Object::getClass, m -> m + .channelMapping(String.class, "stringChannel") + .channelMapping(Integer.class, "integerChannel")) + .get(); +} +---- + [[router-implementations-headervaluerouter]] ===== HeaderValueRouter @@ -627,6 +676,56 @@ However, in cases where the header value is mapped to a channel name but the cha IMPORTANT: With Spring Integration 2.1 the attribute was changed from `ignore-channel-name-resolution-failures` to `resolution-required`. Attribute `resolution-required` will default to `true`. +The equivalent router, using Java configuration: + +[source, java] +---- +@ServiceActivator(inputChannel = "routingChannel") +@Bean +public HeaderValueRouter router() { + HeaderValueRouter router = new HeaderValueRouter("testHeader"); + router.setChannelMapping("someHeaderValue", "channelA"); + router.setChannelMapping("someOtherHeaderValue", "channelB"); + return router; +} +---- + +When using the Java DSL, there are two options; 1) define the router object as above... + +[source, java] +---- +@Bean +public IntegrationFlow routerFlow1() { + return IntegrationFlows.from("routingChannel") + .route(router()) + .get(); +} + +public HeaderValueRouter router() { + HeaderValueRouter router = new HeaderValueRouter("testHeader"); + router.setChannelMapping("someHeaderValue", "channelA"); + router.setChannelMapping("someOtherHeaderValue", "channelB"); + return router; +} +---- + +Note that the router can be, but doesn't have to be, a `@Bean` - the flow will register it if it is not. + +2) define the routing function within the DSL flow itself... + +[source, java] +---- +@Bean +public IntegrationFlow routerFlow2() { + return IntegrationFlows.from("routingChannel") + ., String>route(m -> m.getHeaders().get("testHeader", String.class), m -> m + .channelMapping("someHeaderValue", "channelA") + .channelMapping("someOtherHeaderValue", "channelB"), + e -> e.id("headerValueRouter")) + .get(); +} +---- + _2. Configuration where mapping of header values to channel names is not required since header values themselves represent channel names_ @@ -676,8 +775,45 @@ Spring Integration also provides namespace support for the `RecipientListRouter` ---- +The equivalent router, using Java configuration: + +[source, java] +---- +@ServiceActivator(inputChannel = "routingChannel") +@Bean +public RecipientListRouter router() { + RecipientListRouter router = new RecipientListRouter(); + router.setSendTimeout(1_234L); + router.setIgnoreSendFailures(true); + router.setApplySequence(true); + router.addRecipient("channel1"); + router.addRecipient("channel2"); + router.addRecipient("channel3"); + return router; +} +---- + +The equivalent router, using the Java DSL: + +[source, java] +---- +@Bean +public IntegrationFlow routerFlow() { + return IntegrationFlows.from("routingChannel") + .routeToRecipients(r -> r + .applySequence(true) + .ignoreSendFailures(true) + .recipient("channel1") + .recipient("channel2") + .recipient("channel3"), c -> c.sendTimeout(1_234L)) + .get(); +} +---- + + + NOTE: The 'apply-sequence' flag here has the same effect as it does for a publish-subscribe-channel, and like a publish-subscribe-channel, it is disabled by default on the recipient-list-router. -Refer to<> for more information. +Refer to <> for more information. Another convenient option when configuring a `RecipientListRouter` 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".