From b92d52fe749511ceee83d9b9b2c146ffc37837d9 Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Mon, 20 Sep 2021 10:31:36 -0400 Subject: [PATCH] Improve docs for global default poller * Fix Kotlin deprecation warning in the test * Replace wrong `IntervalTrigger` mentioning in the docs to the proper `PeriodicTrigger` * Fix code snippet in the `channel-adapter.adoc` --- .../integration/dsl/KotlinDslTests.kt | 2 +- src/reference/asciidoc/channel-adapter.adoc | 3 +- src/reference/asciidoc/endpoint.adoc | 84 ++++++++++++++++--- 3 files changed, 77 insertions(+), 12 deletions(-) diff --git a/spring-integration-core/src/test/kotlin/org/springframework/integration/dsl/KotlinDslTests.kt b/spring-integration-core/src/test/kotlin/org/springframework/integration/dsl/KotlinDslTests.kt index cc55bf31db..23bb5c5cb1 100644 --- a/spring-integration-core/src/test/kotlin/org/springframework/integration/dsl/KotlinDslTests.kt +++ b/spring-integration-core/src/test/kotlin/org/springframework/integration/dsl/KotlinDslTests.kt @@ -258,7 +258,7 @@ class KotlinDslTests { @Bean fun functionFlow2() = integrationFlow> { - transform { it.toLowerCase() } + transform { it.lowercase() } filter(UnexpiredMessageSelector()) route, Any?>({ null }) { defaultOutputToParentFlow() } route> { m -> m.headers.replyChannel } diff --git a/src/reference/asciidoc/channel-adapter.adoc b/src/reference/asciidoc/channel-adapter.adoc index c1d741d404..016e2671ef 100644 --- a/src/reference/asciidoc/channel-adapter.adoc +++ b/src/reference/asciidoc/channel-adapter.adoc @@ -124,6 +124,8 @@ However, if you are sure that your method can return null and you need to poll f ==== Starting with version 5.5, a `0` value for `max-messages-per-poll` has a special meaning - skip the `MessageSource.receive()` call altogether, which may be considered as pausing for this inbound channel adapter until the `maxMessagesPerPoll` is changed to a non-zero value at a later time, e.g. via a Control Bus. + +Also see <<./endpoint.adoc#global-default-poller>> for more information. ===== [[channel-adapter-namespace-outbound]] @@ -175,7 +177,6 @@ fun outboundChannelAdapterFlow(myPojo: MyPojo) = If the channel being adapted is a `PollableChannel`, you must provide a poller sub-element (the `@Poller` sub-annotation on the `@ServiceActivator`), as the following example shows: ==== ----- [source, java, role="primary"] .Java ---- diff --git a/src/reference/asciidoc/endpoint.adoc b/src/reference/asciidoc/endpoint.adoc index 2237b244d2..afe98c3b55 100644 --- a/src/reference/asciidoc/endpoint.adoc +++ b/src/reference/asciidoc/endpoint.adoc @@ -95,18 +95,17 @@ The following example shows how to set the trigger: ---- PollingConsumer consumer = new PollingConsumer(channel, handler); -consumer.setTrigger(new IntervalTrigger(30, TimeUnit.SECONDS)); +consumer.setTrigger(new PeriodicTrigger(30, TimeUnit.SECONDS)); ---- ==== -Spring Integration currently provides two implementations of the `Trigger` interface: `IntervalTrigger` and `CronTrigger`. -The `IntervalTrigger` is typically defined with a simple interval (in milliseconds) but also supports an `initialDelay` property and a boolean `fixedRate` property (the default is `false` -- that is, no fixed delay). +The `PeriodicTrigger` is typically defined with a simple interval (in milliseconds) but also supports an `initialDelay` property and a boolean `fixedRate` property (the default is `false` -- that is, no fixed delay). The following example sets both properties: ==== [source,java] ---- -IntervalTrigger trigger = new IntervalTrigger(1000); +PeriodicTrigger trigger = new PeriodicTrigger(1000); trigger.setInitialDelay(5000); trigger.setFixedRate(true); ---- @@ -316,22 +315,87 @@ It is also possible to create top-level pollers, in which case only a `ref` attr NOTE: The `ref` attribute is allowed only on the inner poller definitions. Defining this attribute on a top-level poller results in a configuration exception being thrown during initialization of the application context. -====== Global Default Pollers +[[global-default-poller]] +====== Global Default Poller To simplify the configuration even further, you can define a global default poller. -A single top-level poller within an `ApplicationContext` may have the `default` attribute set to `true`. -In that case, any endpoint with a `PollableChannel` for its input channel, that is defined within the same `ApplicationContext`, and has no explicitly configured `poller` sub-element uses that default. +A single top-level poller component in XML DSL may have the `default` attribute set to `true`. +For Java configuration a `PollerMetadata` bean with the `PollerMetadata.DEFAULT_POLLER` name must be declared in this case. +In that case, any endpoint with a `PollableChannel` for its input channel, that is defined within the same `ApplicationContext`, and has no explicitly configured `poller` uses that default. The following example shows such a poller and a transformer that uses it: -[source,xml] + +==== +[source, java, role="primary"] +.Java DSL ---- - +@Bean(name = PollerMetadata.DEFAULT_POLLER) +public PollerMetadata defaultPoller() { + PollerMetadata pollerMetadata = new PollerMetadata(); + pollerMetadata.setMaxMessagesPerPoll(5); + pollerMetadata.setTrigger(new PeriodicTrigger(3000)); + return pollerMetadata; +} + +// No 'poller' attribute because there is a default global poller +@Bean +public IntegrationFlow transformFlow(MyTransformer transformer) { + return IntegrationFlows.from(MessageChannels.queue("pollable")) + .transform(transformer) // No 'poller' attribute because there is a default global poller + .channel("output") + .get(); +} +---- +[source, java, role="secondary"] +.Java +---- +@Bean(PollerMetadata.DEFAULT_POLLER) +public PollerMetadata defaultPoller() { + PollerMetadata pollerMetadata = new PollerMetadata(); + pollerMetadata.setMaxMessagesPerPoll(5); + pollerMetadata.setTrigger(new PeriodicTrigger(3000)); + return pollerMetadata; +} + +@Bean +public QueueChannel pollable() { + return new QueueChannel(); +} +// No 'poller' attribute because there is a default global poller +@Transformer(inputChannel = "pollable", outputChannel = "output") +public Object transform(Object payload) { + ... +} +---- +[source, kotlin, role="secondary"] +.Kotlin DSL +---- +@Bean(PollerMetadata.DEFAULT_POLLER) +fun defaultPoller() = + PollerMetadata() + .also { + it.maxMessagesPerPoll = 5 + it.trigger = PeriodicTrigger(3000) + } + +@Bean +fun convertFlow() = + integrationFlow(MessageChannels.queue("pollable")) { + transform(transformer) // No 'poller' attribute because there is a default global poller + channel("output") + } +---- +[source, xml, role="secondary"] +.XML +---- + ---- +==== [[transaction-support]] ====== Transaction Support @@ -412,7 +476,7 @@ You should also keep in mind that the `task-executor` attribute can provide a re The `executor` element shown earlier is provided for convenience. As mentioned earlier in the <>, you can also configure a polling consumer in such a way as to emulate event-driven behavior. -With a long `receive-timeout` and a short `interval-trigger`, you can ensure a very timely reaction to arriving messages even on a polled message source. +With a long receive timeout and a short interval in the trigger, you can ensure a very timely reaction to arriving messages even on a polled message source. Note that this applies only to sources that have a blocking wait call with a timeout. For example, the file poller does not block. Each `receive()` call returns immediately and either contains new files or not.