Migrate to Antora Documentation

* Use `providers.provider` for `modifiedFiles`.
This defers the looking up of `modifiedFiles` until it is used. It
also allows other tasks to be ran from the non-primary work tree.
Working in another work tree is essential for Antora which will clone
each branch/tag into a new work tree to process it.

* Migrate Structure

* Insert explicit ids for headers

* Fix image::image

* Copy default antora files

* Fix indentation for all pages

* Split files

* Generate a default navigation

* Remove includes

* Fix cross references

* Enable Section Summary TOC for small pages

* Polish nav.adoc

* Fix duplicate ids

* Escape {firstname} and {lastname}

* Remove links to htmlsingle and pdf Formats

* Fix broken links

* Disable attributes for ${debezium-version}

* Migrate to Asciidoctor Tabs

* Move to src/reference/antora

* Remove Asciidoctor Build

* deploy-docs only spring-projects
This commit is contained in:
Rob Winch
2023-08-10 14:10:47 -05:00
committed by GitHub
parent f9dc75c739
commit eb6ddfe429
319 changed files with 21512 additions and 22487 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -0,0 +1,271 @@
* xref:index.adoc[Home]
* xref:preface.adoc[]
* xref:whats-new.adoc[]
* xref:overview.adoc[Overview]
* xref:core.adoc[]
** xref:channel.adoc[]
*** xref:channel/interfaces.adoc[]
*** xref:channel/implementations.adoc[]
*** xref:channel/interceptors.adoc[]
*** xref:channel/template.adoc[]
*** xref:channel/configuration.adoc[]
*** xref:channel/special-channels.adoc[]
** xref:polling-consumer.adoc[]
** xref:channel-adapter.adoc[]
** xref:bridge.adoc[]
* xref:message.adoc[]
* xref:message-routing.adoc[]
** xref:router.adoc[]
*** xref:router/overview.adoc[]
*** xref:router/common-parameters.adoc[]
*** xref:router/implementations.adoc[]
*** xref:router/namespace.adoc[]
*** xref:router/spel.adoc[]
*** xref:router/annotation.adoc[]
*** xref:router/dynamic-routers.adoc[]
*** xref:router/routing-slip.adoc[]
*** xref:router/process-manager.adoc[]
** xref:filter.adoc[]
** xref:splitter.adoc[]
** xref:aggregator.adoc[]
** xref:resequencer.adoc[]
** xref:chain.adoc[]
** xref:scatter-gather.adoc[]
** xref:barrier.adoc[]
* xref:message-transformation.adoc[]
** xref:transformer.adoc[]
** xref:content-enrichment.adoc[]
** xref:claim-check.adoc[]
** xref:codec.adoc[]
* xref:messaging-endpoints.adoc[]
** xref:endpoint.adoc[]
** xref:endpoint-roles.adoc[]
** xref:leadership-event-handling.adoc[]
** xref:gateway.adoc[]
** xref:service-activator.adoc[]
** xref:delayer.adoc[]
** xref:scripting.adoc[]
** xref:groovy.adoc[]
** xref:handler-advice.adoc[]
*** xref:handler-advice/classes.adoc[]
*** xref:handler-advice/reactive.adoc[]
*** xref:handler-advice/context-holder.adoc[]
*** xref:handler-advice/custom.adoc[]
*** xref:handler-advice/other.adoc[]
*** xref:handler-advice/handle-message.adoc[]
*** xref:handler-advice/tx-handle-message.adoc[]
*** xref:handler-advice/advising-filters.adoc[]
*** xref:handler-advice/advising-with-annotations.adoc[]
*** xref:handler-advice/order.adoc[]
*** xref:handler-advice/advised-properties.adoc[]
*** xref:handler-advice/idempotent-receiver.adoc[]
** xref:logging-adapter.adoc[]
*** xref:functions-support.adoc[]
** xref:kotlin-functions.adoc[]
* xref:dsl.adoc[]
** xref:dsl/java-basics.adoc[]
** xref:dsl/java-channels.adoc[]
** xref:dsl/java-pollers.adoc[]
** xref:dsl/java-reactive.adoc[]
** xref:dsl/java-endpoints.adoc[]
** xref:dsl/java-transformers.adoc[]
** xref:dsl/java-inbound-adapters.adoc[]
** xref:dsl/java-routers.adoc[]
** xref:dsl/java-splitters.adoc[]
** xref:dsl/java-aggregators.adoc[]
** xref:dsl/java-handle.adoc[]
** xref:dsl/java-gateway.adoc[]
** xref:dsl/java-log.adoc[]
** xref:dsl/java-intercept.adoc[]
** xref:dsl/java-wiretap.adoc[]
** xref:dsl/java-flows.adoc[]
** xref:dsl/java-function-expression.adoc[]
** xref:dsl/java-subflows.adoc[]
** xref:dsl/java-protocol-adapters.adoc[]
** xref:dsl/java-flow-adapter.adoc[]
** xref:dsl/java-runtime-flows.adoc[]
** xref:dsl/integration-flow-as-gateway.adoc[]
** xref:dsl/java-extensions.adoc[]
** xref:dsl/integration-flows-composition.adoc[]
* xref:groovy-dsl.adoc[]
* xref:kotlin-dsl.adoc[]
* xref:system-management.adoc[]
** xref:metrics.adoc[]
** xref:message-history.adoc[]
** xref:message-store.adoc[]
** xref:meta-data-store.adoc[]
** xref:control-bus.adoc[]
** xref:shutdown.adoc[]
** xref:graph.adoc[]
** xref:integration-graph-controller.adoc[]
* xref:reactive-streams.adoc[]
* xref:native-aot.adoc[]
* xref:endpoint-summary.adoc[]
* xref:amqp.adoc[AMQP Support]
** xref:amqp/inbound-channel-adapter.adoc[]
** xref:amqp/polled-inbound-channel-adapter.adoc[]
** xref:amqp/inbound-gateway.adoc[]
** xref:amqp/inbound-ack.adoc[]
** xref:amqp/outbound-endpoints.adoc[]
** xref:amqp/outbound-channel-adapter.adoc[]
** xref:amqp/outbound-gateway.adoc[]
** xref:amqp/async-outbound-gateway.adoc[]
** xref:amqp/alternative-confirms-returns.adoc[]
** xref:amqp/conversion-inbound.adoc[]
** xref:amqp/content-type-conversion-outbound.adoc[]
** xref:amqp/user-id.adoc[]
** xref:amqp/delay.adoc[]
** xref:amqp/channels.adoc[]
** xref:amqp/message-headers.adoc[]
** xref:amqp/strict-ordering.adoc[]
** xref:amqp/samples.adoc[]
** xref:amqp/rmq-streams.adoc[]
* xref:camel.adoc[]
* xref:cassandra.adoc[]
* xref:debezium.adoc[]
* xref:event.adoc[]
* xref:feed.adoc[]
* xref:file.adoc[]
** xref:file/reading.adoc[]
** xref:file/writing.adoc[]
** xref:file/transforming.adoc[]
** xref:file/splitter.adoc[]
** xref:file/aggregator.adoc[]
** xref:file/remote-persistent-flf.adoc[]
* xref:ftp.adoc[]
** xref:ftp/session-factory.adoc[]
** xref:ftp/advanced-configuration.adoc[]
** xref:ftp/dsf.adoc[]
** xref:ftp/inbound.adoc[]
** xref:ftp/streaming.adoc[]
** xref:ftp/rotating-server-advice.adoc[]
** xref:ftp/max-fetch.adoc[]
** xref:ftp/outbound.adoc[]
** xref:ftp/outbound-gateway.adoc[]
** xref:ftp/session-caching.adoc[]
** xref:ftp/rft.adoc[]
** xref:ftp/session-callback.adoc[]
** xref:ftp/server-events.adoc[]
** xref:ftp/remote-file-info.adoc[]
* xref:graphql.adoc[]
* xref:hazelcast.adoc[]
* xref:http.adoc[]
** xref:http/inbound.adoc[]
** xref:http/outbound.adoc[]
** xref:http/namespace.adoc[]
** xref:http/java-config.adoc[]
** xref:http/timeout.adoc[]
** xref:http/proxy.adoc[]
** xref:http/header-mapping.adoc[]
** xref:http/int-graph-controller.adoc[]
** xref:http/samples.adoc[]
* xref:jdbc.adoc[]
** xref:jdbc/inbound-channel-adapter.adoc[]
** xref:jdbc/outbound-channel-adapter.adoc[]
** xref:jdbc/outbound-gateway.adoc[]
** xref:jdbc/message-store.adoc[]
** xref:jdbc/stored-procedures.adoc[]
** xref:jdbc/lock-registry.adoc[]
** xref:jdbc/metadata-store.adoc[]
* xref:jpa.adoc[]
** xref:jpa/functionality.adoc[]
** xref:jpa/supported-persistence-providers.adoc[]
** xref:jpa/java-implementation.adoc[]
** xref:jpa/namespace-support.adoc[]
** xref:jpa/inbound-channel-adapter.adoc[]
** xref:jpa/outbound-channel-adapter.adoc[]
** xref:jpa/outbound-gateways.adoc[]
* xref:jms.adoc[]
* xref:jmx.adoc[]
* xref:kafka.adoc[]
* xref:mail.adoc[]
* xref:mongodb.adoc[]
* xref:mqtt.adoc[]
* xref:r2dbc.adoc[]
* xref:redis.adoc[]
* xref:resource.adoc[]
* xref:rsocket.adoc[]
* xref:sftp.adoc[]
** xref:sftp/session-factory.adoc[]
** xref:sftp/dsf.adoc[]
** xref:sftp/session-caching.adoc[]
** xref:sftp/rft.adoc[]
** xref:sftp/inbound.adoc[]
** xref:sftp/streaming.adoc[]
** xref:sftp/rotating-server-advice.adoc[]
** xref:sftp/max-fetch.adoc[]
** xref:sftp/outbound.adoc[]
** xref:sftp/outbound-gateway.adoc[]
** xref:sftp/session-callback.adoc[]
** xref:sftp/server-events.adoc[]
** xref:sftp/remote-file-info.adoc[]
* xref:smb.adoc[]
* xref:stomp.adoc[]
* xref:stream.adoc[]
* xref:syslog.adoc[]
* xref:ip.adoc[]
** xref:ip/intro.adoc[]
** xref:ip/udp-adapters.adoc[]
** xref:ip/tcp-connection-factories.adoc[]
** xref:ip/testing-connections.adoc[]
** xref:ip/interceptors.adoc[]
** xref:ip/tcp-events.adoc[]
** xref:ip/tcp-adapters.adoc[]
** xref:ip/tcp-gateways.adoc[]
** xref:ip/correlation.adoc[]
** xref:ip/note-nio.adoc[]
** xref:ip/ssl-tls.adoc[]
** xref:ip/tcp-advanced-techniques.adoc[]
** xref:ip/endpoint-reference.adoc[]
** xref:ip/msg-headers.adoc[]
** xref:ip/annotation.adoc[]
** xref:ip/dsl.adoc[]
* xref:webflux.adoc[]
* xref:web-sockets.adoc[]
* xref:ws.adoc[]
* xref:xml.adoc[]
** xref:xml/xpath-namespace-support.adoc[]
** xref:xml/transformation.adoc[]
** xref:xml/xpath-transformer.adoc[]
** xref:xml/xpath-splitting.adoc[]
** xref:xml/xpath-routing.adoc[]
** xref:xml/xpath-header-enricher.adoc[]
** xref:xml/xpath-filter.adoc[]
** xref:xml/xpath-spel-function.adoc[]
** xref:xml/validating-filter.adoc[]
* xref:xmpp.adoc[]
* xref:zeromq.adoc[]
* xref:zookeeper.adoc[]
* xref:error-handling.adoc[]
* xref:spel.adoc[]
* xref:message-publishing.adoc[]
* xref:transactions.adoc[]
* xref:security.adoc[]
* xref:configuration.adoc[]
** xref:configuration/namespace.adoc[]
** xref:configuration/namespace-taskscheduler.adoc[]
** xref:configuration/global-properties.adoc[]
** xref:configuration/annotations.adoc[]
** xref:configuration/meta-annotations.adoc[]
** xref:configuration/message-mapping-rules.adoc[]
* xref:testing.adoc[]
* xref:samples.adoc[]
* xref:resources.adoc[]
* xref:history.adoc[]
** xref:changes-6.0-6.1.adoc[]
** xref:changes-5.5-6.0.adoc[]
** xref:changes-5.4-5.5.adoc[]
** xref:changes-5.3-5.4.adoc[]
** xref:changes-5.2-5.3.adoc[]
** xref:changes-5.1-5.2.adoc[]
** xref:changes-5.0-5.1.adoc[]
** xref:changes-4.3-5.0.adoc[]
** xref:changes-4.2-4.3.adoc[]
** xref:changes-4.1-4.2.adoc[]
** xref:changes-4.0-4.1.adoc[]
** xref:changes-3.0-4.0.adoc[]
** xref:changes-2.2-3.0.adoc[]
** xref:changes-2.1-2.2.adoc[]
** xref:changes-2.0-2.1.adoc[]
** xref:changes-1.0-2.0.adoc[]
* xref:zip.adoc[]

View File

@@ -0,0 +1,940 @@
[[aggregator]]
= Aggregator
Basically a mirror-image of the splitter, the aggregator is a type of message handler that receives multiple messages and combines them into a single message.
In fact, an aggregator is often a downstream consumer in a pipeline that includes a splitter.
Technically, the aggregator is more complex than a splitter, because it is stateful.
It must hold the messages to be aggregated and determine when the complete group of messages is ready to be aggregated.
In order to do so, it requires a `MessageStore`.
[[aggregator-functionality]]
== Functionality
The Aggregator combines a group of related messages, by correlating and storing them, until the group is deemed to be complete.
At that point, the aggregator creates a single message by processing the whole group and sends the aggregated message as output.
Implementing an aggregator requires providing the logic to perform the aggregation (that is, the creation of a single message from many).
Two related concepts are correlation and release.
Correlation determines how messages are grouped for aggregation.
In Spring Integration, correlation is done by default, based on the `IntegrationMessageHeaderAccessor.CORRELATION_ID` message header.
Messages with the same `IntegrationMessageHeaderAccessor.CORRELATION_ID` are grouped together.
However, you can customize the correlation strategy to allow other ways of specifying how the messages should be grouped together.
To do so, you can implement a `CorrelationStrategy` (covered later in this chapter).
To determine the point at which a group of messages is ready to be processed, a `ReleaseStrategy` is consulted.
The default release strategy for the aggregator releases a group when all messages included in a sequence are present, based on the `IntegrationMessageHeaderAccessor.SEQUENCE_SIZE` header.
You can override this default strategy by providing a reference to a custom `ReleaseStrategy` implementation.
[[aggregator-api]]
== Programming Model
The Aggregation API consists of a number of classes:
* The interface `MessageGroupProcessor`, and its subclasses: `MethodInvokingAggregatingMessageGroupProcessor` and `ExpressionEvaluatingMessageGroupProcessor`
* The `ReleaseStrategy` interface and its default implementation: `SimpleSequenceSizeReleaseStrategy`
* The `CorrelationStrategy` interface and its default implementation: `HeaderAttributeCorrelationStrategy`
[[aggregatingmessagehandler]]
=== `AggregatingMessageHandler`
The `AggregatingMessageHandler` (a subclass of `AbstractCorrelatingMessageHandler`) is a `MessageHandler` implementation, encapsulating the common functionality of an aggregator (and other correlating use cases), which are as follows:
* Correlating messages into a group to be aggregated
* Maintaining those messages in a `MessageStore` until the group can be released
* Deciding when the group can be released
* Aggregating the released group into a single message
* Recognizing and responding to an expired group
The responsibility for deciding how the messages should be grouped together is delegated to a `CorrelationStrategy` instance.
The responsibility for deciding whether the message group can be released is delegated to a `ReleaseStrategy` instance.
The following listing shows a brief highlight of the base `AbstractAggregatingMessageGroupProcessor` (the responsibility for implementing the `aggregatePayloads` method is left to the developer):
[source,java]
----
public abstract class AbstractAggregatingMessageGroupProcessor
implements MessageGroupProcessor {
protected Map<String, Object> aggregateHeaders(MessageGroup group) {
// default implementation exists
}
protected abstract Object aggregatePayloads(MessageGroup group, Map<String, Object> defaultHeaders);
}
----
See `DefaultAggregatingMessageGroupProcessor`, `ExpressionEvaluatingMessageGroupProcessor` and `MethodInvokingMessageGroupProcessor` as out-of-the-box implementations of the `AbstractAggregatingMessageGroupProcessor`.
Starting with version 5.2, a `Function<MessageGroup, Map<String, Object>>` strategy is available for the `AbstractAggregatingMessageGroupProcessor` to merge and compute (aggregate) headers for an output message.
The `DefaultAggregateHeadersFunction` implementation is available with logic that returns all headers that have no conflicts among the group; an absent header on one or more messages within the group is not considered a conflict.
Conflicting headers are omitted.
Along with the newly introduced `DelegatingMessageGroupProcessor`, this function is used for any arbitrary (non-`AbstractAggregatingMessageGroupProcessor`) `MessageGroupProcessor` implementation.
Essentially, the framework injects a provided function into an `AbstractAggregatingMessageGroupProcessor` instance and wraps all other implementations into a `DelegatingMessageGroupProcessor`.
The difference in logic between the `AbstractAggregatingMessageGroupProcessor` and the `DelegatingMessageGroupProcessor` that the latter doesn't compute headers in advance, before calling the delegate strategy, and doesn't invoke the function if the delegate returns a `Message` or `AbstractIntegrationMessageBuilder`.
In that case, the framework assumes that the target implementation has taken care of producing a proper set of headers populated into the returned result.
The `Function<MessageGroup, Map<String, Object>>` strategy is available as the `headers-function` reference attribute for XML configuration, as the `AggregatorSpec.headersFunction()` option for the Java DSL and as `AggregatorFactoryBean.setHeadersFunction()` for plain Java configuration.
The `CorrelationStrategy` is owned by the `AbstractCorrelatingMessageHandler` and has a default value based on the `IntegrationMessageHeaderAccessor.CORRELATION_ID` message header, as the following example shows:
[source,java]
----
public AbstractCorrelatingMessageHandler(MessageGroupProcessor processor, MessageGroupStore store,
CorrelationStrategy correlationStrategy, ReleaseStrategy releaseStrategy) {
...
this.correlationStrategy = correlationStrategy == null ?
new HeaderAttributeCorrelationStrategy(IntegrationMessageHeaderAccessor.CORRELATION_ID) : correlationStrategy;
this.releaseStrategy = releaseStrategy == null ? new SimpleSequenceSizeReleaseStrategy() : releaseStrategy;
...
}
----
As for the actual processing of the message group, the default implementation is the `DefaultAggregatingMessageGroupProcessor`.
It creates a single `Message` whose payload is a `List` of the payloads received for a given group.
This works well for simple scatter-gather implementations with a splitter, a publish-subscribe channel, or a recipient list router upstream.
NOTE: When using a publish-subscribe channel or a recipient list router in this type of scenario, be sure to enable the `apply-sequence` flag.
Doing so adds the necessary headers: `CORRELATION_ID`, `SEQUENCE_NUMBER`, and `SEQUENCE_SIZE`.
That behavior is enabled by default for splitters in Spring Integration, but it is not enabled for publish-subscribe channels or for recipient list routers because those components may be used in a variety of contexts in which these headers are not necessary.
When implementing a specific aggregator strategy for an application, you can extend `AbstractAggregatingMessageGroupProcessor` and implement the `aggregatePayloads` method.
However, there are better solutions, less coupled to the API, for implementing the aggregation logic, which can be configured either through XML or through annotations.
In general, any POJO can implement the aggregation algorithm if it provides a method that accepts a single `java.util.List` as an argument (parameterized lists are supported as well).
This method is invoked for aggregating messages as follows:
* If the argument is a `java.util.Collection<T>` and the parameter type T is assignable to `Message`, the whole list of messages accumulated for aggregation is sent to the aggregator.
* If the argument is a non-parameterized `java.util.Collection` or the parameter type is not assignable to `Message`, the method receives the payloads of the accumulated messages.
* If the return type is not assignable to `Message`, it is treated as the payload for a `Message` that is automatically created by the framework.
NOTE: In the interest of code simplicity and promoting best practices such as low coupling, testability, and others, the preferred way of implementing the aggregation logic is through a POJO and using the XML or annotation support for configuring it in the application.
Starting with version 5.3, after processing message group, an `AbstractCorrelatingMessageHandler` performs a `MessageBuilder.popSequenceDetails()` message headers modification for the proper splitter-aggregator scenario with several nested levels.
It is done only if the message group release result is not a collection of messages.
In that case a target `MessageGroupProcessor` is responsible for the `MessageBuilder.popSequenceDetails()` call while building those messages.
If the `MessageGroupProcessor` returns a `Message`, a `MessageBuilder.popSequenceDetails()` will be performed on the output message only if the `sequenceDetails` matches with first message in group.
(Previously this has been done only if a plain payload or an `AbstractIntegrationMessageBuilder` has been returned from the `MessageGroupProcessor`.)
This functionality can be controlled by a new `popSequence` `boolean` property, so the `MessageBuilder.popSequenceDetails()` can be disabled in some scenarios when correlation details have not been populated by the standard splitter.
This property, essentially, undoes what has been done by the nearest upstream `applySequence = true` in the `AbstractMessageSplitter`.
See xref:splitter.adoc[Splitter] for more information.
[[agg-message-collection]]
IMPORTANT: The `SimpleMessageGroup.getMessages()` method returns an `unmodifiableCollection`.
Therefore, if an aggregating POJO method has a `Collection<Message>` parameter, the argument passed in is exactly that `Collection` instance and, when you use a `SimpleMessageStore` for the aggregator, that original `Collection<Message>` is cleared after releasing the group.
Consequently, the `Collection<Message>` variable in the POJO is cleared too, if it is passed out of the aggregator.
If you wish to simply release that collection as-is for further processing, you must build a new `Collection` (for example, `new ArrayList<Message>(messages)`).
Starting with version 4.3, the framework no longer copies the messages to a new collection, to avoid undesired extra object creation.
Prior to version 4.2, it was not possible to provide a `MessageGroupProcessor` by using XML configuration.
Only POJO methods could be used for aggregation.
Now, if the framework detects that the referenced (or inner) bean implements `MessageProcessor`, it is used as the aggregator's output processor.
If you wish to release a collection of objects from a custom `MessageGroupProcessor` as the payload of a message, your class should extend `AbstractAggregatingMessageGroupProcessor` and implement `aggregatePayloads()`.
Also, since version 4.2, a `SimpleMessageGroupProcessor` is provided.
It returns the collection of messages from the group, which, as indicated earlier, causes the released messages to be sent individually.
This lets the aggregator work as a message barrier, where arriving messages are held until the release strategy fires and the group is released as a sequence of individual messages.
Starting with version 6.0, the splitting behaviour, described above, works only if the group processor is a `SimpleMessageGroupProcessor`.
Otherwise, with any other `MessageGroupProcessor` implementation that returns a `Collection<Message>`, only a single reply message is emitted with the whole collection of messages as its payload.
Such logic is dictated by the canonical purpose of an aggregator - collect request messages by some key and produce a single grouped message.
[[releasestrategy]]
=== `ReleaseStrategy`
The `ReleaseStrategy` interface is defined as follows:
[source,java]
----
public interface ReleaseStrategy {
boolean canRelease(MessageGroup group);
}
----
In general, any POJO can implement the completion decision logic if it provides a method that accepts a single `java.util.List` as an argument (parameterized lists are supported as well) and returns a boolean value.
This method is invoked after the arrival of each new message, to decide whether the group is complete or not, as follows:
* If the argument is a `java.util.List<T>` and the parameter type `T` is assignable to `Message`, the whole list of messages accumulated in the group is sent to the method.
* If the argument is a non-parametrized `java.util.List` or the parameter type is not assignable to `Message`, the method receives the payloads of the accumulated messages.
* The method must return `true` if the message group is ready for aggregation or false otherwise.
The following example shows how to use the `@ReleaseStrategy` annotation for a `List` of type `Message`:
[source,java]
----
public class MyReleaseStrategy {
@ReleaseStrategy
public boolean canMessagesBeReleased(List<Message<?>>) {...}
}
----
The following example shows how to use the `@ReleaseStrategy` annotation for a `List` of type `String`:
[source,java]
----
public class MyReleaseStrategy {
@ReleaseStrategy
public boolean canMessagesBeReleased(List<String>) {...}
}
----
Based on the signatures in the preceding two examples, the POJO-based release strategy is passed a `Collection` of not-yet-released messages (if you need access to the whole `Message`) or a `Collection` of payload objects (if the type parameter is anything other than `Message`).
This satisfies the majority of use cases.
However, if, for some reason, you need to access the full `MessageGroup`, you should provide an implementation of the `ReleaseStrategy` interface.
[WARNING]
=====
When handling potentially large groups, you should understand how these methods are invoked, because the release strategy may be invoked multiple times before the group is released.
The most efficient is an implementation of `ReleaseStrategy`, because the aggregator can invoke it directly.
The second most efficient is a POJO method with a `Collection<Message<?>>` parameter type.
The least efficient is a POJO method with a `Collection<Something>` type.
The framework has to copy the payloads from the messages in the group into a new collection (and possibly attempt conversion on the payloads to `Something`) every time the release strategy is called.
Using `Collection<?>` avoids the conversion but still requires creating the new `Collection`.
For these reasons, for large groups, we recommended that you implement `ReleaseStrategy`.
=====
When the group is released for aggregation, all its not-yet-released messages are processed and removed from the group.
If the group is also complete (that is, if all messages from a sequence have arrived or if there is no sequence defined), then the group is marked as complete.
Any new messages for this group are sent to the discard channel (if defined).
Setting `expire-groups-upon-completion` to `true` (the default is `false`) removes the entire group, and any new messages (with the same correlation ID as the removed group) form a new group.
You can release partial sequences by using a `MessageGroupStoreReaper` together with `send-partial-result-on-expiry` being set to `true`.
IMPORTANT: To facilitate discarding of late-arriving messages, the aggregator must maintain state about the group after it has been released.
This can eventually cause out-of-memory conditions.
To avoid such situations, you should consider configuring a `MessageGroupStoreReaper` to remove the group metadata.
The expiry parameters should be set to expire groups once a point has been reach after which late messages are not expected to arrive.
For information about configuring a reaper, see xref:aggregator.adoc#reaper[Managing State in an Aggregator: `MessageGroupStore`].
Spring Integration provides an implementation for `ReleaseStrategy`: `SimpleSequenceSizeReleaseStrategy`.
This implementation consults the `SEQUENCE_NUMBER` and `SEQUENCE_SIZE` headers of each arriving message to decide when a message group is complete and ready to be aggregated.
As shown earlier, it is also the default strategy.
NOTE: Before version 5.0, the default release strategy was `SequenceSizeReleaseStrategy`, which does not perform well with large groups.
With that strategy, duplicate sequence numbers are detected and rejected.
This operation can be expensive.
If you are aggregating large groups, you don't need to release partial groups, and you don't need to detect/reject duplicate sequences, consider using the `SimpleSequenceSizeReleaseStrategy` instead - it is much more efficient for these use cases, and is the default since _version 5.0_ when partial group release is not specified.
[[aggregating-large-groups]]
=== Aggregating Large Groups
The 4.3 release changed the default `Collection` for messages in a `SimpleMessageGroup` to `HashSet` (it was previously a `BlockingQueue`).
This was expensive when removing individual messages from large groups (an O(n) linear scan was required).
Although the hash set is generally much faster to remove, it can be expensive for large messages, because the hash has to be calculated on both inserts and removes.
If you have messages that are expensive to hash, consider using some other collection type.
As discussed in xref:message-store.adoc#message-group-factory[Using `MessageGroupFactory`], a `SimpleMessageGroupFactory` is provided so that you can select the `Collection` that best suits your needs.
You can also provide your own factory implementation to create some other `Collection<Message<?>>`.
The following example shows how to configure an aggregator with the previous implementation and a `SimpleSequenceSizeReleaseStrategy`:
[source, xml]
----
<int:aggregator input-channel="aggregate"
output-channel="out" message-store="store" release-strategy="releaser" />
<bean id="store" class="org.springframework.integration.store.SimpleMessageStore">
<property name="messageGroupFactory">
<bean class="org.springframework.integration.store.SimpleMessageGroupFactory">
<constructor-arg value="BLOCKING_QUEUE"/>
</bean>
</property>
</bean>
<bean id="releaser" class="SimpleSequenceSizeReleaseStrategy" />
----
NOTE: If the filter endpoint is involved in the flow upstream of an aggregator, the sequence size release strategy (fixed or based on the `sequenceSize` header) is not going to serve its purpose because some messages from a sequence may be discarded by the filter.
In this case it is recommended to choose another `ReleaseStrategy`, or use compensation messages sent from a discard sub-flow carrying some information in their content to be skipped in a custom complete group function.
See xref:filter.adoc[Filter] for more information.
[[correlation-strategy]]
=== Correlation Strategy
The `CorrelationStrategy` interface is defined as follows:
[source,java]
----
public interface CorrelationStrategy {
Object getCorrelationKey(Message<?> message);
}
----
The method returns an `Object` that represents the correlation key used for associating the message with a message group.
The key must satisfy the criteria used for a key in a `Map` with respect to the implementation of `equals()` and `hashCode()`.
In general, any POJO can implement the correlation logic, and the rules for mapping a message to a method's argument (or arguments) are the same as for a `ServiceActivator` (including support for `@Header` annotations).
The method must return a value, and the value must not be `null`.
Spring Integration provides an implementation for `CorrelationStrategy`: `HeaderAttributeCorrelationStrategy`.
This implementation returns the value of one of the message headers (whose name is specified by a constructor argument) as the correlation key.
By default, the correlation strategy is a `HeaderAttributeCorrelationStrategy` that returns the value of the `CORRELATION_ID` header attribute.
If you have a custom header name you would like to use for correlation, you can configure it on an instance of `HeaderAttributeCorrelationStrategy` and provide that as a reference for the aggregator's correlation strategy.
[[lock-registry]]
=== Lock Registry
Changes to groups are thread safe.
So, when you send messages for the same correlation ID concurrently, only one of them will be processed in the aggregator, making it effectively as a *single-threaded per message group*.
A `LockRegistry` is used to obtain a lock for the resolved correlation ID.
A `DefaultLockRegistry` is used by default (in-memory).
For synchronizing updates across servers where a shared `MessageGroupStore` is being used, you must configure a shared lock registry.
[[aggregator-deadlocks]]
=== Avoiding Deadlocks
As discussed above, when message groups are mutated (messages added or released) a lock is held.
Consider the following flow:
[source]
----
...->aggregator1-> ... ->aggregator2-> ...
----
If there are multiple threads, **and the aggregators share a common lock registry**, it is possible to get a deadlock.
This will cause hung threads and `jstack <pid>` might present a result such as:
[source]
----
Found one Java-level deadlock:
=============================
"t2":
waiting for ownable synchronizer 0x000000076c1cbfa0, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
which is held by "t1"
"t1":
waiting for ownable synchronizer 0x000000076c1ccc00, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
which is held by "t2"
----
There are several ways to avoid this problem:
* ensure each aggregator has its own lock registry (this can be a shared registry across application instances but two or more aggregators in the flow must each have a distinct registry)
* use an `ExecutorChannel` or `QueueChannel` as the output channel of the aggregator so that the downstream flow runs on a new thread
* starting with version 5.1.1, set the `releaseLockBeforeSend` aggregator property to `true`
NOTE: This problem can also be caused if, for some reason, the output of a single aggregator is eventually routed back to the same aggregator.
Of course, the first solution above does not apply in this case.
[[aggregator-java-dsl]]
== Configuring an Aggregator in Java DSL
See xref:dsl/java-aggregators.adoc[Aggregators and Resequencers] for how to configure an aggregator in Java DSL.
[[aggregator-xml]]
=== Configuring an Aggregator with XML
Spring Integration supports the configuration of an aggregator with XML through the `<aggregator/>` element.
The following example shows an example of an aggregator:
[source,xml]
----
<channel id="inputChannel"/>
<int:aggregator id="myAggregator" <1>
auto-startup="true" <2>
input-channel="inputChannel" <3>
output-channel="outputChannel" <4>
discard-channel="throwAwayChannel" <5>
message-store="persistentMessageStore" <6>
order="1" <7>
send-partial-result-on-expiry="false" <8>
send-timeout="1000" <9>
correlation-strategy="correlationStrategyBean" <10>
correlation-strategy-method="correlate" <11>
correlation-strategy-expression="headers['foo']" <12>
ref="aggregatorBean" <13>
method="aggregate" <14>
release-strategy="releaseStrategyBean" <15>
release-strategy-method="release" <16>
release-strategy-expression="size() == 5" <17>
expire-groups-upon-completion="false" <18>
empty-group-min-timeout="60000" <19>
lock-registry="lockRegistry" <20>
group-timeout="60000" <21>
group-timeout-expression="size() ge 2 ? 100 : -1" <22>
expire-groups-upon-timeout="true" <23>
scheduler="taskScheduler" > <24>
<expire-transactional/> <25>
<expire-advice-chain/> <26>
</aggregator>
<int:channel id="outputChannel"/>
<int:channel id="throwAwayChannel"/>
<bean id="persistentMessageStore" class="org.springframework.integration.jdbc.store.JdbcMessageStore">
<constructor-arg ref="dataSource"/>
</bean>
<bean id="aggregatorBean" class="sample.PojoAggregator"/>
<bean id="releaseStrategyBean" class="sample.PojoReleaseStrategy"/>
<bean id="correlationStrategyBean" class="sample.PojoCorrelationStrategy"/>
----
<1> The id of the aggregator is optional.
<2> Lifecycle attribute signaling whether the aggregator should be started during application context startup.
Optional (the default is 'true').
<3> The channel from which where aggregator receives messages.
Required.
<4> The channel to which the aggregator sends the aggregation results.
Optional (because incoming messages can themselves specify a reply channel in the 'replyChannel' message header).
<5> The channel to which the aggregator sends the messages that timed out (if `send-partial-result-on-expiry` is `false`).
Optional.
<6> A reference to a `MessageGroupStore` used to store groups of messages under their correlation key until they are complete.
Optional.
By default, it is a volatile in-memory store.
See xref:message-store.adoc[Message Store] for more information.
<7> The order of this aggregator when more than one handle is subscribed to the same `DirectChannel` (use for load-balancing purposes).
Optional.
<8> Indicates that expired messages should be aggregated and sent to the 'output-channel' or 'replyChannel' once their containing `MessageGroup` is expired (see https://docs.spring.io/spring-integration/api/org/springframework/integration/store/MessageGroupStore.html#expireMessageGroups-long[`MessageGroupStore.expireMessageGroups(long)`]).
One way of expiring a `MessageGroup` is by configuring a `MessageGroupStoreReaper`.
However, you can alternatively expire `MessageGroup` by calling `MessageGroupStore.expireMessageGroups(timeout)`.
You can accomplish that through a Control Bus operation or, if you have a reference to the `MessageGroupStore` instance, by invoking `expireMessageGroups(timeout)`.
Otherwise, by itself, this attribute does nothing.
It serves only as an indicator of whether to discard or send to the output or reply channel any messages that are still in the `MessageGroup` that is about to be expired.
Optional (the default is `false`).
NOTE: This attribute might more properly be called `send-partial-result-on-timeout`, because the group may not actually expire if `expire-groups-upon-timeout` is set to `false`.
<9> The timeout interval to wait when sending a reply `Message` to the `output-channel` or `discard-channel`.
Defaults to `30` seconds.
It is applied only if the output channel has some 'sending' limitations, such as a `QueueChannel` with a fixed 'capacity'.
In this case, a `MessageDeliveryException` is thrown.
For `AbstractSubscribableChannel` implementations, the `send-timeout` is ignored .
For `group-timeout(-expression)`, the `MessageDeliveryException` from the scheduled expiring task leads this task to be rescheduled.
Optional.
<10> A reference to a bean that implements the message correlation (grouping) algorithm.
The bean can be an implementation of the `CorrelationStrategy` interface or a POJO.
In the latter case, the `correlation-strategy-method` attribute must be defined as well.
Optional (by default, the aggregator uses the `IntegrationMessageHeaderAccessor.CORRELATION_ID` header).
<11> A method defined on the bean referenced by `correlation-strategy`.
It implements the correlation decision algorithm.
Optional, with restrictions (`correlation-strategy` must be present).
<12> A SpEL expression representing the correlation strategy.
Example: `"headers['something']"`.
Only one of `correlation-strategy` or `correlation-strategy-expression` is allowed.
<13> A reference to a bean defined in the application context.
The bean must implement the aggregation logic, as described earlier.
Optional (by default, the list of aggregated messages becomes a payload of the output message).
<14> A method defined on the bean referenced by the `ref` attribute.
It implements the message aggregation algorithm.
Optional (it depends on `ref` attribute being defined).
<15> A reference to a bean that implements the release strategy.
The bean can be an implementation of the `ReleaseStrategy` interface or a POJO.
In the latter case, the `release-strategy-method` attribute must be defined as well.
Optional (by default, the aggregator uses the `IntegrationMessageHeaderAccessor.SEQUENCE_SIZE` header attribute).
<16> A method defined on the bean referenced by the `release-strategy` attribute.
It implements the completion decision algorithm.
Optional, with restrictions (`release-strategy` must be present).
<17> A SpEL expression representing the release strategy.
The root object for the expression is a `MessageGroup`.
Example: `"size() == 5"`.
Only one of `release-strategy` or `release-strategy-expression` is allowed.
<18> When set to `true` (the default is `false`), completed groups are removed from the message store, letting subsequent messages with the same correlation form a new group.
The default behavior is to send messages with the same correlation as a completed group to the `discard-channel`.
<19> Applies only if a `MessageGroupStoreReaper` is configured for the `MessageStore` of the `<aggregator>`.
By default, when a `MessageGroupStoreReaper` is configured to expire partial groups, empty groups are also removed.
Empty groups exist after a group is normally released.
The empty groups enable the detection and discarding of late-arriving messages.
If you wish to expire empty groups on a longer schedule than expiring partial groups, set this property.
Empty groups are then not removed from the `MessageStore` until they have not been modified for at least this number of milliseconds.
Note that the actual time to expire an empty group is also affected by the reaper's `timeout` property, and it could be as much as this value plus the timeout.
<20> A reference to a `org.springframework.integration.util.LockRegistry` bean.
It used to obtain a `Lock` based on the `groupId` for concurrent operations on the `MessageGroup`.
By default, an internal `DefaultLockRegistry` is used.
Use of a distributed `LockRegistry`, such as the `ZookeeperLockRegistry`, ensures only one instance of the aggregator can operate on a group concurrently.
See xref:redis.adoc#redis-lock-registry[Redis Lock Registry] or xref:zookeeper.adoc#zk-lock-registry[Zookeeper Lock Registry] for more information.
<21> A timeout (in milliseconds) to force the `MessageGroup` complete when the `ReleaseStrategy` does not release the group when the current message arrives.
This attribute provides a built-in time-based release strategy for the aggregator when there is a need to emit a partial result (or discard the group) if a new message does not arrive for the `MessageGroup` within the timeout which counts from the time the last message arrived.
To set up a timeout which counts from the time the `MessageGroup` was created see `group-timeout-expression` information.
When a new message arrives at the aggregator, any existing `ScheduledFuture<?>` for its `MessageGroup` is canceled.
If the `ReleaseStrategy` returns `false` (meaning do not release) and `groupTimeout > 0`, a new task is scheduled to expire the group.
We do not advise setting this attribute to zero (or a negative value).
Doing so effectively disables the aggregator, because every message group is immediately completed.
You can, however, conditionally set it to zero (or a negative value) by using an expression.
See `group-timeout-expression` for information.
The action taken during the completion depends on the `ReleaseStrategy` and the `send-partial-group-on-expiry` attribute.
See xref:aggregator.adoc#agg-and-group-to[Aggregator and Group Timeout] for more information.
It is mutually exclusive with 'group-timeout-expression' attribute.
<22> The SpEL expression that evaluates to a `groupTimeout` with the `MessageGroup` as the `#root` evaluation context object.
Used for scheduling the `MessageGroup` to be forced complete.
If the expression evaluates to `null`, the completion is not scheduled.
If it evaluates to zero, the group is completed immediately on the current thread.
In effect, this provides a dynamic `group-timeout` property.
As an example, if you wish to forcibly complete a `MessageGroup` after 10 seconds have elapsed since the time the group was created you might consider using the following SpEL expression: `timestamp + 10000 - T(System).currentTimeMillis()` where `timestamp` is provided by `MessageGroup.getTimestamp()` as the `MessageGroup` here is the `#root` evaluation context object.
Bear in mind however that the group creation time might differ from the time of the first arrived message depending on other group expiration properties' configuration.
See `group-timeout` for more information.
Mutually exclusive with 'group-timeout' attribute.
<23> When a group is completed due to a timeout (or by a `MessageGroupStoreReaper`), the group is expired (completely removed) by default.
Late arriving messages start a new group.
You can set this to `false` to complete the group but have its metadata remain so that late arriving messages are discarded.
Empty groups can be expired later using a `MessageGroupStoreReaper` together with the `empty-group-min-timeout` attribute.
It defaults to 'true'.
<24> A `TaskScheduler` bean reference to schedule the `MessageGroup` to be forced complete if no new message arrives for the `MessageGroup` within the `groupTimeout`.
If not provided, the default scheduler (`taskScheduler`) registered in the `ApplicationContext` (`ThreadPoolTaskScheduler`) is used.
This attribute does not apply if `group-timeout` or `group-timeout-expression` is not specified.
<25> Since version 4.1.
It lets a transaction be started for the `forceComplete` operation.
It is initiated from a `group-timeout(-expression)` or by a `MessageGroupStoreReaper` and is not applied to the normal `add`, `release`, and `discard` operations.
Only this sub-element or `<expire-advice-chain/>` is allowed.
<26> Since _version 4.1_.
It allows the configuration of any `Advice` for the `forceComplete` operation.
It is initiated from a `group-timeout(-expression)` or by a `MessageGroupStoreReaper` and is not applied to the normal `add`, `release`, and `discard` operations.
Only this sub-element or `<expire-transactional/>` is allowed.
A transaction `Advice` can also be configured here by using the Spring `tx` namespace.
[[aggregator-expiring-groups]]
[IMPORTANT]
.Expiring Groups
=====
There are two attributes related to expiring (completely removing) groups.
When a group is expired, there is no record of it, and, if a new message arrives with the same correlation, a new group is started.
When a group is completed (without expiry), the empty group remains and late-arriving messages are discarded.
Empty groups can be removed later by using a `MessageGroupStoreReaper` in combination with the `empty-group-min-timeout` attribute.
`expire-groups-upon-completion` relates to "`normal`" completion when the `ReleaseStrategy` releases the group.
This defaults to `false`.
If a group is not completed normally but is released or discarded because of a timeout, the group is normally expired.
Since version 4.1, you can control this behavior by using `expire-groups-upon-timeout`.
It defaults to `true` for backwards compatibility.
NOTE: When a group is timed out, the `ReleaseStrategy` is given one more opportunity to release the group.
If it does so and `expire-groups-upon-timeout` is false, expiration is controlled by `expire-groups-upon-completion`.
If the group is not released by the release strategy during timeout, then the expiration is controlled by the `expire-groups-upon-timeout`.
Timed-out groups are either discarded or a partial release occurs (based on `send-partial-result-on-expiry`).
Since version 5.0, empty groups are also scheduled for removal after `empty-group-min-timeout`.
If `expireGroupsUponCompletion == false` and `minimumTimeoutForEmptyGroups > 0`, the task to remove the group is scheduled when normal or partial sequences release happens.
Starting with version 5.4, the aggregator (and resequencer) can be configured to expire orphaned groups (groups in a persistent message store that might not otherwise be released).
The `expireTimeout` (if greater than `0`) indicates that groups older than this value in the store should be purged.
The `purgeOrphanedGroups()` method is called on start up and, together with the provided `expireDuration`, periodically within a scheduled task.
This method is also can be called externally at any time.
The expiration logic is fully delegated to the `forceComplete(MessageGroup)` functionality according to the provided expiration options mentioned above.
Such a periodic purge functionality is useful when a message store is needed to be cleaned up from those old groups which are not going to be released any more with regular message arrival logic.
In most cases this happens after an application restart, when using a persistent message group store.
The functionality is similar to the `MessageGroupStoreReaper` with a scheduled task, but provides a convenient way to deal with old groups within specific components, when using group timeout instead of a reaper.
The `MessageGroupStore` must be provided exclusively for the current correlation endpoint.
Otherwise, one aggregator may purge groups from another.
With the aggregator, groups expired using this technique will either be discarded or released as a partial group, depending on the `expireGroupsUponCompletion` property.
=====
We generally recommend using a `ref` attribute if a custom aggregator handler implementation may be referenced in other `<aggregator>` definitions.
However, if a custom aggregator implementation is only being used by a single definition of the `<aggregator>`, you can use an inner bean definition (starting with version 1.0.3) to configure the aggregation POJO within the `<aggregator>` element, as the following example shows:
[source,xml]
----
<aggregator input-channel="input" method="sum" output-channel="output">
<beans:bean class="org.foo.PojoAggregator"/>
</aggregator>
----
NOTE: Using both a `ref` attribute and an inner bean definition in the same `<aggregator>` configuration is not allowed, as it creates an ambiguous condition.
In such cases, an Exception is thrown.
The following example shows an implementation of the aggregator bean:
[source,java]
----
public class PojoAggregator {
public Long add(List<Long> results) {
long total = 0l;
for (long partialResult: results) {
total += partialResult;
}
return total;
}
}
----
An implementation of the completion strategy bean for the preceding example might be as follows:
[source,java]
----
public class PojoReleaseStrategy {
...
public boolean canRelease(List<Long> numbers) {
int sum = 0;
for (long number: numbers) {
sum += number;
}
return sum >= maxValue;
}
}
----
NOTE: Wherever it makes sense to do so, the release strategy method and the aggregator method can be combined into a single bean.
An implementation of the correlation strategy bean for the example above might be as follows:
[source,java]
----
public class PojoCorrelationStrategy {
...
public Long groupNumbersByLastDigit(Long number) {
return number % 10;
}
}
----
The aggregator in the preceding example would group numbers by some criterion (in this case, the remainder after dividing by ten) and hold the group until the sum of the numbers provided by the payloads exceeds a certain value.
NOTE: Wherever it makes sense to do so, the release strategy method, the correlation strategy method, and the aggregator method can be combined in a single bean.
(Actually, all of them or any two of them can be combined.)
[[aggregator-spel]]
==== Aggregators and Spring Expression Language (SpEL)
Since Spring Integration 2.0, you can handle the various strategies (correlation, release, and aggregation) with https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#expressions[SpEL], which we recommend if the logic behind such a release strategy is relatively simple.
Suppose you have a legacy component that was designed to receive an array of objects.
We know that the default release strategy assembles all aggregated messages in the `List`.
Now we have two problems.
First, we need to extract individual messages from the list.
Second, we need to extract the payload of each message and assemble the array of objects.
The following example solves both problems:
[source,java]
----
public String[] processRelease(List<Message<String>> messages){
List<String> stringList = new ArrayList<String>();
for (Message<String> message : messages) {
stringList.add(message.getPayload());
}
return stringList.toArray(new String[]{});
}
----
However, with SpEL, such a requirement could actually be handled relatively easily with a one-line expression, thus sparing you from writing a custom class and configuring it as a bean.
The following example shows how to do so:
[source,xml]
----
<int:aggregator input-channel="aggChannel"
output-channel="replyChannel"
expression="#this.![payload].toArray()"/>
----
In the preceding configuration, we use a https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#expressions[collection projection] expression to assemble a new collection from the payloads of all the messages in the list and then transform it to an array, thus achieving the same result as the earlier Java code.
You can apply the same expression-based approach when dealing with custom release and correlation strategies.
Instead of defining a bean for a custom `CorrelationStrategy` in the `correlation-strategy` attribute, you can implement your simple correlation logic as a SpEL expression and configure it in the `correlation-strategy-expression` attribute, as the following example shows:
[source,xml]
----
correlation-strategy-expression="payload.person.id"
----
In the preceding example, we assume that the payload has a `person` attribute with an `id`, which is going to be used to correlate messages.
Likewise, for the `ReleaseStrategy`, you can implement your release logic as a SpEL expression and configure it in the `release-strategy-expression` attribute.
The root object for evaluation context is the `MessageGroup` itself.
The `List` of messages can be referenced by using the `message` property of the group within the expression.
NOTE: In releases prior to version 5.0, the root object was the collection of `Message<?>`, as the previous example shows:
[source,xml]
----
release-strategy-expression="!messages.?[payload==5].empty"
----
In the preceding example, the root object of the SpEL evaluation context is the `MessageGroup` itself, and you are stating that, as soon as there is a message with payload of `5` in this group, the group should be released.
[[agg-and-group-to]]
==== Aggregator and Group Timeout
Starting with version 4.0, two new mutually exclusive attributes have been introduced: `group-timeout` and `group-timeout-expression`.
See xref:aggregator.adoc#aggregator-xml[Configuring an Aggregator with XML].
In some cases, you may need to emit the aggregator result (or discard the group) after a timeout if the `ReleaseStrategy` does not release when the current message arrives.
For this purpose, the `groupTimeout` option lets scheduling the `MessageGroup` be forced to complete, as the following example shows:
[source,xml]
----
<aggregator input-channel="input" output-channel="output"
send-partial-result-on-expiry="true"
group-timeout-expression="size() ge 2 ? 10000 : -1"
release-strategy-expression="messages[0].headers.sequenceNumber == messages[0].headers.sequenceSize"/>
----
With this example, the normal release is possible if the aggregator receives the last message in sequence as defined by the `release-strategy-expression`.
If that specific message does not arrive, the `groupTimeout` forces the group to complete after ten seconds, as long as the group contains at least two Messages.
The results of forcing the group to complete depends on the `ReleaseStrategy` and the `send-partial-result-on-expiry`.
First, the release strategy is again consulted to see if a normal release is to be made.
While the group has not changed, the `ReleaseStrategy` can decide to release the group at this time.
If the release strategy still does not release the group, it is expired.
If `send-partial-result-on-expiry` is `true`, existing messages in the (partial) `MessageGroup` are released as a normal aggregator reply message to the `output-channel`.
Otherwise, it is discarded.
There is a difference between `groupTimeout` behavior and `MessageGroupStoreReaper` (see xref:aggregator.adoc#aggregator-xml[Configuring an Aggregator with XML]).
The reaper initiates forced completion for all `MessageGroup` s in the `MessageGroupStore` periodically.
The `groupTimeout` does it for each `MessageGroup` individually if a new message does not arrive during the `groupTimeout`.
Also, the reaper can be used to remove empty groups (empty groups are retained in order to discard late messages if `expire-groups-upon-completion` is false).
Starting with version 5.5, the `groupTimeoutExpression` can be evaluated to a `java.util.Date` instance.
This can be useful in cases like determining a scheduled task moment based on the group creation time (`MessageGroup.getTimestamp()`) instead of a current message arrival as it is calculated when `groupTimeoutExpression` is evaluated to `long`:
[source,xml]
----
group-timeout-expression="size() ge 2 ? new java.util.Date(timestamp + 200) : null"
----
[[aggregator-annotations]]
=== Configuring an Aggregator with Annotations
The following example shows an aggregator configured with annotations:
[source,java]
----
public class Waiter {
...
@Aggregator <1>
public Delivery aggregatingMethod(List<OrderItem> items) {
...
}
@ReleaseStrategy <2>
public boolean releaseChecker(List<Message<?>> messages) {
...
}
@CorrelationStrategy <3>
public String correlateBy(OrderItem item) {
...
}
}
----
<1> An annotation indicating that this method should be used as an aggregator.
It must be specified if this class is used as an aggregator.
<2> An annotation indicating that this method is used as the release strategy of an aggregator.
If not present on any method, the aggregator uses the `SimpleSequenceSizeReleaseStrategy`.
<3> An annotation indicating that this method should be used as the correlation strategy of an aggregator.
If no correlation strategy is indicated, the aggregator uses the `HeaderAttributeCorrelationStrategy` based on `CORRELATION_ID`.
All of the configuration options provided by the XML element are also available for the `@Aggregator` annotation.
The aggregator can be either referenced explicitly from XML or, if the `@MessageEndpoint` is defined on the class, detected automatically through classpath scanning.
Annotation configuration (`@Aggregator` and others) for the Aggregator component covers only simple use cases, where most default options are sufficient.
If you need more control over those options when using annotation configuration, consider using a `@Bean` definition for the `AggregatingMessageHandler` and mark its `@Bean` method with `@ServiceActivator`, as the following example shows:
[source,java]
----
@ServiceActivator(inputChannel = "aggregatorChannel")
@Bean
public MessageHandler aggregator(MessageGroupStore jdbcMessageGroupStore) {
AggregatingMessageHandler aggregator =
new AggregatingMessageHandler(new DefaultAggregatingMessageGroupProcessor(),
jdbcMessageGroupStore);
aggregator.setOutputChannel(resultsChannel());
aggregator.setGroupTimeoutExpression(new ValueExpression<>(500L));
aggregator.setTaskScheduler(this.taskScheduler);
return aggregator;
}
----
See xref:aggregator.adoc#aggregator-api[Programming Model] and xref:configuration/meta-annotations.adoc#annotations_on_beans[Annotations on `@Bean` Methods] for more information.
NOTE: Starting with version 4.2, the `AggregatorFactoryBean` is available to simplify Java configuration for the `AggregatingMessageHandler`.
[[reaper]]
== Managing State in an Aggregator: `MessageGroupStore`
Aggregator (and some other patterns in Spring Integration) is a stateful pattern that requires decisions to be made based on a group of messages that have arrived over a period of time, all with the same correlation key.
The design of the interfaces in the stateful patterns (such as `ReleaseStrategy`) is driven by the principle that the components (whether defined by the framework or by a user) should be able to remain stateless.
All state is carried by the `MessageGroup` and its management is delegated to the `MessageGroupStore`.
The `MessageGroupStore` interface is defined as follows:
[source,java]
----
public interface MessageGroupStore {
int getMessageCountForAllMessageGroups();
int getMarkedMessageCountForAllMessageGroups();
int getMessageGroupCount();
MessageGroup getMessageGroup(Object groupId);
MessageGroup addMessageToGroup(Object groupId, Message<?> message);
MessageGroup markMessageGroup(MessageGroup group);
MessageGroup removeMessageFromGroup(Object key, Message<?> messageToRemove);
MessageGroup markMessageFromGroup(Object key, Message<?> messageToMark);
void removeMessageGroup(Object groupId);
void registerMessageGroupExpiryCallback(MessageGroupCallback callback);
int expireMessageGroups(long timeout);
}
----
For more information, see the https://docs.spring.io/spring-integration/api/org/springframework/integration/store/MessageGroupStore.html[Javadoc].
The `MessageGroupStore` accumulates state information in `MessageGroups` while waiting for a release strategy to be triggered, and that event might not ever happen.
So, to prevent stale messages from lingering, and for volatile stores to provide a hook for cleaning up when the application shuts down, the `MessageGroupStore` lets you register callbacks to apply to its `MessageGroups` when they expire.
The interface is very straightforward, as the following listing shows:
[source,java]
----
public interface MessageGroupCallback {
void execute(MessageGroupStore messageGroupStore, MessageGroup group);
}
----
The callback has direct access to the store and the message group so that it can manage the persistent state (for example, by entirely removing the group from the store).
The `MessageGroupStore` maintains a list of these callbacks, which it applies, on demand, to all messages whose timestamps are earlier than a time supplied as a parameter (see the `registerMessageGroupExpiryCallback(..)` and `expireMessageGroups(..)` methods, described earlier).
IMPORTANT: It is important not to use the same `MessageGroupStore` instance in different aggregator components, when you intend to rely on the `expireMessageGroups` functionality.
Every `AbstractCorrelatingMessageHandler` registers its own `MessageGroupCallback` based on the `forceComplete()` callback.
This way each group for expiration may be completed or discarded by the wrong aggregator.
Starting with version 5.0.10, a `UniqueExpiryCallback` is used from the `AbstractCorrelatingMessageHandler` for the registration callback in the `MessageGroupStore`.
The `MessageGroupStore`, in turn, checks for presence an instance of this class and logs an error with an appropriate message if one is already present in the callbacks set.
This way the Framework disallows usage of the `MessageGroupStore` instance in different aggregators/resequencers to avoid the mentioned side effect of expiration the groups not created by the particular correlation handler.
You can call the `expireMessageGroups` method with a timeout value.
Any message older than the current time minus this value is expired and has the callbacks applied.
Thus, it is the user of the store that defines what is meant by message group "`expiry`".
As a convenience for users, Spring Integration provides a wrapper for the message expiry in the form of a `MessageGroupStoreReaper`, as the following example shows:
[source,xml]
----
<bean id="reaper" class="org...MessageGroupStoreReaper">
<property name="messageGroupStore" ref="messageStore"/>
<property name="timeout" value="30000"/>
</bean>
<task:scheduled-tasks scheduler="scheduler">
<task:scheduled ref="reaper" method="run" fixed-rate="10000"/>
</task:scheduled-tasks>
----
The reaper is a `Runnable`.
In the preceding example, the message group store's expire method is called every ten seconds.
The timeout itself is 30 seconds.
NOTE: It is important to understand that the 'timeout' property of `MessageGroupStoreReaper` is an approximate value and is impacted by the rate of the task scheduler, since this property is only checked on the next scheduled execution of the `MessageGroupStoreReaper` task.
For example, if the timeout is set for ten minutes but the `MessageGroupStoreReaper` task is scheduled to run every hour and the last execution of the `MessageGroupStoreReaper` task happened one minute before the timeout, the `MessageGroup` does not expire for the next 59 minutes.
Consequently, we recommend setting the rate to be at least equal to the value of the timeout or shorter.
In addition to the reaper, the expiry callbacks are invoked when the application shuts down through a lifecycle callback in the `AbstractCorrelatingMessageHandler`.
The `AbstractCorrelatingMessageHandler` registers its own expiry callback, and this is the link with the boolean flag `send-partial-result-on-expiry` in the XML configuration of the aggregator.
If the flag is set to `true`, then, when the expiry callback is invoked, any unmarked messages in groups that are not yet released can be sent on to the output channel.
IMPORTANT: Since the `MessageGroupStoreReaper` is called from a scheduled task, and may result in the production of a message (depending on the `sendPartialResultOnExpiry` option) to a downstream integration flow, it is recommended to supply a custom `TaskScheduler` with a `MessagePublishingErrorHandler` to handler exceptions via an `errorChannel`, as it might be expected by the regular aggregator release functionality.
The same logic applies for group timeout functionality which also relies on a `TaskScheduler`.
See xref:scatter-gather.adoc#scatter-gather-error-handling[Error Handling] for more information.
[IMPORTANT]
=====
When a shared `MessageStore` is used for different correlation endpoints, you must configure a proper `CorrelationStrategy` to ensure uniqueness for group IDs.
Otherwise, unexpected behavior may happen when one correlation endpoint releases or expire messages from others.
Messages with the same correlation key are stored in the same message group.
Some `MessageStore` implementations allow using the same physical resources, by partitioning the data.
For example, the `JdbcMessageStore` has a `region` property, and the `MongoDbMessageStore` has a `collectionName` property.
For more information about the `MessageStore` interface and its implementations, see xref:message-store.adoc[Message Store].
=====
[[flux-aggregator]]
== Flux Aggregator
In version 5.2, the `FluxAggregatorMessageHandler` component has been introduced.
It is based on the Project Reactor `Flux.groupBy()` and `Flux.window()` operators.
The incoming messages are emitted into the `FluxSink` initiated by the `Flux.create()` in the constructor of this component.
If the `outputChannel` is not provided or it is not an instance of `ReactiveStreamsSubscribableChannel`, the subscription to the main `Flux` is done from the `Lifecycle.start()` implementation.
Otherwise, it is postponed to the subscription done by the `ReactiveStreamsSubscribableChannel` implementation.
The messages are grouped by the `Flux.groupBy()` using a `CorrelationStrategy` for the group key.
By default, the `IntegrationMessageHeaderAccessor.CORRELATION_ID` header of the message is consulted.
By default, every closed window is released as a `Flux` in payload of a message to produce.
This message contains all the headers from the first message in the window.
This `Flux` in the output message payload must be subscribed and processed downstream.
Such a logic can be customized (or superseded) by the `setCombineFunction(Function<Flux<Message<?>>, Mono<Message<?>>>)` configuration option of the `FluxAggregatorMessageHandler`.
For example, if we would like to have a `List` of payloads in the final message, we can configure a `Flux.collectList()` like this:
[source,java]
----
fluxAggregatorMessageHandler.setCombineFunction(
(messageFlux) ->
messageFlux
.map(Message::getPayload)
.collectList()
.map(GenericMessage::new));
----
There are several options in the `FluxAggregatorMessageHandler` to select an appropriate window strategy:
* `setBoundaryTrigger(Predicate<Message<?>>)` - is propagated to the `Flux.windowUntil()` operator.
See its JavaDocs for more information.
Has a precedence over all other window options.
* `setWindowSize(int)` and `setWindowSizeFunction(Function<Message<?>, Integer>)` - is propagated to the `Flux.window(int)` or `windowTimeout(int, Duration)`.
By default, a window size is calculated from the first message in group and its `IntegrationMessageHeaderAccessor.SEQUENCE_SIZE` header.
* `setWindowTimespan(Duration)` - is propagated to the `Flux.window(Duration)` or `windowTimeout(int, Duration)` depending on the window size configuration.
* `setWindowConfigurer(Function<Flux<Message<?>>, Flux<Flux<Message<?>>>>)` - a function to apply a transformation into the grouped fluxes for any custom window operation not covered by the exposed options.
Since this component is a `MessageHandler` implementation it can simply be used as a `@Bean` definition together with a `@ServiceActivator` messaging annotation.
With Java DSL it can be used from the `.handle()` EIP-method.
The sample below demonstrates how we can register an `IntegrationFlow` at runtime and how a `FluxAggregatorMessageHandler` can be correlated with a splitter upstream:
[source,java]
----
IntegrationFlow fluxFlow =
(flow) -> flow
.split()
.channel(MessageChannels.flux())
.handle(new FluxAggregatorMessageHandler());
IntegrationFlowContext.IntegrationFlowRegistration registration =
this.integrationFlowContext.registration(fluxFlow)
.register();
@SuppressWarnings("unchecked")
Flux<Message<?>> window =
registration.getMessagingTemplate()
.convertSendAndReceive(new Integer[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, Flux.class);
----
[[agg-message-group-condition]]
== Condition on the Message Group
Starting with version 5.5, an `AbstractCorrelatingMessageHandler` (including its Java & XML DSLs) exposes a `groupConditionSupplier` option of the `BiFunction<Message<?>, String, String>` implementation.
This function is used on each message added to the group and a result condition sentence is stored into the group for future consideration.
The `ReleaseStrategy` may consult this condition instead of iterating over all the messages in the group.
See `GroupConditionProvider` JavaDocs and xref:message-store.adoc#message-group-condition[Message Group Condition] for more information.
See also xref:file/aggregator.adoc[File Aggregator].

View File

@@ -0,0 +1,49 @@
[[amqp]]
= AMQP (RabbitMQ) Support
Spring Integration provides channel adapters for receiving and sending messages by using the Advanced Message Queuing Protocol (AMQP).
You need to include this dependency into your project:
[tabs]
======
Maven::
+
[source, xml, subs="normal", role="primary"]
----
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-amqp</artifactId>
<version>{project-version}</version>
</dependency>
----
Gradle::
+
[source, groovy, subs="normal", role="secondary"]
----
compile "org.springframework.integration:spring-integration-amqp:{project-version}"
----
======
The following adapters are available:
* xref:amqp/inbound-channel-adapter.adoc[Inbound Channel Adapter]
* xref:amqp/inbound-gateway.adoc[Inbound Gateway]
* xref:amqp/outbound-channel-adapter.adoc[Outbound Channel Adapter]
* xref:amqp/outbound-gateway.adoc[Outbound Gateway]
* xref:amqp-async-outbound-gateway[Async Outbound Gateway]
* xref:amqp/rmq-streams.adoc#rmq-stream-inbound-channel-adapter[RabbitMQ Stream Queue Inbound Channel Adapter]
* xref:amqp/rmq-streams.adoc#rmq-stream-outbound-channel-adapter[RabbitMQ Stream Queue Outbound Channel Adapter]
Spring Integration also provides a point-to-point message channel and a publish-subscribe message channel backed by AMQP Exchanges and Queues.
To provide AMQP support, Spring Integration relies on (https://projects.spring.io/spring-amqp[Spring AMQP]), which applies core Spring concepts to the development of AMQP-based messaging solutions.
Spring AMQP provides similar semantics to (https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#jms[Spring JMS]).
Whereas the provided AMQP Channel Adapters are intended for unidirectional messaging (send or receive) only, Spring Integration also provides inbound and outbound AMQP gateways for request-reply operations.
TIP:
You should familiarize yourself with the https://docs.spring.io/spring-amqp/reference/html/[reference documentation of the Spring AMQP project].
It provides much more in-depth information about Spring's integration with AMQP in general and RabbitMQ in particular.

View File

@@ -0,0 +1,31 @@
[[alternative-confirms-returns]]
= Alternative Mechanism for Publisher Confirms and Returns
When the connection factory is configured for publisher confirms and returns, the sections above discuss the configuration of message channels to receive the confirms and returns asynchronously.
Starting with version 5.4, there is an additional mechanism which is generally easier to use.
In this case, do not configure a `confirm-correlation-expression` or the confirm and return channels.
Instead, add a `CorrelationData` instance in the `AmqpHeaders.PUBLISH_CONFIRM_CORRELATION` header; you can then wait for the result(s) later, by checking the state of the future in the `CorrelationData` instances for which you have sent messages.
The `returnedMessage` field will always be populated (if a message is returned) before the future is completed.
[source, java]
----
CorrelationData corr = new CorrelationData("someId"); // <--- Unique "id" is required for returns
someFlow.getInputChannel().send(MessageBuilder.withPayload("test")
.setHeader("rk", "someKeyThatWontRoute")
.setHeader(AmqpHeaders.PUBLISH_CONFIRM_CORRELATION, corr)
.build());
...
try {
Confirm Confirm = corr.getFuture().get(10, TimeUnit.SECONDS);
Message returned = corr.getReturnedMessage();
if (returned !- null) {
// message could not be routed
}
}
catch { ... }
----
To improve performance, you may wish to send multiple messages and wait for the confirmations later, rather than one-at-a-time.
The returned message is the raw message after conversion; you can sub-class a `CorrelationData` with whatever additional data you need.

View File

@@ -0,0 +1,185 @@
[[amqp-async-outbound-gateway]]
= Asynchronous Outbound Gateway
The gateway discussed in the previous section is synchronous, in that the sending thread is suspended until a
reply is received (or a timeout occurs).
Spring Integration version 4.3 added an asynchronous gateway, which uses the `AsyncRabbitTemplate` from Spring AMQP.
When a message is sent, the thread returns immediately after the send operation completes, and, when the message is received, the reply is sent on the template's listener container thread.
This can be useful when the gateway is invoked on a poller thread.
The thread is released and is available for other tasks in the framework.
The following listing shows the possible configuration options for an AMQP asynchronous outbound gateway:
[tabs]
======
Java DSL::
+
[source,java,role="primary"]
----
@Configuration
public class AmqpAsyncApplication {
@Bean
public IntegrationFlow asyncAmqpOutbound(AsyncRabbitTemplate asyncRabbitTemplate) {
return f -> f
.handle(Amqp.asyncOutboundGateway(asyncRabbitTemplate)
.routingKey("queue1")); // default exchange - route to queue 'queue1'
}
@MessagingGateway(defaultRequestChannel = "asyncAmqpOutbound.input")
public interface MyGateway {
String sendToRabbit(String data);
}
}
----
Java::
+
[source,java,role="secondary"]
----
@Configuration
public class AmqpAsyncConfig {
@Bean
@ServiceActivator(inputChannel = "amqpOutboundChannel")
public AsyncAmqpOutboundGateway amqpOutbound(AsyncRabbitTemplate asyncTemplate) {
AsyncAmqpOutboundGateway outbound = new AsyncAmqpOutboundGateway(asyncTemplate);
outbound.setRoutingKey("foo"); // default exchange - route to queue 'foo'
return outbound;
}
@Bean
public AsyncRabbitTemplate asyncTemplate(RabbitTemplate rabbitTemplate,
SimpleMessageListenerContainer replyContainer) {
return new AsyncRabbitTemplate(rabbitTemplate, replyContainer);
}
@Bean
public SimpleMessageListenerContainer replyContainer() {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(ccf);
container.setQueueNames("asyncRQ1");
return container;
}
@Bean
public MessageChannel amqpOutboundChannel() {
return new DirectChannel();
}
}
----
XML::
+
[source,xml,role="secondary"]
----
<int-amqp:outbound-async-gateway id="asyncOutboundGateway" <1>
request-channel="myRequestChannel" <2>
async-template="" <3>
exchange-name="" <4>
exchange-name-expression="" <5>
order="1" <6>
reply-channel="" <7>
reply-timeout="" <8>
requires-reply="" <9>
routing-key="" <10>
routing-key-expression="" <11>
default-delivery-mode"" <12>
confirm-correlation-expression="" <13>
confirm-ack-channel="" <14>
confirm-nack-channel="" <15>
confirm-timeout="" <16>
return-channel="" <17>
lazy-connect="true" /> <18>
----
======
<1> The unique ID for this adapter.
Optional.
<2> Message channel to which messages should be sent in order to have them converted and published to an AMQP exchange.
Required.
<3> Bean reference to the configured `AsyncRabbitTemplate`.
Optional (it defaults to `asyncRabbitTemplate`).
<4> The name of the AMQP exchange to which messages should be sent.
If not provided, messages are sent to the default, no-name exchange.
Mutually exclusive with 'exchange-name-expression'.
Optional.
<5> A SpEL expression that is evaluated to determine the name of the AMQP exchange to which messages are sent, with the message as the root object.
If not provided, messages are sent to the default, no-name exchange.
Mutually exclusive with 'exchange-name'.
Optional.
<6> The order for this consumer when multiple consumers are registered, thereby enabling load-balancing and failover.
Optional (it defaults to `Ordered.LOWEST_PRECEDENCE [=Integer.MAX_VALUE]`).
<7> Message channel to which replies should be sent after being received from an AMQP queue and converted.
Optional.
<8> The time the gateway waits when sending the reply message to the `reply-channel`.
This only applies if the `reply-channel` can block -- such as a `QueueChannel` with a capacity limit that is currently full.
The default is infinity.
<9> When no reply message is received within the `AsyncRabbitTemplate`'s `receiveTimeout` property and this setting is `true`, the gateway sends an error message to the inbound message's `errorChannel` header.
When no reply message is received within the `AsyncRabbitTemplate`'s `receiveTimeout` property and this setting is `false`, the gateway sends an error message to the default `errorChannel` (if available).
It defaults to `true`.
<10> The routing-key to use when sending Messages.
By default, this is an empty `String`.
Mutually exclusive with 'routing-key-expression'.
Optional.
<11> A SpEL expression that is evaluated to determine the routing-key to use when sending messages,
with the message as the root object (for example, 'payload.key').
By default, this is an empty `String`.
Mutually exclusive with 'routing-key'.
Optional.
<12> The default delivery mode for messages: `PERSISTENT` or `NON_PERSISTENT`.
Overridden if the `header-mapper` sets the delivery mode.
If the Spring Integration message header (`amqp_deliveryMode`) is present, the `DefaultHeaderMapper` sets the value.
If this attribute is not supplied and the header mapper does not set it, the default depends on the underlying Spring AMQP `MessagePropertiesConverter` used by the `RabbitTemplate`.
If that is not customized, the default is `PERSISTENT`.
Optional.
<13> An expression that defines correlation data.
When provided, this configures the underlying AMQP template to receive publisher confirmations.
Requires a dedicated `RabbitTemplate` and a `CachingConnectionFactory` with its `publisherConfirms` property set to `true`.
When a publisher confirmation is received and correlation data is supplied, the confirmation is written to either the `confirm-ack-channel` or the `confirm-nack-channel`, depending on the confirmation type.
The payload of the confirmation is the correlation data as defined by this expression, and the message has its 'amqp_publishConfirm' header set to `true` (`ack`) or `false` (`nack`).
For `nack` instances, an additional header (`amqp_publishConfirmNackCause`) is provided.
Examples: `headers['myCorrelationData']`, `payload`.
If the expression resolves to a `Message<?>` instance (such as "`#this`"), the message emitted on the `ack`/`nack` channel is based on that message, with the additional headers added.
Also see xref:amqp/alternative-confirms-returns.adoc[Alternative Mechanism for Publisher Confirms and Returns].
Optional.
<14> The channel to which positive (`ack`) publisher confirmations are sent.
The payload is the correlation data defined by the `confirm-correlation-expression`.
Requires the underlying `AsyncRabbitTemplate` to have its `enableConfirms` property set to `true`.
Also see xref:amqp/alternative-confirms-returns.adoc[Alternative Mechanism for Publisher Confirms and Returns].
Optional (the default is `nullChannel`).
<15> Since version 4.2.
The channel to which negative (`nack`) publisher confirmations are sent.
The payload is the correlation data defined by the `confirm-correlation-expression`.
Requires the underlying `AsyncRabbitTemplate` to have its `enableConfirms` property set to `true`.
Also see xref:amqp/alternative-confirms-returns.adoc[Alternative Mechanism for Publisher Confirms and Returns].
Optional (the default is `nullChannel`).
<16> When set, the gateway will synthesize a negative acknowledgment (nack) if a publisher confirm is not received within this time in milliseconds.
Pending confirms are checked every 50% of this value, so the actual time a nack is sent will be between 1x and 1.5x this value.
Also see xref:amqp/alternative-confirms-returns.adoc[Alternative Mechanism for Publisher Confirms and Returns].
Default none (nacks will not be generated).
<17> The channel to which returned messages are sent.
When provided, the underlying AMQP template is configured to return undeliverable messages to the gateway.
The message is constructed from the data received from AMQP, with the following additional headers: `amqp_returnReplyCode`, `amqp_returnReplyText`, `amqp_returnExchange`, and `amqp_returnRoutingKey`.
Requires the underlying `AsyncRabbitTemplate` to have its `mandatory` property set to `true`.
Also see xref:amqp/alternative-confirms-returns.adoc[Alternative Mechanism for Publisher Confirms and Returns].
Optional.
<18> When set to `false`, the endpoint tries to connect to the broker during application context initialization.
Doing so allows "`fail fast`" detection of bad configuration, by logging an error message if the broker is down.
When `true` (the default), the connection is established (if it does not already exist because some other component established
it) when the first message is sent.
See also xref:service-activator.adoc#async-service-activator[Asynchronous Service Activator] for more information.
[IMPORTANT]
.RabbitTemplate
====
When you use confirmations and returns, we recommend that the `RabbitTemplate` wired into the `AsyncRabbitTemplate` be dedicated.
Otherwise, unexpected side effects may be encountered.
====

View File

@@ -0,0 +1,136 @@
[[amqp-channels]]
= AMQP-backed Message Channels
There are two message channel implementations available.
One is point-to-point, and the other is publish-subscribe.
Both of these channels provide a wide range of configuration attributes for the underlying `AmqpTemplate` and
`SimpleMessageListenerContainer` (as shown earlier in this chapter for the channel adapters and gateways).
However, the examples we show here have minimal configuration.
Explore the XML schema to view the available attributes.
A point-to-point channel might look like the following example:
[source,xml]
----
<int-amqp:channel id="p2pChannel"/>
----
Under the covers, the preceding example causes a `Queue` named `si.p2pChannel` to be declared, and this channel sends to that `Queue` (technically, by sending to the no-name direct exchange with a routing key that matches the name of this `Queue`).
This channel also registers a consumer on that `Queue`.
If you want the channel to be "`pollable`" instead of message-driven, provide the `message-driven` flag with a value of `false`, as the following example shows:
[source,xml]
----
<int-amqp:channel id="p2pPollableChannel" message-driven="false"/>
----
A publish-subscribe channel might look like the following:
[source,xml]
----
<int-amqp:publish-subscribe-channel id="pubSubChannel"/>
----
Under the covers, the preceding example causes a fanout exchange named `si.fanout.pubSubChannel` to be declared, and this channel sends to that fanout exchange.
This channel also declares a server-named exclusive, auto-delete, non-durable `Queue` and binds that to the fanout exchange while registering a consumer on that `Queue` to receive messages.
There is no "`pollable`" option for a publish-subscribe-channel.
It must be message-driven.
Starting with version 4.1, AMQP-backed message channels (in conjunction with `channel-transacted`) support
`template-channel-transacted` to separate `transactional` configuration for the `AbstractMessageListenerContainer` and
for the `RabbitTemplate`.
Note that, previously, `channel-transacted` was `true` by default.
Now, by default, it is `false` for the `AbstractMessageListenerContainer`.
Prior to version 4.3, AMQP-backed channels only supported messages with `Serializable` payloads and headers.
The entire message was converted (serialized) and sent to RabbitMQ.
Now, you can set the `extract-payload` attribute (or `setExtractPayload()` when using Java configuration) to `true`.
When this flag is `true`, the message payload is converted and the headers are mapped, in a manner similar to when you use channel adapters.
This arrangement lets AMQP-backed channels be used with non-serializable payloads (perhaps with another message converter, such as the `Jackson2JsonMessageConverter`).
See xref:amqp/message-headers.adoc[AMQP Message Headers] for more about the default mapped headers.
You can modify the mapping by providing custom mappers that use the `outbound-header-mapper` and `inbound-header-mapper` attributes.
You can now also specify a `default-delivery-mode`, which is used to set the delivery mode when there is no `amqp_deliveryMode` header.
By default, Spring AMQP `MessageProperties` uses `PERSISTENT` delivery mode.
IMPORTANT: As with other persistence-backed channels, AMQP-backed channels are intended to provide message persistence to avoid message loss.
They are not intended to distribute work to other peer applications.
For that purpose, use channel adapters instead.
IMPORTANT: Starting with version 5.0, the pollable channel now blocks the poller thread for the specified `receiveTimeout` (the default is 1 second).
Previously, unlike other `PollableChannel` implementations, the thread returned immediately to the scheduler if no message was available, regardless of the receive timeout.
Blocking is a little more expensive than using a `basicGet()` to retrieve a message (with no timeout), because a consumer has to be created to receive each message.
To restore the previous behavior, set the poller's `receiveTimeout` to 0.
[[configuring-with-java-configuration]]
== Configuring with Java Configuration
The following example shows how to configure the channels with Java configuration:
[source, java]
----
@Bean
public AmqpChannelFactoryBean pollable(ConnectionFactory connectionFactory) {
AmqpChannelFactoryBean factoryBean = new AmqpChannelFactoryBean();
factoryBean.setConnectionFactory(connectionFactory);
factoryBean.setQueueName("foo");
factoryBean.setPubSub(false);
return factoryBean;
}
@Bean
public AmqpChannelFactoryBean messageDriven(ConnectionFactory connectionFactory) {
AmqpChannelFactoryBean factoryBean = new AmqpChannelFactoryBean(true);
factoryBean.setConnectionFactory(connectionFactory);
factoryBean.setQueueName("bar");
factoryBean.setPubSub(false);
return factoryBean;
}
@Bean
public AmqpChannelFactoryBean pubSub(ConnectionFactory connectionFactory) {
AmqpChannelFactoryBean factoryBean = new AmqpChannelFactoryBean(true);
factoryBean.setConnectionFactory(connectionFactory);
factoryBean.setQueueName("baz");
factoryBean.setPubSub(false);
return factoryBean;
}
----
[[configuring-with-the-java-dsl]]
== Configuring with the Java DSL
The following example shows how to configure the channels with the Java DSL:
[source, java]
----
@Bean
public IntegrationFlow pollableInFlow(ConnectionFactory connectionFactory) {
return IntegrationFlow.from(...)
...
.channel(Amqp.pollableChannel(connectionFactory)
.queueName("foo"))
...
.get();
}
@Bean
public IntegrationFlow messageDrivenInFow(ConnectionFactory connectionFactory) {
return IntegrationFlow.from(...)
...
.channel(Amqp.channel(connectionFactory)
.queueName("bar"))
...
.get();
}
@Bean
public IntegrationFlow pubSubInFlow(ConnectionFactory connectionFactory) {
return IntegrationFlow.from(...)
...
.channel(Amqp.publishSubscribeChannel(connectionFactory)
.queueName("baz"))
...
.get();
}
----

View File

@@ -0,0 +1,57 @@
[[content-type-conversion-outbound]]
= Outbound Message Conversion
Spring AMQP 1.4 introduced the `ContentTypeDelegatingMessageConverter`, where the actual converter is selected based
on the incoming content type message property.
This can be used by inbound endpoints.
As of Spring Integration version 4.3, you can use the `ContentTypeDelegatingMessageConverter` on outbound endpoints as well, with the `contentType` header specifying which converter is used.
The following example configures a `ContentTypeDelegatingMessageConverter`, with the default converter being the `SimpleMessageConverter` (which handles Java serialization and plain text), together with a JSON converter:
[source, xml]
----
<amqp:outbound-channel-adapter id="withContentTypeConverter" channel="ctRequestChannel"
exchange-name="someExchange"
routing-key="someKey"
amqp-template="amqpTemplateContentTypeConverter" />
<int:channel id="ctRequestChannel"/>
<rabbit:template id="amqpTemplateContentTypeConverter"
connection-factory="connectionFactory" message-converter="ctConverter" />
<bean id="ctConverter"
class="o.s.amqp.support.converter.ContentTypeDelegatingMessageConverter">
<property name="delegates">
<map>
<entry key="application/json">
<bean class="o.s.amqp.support.converter.Jackson2JsonMessageConverter" />
</entry>
</map>
</property>
</bean>
----
Sending a message to `ctRequestChannel` with the `contentType` header set to `application/json` causes the JSON converter to be selected.
This applies to both the outbound channel adapter and gateway.
[NOTE]
====
Starting with version 5.0, headers that are added to the `MessageProperties` of the outbound message are never overwritten by mapped headers (by default).
Previously, this was only the case if the message converter was a `ContentTypeDelegatingMessageConverter` (in that case, the header was mapped first so that the proper converter could be selected).
For other converters, such as the `SimpleMessageConverter`, mapped headers overwrote any headers added by the converter.
This caused problems when an outbound message had some leftover `contentType` headers (perhaps from an inbound channel adapter) and the correct outbound `contentType` was incorrectly overwritten.
The work-around was to use a header filter to remove the header before sending the message to the outbound endpoint.
There are, however, cases where the previous behavior is desired -- for example, when a `String` payload that contains JSON, the `SimpleMessageConverter` is not aware of the content and sets the `contentType` message property to `text/plain` but your application would like to override that to `application/json` by setting the `contentType` header of the message sent to the outbound endpoint.
The `ObjectToJsonTransformer` does exactly that (by default).
There is now a property called `headersMappedLast` on the outbound channel adapter and gateway (as well as on AMQP-backed channels).
Setting this to `true` restores the behavior of overwriting the property added by the converter.
Starting with version 5.1.9, a similar `replyHeadersMappedLast` is provided for the `AmqpInboundGateway` when we produce a reply and would like to override headers populated by the converter.
See its JavaDocs for more information.
====

View File

@@ -0,0 +1,15 @@
[[amqp-conversion-inbound]]
= Inbound Message Conversion
:page-section-summary-toc: 1
Inbound messages, arriving at the channel adapter or gateway, are converted to the `spring-messaging` `Message<?>` payload using a message converter.
By default, a `SimpleMessageConverter` is used, which handles java serialization and text.
Headers are mapped using the `DefaultHeaderMapper.inboundMapper()` by default.
If a conversion error occurs, and there is no error channel defined, the exception is thrown to the container and handled by the listener container's error handler.
The default error handler treats conversion errors as fatal and the message will be rejected (and routed to a dead-letter exchange, if the queue is so configured).
If an error channel is defined, the `ErrorMessage` payload is a `ListenerExecutionFailedException` with properties `failedMessage` (the Spring AMQP message that could not be converted) and the `cause`.
If the container `AcknowledgeMode` is `AUTO` (the default) and the error flow consumes the error without throwing an exception, the original message will be acknowledged.
If the error flow throws an exception, the exception type, in conjunction with the container's error handler, will determine whether the message is requeued.
If the container is configured with `AcknowledgeMode.MANUAL`, the payload is a `ManualAckListenerExecutionFailedException` with additional properties `channel` and `deliveryTag`.
This enables the error flow to call `basicAck` or `basicNack` (or `basicReject`) for the message, to control its disposition.

View File

@@ -0,0 +1,10 @@
[[amqp-delay]]
= Delayed Message Exchange
:page-section-summary-toc: 1
Spring AMQP supports the https://docs.spring.io/spring-amqp/reference/html/#delayed-message-exchange[RabbitMQ Delayed Message Exchange Plugin].
For inbound messages, the `x-delay` header is mapped to the `AmqpHeaders.RECEIVED_DELAY` header.
Setting the `AMQPHeaders.DELAY` header causes the corresponding `x-delay` header to be set in outbound messages.
You can also specify the `delay` and `delayExpression` properties on outbound endpoints (`delay-expression` when using XML configuration).
These properties take precedence over the `AmqpHeaders.DELAY` header.

View File

@@ -0,0 +1,36 @@
[[amqp-inbound-ack]]
= Inbound Endpoint Acknowledge Mode
By default, the inbound endpoints use the `AUTO` acknowledge mode, which means the container automatically acknowledges the message when the downstream integration flow completes (or a message is handed off to another thread by using a `QueueChannel` or `ExecutorChannel`).
Setting the mode to `NONE` configures the consumer such that acknowledgments are not used at all (the broker automatically acknowledges the message as soon as it is sent).
Setting the mode to `MANUAL` lets user code acknowledge the message at some other point during processing.
To support this, with this mode, the endpoints provide the `Channel` and `deliveryTag` in the `amqp_channel` and `amqp_deliveryTag` headers, respectively.
You can perform any valid Rabbit command on the `Channel` but, generally, only `basicAck` and `basicNack` (or `basicReject`) are used.
In order to not interfere with the operation of the container, you should not retain a reference to the channel and use it only in the context of the current message.
NOTE: Since the `Channel` is a reference to a "`live`" object, it cannot be serialized and is lost if a message is persisted.
The following example shows how you might use `MANUAL` acknowledgement:
[source,java]
----
@ServiceActivator(inputChannel = "foo", outputChannel = "bar")
public Object handle(@Payload String payload, @Header(AmqpHeaders.CHANNEL) Channel channel,
@Header(AmqpHeaders.DELIVERY_TAG) Long deliveryTag) throws Exception {
// Do some processing
if (allOK) {
channel.basicAck(deliveryTag, false);
// perhaps do some more processing
}
else {
channel.basicNack(deliveryTag, false, true);
}
return someResultForDownStreamProcessing;
}
----

View File

@@ -0,0 +1,263 @@
[[amqp-inbound-channel-adapter]]
= Inbound Channel Adapter
The following listing shows the possible configuration options for an AMQP Inbound Channel Adapter:
[tabs]
======
Java DSL::
+
[source, java, role="primary"]
----
@Bean
public IntegrationFlow amqpInbound(ConnectionFactory connectionFactory) {
return IntegrationFlow.from(Amqp.inboundAdapter(connectionFactory, "aName"))
.handle(m -> System.out.println(m.getPayload()))
.get();
}
----
Java::
+
[source, java, role="secondary"]
----
@Bean
public MessageChannel amqpInputChannel() {
return new DirectChannel();
}
@Bean
public AmqpInboundChannelAdapter inbound(SimpleMessageListenerContainer listenerContainer,
@Qualifier("amqpInputChannel") MessageChannel channel) {
AmqpInboundChannelAdapter adapter = new AmqpInboundChannelAdapter(listenerContainer);
adapter.setOutputChannel(channel);
return adapter;
}
@Bean
public SimpleMessageListenerContainer container(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container =
new SimpleMessageListenerContainer(connectionFactory);
container.setQueueNames("aName");
container.setConcurrentConsumers(2);
// ...
return container;
}
@Bean
@ServiceActivator(inputChannel = "amqpInputChannel")
public MessageHandler handler() {
return new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
System.out.println(message.getPayload());
}
};
}
----
XML::
+
[source, xml, role="secondary"]
----
<int-amqp:inbound-channel-adapter
id="inboundAmqp" <1>
channel="inboundChannel" <2>
queue-names="si.test.queue" <3>
acknowledge-mode="AUTO" <4>
advice-chain="" <5>
channel-transacted="" <6>
concurrent-consumers="" <7>
connection-factory="" <8>
error-channel="" <9>
expose-listener-channel="" <10>
header-mapper="" <11>
mapped-request-headers="" <12>
listener-container="" <13>
message-converter="" <14>
message-properties-converter="" <15>
phase="" <16>
prefetch-count="" <17>
receive-timeout="" <18>
recovery-interval="" <19>
missing-queues-fatal="" <20>
shutdown-timeout="" <21>
task-executor="" <22>
transaction-attribute="" <23>
transaction-manager="" <24>
tx-size="" <25>
consumers-per-queue <26>
batch-mode="MESSAGES"/> <27>
<1> The unique ID for this adapter.
Optional.
<2> Message channel to which converted messages should be sent.
Required.
<3> Names of the AMQP queues (comma-separated list) from which messages should be consumed.
Required.
<4> Acknowledge mode for the `MessageListenerContainer`.
When set to `MANUAL`, the delivery tag and channel are provided in message headers `amqp_deliveryTag` and `amqp_channel`, respectively.
The user application is responsible for acknowledgement.
`NONE` means no acknowledgements (`autoAck`).
`AUTO` means the adapter's container acknowledges when the downstream flow completes.
Optional (defaults to AUTO).
See xref:amqp/inbound-ack.adoc[Inbound Endpoint Acknowledge Mode].
<5> Extra AOP Advices to handle cross-cutting behavior associated with this inbound channel adapter.
Optional.
<6> Flag to indicate that channels created by this component are transactional.
If true, it tells the framework to use a transactional channel and to end all operations (send or receive) with a commit or rollback, depending on the outcome, with an exception that signals a rollback.
Optional (Defaults to false).
<7> Specify the number of concurrent consumers to create.
The default is `1`.
We recommend raising the number of concurrent consumers to scale the consumption of messages coming in from a queue.
However, note that any ordering guarantees are lost once multiple consumers are registered.
In general, use one consumer for low-volume queues.
Not allowed when 'consumers-per-queue' is set.
Optional.
<8> Bean reference to the RabbitMQ `ConnectionFactory`.
Optional (defaults to `connectionFactory`).
<9> Message channel to which error messages should be sent.
Optional.
<10> Whether the listener channel (com.rabbitmq.client.Channel) is exposed to a registered `ChannelAwareMessageListener`.
Optional (defaults to true).
<11> A reference to an `AmqpHeaderMapper` to use when receiving AMQP Messages.
Optional.
By default, only standard AMQP properties (such as `contentType`) are copied to Spring Integration `MessageHeaders`.
Any user-defined headers within the AMQP `MessageProperties` are NOT copied to the message by the default `DefaultAmqpHeaderMapper`.
Not allowed if 'request-header-names' is provided.
<12> Comma-separated list of the names of AMQP Headers to be mapped from the AMQP request into the `MessageHeaders`.
This can only be provided if the 'header-mapper' reference is not provided.
The values in this list can also be simple patterns to be matched against the header names (such as "\*" or "thing1*, thing2" or "*something").
<13> Reference to the `AbstractMessageListenerContainer` to use for receiving AMQP Messages.
If this attribute is provided, no other attribute related to the listener container configuration should be provided.
In other words, by setting this reference, you must take full responsibility for the listener container configuration.
The only exception is the `MessageListener` itself.
Since that is actually the core responsibility of this channel adapter implementation, the referenced listener container must not already have its own `MessageListener`.
Optional.
<14> The `MessageConverter` to use when receiving AMQP messages.
Optional.
<15> The `MessagePropertiesConverter` to use when receiving AMQP messages.
Optional.
<16> Specifies the phase in which the underlying `AbstractMessageListenerContainer` should be started and stopped.
The startup order proceeds from lowest to highest, and the shutdown order is the reverse of that.
By default, this value is `Integer.MAX_VALUE`, meaning that this container starts as late as possible and stops as soon as possible.
Optional.
<17> Tells the AMQP broker how many messages to send to each consumer in a single request.
Often, you can set this value high to improve throughput.
It should be greater than or equal to the transaction size (see the `tx-size` attribute, later in this list).
Optional (defaults to `1`).
<18> Receive timeout in milliseconds.
Optional (defaults to `1000`).
<19> Specifies the interval between recovery attempts of the underlying `AbstractMessageListenerContainer` (in milliseconds).
Optional (defaults to `5000`).
<20> If 'true' and none of the queues are available on the broker, the container throws a fatal exception during startup and stops if the queues are deleted when the container is running (after making three attempts to passively declare the queues).
If `false`, the container does not throw an exception and goes into recovery mode, attempting to restart according to the `recovery-interval`.
Optional (defaults to `true`).
<21> The time to wait for workers (in milliseconds) after the underlying `AbstractMessageListenerContainer` is stopped and before the AMQP connection is forced closed.
If any workers are active when the shutdown signal comes, they are allowed to finish processing as long as they can finish within this timeout.
Otherwise, the connection is closed and messages remain unacknowledged (if the channel is transactional).
Optional (defaults to `5000`).
<22> By default, the underlying `AbstractMessageListenerContainer` uses a `SimpleAsyncTaskExecutor` implementation, that fires up a new thread for each task, running it asynchronously.
By default, the number of concurrent threads is unlimited.
Note that this implementation does not reuse threads.
Consider using a thread-pooling `TaskExecutor` implementation as an alternative.
Optional (defaults to `SimpleAsyncTaskExecutor`).
<23> By default, the underlying `AbstractMessageListenerContainer` creates a new instance of the `DefaultTransactionAttribute` (it takes the EJB approach to rolling back on runtime but not checked exceptions).
Optional (defaults to `DefaultTransactionAttribute`).
<24> Sets a bean reference to an external `PlatformTransactionManager` on the underlying `AbstractMessageListenerContainer`.
The transaction manager works in conjunction with the `channel-transacted` attribute.
If there is already a transaction in progress when the framework is sending or receiving a message and the `channelTransacted` flag is `true`, the commit or rollback of the messaging transaction is deferred until the end of the current transaction.
If the `channelTransacted` flag is `false`, no transaction semantics apply to the messaging operation (it is auto-acked).
For further information, see
https://docs.spring.io/spring-amqp/reference/html/%255Freference.html#%5Ftransactions[Transactions with Spring AMQP].
Optional.
<25> Tells the `SimpleMessageListenerContainer` how many messages to process in a single transaction (if the channel is transactional).
For best results, it should be less than or equal to the value set in `prefetch-count`.
Not allowed when 'consumers-per-queue' is set.
Optional (defaults to `1`).
<26> Indicates that the underlying listener container should be a `DirectMessageListenerContainer` instead of the default `SimpleMessageListenerContainer`.
See the https://docs.spring.io/spring-amqp/reference/html/[Spring AMQP Reference Manual] for more information.
<27> When the container's `consumerBatchEnabled` is `true`, determines how the adapter presents the batch of messages in the message payload.
When set to `MESSAGES` (default), the payload is a `List<Message<?>>` where each message has headers mapped from the incoming AMQP `Message` and the payload is the converted `body`.
When set to `EXTRACT_PAYLOADS`, the payload is a `List<?>` where the elements are converted from the AMQP `Message` body.
`EXTRACT_PAYLOADS_WITH_HEADERS` is similar to `EXTRACT_PAYLOADS` but, in addition, the headers from each message are mapped from the `MessageProperties` into a `List<Map<String, Object>` at the corresponding index; the header name is `AmqpInboundChannelAdapter.CONSOLIDATED_HEADERS`.
----
======
[NOTE]
.container
====
Note that when configuring an external container with XML, you cannot use the Spring AMQP namespace to define the container.
This is because the namespace requires at least one `<listener/>` element.
In this environment, the listener is internal to the adapter.
For this reason, you must define the container by using a normal Spring `<bean/>` definition, as the following example shows:
[source,xml]
----
<bean id="container"
class="org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="queueNames" value="aName.queue" />
<property name="defaultRequeueRejected" value="false"/>
</bean>
----
====
IMPORTANT: Even though the Spring Integration JMS and AMQP support is similar, important differences exist.
The JMS inbound channel adapter is using a `JmsDestinationPollingSource` under the covers and expects a configured poller.
The AMQP inbound channel adapter uses an `AbstractMessageListenerContainer` and is message driven.
In that regard, it is more similar to the JMS message-driven channel adapter.
Starting with version 5.5, the `AmqpInboundChannelAdapter` can be configured with an `org.springframework.amqp.rabbit.retry.MessageRecoverer` strategy which is used in the `RecoveryCallback` when the retry operation is called internally.
See `setMessageRecoverer()` JavaDocs for more information.
The `@Publisher` annotation also can be used in combination with a `@RabbitListener`:
[source, java]
----
@Configuration
@EnableIntegration
@EnableRabbit
@EnablePublisher
public static class ContextConfiguration {
@Bean
QueueChannel fromRabbitViaPublisher() {
return new QueueChannel();
}
@RabbitListener(queuesToDeclare = @Queue("publisherQueue"))
@Publisher("fromRabbitViaPublisher")
@Payload("#args.payload.toUpperCase()")
public void consumeForPublisher(String payload) {
}
}
----
By default, the `@Publisher` AOP interceptor deals with a return value from a method call.
However, the return value from a `@RabbitListener` method is treated as an AMQP reply message.
Therefore, such an approach cannot be used together with a `@Publisher`, so a `@Payload` annotation with respective SpEL expression against method arguments is a recommended way for this combination.
See more information about the `@Publisher` in the xref:message-publishing.adoc#publisher-annotation[Annotation-driven Configuration] section.
IMPORTANT: When using exclusive or single-active consumers in the listener container, it is recommended that you set the container property `forceStop` to `true`.
This will prevent a race condition where, after stopping the container, another consumer could start consuming messages before this instance has fully stopped.
[[amqp-debatching]]
== Batched Messages
See https://docs.spring.io/spring-amqp/docs/current/reference/html/#template-batching[the Spring AMQP Documentation] for more information about batched messages.
To produce batched messages with Spring Integration, simply configure the outbound endpoint with a `BatchingRabbitTemplate`.
When receiving batched messages, by default, the listener containers extract each fragment message and the adapter will produce a `Message<?>` for each fragment.
Starting with version 5.2, if the container's `deBatchingEnabled` property is set to `false`, the de-batching is performed by the adapter instead, and a single `Message<List<?>>` is produced with the payload being a list of the fragment payloads (after conversion if appropriate).
The default `BatchingStrategy` is the `SimpleBatchingStrategy`, but this can be overridden on the adapter.
NOTE: The `org.springframework.amqp.rabbit.retry.MessageBatchRecoverer` must be used with batches when recovery is required for retry operations.

View File

@@ -0,0 +1,121 @@
[[amqp-inbound-gateway]]
= Inbound Gateway
The inbound gateway supports all the attributes on the inbound channel adapter (except that 'channel' is replaced by 'request-channel'), plus some additional attributes.
The following listing shows the available attributes:
[tabs]
======
Java DSL::
+
[source, java, role="primary"]
----
@Bean // return the upper cased payload
public IntegrationFlow amqpInboundGateway(ConnectionFactory connectionFactory) {
return IntegrationFlow.from(Amqp.inboundGateway(connectionFactory, "foo"))
.transform(String.class, String::toUpperCase)
.get();
}
----
Java::
+
[source, java, role="secondary"]
----
@Bean
public MessageChannel amqpInputChannel() {
return new DirectChannel();
}
@Bean
public AmqpInboundGateway inbound(SimpleMessageListenerContainer listenerContainer,
@Qualifier("amqpInputChannel") MessageChannel channel) {
AmqpInboundGateway gateway = new AmqpInboundGateway(listenerContainer);
gateway.setRequestChannel(channel);
gateway.setDefaultReplyTo("bar");
return gateway;
}
@Bean
public SimpleMessageListenerContainer container(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container =
new SimpleMessageListenerContainer(connectionFactory);
container.setQueueNames("foo");
container.setConcurrentConsumers(2);
// ...
return container;
}
@Bean
@ServiceActivator(inputChannel = "amqpInputChannel")
public MessageHandler handler() {
return new AbstractReplyProducingMessageHandler() {
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
return "reply to " + requestMessage.getPayload();
}
};
}
----
XML::
+
[source, xml, role="secondary"]
----
<int-amqp:inbound-gateway
id="inboundGateway" <1>
request-channel="myRequestChannel" <2>
header-mapper="" <3>
mapped-request-headers="" <4>
mapped-reply-headers="" <5>
reply-channel="myReplyChannel" <6>
reply-timeout="1000" <7>
amqp-template="" <8>
default-reply-to="" /> <9>
----
======
<1> The Unique ID for this adapter.
Optional.
<2> Message channel to which converted messages are sent.
Required.
<3> A reference to an `AmqpHeaderMapper` to use when receiving AMQP Messages.
Optional.
By default, only standard AMQP properties (such as `contentType`) are copied to and from Spring Integration `MessageHeaders`.
Any user-defined headers within the AMQP `MessageProperties` are not copied to or from an AMQP message by the default `DefaultAmqpHeaderMapper`.
Not allowed if 'request-header-names' or 'reply-header-names' is provided.
<4> Comma-separated list of names of AMQP Headers to be mapped from the AMQP request into the `MessageHeaders`.
This attribute can be provided only if the 'header-mapper' reference is not provided.
The values in this list can also be simple patterns to be matched against the header names (e.g. `"\*"` or `"thing1*, thing2"` or `"*thing1"`).
<5> Comma-separated list of names of `MessageHeaders` to be mapped into the AMQP message properties of the AMQP reply message.
All standard Headers (such as `contentType`) are mapped to AMQP Message Properties, while user-defined headers are mapped to the 'headers' property.
This attribute can only be provided if the 'header-mapper' reference is not provided.
The values in this list can also be simple patterns to be matched against the header names (for example, `"\*"` or `"foo*, bar"` or `"*foo"`).
<6> Message Channel where reply Messages are expected.
Optional.
<7> Sets the `receiveTimeout` on the underlying `o.s.i.core.MessagingTemplate` for receiving messages from the reply channel.
If not specified, this property defaults to `1000` (1 second).
Only applies if the container thread hands off to another thread before the reply is sent.
<8> The customized `AmqpTemplate` bean reference (to have more control over the reply messages to send).
You can provide an alternative implementation to the `RabbitTemplate`.
<9> The `replyTo` `o.s.amqp.core.Address` to be used when the `requestMessage` does not have a `replyTo`
property.
If this option is not specified, no `amqp-template` is provided, no `replyTo` property exists in the request message, and
an `IllegalStateException` is thrown because the reply cannot be routed.
If this option is not specified and an external `amqp-template` is provided, no exception is thrown.
You must either specify this option or configure a default `exchange` and `routingKey` on that template,
if you anticipate cases when no `replyTo` property exists in the request message.
See the note in xref:amqp/inbound-channel-adapter.adoc[Inbound Channel Adapter] about configuring the `listener-container` attribute.
Starting with version 5.5, the `AmqpInboundChannelAdapter` can be configured with an `org.springframework.amqp.rabbit.retry.MessageRecoverer` strategy which is used in the `RecoveryCallback` when the retry operation is called internally.
See `setMessageRecoverer()` JavaDocs for more information.
[[amqp-gateway-debatching]]
== Batched Messages
See xref:amqp/inbound-channel-adapter.adoc#amqp-debatching[Batched Messages].

View File

@@ -0,0 +1,97 @@
[[amqp-message-headers]]
= AMQP Message Headers
[[overview]]
== Overview
The Spring Integration AMQP Adapters automatically map all AMQP properties and headers.
(This is a change from 4.3 - previously, only standard headers were mapped).
By default, these properties are copied to and from Spring Integration `MessageHeaders` by using the
https://docs.spring.io/spring-integration/api/org/springframework/integration/amqp/support/DefaultAmqpHeaderMapper.html[`DefaultAmqpHeaderMapper`].
You can pass in your own implementation of AMQP-specific header mappers, as the adapters have properties to support doing so.
Any user-defined headers within the AMQP https://docs.spring.io/spring-amqp/api/org/springframework/amqp/core/MessageProperties.html[`MessageProperties`] are copied to or from an AMQP message, unless explicitly negated by the `requestHeaderNames` or `replyHeaderNames` properties of the `DefaultAmqpHeaderMapper`.
By default, for an outbound mapper, no `x-*` headers are mapped.
See the xref:amqp/message-headers.adoc#header-copy-caution[caution] that appears later in this section for why.
To override the default and revert to the pre-4.3 behavior, use `STANDARD_REQUEST_HEADERS` and
`STANDARD_REPLY_HEADERS` in the properties.
TIP: When mapping user-defined headers, the values can also contain simple wildcard patterns (such as `thing*` or `\*thing`) to be matched.
The `*` matches all headers.
Starting with version 4.1, the `AbstractHeaderMapper` (a `DefaultAmqpHeaderMapper` superclass) lets the `NON_STANDARD_HEADERS` token be configured for the `requestHeaderNames` and `replyHeaderNames` properties (in addition to the existing `STANDARD_REQUEST_HEADERS` and `STANDARD_REPLY_HEADERS`) to map all user-defined headers.
The `org.springframework.amqp.support.AmqpHeaders` class identifies the default headers that are used by the `DefaultAmqpHeaderMapper`:
* `amqp_appId`
* `amqp_clusterId`
* `amqp_contentEncoding`
* `amqp_contentLength`
* `content-type` (see xref:amqp/message-headers.adoc#amqp-content-type[The `contentType` Header])
* `amqp_correlationId`
* `amqp_delay`
* `amqp_deliveryMode`
* `amqp_deliveryTag`
* `amqp_expiration`
* `amqp_messageCount`
* `amqp_messageId`
* `amqp_receivedDelay`
* `amqp_receivedDeliveryMode`
* `amqp_receivedExchange`
* `amqp_receivedRoutingKey`
* `amqp_redelivered`
* `amqp_replyTo`
* `amqp_timestamp`
* `amqp_type`
* `amqp_userId`
* `amqp_publishConfirm`
* `amqp_publishConfirmNackCause`
* `amqp_returnReplyCode`
* `amqp_returnReplyText`
* `amqp_returnExchange`
* `amqp_returnRoutingKey`
* `amqp_channel`
* `amqp_consumerTag`
* `amqp_consumerQueue`
[[header-copy-caution]]
CAUTION: As mentioned earlier in this section, using a header mapping pattern of `\*` is a common way to copy all headers.
However, this can have some unexpected side effects, because certain RabbitMQ proprietary properties/headers are also copied.
For example, when you use https://www.rabbitmq.com/federated-exchanges.html[federation], the received message may have a property named `x-received-from`, which contains the node that sent the message.
If you use the wildcard character `*` for the request and reply header mapping on the inbound gateway, this header is copied, which may cause some issues with federation.
This reply message may be federated back to the sending broker, which may think that a message is looping and, as a result, silently drop it.
If you wish to use the convenience of wildcard header mapping, you may need to filter out some headers in the downstream flow.
For example, to avoid copying the `x-received-from` header back to the reply you can use `<int:header-filter ... header-names="x-received-from">` before sending the reply to the AMQP inbound gateway.
Alternatively, you can explicitly list those properties that you actually want mapped, instead of using wildcards.
For these reasons, for inbound messages, the mapper (by default) does not map any `x-*` headers.
It also does not map the `deliveryMode` to the `amqp_deliveryMode` header, to avoid propagation of that header from an inbound message to an outbound message.
Instead, this header is mapped to `amqp_receivedDeliveryMode`, which is not mapped on output.
Starting with version 4.3, patterns in the header mappings can be negated by preceding the pattern with `!`.
Negated patterns get priority, so a list such as `STANDARD_REQUEST_HEADERS,thing1,ba*,!thing2,!thing3,qux,!thing1` does not map `thing1` (nor `thing2` nor `thing3`).
The standard headers plus `bad` and `qux` are mapped.
The negation technique can be useful for example to not map JSON type headers for incoming messages when a JSON deserialization logic is done in the receiver downstream different way.
For this purpose a `!json_*` pattern should be configured for header mapper of the inbound channel adapter/gateway.
IMPORTANT: If you have a user-defined header that begins with `!` that you do wish to map, you need to escape it with `\`, as follows: `STANDARD_REQUEST_HEADERS,\!myBangHeader`.
The header named `!myBangHeader` is now mapped.
NOTE: Starting with version 5.1, the `DefaultAmqpHeaderMapper` will fall back to mapping `MessageHeaders.ID` and `MessageHeaders.TIMESTAMP` to `MessageProperties.messageId` and `MessageProperties.timestamp` respectively, if the corresponding `amqp_messageId` or `amqp_timestamp` headers are not present on outbound messages.
Inbound properties will be mapped to the `amqp_*` headers as before.
It is useful to populate the `messageId` property when message consumers are using stateful retry.
[[amqp-content-type]]
== The `contentType` Header
Unlike other headers, the `AmqpHeaders.CONTENT_TYPE` is not prefixed with `amqp_`; this allows transparent passing of the contentType header across different technologies.
For example an inbound HTTP message sent to a RabbitMQ queue.
The `contentType` header is mapped to Spring AMQP's `MessageProperties.contentType` property and that is subsequently mapped to RabbitMQ's `content_type` property.
Prior to version 5.1, this header was also mapped as an entry in the `MessageProperties.headers` map; this was incorrect and, furthermore, the value could be wrong since the underlying Spring AMQP message converter might have changed the content type.
Such a change would be reflected in the first-class `content_type` property, but not in the RabbitMQ headers map.
Inbound mapping ignored the headers map value.
`contentType` is no longer mapped to an entry in the headers map.

View File

@@ -0,0 +1,159 @@
[[amqp-outbound-channel-adapter]]
= Outbound Channel Adapter
The following example shows the available properties for an AMQP outbound channel adapter:
[tabs]
======
Java DSL::
+
[source,java,role="primary"]
----
@Bean
public IntegrationFlow amqpOutbound(AmqpTemplate amqpTemplate,
MessageChannel amqpOutboundChannel) {
return IntegrationFlow.from(amqpOutboundChannel)
.handle(Amqp.outboundAdapter(amqpTemplate)
.routingKey("queue1")) // default exchange - route to queue 'queue1'
.get();
}
----
Java::
+
[source,java,role="secondary"]
----
@Bean
@ServiceActivator(inputChannel = "amqpOutboundChannel")
public AmqpOutboundEndpoint amqpOutbound(AmqpTemplate amqpTemplate) {
AmqpOutboundEndpoint outbound = new AmqpOutboundEndpoint(amqpTemplate);
outbound.setRoutingKey("queue1"); // default exchange - route to queue 'queue1'
return outbound;
}
@Bean
public MessageChannel amqpOutboundChannel() {
return new DirectChannel();
}
----
XML::
+
[source,xml,role="secondary"]
----
<int-amqp:outbound-channel-adapter id="outboundAmqp" <1>
channel="outboundChannel" <2>
amqp-template="myAmqpTemplate" <3>
exchange-name="" <4>
exchange-name-expression="" <5>
order="1" <6>
routing-key="" <7>
routing-key-expression="" <8>
default-delivery-mode"" <9>
confirm-correlation-expression="" <10>
confirm-ack-channel="" <11>
confirm-nack-channel="" <12>
confirm-timeout="" <13>
wait-for-confirm="" <14>
return-channel="" <15>
error-message-strategy="" <16>
header-mapper="" <17>
mapped-request-headers="" <18>
lazy-connect="true" <19>
multi-send="false"/> <20>
----
======
<1> The unique ID for this adapter.
Optional.
<2> Message channel to which messages should be sent to have them converted and published to an AMQP exchange.
Required.
<3> Bean reference to the configured AMQP template.
Optional (defaults to `amqpTemplate`).
<4> The name of the AMQP exchange to which messages are sent.
If not provided, messages are sent to the default, no-name exchange.
Mutually exclusive with 'exchange-name-expression'.
Optional.
<5> A SpEL expression that is evaluated to determine the name of the AMQP exchange to which messages are sent, with the message as the root object.
If not provided, messages are sent to the default, no-name exchange.
Mutually exclusive with 'exchange-name'.
Optional.
<6> The order for this consumer when multiple consumers are registered, thereby enabling load-balancing and failover.
Optional (defaults to `Ordered.LOWEST_PRECEDENCE [=Integer.MAX_VALUE]`).
<7> The fixed routing-key to use when sending messages.
By default, this is an empty `String`.
Mutually exclusive with 'routing-key-expression'.
Optional.
<8> A SpEL expression that is evaluated to determine the routing key to use when sending messages, with the message as the root object (for example, 'payload.key').
By default, this is an empty `String`.
Mutually exclusive with 'routing-key'.
Optional.
<9> The default delivery mode for messages: `PERSISTENT` or `NON_PERSISTENT`.
Overridden if the `header-mapper` sets the delivery mode.
If the Spring Integration message header `amqp_deliveryMode` is present, the `DefaultHeaderMapper` sets the value.
If this attribute is not supplied and the header mapper does not set it, the default depends on the underlying Spring AMQP `MessagePropertiesConverter` used by the `RabbitTemplate`.
If that is not customized at all, the default is `PERSISTENT`.
Optional.
<10> An expression that defines correlation data.
When provided, this configures the underlying AMQP template to receive publisher confirmations.
Requires a dedicated `RabbitTemplate` and a `CachingConnectionFactory` with the `publisherConfirms` property set to `true`.
When a publisher confirmation is received and correlation data is supplied, it is written to either the `confirm-ack-channel` or the `confirm-nack-channel`, depending on the confirmation type.
The payload of the confirmation is the correlation data, as defined by this expression.
The message has an 'amqp_publishConfirm' header set to `true` (`ack`) or `false` (`nack`).
Examples: `headers['myCorrelationData']` and `payload`.
Version 4.1 introduced the `amqp_publishConfirmNackCause` message header.
It contains the `cause` of a 'nack' for a publisher confirmation.
Starting with version 4.2, if the expression resolves to a `Message<?>` instance (such as `#this`), the message emitted on the `ack`/`nack` channel is based on that message, with the additional header(s) added.
Previously, a new message was created with the correlation data as its payload, regardless of type.
Also see xref:amqp/alternative-confirms-returns.adoc[Alternative Mechanism for Publisher Confirms and Returns].
Optional.
<11> The channel to which positive (`ack`) publisher confirms are sent.
The payload is the correlation data defined by the `confirm-correlation-expression`.
If the expression is `#root` or `#this`, the message is built from the original message, with the `amqp_publishConfirm` header set to `true`.
Also see xref:amqp/alternative-confirms-returns.adoc[Alternative Mechanism for Publisher Confirms and Returns].
Optional (the default is `nullChannel`).
<12> The channel to which negative (`nack`) publisher confirmations are sent.
The payload is the correlation data defined by the `confirm-correlation-expression` (if there is no `ErrorMessageStrategy` configured).
If the expression is `#root` or `#this`, the message is built from the original message, with the `amqp_publishConfirm` header set to `false`.
When there is an `ErrorMessageStrategy`, the message is an `ErrorMessage` with a `NackedAmqpMessageException` payload.
Also see xref:amqp/alternative-confirms-returns.adoc[Alternative Mechanism for Publisher Confirms and Returns].
Optional (the default is `nullChannel`).
<13> When set, the adapter will synthesize a negative acknowledgment (nack) if a publisher confirm is not received within this time in milliseconds.
Pending confirms are checked every 50% of this value, so the actual time a nack is sent will be between 1x and 1.5x this value.
Also see xref:amqp/alternative-confirms-returns.adoc[Alternative Mechanism for Publisher Confirms and Returns].
Default none (nacks will not be generated).
<14> When set to true, the calling thread will block, waiting for a publisher confirmation.
This requires a `RabbitTemplate` configured for confirms as well as a `confirm-correlation-expression`.
The thread will block for up to `confirm-timeout` (or 5 seconds by default).
If a timeout occurs, a `MessageTimeoutException` will be thrown.
If returns are enabled and a message is returned, or any other exception occurs while awaiting the confirmation, a `MessageHandlingException` will be thrown, with an appropriate message.
<15> The channel to which returned messages are sent.
When provided, the underlying AMQP template is configured to return undeliverable messages to the adapter.
When there is no `ErrorMessageStrategy` configured, the message is constructed from the data received from AMQP, with the following additional headers: `amqp_returnReplyCode`, `amqp_returnReplyText`, `amqp_returnExchange`, `amqp_returnRoutingKey`.
When there is an `ErrorMessageStrategy`, the message is an `ErrorMessage` with a `ReturnedAmqpMessageException` payload.
Also see xref:amqp/alternative-confirms-returns.adoc[Alternative Mechanism for Publisher Confirms and Returns].
Optional.
<16> A reference to an `ErrorMessageStrategy` implementation used to build `ErrorMessage` instances when sending returned or negatively acknowledged messages.
<17> A reference to an `AmqpHeaderMapper` to use when sending AMQP Messages.
By default, only standard AMQP properties (such as `contentType`) are copied to the Spring Integration `MessageHeaders`.
Any user-defined headers is not copied to the message by the default`DefaultAmqpHeaderMapper`.
Not allowed if 'request-header-names' is provided.
Optional.
<18> Comma-separated list of names of AMQP Headers to be mapped from the `MessageHeaders` to the AMQP Message.
Not allowed if the 'header-mapper' reference is provided.
The values in this list can also be simple patterns to be matched against the header names (e.g. `"\*"` or `"thing1*, thing2"` or `"*thing1"`).
<19> When set to `false`, the endpoint attempts to connect to the broker during application context initialization.
This allows "`fail fast`" detection of bad configuration but also causes initialization to fail if the broker is down.
When `true` (the default), the connection is established (if it does not already exist because some other component established it) when the first message is sent.
<20> When set to `true`, payloads of type `Iterable<Message<?>>` will be sent as discrete messages on the same channel within the scope of a single `RabbitTemplate` invocation.
Requires a `RabbitTemplate`.
When `wait-for-confirms` is true, `RabbitTemplate.waitForConfirmsOrDie()` is invoked after the messages have been sent.
With a transactional template, the sends will be performed in either a new transaction or one that has already been started (if present).
[IMPORTANT]
.return-channel
=====
Using a `return-channel` requires a `RabbitTemplate` with the `mandatory` property set to `true` and a `CachingConnectionFactory` with the `publisherReturns` property set to `true`.
When using multiple outbound endpoints with returns, a separate `RabbitTemplate` is needed for each endpoint.
=====

View File

@@ -0,0 +1,10 @@
[[amqp-outbound-endpoints]]
= Outbound Endpoints
:page-section-summary-toc: 1
The following outbound endpoints have many similar configuration options.
Starting with version 5.2, the `confirm-timeout` has been added.
Normally, when publisher confirms are enabled, the broker will quickly return an ack (or nack) which will be sent to the appropriate channel.
If a channel is closed before the confirm is received, the Spring AMQP framework will synthesize a nack.
"Missing" acks should never occur but, if you set this property, the endpoint will periodically check for them and synthesize a nack if the time elapses without a confirm being received.

View File

@@ -0,0 +1,168 @@
[[amqp-outbound-gateway]]
= Outbound Gateway
The following listing shows the possible properties for an AMQP Outbound Gateway:
[tabs]
======
Java DSL::
+
[source,java,role="primary"]
----
@Bean
public IntegrationFlow amqpOutbound(AmqpTemplate amqpTemplate) {
return f -> f.handle(Amqp.outboundGateway(amqpTemplate)
.routingKey("foo")) // default exchange - route to queue 'foo'
.get();
}
@MessagingGateway(defaultRequestChannel = "amqpOutbound.input")
public interface MyGateway {
String sendToRabbit(String data);
}
----
Java::
+
[source,java,role="secondary"]
----
@Bean
@ServiceActivator(inputChannel = "amqpOutboundChannel")
public AmqpOutboundEndpoint amqpOutbound(AmqpTemplate amqpTemplate) {
AmqpOutboundEndpoint outbound = new AmqpOutboundEndpoint(amqpTemplate);
outbound.setExpectReply(true);
outbound.setRoutingKey("foo"); // default exchange - route to queue 'foo'
return outbound;
}
@Bean
public MessageChannel amqpOutboundChannel() {
return new DirectChannel();
}
@MessagingGateway(defaultRequestChannel = "amqpOutboundChannel")
public interface MyGateway {
String sendToRabbit(String data);
}
----
XML::
+
[source,xml,role="secondary"]
----
<int-amqp:outbound-gateway id="outboundGateway" <1>
request-channel="myRequestChannel" <2>
amqp-template="" <3>
exchange-name="" <4>
exchange-name-expression="" <5>
order="1" <6>
reply-channel="" <7>
reply-timeout="" <8>
requires-reply="" <9>
routing-key="" <10>
routing-key-expression="" <11>
default-delivery-mode"" <12>
confirm-correlation-expression="" <13>
confirm-ack-channel="" <14>
confirm-nack-channel="" <15>
confirm-timeout="" <16>
return-channel="" <17>
error-message-strategy="" <18>
lazy-connect="true" /> <19>
----
======
<1> The unique ID for this adapter.
Optional.
<2> Message channel to which messages are sent to have them converted and published to an AMQP exchange.
Required.
<3> Bean reference to the configured AMQP template.
Optional (defaults to `amqpTemplate`).
<4> The name of the AMQP exchange to which messages should be sent.
If not provided, messages are sent to the default, no-name cxchange.
Mutually exclusive with 'exchange-name-expression'.
Optional.
<5> A SpEL expression that is evaluated to determine the name of the AMQP exchange to which messages should be sent, with the message as the root object.
If not provided, messages are sent to the default, no-name exchange.
Mutually exclusive with 'exchange-name'.
Optional.
<6> The order for this consumer when multiple consumers are registered, thereby enabling load-balancing and failover.
Optional (defaults to `Ordered.LOWEST_PRECEDENCE [=Integer.MAX_VALUE]`).
<7> Message channel to which replies should be sent after being received from an AMQP queue and converted.
Optional.
<8> The time the gateway waits when sending the reply message to the `reply-channel`.
This only applies if the `reply-channel` can block -- such as a `QueueChannel` with a capacity limit that is currently full.
Defaults to infinity.
<9> When `true`, the gateway throws an exception if no reply message is received within the `AmqpTemplate`'s `replyTimeout` property.
Defaults to `true`.
<10> The `routing-key` to use when sending messages.
By default, this is an empty `String`.
Mutually exclusive with 'routing-key-expression'.
Optional.
<11> A SpEL expression that is evaluated to determine the `routing-key` to use when sending messages, with the message as the root object (for example, 'payload.key').
By default, this is an empty `String`.
Mutually exclusive with 'routing-key'.
Optional.
<12> The default delivery mode for messages: `PERSISTENT` or `NON_PERSISTENT`.
Overridden if the `header-mapper` sets the delivery mode.
If the Spring Integration message header `amqp_deliveryMode` is present, the `DefaultHeaderMapper` sets the value.
If this attribute is not supplied and the header mapper does not set it, the default depends on the underlying Spring AMQP `MessagePropertiesConverter` used by the `RabbitTemplate`.
If that is not customized at all, the default is `PERSISTENT`.
Optional.
<13> Since version 4.2.
An expression defining correlation data.
When provided, this configures the underlying AMQP template to receive publisher confirms.
Requires a dedicated `RabbitTemplate` and a `CachingConnectionFactory` with the `publisherConfirms` property set to `true`.
When a publisher confirm is received and correlation data is supplied, it is written to either the `confirm-ack-channel` or the `confirm-nack-channel`, depending on the confirmation type.
The payload of the confirm is the correlation data, as defined by this expression.
The message has a header 'amqp_publishConfirm' set to `true` (`ack`) or `false` (`nack`).
For `nack` confirmations, Spring Integration provides an additional header `amqp_publishConfirmNackCause`.
Examples: `headers['myCorrelationData']` and `payload`.
If the expression resolves to a `Message<?>` instance (such as `#this`), the message
emitted on the `ack`/`nack` channel is based on that message, with the additional headers added.
Previously, a new message was created with the correlation data as its payload, regardless of type.
Also see xref:amqp/alternative-confirms-returns.adoc[Alternative Mechanism for Publisher Confirms and Returns].
Optional.
<14> The channel to which positive (`ack`) publisher confirmations are sent.
The payload is the correlation data defined by `confirm-correlation-expression`.
If the expression is `#root` or `#this`, the message is built from the original message, with the `amqp_publishConfirm` header set to `true`.
Also see xref:amqp/alternative-confirms-returns.adoc[Alternative Mechanism for Publisher Confirms and Returns].
Optional (the default is `nullChannel`).
<15> The channel to which negative (`nack`) publisher confirmations are sent.
The payload is the correlation data defined by `confirm-correlation-expression` (if there is no `ErrorMessageStrategy` configured).
If the expression is `#root` or `#this`, the message is built from the original message, with the `amqp_publishConfirm` header set to `false`.
When there is an `ErrorMessageStrategy`, the message is an `ErrorMessage` with a `NackedAmqpMessageException` payload.
Also see xref:amqp/alternative-confirms-returns.adoc[Alternative Mechanism for Publisher Confirms and Returns].
Optional (the default is `nullChannel`).
<16> When set, the gateway will synthesize a negative acknowledgment (nack) if a publisher confirm is not received within this time in milliseconds.
Pending confirms are checked every 50% of this value, so the actual time a nack is sent will be between 1x and 1.5x this value.
Default none (nacks will not be generated).
<17> The channel to which returned messages are sent.
When provided, the underlying AMQP template is configured to return undeliverable messages to the adapter.
When there is no `ErrorMessageStrategy` configured, the message is constructed from the data received from AMQP, with the following additional headers: `amqp_returnReplyCode`, `amqp_returnReplyText`, `amqp_returnExchange`, and `amqp_returnRoutingKey`.
When there is an `ErrorMessageStrategy`, the message is an `ErrorMessage` with a `ReturnedAmqpMessageException` payload.
Also see xref:amqp/alternative-confirms-returns.adoc[Alternative Mechanism for Publisher Confirms and Returns].
Optional.
<18> A reference to an `ErrorMessageStrategy` implementation used to build `ErrorMessage` instances when sending returned or negatively acknowledged messages.
<19> When set to `false`, the endpoint attempts to connect to the broker during application context initialization.
This allows "`fail fast`" detection of bad configuration by logging an error message if the broker is down.
When `true` (the default), the connection is established (if it does not already exist because some other component established it) when the first message is sent.
[IMPORTANT]
.return-channel
=====
Using a `return-channel` requires a `RabbitTemplate` with the `mandatory` property set to `true` and a `CachingConnectionFactory` with the `publisherReturns` property set to `true`.
When using multiple outbound endpoints with returns, a separate `RabbitTemplate` is needed for each endpoint.
=====
IMPORTANT: The underlying `AmqpTemplate` has a default `replyTimeout` of five seconds.
If you require a longer timeout, you must configure it on the `template`.
Note that the only difference between the outbound adapter and outbound gateway configuration is the setting of the
`expectReply` property.

View File

@@ -0,0 +1,56 @@
[[polled-inbound-channel-adapter]]
= Polled Inbound Channel Adapter
[[overview]]
== Overview
Version 5.0.1 introduced a polled channel adapter, letting you fetch individual messages on demand -- for example, with a `MessageSourcePollingTemplate` or a poller.
See xref:polling-consumer.adoc#deferred-acks-message-source[Deferred Acknowledgment Pollable Message Source] for more information.
It does not currently support XML configuration.
The following example shows how to configure an `AmqpMessageSource`:
[tabs]
======
Java DSL::
+
[source, java, role="primary"]
----
@Bean
public IntegrationFlow flow() {
return IntegrationFlow.from(Amqp.inboundPolledAdapter(connectionFactory(), DSL_QUEUE),
e -> e.poller(Pollers.fixedDelay(1_000)).autoStartup(false))
.handle(p -> {
...
})
.get();
}
----
Java::
+
[source, java, role="secondary"]
----
@Bean
public AmqpMessageSource source(ConnectionFactory connectionFactory) {
return new AmqpMessageSource(connectionFactory, "someQueue");
}
----
======
See the https://docs.spring.io/spring-integration/api/org/springframework/integration/amqp/inbound/AmqpMessageSource.html[Javadoc] for configuration properties.
[source, xml, role="secondary"]
.XML
----
This adapter currently does not have XML configuration support.
----
[[amqp-polled-debatching]]
== Batched Messages
See xref:amqp/inbound-channel-adapter.adoc#amqp-debatching[Batched Messages].
For the polled adapter, there is no listener container, batched messages are always debatched (if the `BatchingStrategy` supports doing so).

View File

@@ -0,0 +1,40 @@
[[rmq-streams]]
= RabbitMQ Stream Queue Support
Version 6.0 introduced support for RabbitMQ Stream Queues.
The DSL factory class for these endpoints is `RabbitStream`.
[[rmq-stream-inbound-channel-adapter]]
== RabbitMQ Stream Inbound Channel Adapter
[source, java]
----
@Bean
IntegrationFlow simpleStream(Environment env) {
return IntegrationFlow.from(RabbitStream.inboundAdapter(env).streamName("my.stream"))
// ...
.get();
}
@Bean
IntegrationFlow superStream(Environment env) {
return IntegrationFlow.from(RabbitStream.inboundAdapter(env).superStream("my.super.stream", "my.consumer"))
// ...
.get();
}
----
[[rmq-stream-outbound-channel-adapter]]
== RabbitMQ Stream Outbound Channel Adapter
[source, java]
----
@Bean
IntegrationFlow outbound(Environment env) {
return f -> f
// ...
.handle(RabbitStream.outboundStreamAdapter(env, "my.stream"));
}
----

View File

@@ -0,0 +1,21 @@
[[amqp-samples]]
= AMQP Samples
:page-section-summary-toc: 1
To experiment with the AMQP adapters, check out the samples available in the Spring Integration samples git repository at https://github.com/spring-projects/spring-integration-samples[https://github.com/SpringSource/spring-integration-samples]
Currently, one sample demonstrates the basic functionality of the Spring Integration AMQP adapter by using an outbound channel adapter and an inbound channel adapter.
As AMQP broker implementation in the sample uses https://www.rabbitmq.com/[RabbitMQ].
NOTE: In order to run the example, you need a running instance of RabbitMQ.
A local installation with just the basic defaults suffices.
For detailed RabbitMQ installation procedures, see https://www.rabbitmq.com/install.html[https://www.rabbitmq.com/install.html]
Once the sample application is started, enter some text on the command prompt and a message containing that entered text is dispatched to the AMQP queue.
In return, that message is retrieved by Spring Integration and printed to the console.
The following image illustrates the basic set of Spring Integration components used in this sample:
.The Spring Integration graph of the AMQP sample
image::spring-integration-amqp-sample-graph.png[align="center"]

View File

@@ -0,0 +1,59 @@
[[amqp-strict-ordering]]
= Strict Message Ordering
This section describes message ordering for inbound and outbound messages.
[[inbound]]
== Inbound
If you require strict ordering of inbound messages, you must configure the inbound listener container's `prefetchCount` property to `1`.
This is because, if a message fails and is redelivered, it arrives after existing prefetched messages.
Since Spring AMQP version 2.0, the `prefetchCount` defaults to `250` for improved performance.
Strict ordering requirements come at the cost of decreased performance.
[[outbound]]
== Outbound
Consider the following integration flow:
[source, java]
----
@Bean
public IntegrationFlow flow(RabbitTemplate template) {
return IntegrationFlow.from(Gateway.class)
.splitWith(s -> s.delimiters(","))
.<String, String>transform(String::toUpperCase)
.handle(Amqp.outboundAdapter(template).routingKey("rk"))
.get();
}
----
Suppose we send messages `A`, `B` and `C` to the gateway.
While it is likely that messages `A`, `B`, `C` are sent in order, there is no guarantee.
This is because the template "`borrows`" a channel from the cache for each send operation, and there is no guarantee that the same channel is used for each message.
One solution is to start a transaction before the splitter, but transactions are expensive in RabbitMQ and can reduce performance several hundred-fold.
To solve this problem in a more efficient manner, starting with version 5.1, Spring Integration provides the `BoundRabbitChannelAdvice` which is a `HandleMessageAdvice`.
See xref:handler-advice/handle-message.adoc[Handling Message Advice].
When applied before the splitter, it ensures that all downstream operations are performed on the same channel and, optionally, can wait until publisher confirmations for all sent messages are received (if the connection factory is configured for confirmations).
The following example shows how to use `BoundRabbitChannelAdvice`:
[source, java]
----
@Bean
public IntegrationFlow flow(RabbitTemplate template) {
return IntegrationFlow.from(Gateway.class)
.splitWith(s -> s.delimiters(",")
.advice(new BoundRabbitChannelAdvice(template, Duration.ofSeconds(10))))
.<String, String>transform(String::toUpperCase)
.handle(Amqp.outboundAdapter(template).routingKey("rk"))
.get();
}
----
Notice that the same `RabbitTemplate` (which implements `RabbitOperations`) is used in the advice and the outbound adapter.
The advice runs the downstream flow within the template's `invoke` method so that all operations run on the same channel.
If the optional timeout is provided, when the flow completes, the advice calls the `waitForConfirmsOrDie` method, which throws an exception if the confirmations are not received within the specified time.
IMPORTANT: There must be no thread hands-off in the downstream flow (`QueueChannel`, `ExecutorChannel`, and others).

View File

@@ -0,0 +1,14 @@
[[amqp-user-id]]
= Outbound User ID
:page-section-summary-toc: 1
Spring AMQP version 1.6 introduced a mechanism to allow the specification of a default user ID for outbound messages.
It has always been possible to set the `AmqpHeaders.USER_ID` header, which now takes precedence over the default.
This might be useful to message recipients.
For inbound messages, if the message publisher sets the property, it is made available in the `AmqpHeaders.RECEIVED_USER_ID` header.
Note that RabbitMQ https://www.rabbitmq.com/validated-user-id.html[validates that the user ID is the actual user ID for the connection or that the connection allows impersonation].
To configure a default user ID for outbound messages, configure it on a `RabbitTemplate` and configure the outbound adapter or gateway to use that template.
Similarly, to set the user ID property on replies, inject an appropriately configured template into the inbound gateway.
See the https://docs.spring.io/spring-amqp/reference/html/_reference.html#template-user-id[Spring AMQP documentation] for more information.

View File

@@ -0,0 +1,79 @@
[[barrier]]
= Thread Barrier
Sometimes, we need to suspend a message flow thread until some other asynchronous event occurs.
For example, consider an HTTP request that publishes a message to RabbitMQ.
We might wish to not reply to the user until the RabbitMQ broker has issued an acknowledgment that the message was received.
In version 4.2, Spring Integration introduced the `<barrier/>` component for this purpose.
The underlying `MessageHandler` is the `BarrierMessageHandler`.
This class also implements `MessageTriggerAction`, in which a message passed to the `trigger()` method releases a corresponding thread in the `handleRequestMessage()` method (if present).
The suspended thread and trigger thread are correlated by invoking a `CorrelationStrategy` on the messages.
When a message is sent to the `input-channel`, the thread is suspended for up to `requestTimeout` milliseconds, waiting for a corresponding trigger message.
The default correlation strategy uses the `IntegrationMessageHeaderAccessor.CORRELATION_ID` header.
When a trigger message arrives with the same correlation, the thread is released.
The message sent to the `output-channel` after release is constructed by using a `MessageGroupProcessor`.
By default, the message is a `Collection<?>` of the two payloads, and the headers are merged by using a `DefaultAggregatingMessageGroupProcessor`.
CAUTION: If the `trigger()` method is invoked first (or after the main thread times out), it is suspended for up to `triggerTimeout` waiting for the suspending message to arrive.
If you do not want to suspend the trigger thread, consider handing off to a `TaskExecutor` instead so that its thread is suspended instead.
NOTE: Prior version 5.4, there was only one `timeout` option for both request and trigger messages, but in some use-case it is better to have different timeouts for those actions.
Therefore `requestTimeout` and `triggerTimeout` options have been introduced.
The `requires-reply` property determines the action to take if the suspended thread times out before the trigger message arrives.
By default, it is `false`, which means the endpoint returns `null`, the flow ends, and the thread returns to the caller.
When `true`, a `ReplyRequiredException` is thrown.
You can call the `trigger()` method programmatically (obtain the bean reference by using the name, `barrier.handler` -- where `barrier` is the bean name of the barrier endpoint).
Alternatively, you can configure an `<outbound-channel-adapter/>` to trigger the release.
IMPORTANT: Only one thread can be suspended with the same correlation.
The same correlation can be used multiple times but only once concurrently.
An exception is thrown if a second thread arrives with the same correlation.
The following example shows how to use a custom header for correlation:
[tabs]
======
Java::
+
[source, java, role="primary"]
----
@ServiceActivator(inputChannel="in")
@Bean
public BarrierMessageHandler barrier(MessageChannel out, MessageChannel lateTriggerChannel) {
BarrierMessageHandler barrier = new BarrierMessageHandler(10000);
barrier.setOutputChannel(out());
barrier.setDiscardChannel(lateTriggerChannel);
return barrier;
}
@ServiceActivator (inputChannel="release")
@Bean
public MessageHandler releaser(MessageTriggerAction barrier) {
return barrier::trigger;
}
----
XML::
+
[source, xml, role="secondary"]
----
<int:barrier id="barrier1" input-channel="in" output-channel="out"
correlation-strategy-expression="headers['myHeader']"
output-processor="myOutputProcessor"
discard-channel="lateTriggerChannel"
timeout="10000">
</int:barrier>
<int:outbound-channel-adapter channel="release" ref="barrier1.handler" method="trigger" />
----
======
Depending on which one has a message arrive first, either the thread sending a message to `in` or the thread sending a message to `release` waits for up to ten seconds until the other message arrives.
When the message is released, the `out` channel is sent a message that combines the result of invoking the custom `MessageGroupProcessor` bean, named `myOutputProcessor`.
If the main thread times out and a trigger arrives later, you can configure a discard channel to which the late trigger is sent.
For an example of this component, see the https://github.com/spring-projects/spring-integration-samples/tree/main/basic/barrier[barrier sample application].

View File

@@ -0,0 +1,119 @@
[[bridge]]
= Messaging Bridge
A messaging bridge is a relatively trivial endpoint that connects two message channels or channel adapters.
For example, you may want to connect a `PollableChannel` to a `SubscribableChannel` so that the subscribing endpoints do not have to worry about any polling configuration.
Instead, the messaging bridge provides the polling configuration.
By providing an intermediary poller between two channels, you can use a messaging bridge to throttle inbound messages.
The poller's trigger determines the rate at which messages arrive at the second channel, and the poller's `maxMessagesPerPoll` property enforces a limit on the throughput.
Another valid use for a messaging bridge is to connect two different systems.
In such a scenario, Spring Integration's role is limited to making the connection between these systems and managing a poller, if necessary.
It is probably more common to have at least a transformer between the two systems, to translate between their formats.
In that case, the channels can be provided as the 'input-channel' and 'output-channel' of a transformer endpoint.
If data format translation is not required, the messaging bridge may indeed be sufficient.
[[bridge-namespace]]
== Configuring a Bridge with XML
You can use the `<bridge>` element is used to create a messaging bridge between two message channels or channel adapters.
To do so, provide the `input-channel` and `output-channel` attributes, as the following example shows:
[source,xml]
----
<int:bridge input-channel="input" output-channel="output"/>
----
As mentioned above, a common use case for the messaging bridge is to connect a `PollableChannel` to a `SubscribableChannel`.
When performing this role, the messaging bridge may also serve as a throttler:
[source,xml]
----
<int:bridge input-channel="pollable" output-channel="subscribable">
<int:poller max-messages-per-poll="10" fixed-rate="5000"/>
</int:bridge>
----
You can use a similar mechanism to connecting channel adapters.
The following example shows a simple "`echo`" between the `stdin` and `stdout` adapters from Spring Integration's `stream` namespace:
[source,xml]
----
<int-stream:stdin-channel-adapter id="stdin"/>
<int-stream:stdout-channel-adapter id="stdout"/>
<int:bridge id="echo" input-channel="stdin" output-channel="stdout"/>
----
Similar configurations work for other (potentially more useful) Channel Adapter bridges, such as file-to-JMS or mail-to-file.
Upcoming chapters cover the various channel adapters.
NOTE: If no 'output-channel' is defined on a bridge, the reply channel provided by the inbound message is used, if available.
If neither an output nor a reply channel is available, an exception is thrown.
[[bridge-annot]]
== Configuring a Bridge with Java Configuration
The following example shows how to configure a bridge in Java by using the `@BridgeFrom` annotation:
[source, java]
----
@Bean
public PollableChannel polled() {
return new QueueChannel();
}
@Bean
@BridgeFrom(value = "polled", poller = @Poller(fixedDelay = "5000", maxMessagesPerPoll = "10"))
public SubscribableChannel direct() {
return new DirectChannel();
}
----
The following example shows how to configure a bridge in Java by using the `@BridgeTo` annotation:
[source, java]
----
@Bean
@BridgeTo(value = "direct", poller = @Poller(fixedDelay = "5000", maxMessagesPerPoll = "10"))
public PollableChannel polled() {
return new QueueChannel();
}
@Bean
public SubscribableChannel direct() {
return new DirectChannel();
}
----
Alternately, you can use a `BridgeHandler`, as the following example shows:
[source, java]
----
@Bean
@ServiceActivator(inputChannel = "polled",
poller = @Poller(fixedRate = "5000", maxMessagesPerPoll = "10"))
public BridgeHandler bridge() {
BridgeHandler bridge = new BridgeHandler();
bridge.setOutputChannelName("direct");
return bridge;
}
----
[[bridge-dsl]]
== Configuring a Bridge with the Java DSL
You can use the Java Domain Specific Language (DSL) to configure a bridge, as the following example shows:
[source, java]
----
@Bean
public IntegrationFlow bridgeFlow() {
return IntegrationFlow.from("polled")
.bridge(e -> e.poller(Pollers.fixedDelay(5000).maxMessagesPerPoll(10)))
.channel("direct")
.get();
}
----

View File

@@ -0,0 +1,101 @@
[[camel]]
= Apache Camel Support
Spring Integration provides an API and configuration to communicate with https://camel.apache.org[Apache Camel] endpoints declared in the same application context.
You need to include this dependency into your project:
[tabs]
======
Maven::
+
[source, xml, subs="normal", role="primary"]
----
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-camel</artifactId>
<version>{project-version}</version>
</dependency>
----
Gradle::
+
[source, groovy, subs="normal", role="secondary"]
----
compile "org.springframework.integration:spring-integration-camel:{project-version}"
----
======
Spring Integration and Apache Camel implement Enterprise Integration Patterns and provide a convenient way to compose them, but the projects use a different approach for their API and abstractions implementation.
Spring Integration fully relies on a dependency injection container from Spring Core.
It uses many other Spring projects (Spring Data, Spring AMQP, Spring for Apache Kafka etc.) for its channel adapter implementations.
It also uses the `MessageChannel` abstraction as a first class citizen of which developers need to be aware of, when composing their integration flows.
Apache Camel, on the other hand, does not provide a first class citizen abstraction of a message channel and proposes to compose its routes via internal exchanges, hidden from the API.
In addition, it requires some extra https://camel.apache.org/components/3.18.x/spring-summary.html[dependencies and configurations] for it to be used in a Spring application.
Even if it doesn't matter for the final enterprise integration solution how its parts are implemented, a developer experience and high productivity are taken into account.
Therefore, developers may choose one framework over another for many reasons, or both if there is a gap in some target systems support.
Spring Integration and Apache Camel applications can interact with each other through many external protocols for which they implement channel adapters.
For example, a Spring Integration flow may publish a record to an Apache Kafka topic which is consumed by an Apache Camel endpoint on the consumer side.
Or, an Apache Camel route may write data into an SFTP file the directory, which is polled by a SFTP Inbound Channel Adapter from Spring Integration.
Or, within the same Spring application context they can communicate via an `ApplicationEvent` https://camel.apache.org/components/3.18.x/spring-event-component.html[abstraction].
To make a development process easier, and to avoid unnecessary network hops, Apache Camel provides a https://camel.apache.org/components/3.18.x/spring-integration-component.html[module] to communicate with Spring Integration via message channels.
All that is needed is a reference to a `MessageChannel` from the application context, to send or consume messages.
This works well when Apache Camel routes are initiators of the message flow and Spring Integration plays only a supporting role as a part of the solution.
For a similar developer experience, Spring Integration now provides a channel adapter to call an Apache Camel endpoint and, optionally, wait for a reply.
There is no inbound channel adapter because subscribing to a `MessageChannel` for consuming Apache Camel messages is enough from the Spring Integration API and abstractions perspective.
[[camel-channel-adapter]]
== Outbound Channel Adapter for Apache Camel
The `CamelMessageHandler` is an `AbstractReplyProducingMessageHandler` implementation and can work in both one-way (default) and request-reply modes.
It uses an `org.apache.camel.ProducerTemplate` to send (or send and receive) into an `org.apache.camel.Endpoint`.
An interaction mode can be controlled by the `ExchangePattern` option (which can be evaluated at runtime against the request message via a SpEL expression).
The target Apache Camel endpoint can be configured explicitly or as a SpEL expression to be evaluated at runtime.
Otherwise, it falls back to the `defaultEndpoint` provided on the `ProducerTemplate`.
Instead of specifying the endpoint, an in-line, explicit `LambdaRouteBuilder` can be provided, for example to make a call into an Apache Camel component for which there is no channel adapter support in Spring Integration.
In addition, a `HeaderMapper<org.apache.camel.Message>` (the `CamelHeaderMapper` is a default implementation) can be provided, to determine which headers to map between the Spring Integration and Apache Camel messages.
By default, all headers are mapped.
The `CamelMessageHandler` supports an `async` mode calling `ProducerTemplate.asyncSend()` and producing a `CompletableFuture` for reply processing (if any).
The `exchangeProperties` can be customized via a SpEL expression, which must evaluate to a `Map`.
If a `ProducerTemplate` is not provided, it is created via a `CamelContext` bean resolved from the application context.
[source, java]
----
@Bean
@ServiceActivator(inputChannel = "sendToCamel")
CamelMessageHandler camelService(ProducerTemplate producerTemplate) {
CamelHeaderMapper headerMapper = new CamelHeaderMapper();
headerMapper.setOutboundHeaderNames("");
headerMapper.setInboundHeaderNames("testHeader");
CamelMessageHandler camelMessageHandler = new CamelMessageHandler(producerTemplate);
camelMessageHandler.setEndpointUri("direct:simple");
camelMessageHandler.setExchangePatternExpression(spelExpressionParser.parseExpression("headers.exchangePattern"));
camelMessageHandler.setHeaderMapper(headerMapper);
return camelMessageHandler;
}
----
For Java DSL flow definitions this channel adapter can be configured with a few variants provided by the `Camel` factory:
[source, java]
----
@Bean
IntegrationFlow camelFlow() {
return f -> f
.handle(Camel.gateway().endpointUri("direct:simple"))
.handle(Camel.route(this::camelRoute))
.handle(Camel.handler().endpointUri("log:com.mycompany.order?level=WARN"));
}
private void camelRoute(RouteBuilder routeBuilder) {
routeBuilder.from("direct:inbound").transform(routeBuilder.simple("${body.toUpperCase()}"));
}
----

View File

@@ -0,0 +1,183 @@
[[cassandra]]
= Apache Cassandra Support
Spring Integration provides channel adapters (starting with version 6.0) for performing database operations against an Apache Cassandra cluster.
It is fully based on the https://spring.io/projects/spring-data-cassandra[Spring Data for Apache Cassandra] project.
You need to include this dependency into your project:
[tabs]
======
Maven::
+
[source, xml, subs="normal", role="primary"]
----
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-cassandra</artifactId>
<version>{project-version}</version>
</dependency>
----
Gradle::
+
[source, groovy, subs="normal", role="secondary"]
----
compile "org.springframework.integration:spring-integration-cassandra:{project-version}"
----
======
[[cassandra-outbound]]
== Cassandra Outbound Components
The `CassandraMessageHandler` is an `AbstractReplyProducingMessageHandler` implementation and can work in both one-way (default) and request-reply modes (a `producesReply` option).
It is asynchronous by default (`setAsync(false)` to reset) and performs reactive `INSERT`, `UPDATE`, `DELETE` or `STATEMENT` operations against the provided `ReactiveCassandraOperations`.
The type of operation can be configured via the `CassandraMessageHandler.Type` option.
The `ingestQuery` sets the mode into an `INSERT`; the `query` or `statementExpression`, or `statementProcessor` sets the mode into a `STATEMENT`.
The following code snippets demonstrate various configuration for this channel adapter or gateway:
[tabs]
======
Java DSL::
+
[source, java, role="primary"]
----
@Bean
IntegrationFlow cassandraSelectFlow(ReactiveCassandraOperations cassandraOperations) {
return flow -> flow
.handle(Cassandra.outboundGateway(cassandraOperations)
.query("SELECT * FROM book WHERE author = :author limit :size")
.parameter("author", "payload")
.parameter("size", m -> m.getHeaders().get("limit")))
.channel(c -> c.flux("resultChannel"));
}
----
Kotlin DSL::
+
[source, kotlin, role="secondary"]
----
@Bean
fun outboundReactive(cassandraOperations: ReactiveCassandraOperations) =
integrationFlow {
handle(
Cassandra.outboundChannelAdapter(cassandraOperations)
.statementExpression("T(QueryBuilder).truncate('book').build()")
) { async(false) }
}
----
Java::
+
[source, java, role="secondary"]
----
@ServiceActivator(inputChannel = "cassandraSelectChannel")
@Bean
public MessageHandler cassandraMessageHandler() {
CassandraMessageHandler cassandraMessageHandler = new CassandraMessageHandler(this.template);
cassandraMessageHandler.setQuery("SELECT * FROM book WHERE author = :author limit :size");
Map<String, Expression> params = new HashMap<>();
params.put("author", PARSER.parseExpression("payload"));
params.put("size", PARSER.parseExpression("headers.limit"));
cassandraMessageHandler.setParameterExpressions(params);
cassandraMessageHandler.setOutputChannel(resultChannel());
cassandraMessageHandler.setProducesReply(true);
return cassandraMessageHandler;
}
----
XML::
+
[source, xml, role="secondary"]
----
<int-cassandra:outbound-channel-adapter id="outboundAdapter"
cassandra-template="cassandraTemplate"
write-options="writeOptions"
auto-startup="false"
async="false"/>
<int-cassandra:outbound-gateway id="outgateway"
request-channel="input"
cassandra-template="cassandraTemplate"
mode="STATEMENT"
write-options="writeOptions"
query="SELECT * FROM book limit :size"
reply-channel="resultChannel"
auto-startup="true">
<int-cassandra:parameter-expression name="author" expression="payload"/>
<int-cassandra:parameter-expression name="size" expression="headers.limit"/>
</int-cassandra:outbound-gateway>
----
======
If a `CassandraMessageHandler` is used as a gateway in the default async mode, a `Mono<WriteResult>` is produced, which is handled according to the provided `MessageChannel` implementation.
For true reactive processing a `FluxMessageChannel` is recommended for the output channel configuration.
In sync mode `Mono.block()` is called to obtain the reply value.
If `INSERT`, `UPDATE` or `DELETE` operations are performed, an entity (marked `org.springframework.data.cassandra.core.mapping.Table`) is expected in the request message payload.
If the payload is a list of entities, then the respective batch operation is performed.
The `ingestQuery` mode expects the payload to be present as a matrix of values to insert - `List<List<?>>`.
For example, if the entity is like this:
[source,java]
----
@Table("book")
public record Book(@PrimaryKey String isbn,
String title,
@Indexed String author,
int pages,
LocalDate saleDate,
boolean isInStock) {
}
----
And channel adapter has this configuration:
[source,java]
----
@Bean
public MessageHandler cassandraMessageHandler3() {
CassandraMessageHandler cassandraMessageHandler = new CassandraMessageHandler(this.template);
String cqlIngest = "insert into book (isbn, title, author, pages, saleDate, isInStock) values (?, ?, ?, ?, ?, ?)";
cassandraMessageHandler.setIngestQuery(cqlIngest);
cassandraMessageHandler.setAsync(false);
return cassandraMessageHandler;
}
----
The request message payload must be converted like this:
[source,java]
----
List<List<Object>> ingestBooks =
payload.stream()
.map(book ->
List.<Object>of(
book.isbn(),
book.title(),
book.author(),
book.pages(),
book.saleDate(),
book.isInStock()))
.toList();
----
For more sophisticated use-cases, the payload can be as an instance of `com.datastax.oss.driver.api.core.cql.Statement`.
The `com.datastax.oss.driver.api.querybuilder.QueryBuilder` API is recommended to build various statements to execute against Apache Cassandra.
For example, to remove all the data from the `Book` table, a message with a payload like this can be sent to the `CassandraMessageHandler`: `QueryBuilder.truncate("book").build()`.
Alternatively, for logic based on a request message, a `statementExpression` or `statementProcessor` can be provided for the `CassandraMessageHandler` to build a `Statement` based on that message.
For convenience, a `com.datastax.oss.driver.api.querybuilder` is registered as an `import` into a SpEL evaluation context, so a target expression can be as simple as this:
[source,xml]
----
statement-expression="T(QueryBuilder).selectFrom("book").all()"
----
The `setParameterExpressions(Map<String, Expression> parameterExpressions)` represents bindable named query parameters and is used only with a `setQuery(String query)` option.
See Java and XML samples mentioned above.

View File

@@ -0,0 +1,172 @@
[[chain]]
= Message Handler Chain
The `MessageHandlerChain` is an implementation of `MessageHandler` that can be configured as a single message endpoint while actually delegating to a chain of other handlers, such as filters, transformers, splitters, and so on.
When several handlers need to be connected in a fixed, linear progression, this can lead to a much simpler configuration.
For example, it is fairly common to provide a transformer before other components.
Similarly, when you provide a filter before some other component in a chain, you essentially create a https://www.enterpriseintegrationpatterns.com/MessageSelector.html[selective consumer].
In either case, the chain requires only a single `input-channel` and a single `output-channel`, eliminating the need to define channels for each individual component.
NOTE: The `MessageHandlerChain` is mostly designed for an XML configuration.
For Java DSL, an `IntegrationFlow` definition can be treated as a chain component, but it has nothing to do with concepts and principles described in this chapter below.
See xref:dsl.adoc#java-dsl[Java DSL] for more information.
TIP: Spring Integration's `Filter` provides a boolean property: `throwExceptionOnRejection`.
When you provide multiple selective consumers on the same point-to-point channel with different acceptance criteria, you should set this value 'true' (the default is `false`) so that the dispatcher knows that the message was rejected and, as a result, tries to pass the message on to other subscribers.
If the exception were not thrown, it would appear to the dispatcher that the message had been passed on successfully even though the filter had dropped the message to prevent further processing.
If you do indeed want to "`drop`" the messages, the filter's 'discard-channel' might be useful, since it does give you a chance to perform some operation with the dropped message (such as sending it to a JMS queue or writing it to a log).
The handler chain simplifies configuration while internally maintaining the same degree of loose coupling between components, and it is trivial to modify the configuration if at some point a non-linear arrangement is required.
Internally, the chain is expanded into a linear setup of the listed endpoints, separated by anonymous channels.
The reply channel header is not taken into account within the chain.
Only after the last handler is invoked is the resulting message forwarded to the reply channel or the chain's output channel.
Because of this setup, all handlers except the last must implement the `MessageProducer` interface (which provides a 'setOutputChannel()' method).
If the `outputChannel` on the `MessageHandlerChain` is set, the last handler needs only an output channel.
NOTE: As with other endpoints, the `output-channel` is optional.
If there is a reply message at the end of the chain, the output-channel takes precedence.
However, if it is not available, the chain handler checks for a reply channel header on the inbound message as a fallback.
In most cases, you need not implement `MessageHandler` yourself.
The next section focuses on namespace support for the chain element.
Most Spring Integration endpoints, such as service activators and transformers, are suitable for use within a `MessageHandlerChain`.
[[chain-namespace]]
== Configuring a Chain
The `<chain>` element provides an `input-channel` attribute.
If the last element in the chain is capable of producing reply messages (optional), it also supports an `output-channel` attribute.
The sub-elements are then filters, transformers, splitters, and service-activators.
The last element may also be a router or an outbound channel adapter.
The following example shows a chain definition:
[source,xml]
----
<int:chain input-channel="input" output-channel="output">
<int:filter ref="someSelector" throw-exception-on-rejection="true"/>
<int:header-enricher>
<int:header name="thing1" value="thing2"/>
</int:header-enricher>
<int:service-activator ref="someService" method="someMethod"/>
</int:chain>
----
The `<header-enricher>` element used in the preceding example sets a message header named `thing1` with a value of `thing2` on the message.
A header enricher is a specialization of `Transformer` that touches only header values.
You could obtain the same result by implementing a `MessageHandler` that did the header modifications and wiring that as a bean, but the header-enricher is a simpler option.
The `<chain>` can be configured as the last "`closed-box`" consumer of the message flow.
For this solution, you can to put it at the end of the <chain> some <outbound-channel-adapter>, as the following example shows:
[source,xml]
----
<int:chain input-channel="input">
<int-xml:marshalling-transformer marshaller="marshaller" result-type="StringResult" />
<int:service-activator ref="someService" method="someMethod"/>
<int:header-enricher>
<int:header name="thing1" value="thing2"/>
</int:header-enricher>
<int:logging-channel-adapter level="INFO" log-full-message="true"/>
</int:chain>
----
.Disallowed Attributes and Elements
[IMPORTANT]
=====
Certain attributes, such as `order` and `input-channel` are not allowed to be specified on components used within a chain.
The same is true for the poller sub-element.
For the Spring Integration core components, the XML schema itself enforces some of these constraints.
However, for non-core components or your own custom components, these constraints are enforced by the XML namespace parser, not by the XML schema.
These XML namespace parser constraints were added with Spring Integration 2.2.
If you try to use disallowed attributes and elements, the XML namespace parser throws a `BeanDefinitionParsingException`.
=====
[[using-the-id-attribute]]
== Using the 'id' Attribute
Beginning with Spring Integration 3.0, if a chain element is given an `id` attribute, the bean name for the element is a combination of the chain's `id` and the `id` of the element itself.
Elements without `id` attributes are not registered as beans, but each one is given a `componentName` that includes the chain `id`.
Consider the following example:
[source,xml]
----
<int:chain id="somethingChain" input-channel="input">
<int:service-activator id="somethingService" ref="someService" method="someMethod"/>
<int:object-to-json-transformer/>
</int:chain>
----
In the preceding example:
* The `<chain>` root element has an `id` of 'somethingChain'.
Consequently, the `AbstractEndpoint` implementation (`PollingConsumer` or `EventDrivenConsumer`, depending on the `input-channel` type) bean takes this value as its bean name.
* The `MessageHandlerChain` bean acquires a bean alias ('somethingChain.handler'), which allows direct access to this bean from the `BeanFactory`.
* The `<service-activator>` is not a fully fledged messaging endpoint (it is not a `PollingConsumer` or `EventDrivenConsumer`).
It is a `MessageHandler` within the `<chain>`.
In this case, the bean name registered with the `BeanFactory` is 'somethingChain$child.somethingService.handler'.
* The `componentName` of this `ServiceActivatingHandler` takes the same value but without the '.handler' suffix.
It becomes 'somethingChain$child.somethingService'.
* The last `<chain>` sub-component, `<object-to-json-transformer>`, does not have an `id` attribute.
Its `componentName` is based on its position in the `<chain>`.
In this case, it is 'somethingChain$child#1'.
(The final element of the name is the order within the chain, beginning with '#0').
Note, this transformer is not registered as a bean within the application context, so it does not get a `beanName`.
However, its `componentName` has a value that is useful for logging and other purposes.
The `id` attribute for `<chain>` elements lets them be eligible for xref:jmx.adoc#jmx-mbean-exporter[JMX export], and they are trackable in the xref:message-history.adoc[message history].
You can access them from the `BeanFactory` by using the appropriate bean name, as discussed earlier.
TIP: It is useful to provide an explicit `id` attribute on `<chain>` elements to simplify the identification of sub-components in logs and to provide access to them from the `BeanFactory` etc.
[[chain-gateway]]
== Calling a Chain from within a Chain
Sometimes, you need to make a nested call to another chain from within a chain and then come back and continue execution within the original chain.
To accomplish this, you can use a messaging gateway by including a <gateway> element, as the following example shows:
[source,xml]
----
<int:chain id="main-chain" input-channel="in" output-channel="out">
<int:header-enricher>
<int:header name="name" value="Many" />
</int:header-enricher>
<int:service-activator>
<bean class="org.foo.SampleService" />
</int:service-activator>
<int:gateway request-channel="inputA"/>
</int:chain>
<int:chain id="nested-chain-a" input-channel="inputA">
<int:header-enricher>
<int:header name="name" value="Moe" />
</int:header-enricher>
<int:gateway request-channel="inputB"/>
<int:service-activator>
<bean class="org.foo.SampleService" />
</int:service-activator>
</int:chain>
<int:chain id="nested-chain-b" input-channel="inputB">
<int:header-enricher>
<int:header name="name" value="Jack" />
</int:header-enricher>
<int:service-activator>
<bean class="org.foo.SampleService" />
</int:service-activator>
</int:chain>
----
In the preceding example, `nested-chain-a` is called at the end of `main-chain` processing by the 'gateway' element configured there.
While in `nested-chain-a`, a call to a `nested-chain-b` is made after header enrichment.
Then the flow comes back to finish execution in `nested-chain-b`.
Finally, the flow returns to `main-chain`.
When the nested version of a `<gateway>` element is defined in the chain, it does not require the `service-interface` attribute.
Instead, it takes the message in its current state and places it on the channel defined in the `request-channel` attribute.
When the downstream flow initiated by that gateway completes, a `Message` is returned to the gateway and continues its journey within the current chain.

View File

@@ -0,0 +1,176 @@
[[migration-1.0-2.0]]
= Changes between Versions 1.0 and 2.0
See the https://github.com/spring-projects/spring-integration/wiki/Spring-Integration-1.0-to-2.0-Migration-Guide[Migration Guide] for important changes that might affect your applications.
[[migration-spring-30-support]]
== Spring 3 support
Spring Integration 2.0 is built on top of Spring 3.0.5 and makes many of its features available to our users.
[[2.0-spel-support]]
=== Support for the Spring Expression Language (SpEL)
You can now use SpEL expressions within the transformer, router, filter, splitter, aggregator, service-activator, header-enricher, and many more elements of the Spring Integration core namespace as well as within various adapters.
This guide includes many samples.
[[conversion-support]]
=== Conversion Service and Converter
You can now benefit from the conversion service support provided with Spring while configuring many Spring Integration components, such as a https://www.enterpriseintegrationpatterns.com/DatatypeChannel.html[Datatype channel].
See xref:channel/implementations.adoc[Message Channel Implementations] and xref:service-activator.adoc[Service Activator].
Also, the SpEL support mentioned in the previous point also relies upon the conversion service.
Therefore, you can register converters once and take advantage of them anywhere you use SpEL expressions.
[[task-scheduler-poller-support]]
=== `TaskScheduler` and `Trigger`
Spring 3.0 defines two new strategies related to scheduling: `TaskScheduler` and `Trigger`.
Spring Integration (which uses a lot of scheduling) now builds upon these.
In fact, Spring Integration 1.0 had originally defined some of the components (such as `CronTrigger`) that have now been migrated into Spring 3.0's core API.
Now you can benefit from reusing the same components within the entire application context (not just Spring Integration configuration).
We also greatly simplified configuration of Spring Integration pollers by providing attributes for directly configuring rates, delays, cron expressions, and trigger references.
See xref:overview.adoc#overview-endpoints-channeladapter[Channel Adapter] for sample configurations.
[[rest-support]]
=== `RestTemplate` and `HttpMessageConverter`
Our outbound HTTP adapters now delegate to Spring's `RestTemplate` for executing the HTTP request and handling its response.
This also means that you can reuse any custom `HttpMessageConverter` implementations.
See xref:http/outbound.adoc[HTTP Outbound Components] for more details.
[[new-eip]]
== Enterprise Integration Pattern Additions
Also in 2.0, we have added support for even more of the patterns described in Hohpe and Woolf's https://www.enterpriseintegrationpatterns.com/[Enterprise Integration Patterns] book.
[[new-message-history]]
=== Message History
We now provide support for the https://www.enterpriseintegrationpatterns.com/MessageHistory.html[message history] pattern, letting you keep track of all traversed components, including the name of each channel and endpoint as well as the timestamp of that traversal.
See xref:message-history.adoc[Message History] for more details.
[[new-message-store]]
=== Message Store
We now provide support for the https://www.enterpriseintegrationpatterns.com/MessageStore.html[message store] pattern.
The message store provides a strategy for persisting messages on behalf of any process whose scope extends beyond a single transaction, such as the aggregator and the resequencer.
Many sections of this guide include samples of how to use a message store, as it affects several areas of Spring Integration.
See xref:message-store.adoc[Message Store], xref:claim-check.adoc[Claim Check], xref:channel.adoc[Message Channels], xref:overview.adoc#overview-endpoints-aggregator[Aggregator], xref:jdbc.adoc[JDBC Support]`", and xref:resequencer.adoc[Resequencer] for more details.
[[new-claim-check]]
=== Claim Check
We have added an implementation of the https://www.enterpriseintegrationpatterns.com/StoreInLibrary.html[claim check] pattern.
The idea behind the claim check pattern is that you can exchange a message payload for a "`claim ticket`".
This lets you reduce bandwidth and avoid potential security issues when sending messages across channels.
See xref:claim-check.adoc[Claim Check] for more details.
[[new-control-bus]]
=== Control Bus
We have provided implementations of the https://www.enterpriseintegrationpatterns.com/ControlBus.html[control bus] pattern, which lets you use messaging to manage and monitor endpoints and channels.
The implementations include both a SpEL-based approach and one that runs Groovy scripts.
See xref:groovy.adoc#groovy-control-bus[Control Bus] and xref:groovy.adoc#groovy-control-bus[Control Bus] for more details.
[[new-adapters]]
== New Channel Adapters and Gateways
We have added several new channel adapters and messaging gateways in Spring Integration 2.0.
[[new-ip]]
=== TCP and UDP Adapters
We have added channel adapters for receiving and sending messages over the TCP and UDP internet protocols.
See xref:ip.adoc[TCP and UDP Support] for more details.
See also the following blog: https://spring.io/blog/2010/03/29/using-udp-and-tcp-adapters-in-spring-integration-2-0-m3/["`Using UDP and TCP Adapters in Spring Integration 2.0 M3`"].
[[new-twitter]]
=== Twitter Adapters
Twitter adapters provides support for sending and receiving Twitter status updates as well as direct messages.
You can also perform Twitter Searches with an inbound channel adapter.
See https://github.com/spring-projects/spring-integration-extensions/tree/main/spring-integration-social-twitter[Spring Integration Social Twitter] for more details.
[[new-xmpp]]
=== XMPP Adapters
The new XMPP adapters support both chat messages and presence events.
See xref:xmpp.adoc[XMPP Support] for more details.
[[new-ftp]]
=== FTP and FTPS Adapters
Inbound and outbound file transfer support over FTP and FTPS is now available.
See xref:ftp.adoc[FTP/FTPS Adapters] for more details.
[[new-sftp]]
=== SFTP Adapters
Inbound and outbound file transfer support over SFTP is now available.
See xref:sftp.adoc[SFTP Adapters] for more details.
[[new-feed]]
=== Feed Adapters
We have also added channel adapters for receiving news feeds (ATOM and RSS).
See xref:feed.adoc[Feed Adapter] for more details.
[[new-other]]
== Other Additions
Spring Integration adds a number of other features.
This section describes them.
[[new-groovy]]
=== Groovy Support
Spring Integration 2.0 added Groovy support, letting you use the Groovy scripting language to provide integration and business logic.
See xref:groovy.adoc[Groovy support] for more details.
[[new-map-transformer]]
=== Map Transformers
These symmetrical transformers convert payload objects to and from `Map` objects.
See xref:transformer.adoc[Transformer] for more details.
[[new-json-transformer]]
=== JSON Transformers
These symmetrical transformers convert payload objects to and from JSON.
See xref:transformer.adoc[Transformer] for more details.
[[new-serialize-transformer]]
=== Serialization Transformers
These symmetrical transformers convert payload objects to and from byte arrays.
They also support the serializer and deserializer strategy interfaces that Spring 3.0.5 added.
See xref:transformer.adoc[Transformer] for more details.
[[new-refactoring]]
== Framework Refactoring
The core API went through some significant refactoring to make it simpler and more usable.
Although we anticipate that the impact to developers should be minimal, you should read through this document to find what was changed.
Specifically, you should read xref:router/dynamic-routers.adoc[Dynamic Routers], xref:gateway.adoc[Messaging Gateways], xref:http/outbound.adoc[HTTP Outbound Components], xref:overview.adoc#overview-components-message[Message], and xref:overview.adoc#overview-endpoints-aggregator[Aggregator].
If you directly depend on some of the core components (`Message`, `MessageHeaders`, `MessageChannel`, `MessageBuilder`, and others), you need to update any import statements.
We restructured some packaging to provide the flexibility we needed for extending the domain model while avoiding any cyclical dependencies (it is a policy of the framework to avoid such "`tangles`").
[[new-infrastructure]]
== New Source Control Management and Build Infrastructure
With Spring Integration 2.0, we switched our build environment to use Git for source control.
To access our repository, visit https://git.springsource.org/spring-integration.
We have also switched our build system to https://gradle.org/[Gradle].
[[new-samples]]
== New Spring Integration Samples
With Spring Integration 2.0, we have decoupled the samples from our main release distribution.
Please read the following blog to get more information: https://spring.io/blog/2010/09/29/new-spring-integration-samples/[New Spring Integration Samples].
We have also created many new samples, including samples for every new adapter.
[[new-sts]]
== Spring Tool Suite Visual Editor for Spring Integration
There is an amazing new visual editor for Spring Integration included within the latest version of SpringSource Tool Suite.
If you are not already using STS, you can download it at https://spring.io/tools/sts[Spring Tool Suite].

View File

@@ -0,0 +1,212 @@
[[migration-2.0-2.1]]
= Changes between 2.0 and 2.1
See the https://github.com/spring-projects/spring-integration/wiki/Spring-Integration-2.0-to-2.1-Migration-Guide[Migration Guide] for important changes that might affect your applications.
[[x2.1-new-components]]
== New Components
Version 2.1 added a number of new components.
[[x2.1-new-scripting-support]]
=== JSR-223 Scripting Support
In Spring Integration 2.0, we added support for https://groovy.codehaus.org/[Groovy].
With Spring Integration 2.1, we expanded support for additional languages substantially by implementing support for https://www.jcp.org/en/jsr/detail?id=223[JSR-223] ("`Scripting for the Java™ Platform`").
Now you have the ability to use any scripting language that supports JSR-223 including:
* Javascript
* Ruby and JRuby
* Python and Jython
* Groovy
For further details, see xref:scripting.adoc[Scripting Support].
[[x2.1-new-amqp-support]]
=== AMQP Support
Spring Integration 2.1 added several channel adapters for receiving and sending messages by using the https://www.amqp.org/[Advanced Message Queuing Protocol] (AMQP).
Furthermore, Spring Integration also provides a point-to-point message channel and a publish-subscribe message channel, both of which are backed by AMQP Exchanges and Queues.
For further details, see xref:amqp.adoc[AMQP Support].
[[x2.1-new-mongodb-support]]
=== MongoDB Support
As of version 2.1, Spring Integration provides support for https://www.mongodb.org/[MongoDB] by providing a MongoDB-based `MessageStore`.
For further details, see xref:mongodb.adoc[MongoDb Support].
[[x2.1-new-redis-support]]
=== Redis Support
As of version 2.1, Spring Integration supports https://redis.io/[Redis], an advanced key-value store, by providing a Redis-based `MessageStore` as well as publish-subscribe messaging adapters.
For further details, see xref:redis.adoc[Redis Support].
[[x2.1-new-resource-support]]
=== Support for Spring's Resource abstraction
In version 2.1, we introduced a new resource inbound channel adapter that builds upon Spring's resource abstraction to support greater flexibility across a variety of actual types of underlying resources, such as a file, a URL, or a classpath resource.
Therefore, it is similar to but more generic than the file inbound channel adapter.
For further details, see xref:resource.adoc#resource-inbound-channel-adapter[Resource Inbound Channel Adapter].
[[x2.1-new-stored-proc-support]]
=== Stored Procedure Components
With Spring Integration 2.1, the `JDBC` Module also provides stored procedure support by adding several new components, including inbound and outbound channel adapters and an outbound gateway.
The stored procedure support leverages Spring's https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/core/simple/SimpleJdbcCall.html[`SimpleJdbcCall`] class and consequently supports stored procedures for:
* Apache Derby
* DB2
* MySQL
* Microsoft SQL Server
* Oracle
* PostgreSQL
* Sybase
The stored procedure components also support SQL functions for the following databases:
* MySQL
* Microsoft SQL Server
* Oracle
* PostgreSQL
For further details, see xref:jdbc/stored-procedures.adoc[Stored Procedures].
[[x2.1-new-xpath-filter-support]]
=== XPath and XML Validating Filter
Spring Integration 2.1 provides a new XPath-based message filter.
It is part of the `XML` module.
The XPath filter lets you filter messages by using XPath Expressions.
We also added documentation for the XML validating filter.
For more details, see xref:xml/xpath-filter.adoc[Using the XPath Filter] and xref:xml/validating-filter.adoc[XML Validating Filter].
[[x2.1-new-payload-enricher-support]]
=== Payload Enricher
Since Spring Integration 2.1, we added the payload enricher.
A payload enricher defines an endpoint that typically passes a https://docs.spring.io/spring-integration/api/org/springframework/integration/Message.html[`Message`] to the exposed request channel and then expects a reply message.
The reply message then becomes the root object for evaluation of expressions to enrich the target payload.
For further details, see xref:content-enrichment.adoc#payload-enricher[Payload Enricher].
[[x2.1-new-ftp-outbound-gateway]]
=== FTP and SFTP Outbound Gateways
Spring Integration 2.1 provides two new outbound gateways to interact with remote File Transfer Protocol (FTP) or Secure File Transfer Protocol (SFT) servers.
These two gateways let you directly execute a limited set of remote commands.
For instance, you can use these outbound gateways to list, retrieve, and delete remote files and have the Spring Integration message flow continue with the remote server's response.
For further details, see xref:ftp/outbound-gateway.adoc[FTP Outbound Gateway] and xref:sftp/outbound-gateway.adoc[SFTP Outbound Gateway].
[[x2.1-new-ftp-session-caching]]
=== FTP Session Caching
As of version 2.1, we have exposed more flexibility with regards to session management for remote file adapters (for example, FTP, SFTP, and others).
Specifically, we deprecated the `cache-sessions` attribute (which is available via the XML namespace support).
As an alternative, we added the `sessionCacheSize` and `sessionWaitTimeout` attributes on the `CachingSessionFactory`.
For further details, see xref:ftp/session-caching.adoc[FTP Session Caching] and xref:sftp/session-caching.adoc[SFTP Session Caching].
[[x2.1-framework-refactorings]]
== Framework Refactoring
We refactored the Spring Integration framework in a number of ways, all described in this section.
[[x2.1-router-standardization]]
=== Standardizing Router Configuration
We standardized router parameters across all router implementations with Spring Integration 2.1 to provide a more consistent user experience.
In Spring Integration 2.1, we removed the `ignore-channel-name-resolution-failures` attribute in favor of consolidating its behavior with the `resolution-required` attribute.
Also, the `resolution-required` attribute now defaults to `true`.
Starting with Spring Integration 2.1, routers no longer silently drop any messages if no default output channel was defined.
This means that, by default, routers now require at least one resolved channel (if no `default-output-channel` was set) and, by default, throw a `MessageDeliveryException` if no channel was determined (or an attempt to send was not successful).
If, however, you do want to drop messages silently, you can set `default-output-channel="nullChannel"`.
IMPORTANT: With the standardization of router parameters and the consolidation of the parameters described earlier, older Spring Integration based applications may break.
For further details, see `xref:router.adoc[Routers]`.
[[x2.1-schema-updated]]
=== XML Schemas updated to 2.1
Spring Integration 2.1 ships with an updated XML Schema (version 2.1).
It provides many improvements, such as the Router standardizations xref:changes-2.0-2.1.adoc#x2.1-router-standardization[discussed earlier].
From now on, developers must always declare the latest XML schema (currently version 2.1).
Alternatively, they can use the version-less schema.
Generally, the best option is to use version-less namespaces, as these automatically use the latest available version of Spring Integration.
The following example declares a version-less Spring Integration namespace:
[source,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/integration
https://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
...
</beans>
----
The following example declares a Spring Integration namespace with an explicit version:
[source,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/integration
https://www.springframework.org/schema/integration/spring-integration-2.2.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
...
</beans>
----
The old 1.0 and 2.0 schemas are still there.
However, if an application context still references one of those deprecated schemas, the validator fails on initialization.
[[x2.1-source-control-infrastructure]]
== Source Control Management and Build Infrastructure
Version 2.1 introduced a number of changes to source control management and build infrastructure.
This section covers those changes.
[[x2.1-move-to-github]]
=== Source Code Now Hosted on Github
Since version 2.0, the Spring Integration project uses https://git-scm.com/[Git] for version control.
To increase community visibility even further, the project was moved from SpringSource hosted Git repositories to https://www.github.com/[Github].
The Spring Integration Git repository is located at: https://github.com/spring-projects/spring-integration[spring-integration].
For the project, we also improved the process of providing code contributions.
Further, we ensure that every commit is peer-reviewed.
In fact, core committers now follow the same process as contributors.
For more details, see https://github.com/spring-projects/spring-integration/blob/main/CONTRIBUTING.adoc[Contributing].
[[x2.1-sonar]]
=== Improved Source Code Visibility with Sonar
In an effort to provide better source code visibility and consequently to monitor the quality of Spring Integration's source code, we set up an instance of https://www.sonarqube.org/[Sonar].
We gather metrics nightly and make them available at https://sonar.spring.io/dashboard?id=org.springframework.integration%3Aspring-integration%3Amain[sonar.spring.io].
[[x2.1-new-samples]]
== New Samples
For the 2.1 release of Spring Integration, we also expanded the Spring Integration Samples project and added many new samples, such as samples that cover AMQP support, a sample that showcases the new payload enricher, a sample illustrating techniques for testing Spring Integration flow fragments, and a sample for executing stored procedures against Oracle databases.
For details, visit https://github.com/spring-projects/spring-integration-samples[spring-integration-samples].

View File

@@ -0,0 +1,172 @@
[[migration-2.1-2.2]]
= Changes between 2.1 and 2.2
See the https://github.com/spring-projects/spring-integration/wiki/Spring-Integration-2.1-to-2.2-Migration-Guide[Migration Guide] for important changes that might affect your applications.
You can find migration guides for all versions back to 2.1 on the https://github.com/spring-projects/spring-integration/wiki[wiki].
[[x2.2-new-components]]
== New Components
Version 2.2 added a number of new components.
[[x2.2-redis-store-adapters]]
=== `RedisStore` Inbound and Outbound Channel Adapters
Spring Integration now has `RedisStore` Inbound and Outbound Channel Adapters, letting you write and read `Message` payloads to and from Redis collections.
For more information, see xref:redis.adoc#redis-store-outbound-channel-adapter[RedisStore Outbound Channel Adapter] and xref:redis.adoc#redis-store-inbound-channel-adapter[Redis Store Inbound Channel Adapter].
[[x2.2-mongo-adapters]]
=== MongoDB Inbound and Outbound Channel Adapters
Spring Integration now has MongoDB inbound and outbound channel adapters, letting you write and read `Message` payloads to and from a MongoDB document store.
For more information, see xref:mongodb.adoc#mongodb-outbound-channel-adapter[MongoDB Outbound Channel Adapter] and xref:mongodb.adoc#mongodb-inbound-channel-adapter[MongoDB Inbound Channel Adapter].
[[x2.2-jpa]]
=== JPA Endpoints
Spring Integration now includes components for the Java Persistence API (JPA) for retrieving and persisting JPA entity objects.
The JPA Adapter includes the following components:
* xref:jpa/inbound-channel-adapter.adoc[Inbound channel adapter]
* xref:jpa/outbound-channel-adapter.adoc[Outbound channel adapter]
* xref:jpa/outbound-gateways.adoc#jpa-updating-outbound-gateway[Updating outbound gateway]
* xref:jpa/outbound-gateways.adoc#jpa-retrieving-outbound-gateway[Retrieving outbound gateway]
For more information, see xref:jpa.adoc[JPA Support].
[[x2.2-general]]
== General Changes
This section describes general changes from version 2.1 to version 2.2.
[[x2.2-spring-31]]
=== Spring 3.1 Used by Default
Spring Integration now uses Spring 3.1.
[[x2.2-handler-advice]]
=== Adding Behavior to Endpoints
The ability to add an `<advice-chain/>` to a poller has been available for some time.
However, the behavior added by this affects the entire integration flow.
It did not address the ability to add (for example) retry to an individual endpoint.
The 2.2 release introduced the `<request-handler-advice-chain/>` to many endpoints.
In addition, we added three standard advice classes for this purpose:
* `MessageHandlerRetryAdvice`
* `MessageHandlerCircuitBreakerAdvice`
* `ExpressionEvaluatingMessageHandlerAdvice`
For more information, see xref:handler-advice.adoc[Adding Behavior to Endpoints].
[[x2.2-transaction-sync]]
=== Transaction Synchronization and Pseudo Transactions
Pollers can now participate in Spring's Transaction Synchronization feature.
This allows for synchronizing such operations as renaming files by an inbound channel adapter, depending on whether the transaction commits or rolls back.
In addition, you can enable these features when no "`real`" transaction is present, by means of a `PseudoTransactionManager`.
For more information, see xref:transactions.adoc#transaction-synchronization[Transaction Synchronization].
[[x2.2-file-adapter]]
=== File Adapter: Improved File Overwrite and Append Handling
When using the file outbound channel adapter or the file outbound gateway, you can use a new `mode` property.
Prior to Spring Integration 2.2, target files were replaced when they existed.
Now you can specify the following options:
* `REPLACE` (default)
* `APPEND`
* `FAIL`
* `IGNORE`
For more information, see xref:file/writing.adoc#file-writing-destination-exists[Dealing with Existing Destination Files].
[[x2.2-outbound-gateways]]
=== Reply-Timeout Added to More Outbound Gateways
The XML Namespace support adds the reply-timeout attribute to the following outbound gateways:
* AMQP Outbound Gateway
* File Outbound Gateway
* FTP Outbound Gateway
* SFTP Outbound Gateway
* WS Outbound Gateway
[[x2.2-amqp-11]]
=== Spring-AMQP 1.1
Spring Integration now uses Spring AMQP 1.1.
This enables several features to be used within a Spring Integration application, including the following:
* A fixed reply queue for the outbound gateway
* HA (mirrored) queues
* Publisher confirmations
* Returned messages
* Support for dead letter exchanges and dead letter queues
[[x2.2-jdbc-11]]
=== JDBC Support - Stored Procedures Components
[[spel-support]]
==== SpEL Support
When using the stored procedure components of the Spring Integration JDBC Adapter, you can now provide stored procedure names or stored function names by using the Spring Expression Language (SpEL).
Doing so lets you specify the stored procedures to be invoked at runtime.
For example, you can provide stored procedure names that you would like to execute through message headers.
For more information, see xref:jdbc/stored-procedures.adoc[Stored Procedures].
[[jmx-support]]
==== JMX Support
The stored procedure components now provide basic JMX support, exposing some of their properties as MBeans:
* Stored procedure name
* Stored procedure name expression
* `JdbcCallOperations` cache statistics
[[x2.2-jdbc-gateway-update-optional]]
=== JDBC Support: Outbound Gateway
When you use the JDBC outbound gateway, the update query is no longer mandatory.
You can now provide only a select query by using the request message as a source of parameters.
[[x2.2-jdbc-message-store-channels]]
=== JDBC Support: Channel-specific Message Store Implementation
We added a new message channel-specific message store implementation, providing a more scalable solution using database-specific SQL queries.
For more information, see xref:jdbc/message-store.adoc#jdbc-message-store-channels[Backing Message Channels].
[[x2.2-shutdown]]
=== Orderly Shutdown
We added a method called `stopActiveComponents()` to the `IntegrationMBeanExporter`.
It allows a Spring Integration application to be shut down in an orderly manner, disallowing new inbound messages to certain adapters and waiting for some time to allow in-flight messages to complete.
[[x2.2-jms-og]]
=== JMS Outbound Gateway Improvements
You can now configure the JMS outbound gateway to use a `MessageListener` container to receive replies.
Doing so can improve performance of the gateway.
[[x2.2-o-t-j-t]]
=== `ObjectToJsonTransformer`
By default, the `ObjectToJsonTransformer` now sets the `content-type` header to `application/json`.
For more information, see xref:transformer.adoc[Transformer].
[[httpChanges]]
=== HTTP Support
Java serialization over HTTP is no longer enabled by default.
Previously, when setting an `expected-response-type` on a `Serializable` object, the `Accept` header was not properly set up.
We updated the `SerializingHttpMessageConverter` to set the `Accept` header to `application/x-java-serialized-object`.
However, because this could cause incompatibility with existing applications, we decided to no longer automatically add this converter to the HTTP endpoints.
If you wish to use Java serialization, you need to add the `SerializingHttpMessageConverter` to the appropriate endpoints by using the `message-converters` attribute (when you use XML configuration) or by using the `setMessageConverters()` method (in Java).
Alternatively, you may wish to consider using JSON instead.
It is enabled by having `Jackson` on the classpath.

View File

@@ -0,0 +1,427 @@
[[migration-2.2-3.0]]
= Changes Between 2.2 and 3.0
See the https://github.com/spring-projects/spring-integration/wiki/Spring-Integration-2.2-to-3.0-Migration-Guide[Migration Guide] for important changes that might affect your applications.
You can find migration guides for all versions back to 2.1 on the https://github.com/spring-projects/spring-integration/wiki[wiki].
[[x3.0-new-components]]
== New Components
Version 3.0 added a number of new components.
[[x3.0-request-mapping]]
=== HTTP Request Mapping
The HTTP module now provides powerful request mapping support for inbound endpoints.
We replaced the `UriPathHandlerMapping` class with `IntegrationRequestMappingHandlerMapping`, which is registered under the bean name of `integrationRequestMappingHandlerMapping` in the application context.
Upon parsing of the HTTP inbound endpoint, either a new `IntegrationRequestMappingHandlerMapping` bean is registered or an existing bean is reused.
To achieve flexible request mapping configuration, Spring Integration provides the `<request-mapping/>` child element for `<http:inbound-channel-adapter/>` and the `<http:inbound-gateway/>`.
Both HTTP inbound endpoints are now fully based on the request mapping infrastructure that was introduced with Spring MVC 3.1.
For example, multiple paths are supported on a single inbound endpoint.
For more information see xref:http/namespace.adoc[HTTP Namespace Support].
[[x3.0-spel-customization]]
=== Spring Expression Language (SpEL) Configuration
We added a new `IntegrationEvaluationContextFactoryBean` to allow configuration of custom `PropertyAccessor` implementations and functions for use in SpEL expressions throughout the framework.
For more information, see xref:spel.adoc[Spring Expression Language (SpEL)].
[[x3.0-spel-functions]]
=== SpEL Functions Support
To customize the SpEL `EvaluationContext` with static `Method` functions, we introduced the `<spel-function/>` component.
We also added two built-in functions: `#jsonPath` and `#xpath`.
For more information, see xref:spel.adoc#spel-functions[SpEL Functions].
[[x3.0-spel-property-accessors]]
=== SpEL PropertyAccessors Support
To customize the SpEL `EvaluationContext` with `PropertyAccessor` implementations, we added the `<spel-property-accessors/>` component.
For more information, see xref:spel.adoc#spel-property-accessors[Property Accessors].
[[x3.0-redis-new-components]]
=== Redis: New Components
We added a new Redis-based https://docs.spring.io/spring-integration/api/org/springframework/integration/metadata/MetadataStore.html[`MetadataStore`] implementation.
You can use the `RedisMetadataStore` to maintain the state of a `MetadataStore` across application restarts.
This new `MetadataStore` implementation can be used with adapters, such as:
* Twitter inbound adapters
* Feed inbound channel adapter
We added new queue-based components.
We added the `<int-redis:queue-inbound-channel-adapter/>` and `<int-redis:queue-outbound-channel-adapter/>` components to perform 'right pop' and 'left push' operations, respectively, on a Redis List.
For more information, "`see xref:redis.adoc[Redis Support]`".
[[x3.0-hcr]]
=== Header Channel Registry
You can now instruct the framework to store reply channels and error channels in a registry for later resolution.
This is useful for cases where the `replyChannel` or `errorChannel` might be lost (for example, when serializing a message).
See xref:content-enrichment.adoc#header-enricher[Header Enricher] for more information.
[[x3.0-configurable-mongo-MS]]
=== MongoDB support: New `ConfigurableMongoDbMessageStore`
In addition to the existing `eMongoDbMessageStore`, we introduced a new `ConfigurableMongoDbMessageStore`.
This provides a more robust and flexible implementation of `MessageStore` for MongoDB.
It does not have backward compatibility with the existing store, but we recommend using it for new applications.
Existing applications can use it, but messages in the old store are not available.
See xref:mongodb.adoc[MongoDb Support] for more information.
[[x3.0-syslog]]
=== Syslog Support
Building on the 2.2 `SyslogToMapTransformer`, Spring Integration 3.0 introduces `UDP` and `TCP` inbound channel adapters especially tailored for receiving SYSLOG messages.
For more information, see xref:syslog.adoc[Syslog Support].
[[x3.0-tail]]
=== `tail` Support
We added file inbound channel adapters that use the `tail` command to generate messages when lines are added to the end of text files.
See xref:file/reading.adoc#file-tailing['tail'ing Files].
[[x3.0-jmx]]
=== JMX Support
We added `<int-jmx:tree-polling-channel-adapter/>`.
This adapter queries the JMX MBean tree and sends a message with a payload that is the graph of objects that match the query.
By default, the MBeans are mapped to primitives and simple Objects (such as `Map`, `List`, and arrays).
It permits simple transformation to, for example, JSON.
The `IntegrationMBeanExporter` now allows the configuration of a custom `ObjectNamingStrategy` by using the `naming-strategy` attribute.
For more information, see xref:jmx.adoc[JMX Support].
[[x3.0-tcp-events]]
=== TCP/IP Connection Events and Connection Management
`TcpConnection` instances now emit `ApplicationEvent` instances (specifically `TcpConnectionEvent` instances) when connections are opened or closed or when an exception occurs.
This change lets applications be informed of changes to TCP connections by using the normal Spring `ApplicationListener` mechanism.
We renamed `AbstractTcpConnection` to `TcpConnectionSupport`.
Custom connections that are subclasses of this class can use its methods to publish events.
Similarly, we renamed `AbstractTcpConnectionInterceptor` to `TcpConnectionInterceptorSupport`.
In addition, we added `<int-ip:tcp-connection-event-inbound-channel-adapter/>`.
By default, this adapter sends all `TcpConnectionEvent` instances to a `Channel`.
Further, the TCP connection factories now provide a new method called `getOpenConnectionIds()`, which returns a list of identifiers for all open connections.
It lets applications broadcast to all open connections, among other uses.
Finally, the connection factories also provide a new method called `closeConnection(String connectionId)`, which lets applications explicitly close a connection by using its ID.
For more information see xref:changes-4.1-4.2.adoc#x4.2-tcp-events[TCP Connection Events].
[[x3.0-inbound-script]]
=== Inbound Channel Adapter Script Support
The `<int:inbound-channel-adapter/>` now supports using `<expression/>` and `<script/>` child elements to create a `MessageSource`.
See xref:channel-adapter.adoc#channel-adapter-expressions-and-scripts[Channel Adapter Expressions and Scripts].
[[x3.0-content-enricher-headers]]
=== Content Enricher: Headers Enrichment Support
The content enricher now provides configuration for `<header/>` child elements, to enrich the outbound message with headers based on the reply message from the underlying message flow.
For more information see xref:content-enrichment.adoc#payload-enricher[Payload Enricher].
[[x3.0-general]]
== General Changes
This section describes general changes from version 2.2 to version 3.0.
[[x3.0-message-id]]
=== Message ID Generation
Previously, message IDs were generated by using the JDK `UUID.randomUUID()` method.
With this release, the default mechanism has been changed to use a more efficient and significantly faster algorithm.
In addition, we added the ability to change the strategy used to generate message IDs.
For more information see xref:message.adoc#message-id-generation[Message ID Generation].
[[x3.0-gateway]]
=== "`<gateway>`" Changes
You can now set common headers across all gateway methods, and we added more options for adding information to the message about which method was invoked.
You can now entirely customize the way that gateway method calls are mapped to messages.
The `GatewayMethodMetadata` is now a public class.
It lets you programmatically configure the `GatewayProxyFactoryBean` from Java.
For more information, see xref:gateway.adoc[Messaging Gateways].
[[x3.0-http-endpointss]]
=== HTTP Endpoint Changes
* *Outbound Endpoint `encode-uri`*: `<http:outbound-gateway/>` and `<http:outbound-channel-adapter/>` now provide an `encode-uri` attribute to allow disabling the encoding of the URI object before sending the request.
* *Inbound Endpoint `merge-with-default-converters`*: `<http:inbound-gateway/>` and `<http:inbound-channel-adapter/>` now have a `merge-with-default-converters` attribute to include the list of default `HttpMessageConverter` instances after the custom message converters.
* *`If-Modified-Since` and `If-Unmodified-Since` HTTP Headers*: Previously, the `If-Modified-Since` and `If-Unmodified-Since` HTTP headers were incorrectly processed within from and to HTTP headers mapped in the `DefaultHttpHeaderMapper`.
Now, in addition to correcting that issue, `DefaultHttpHeaderMapper` provides date parsing from formatted strings for any HTTP headers that accept date-time values.
* *Inbound Endpoint Expression Variables*: In addition to the existing `#requestParams` and `#pathVariables`, the `<http:inbound-gateway/>` and `<http:inbound-channel-adapter/>` now support additional useful variables: `#matrixVariables`, `#requestAttributes`, `#requestHeaders`, and `#cookies`.
These variables are available in both payload and header expressions.
* *Outbound Endpoint 'uri-variables-expression'*: HTTP outbound endpoints now support the `uri-variables-expression` attribute to specify an `Expression` to evaluate a `Map` for all URI variable placeholders within URL template.
This allows selection of a different map of expressions based on the outgoing message.
For more information, see xref:http.adoc[HTTP Support].
[[x3.0-json-transformers]]
=== Jackson Support (JSON)
* A new abstraction for JSON conversion has been introduced.
Implementations for Jackson 1.x and Jackson 2 are currently provided, with the version being determined by presence on the classpath.
Previously, only Jackson 1.x was supported.
* The `ObjectToJsonTransformer` and `JsonToObjectTransformer` now emit/consume headers containing type information.
For more information, see "`JSON Transformers`" in xref:transformer.adoc[Transformer].
[[x3.0-id-for-chain-sub-components]]
=== Chain Elements `id` Attribute
Previously, the `id` attribute for elements within a `<chain>` was ignored and, in some cases, disallowed.
Now, the `id` attribute is allowed for all elements within a `<chain>`.
The bean names of chain elements is a combination of the surrounding chain's `id` and the `id` of the element itself.
For example: 'myChain$child.myTransformer.handler'.
For more information see, xref:chain.adoc[Message Handler Chain].
[[x3.0-corr-endpoint-empty-groups]]
=== Aggregator 'empty-group-min-timeout' property
The `AbstractCorrelatingMessageHandler` provides a new property called `empty-group-min-timeout` to allow empty group expiry to run on a longer schedule than expiring partial groups.
Empty groups are not removed from the `MessageStore` until they have not been modified for at least this number of milliseconds.
For more information, see xref:aggregator.adoc#aggregator-xml[Configuring an Aggregator with XML].
[[x3.0-filelistfilter]]
=== Persistent File List Filters (file, (S)FTP)
New `FileListFilter` implementations that use a persistent `MetadataStore` are now available.
You can use these to prevent duplicate files after a system restart.
See xref:file/reading.adoc[Reading Files], xref:ftp/inbound.adoc[FTP Inbound Channel Adapter], and xref:sftp/inbound.adoc[SFTP Inbound Channel Adapter] for more information.
[[x3.0-scripting-variables]]
=== Scripting Support: Variables Changes
We introduced a new `variables` attribute for scripting components.
In addition, variable bindings are now allowed for inline scripts.
See xref:groovy.adoc[Groovy support] and xref:scripting.adoc[Scripting Support] for more information.
[[x3.0-direct-channel-lb-ref]]
=== Direct Channel Load Balancing configuration
Previously, when configuring `LoadBalancingStrategy` on the channel's `dispatcher` child element, the only available option was to use a pre-defined enumeration of values which did not let developers set a custom implementation of the `LoadBalancingStrategy`.
You can now use `load-balancer-ref` to provide a reference to a custom implementation of the `LoadBalancingStrategy`.
For more information, see xref:channel/implementations.adoc#channel-implementations-directchannel[`DirectChannel`].
[[x3.0-pub-sub]]
=== PublishSubscribeChannel Behavior
Previously, sending to a <publish-subscribe-channel/> that had no subscribers would return a `false` result.
If used in conjunction with a `MessagingTemplate`, this would result in an exception being thrown.
Now, the `PublishSubscribeChannel` has a property called `minSubscribers` (default: `0`).
If the message is sent to at least the minimum number of subscribers, the send operation is deemed to be successful (even if the number is zero).
If an application expects to get an exception under these conditions, set the minimum subscribers to at least 1.
[[x3.0--s-ftp-changes]]
=== FTP, SFTP and FTPS Changes
The FTP, SFTP and FTPS endpoints no longer cache sessions by default.
We removed the deprecated `cached-sessions` attribute from all endpoints.
Previously, the embedded caching mechanism controlled by this attribute's value did not provide a way to limit the size of the cache, which could grow indefinitely.
Release 2.1 introduced `CachingConnectionFactory`, and it became the preferred (and is now the only) way to cache sessions.
`CachingConnectionFactory` now provides a new method: `resetCache()`.
This method immediately closes idle sessions and causes in-use sessions to be closed as and when they are returned to the cache.
The `DefaultSftpSessionFactory` (in conjunction with a `CachingSessionFactory`) now supports multiplexing channels over a single SSH connection (SFTP Only).
[[ftp-sftp-and-ftps-inbound-adapters]]
==== FTP, SFTP and FTPS Inbound Adapters
Previously, there was no way to override the default filter used to process files retrieved from a remote server.
The `filter` attribute determines which files are retrieved, but the `FileReadingMessageSource` uses an `AcceptOnceFileListFilter`.
This means that, if a new copy of a file is retrieved with the same name as a previously copied file, no message was sent from the adapter.
With this release, a new attribute `local-filter` lets you override the default filter (for example, with an `AcceptAllFileListFilter` or some other custom filter).
If you want the behavior of the `AcceptOnceFileListFilter` to be maintained across JVM executions, you can now configure a custom filter that retains state, perhaps on the file system.
Inbound channel adapters now support the `preserve-timestamp` attribute, which sets the local file modified timestamp to the timestamp from the server (default: `false`).
[[ftp-sftp-and-ftps-gateways]]
==== FTP, SFTP, and FTPS Gateways
The gateways now support the `mv` command, enabling the renaming of remote files.
The gateways now support recursive `ls` and `mget` commands, enabling the retrieval of a remote file tree.
The gateways now support `put` and `mput` commands, enabling sending files to the remote server.
The `local-filename-generator-expression` attribute is now supported, enabling the naming of local files during retrieval.
By default, the same name as the remote file is used.
The `local-directory-expression` attribute is now supported, enabling the naming of local directories during retrieval (based on the remote directory).
[[remote-file-template]]
==== Remote File Template
A new higher-level abstraction (`RemoteFileTemplate`) is provided over the `Session` implementations used by the FTP and SFTP modules.
While it is used internally by endpoints, you can also use this abstraction programmatically.
Like all Spring `*Template` implementations, it reliably closes the underlying session while allowing low level access to the session.
For more information, see xref:ftp.adoc[FTP/FTPS Adapters] and xref:sftp.adoc[SFTP Adapters].
[[x3.0-outbound-gateway-requires-reply]]
=== 'requires-reply' Attribute for Outbound Gateways
All outbound gateways (such as `<jdbc:outbound-gateway/>` or `<jms:outbound-gateway/>`) are designed for 'request-reply' scenarios.
A response is expected from the external service and is published to the `reply-channel` or the `replyChannel` message header.
However, there are some cases where the external system might not always return a result (for example,
a `<jdbc:outbound-gateway/>` when a SELECT ends with an empty `ResultSet` or perhaps a one-way web service).
Consequently, developers needed an option to configure whether or not a reply is required.
For this purpose, we introduced the `requires-reply` attribute for outbound gateway components.
In most cases, the default value for `requires-reply` is `true`.
If there is no result, a `ReplyRequiredException` is thrown.
Changing the value to `false` means that, if an external service does not return anything, the message flow ends at that point, similar to an outbound channel adapter.
NOTE: The WebService outbound gateway has an additional attribute called `ignore-empty-responses`.
It is used to treat an empty `String` response as if no response were received.
By default, it is `true`, but you can set it to `false` to allow the application to receive an empty `String` in the reply message payload.
When the attribute is `true`, an empty string is treated as no response for the purposes of the `requires-reply` attribute.
By default, `requires-reply` is false for the WebService outbound gateway.
Note that the `requiresReply` property was previously present but set to `false` in the `AbstractReplyProducingMessageHandler`, and there was no way to configure it on outbound gateways by using the XML namespace.
IMPORTANT: Previously, a gateway receiving no reply would silently end the flow (with a DEBUG log message).
By default, with this change, an exception is now thrown by most gateways.
To revert to the previous behavior, set `requires-reply` to `false`.
[[x3.0-amqp-mapping]]
=== AMQP Outbound Gateway Header Mapping
Previously, the <int-amqp:outbound-gateway/> mapped headers before invoking the message converter, and the converter could overwrite headers such as `content-type`.
The outbound adapter maps the headers after the conversion, which means headers like `content-type` from the outbound `Message` (if present) are used.
Starting with this release, the gateway now maps the headers after the message conversion, consistent with the adapter.
If your application relies on the previous behavior (where the converter's headers overrode the mapped headers), you either need to filter those headers (before the message reaches the gateway) or set them appropriately.
The headers affected by the `SimpleMessageConverter` are `content-type` and `content-encoding`.
Custom message converters may set other headers.
[[x3.0-stored-proc-sql-return-type]]
=== Stored Procedure Components Improvements
For more complex database-specific types not supported by the standard `CallableStatement.getObject` method, we introduced two new additional attributes to the `<sql-parameter-definition/>` element with OUT-direction:
* `type-name`
* `return-type`
The `row-mapper` attribute of the stored procedure inbound channel adapter `<returning-resultset/>` child element now supports a reference to a `RowMapper` bean definition.
Previously, it contained only a class name (which is still supported).
For more information, see xref:jdbc/stored-procedures.adoc[Stored Procedures].
[[x3.0-ws-outbound-uri-substitution]]
=== Web Service Outbound URI Configuration
The web service outbound gateway 'uri' attribute now supports `<uri-variable/>` substitution for all URI schemes supported by Spring Web Services.
For more information, see xref:ws.adoc#outbound-uri[Outbound URI Configuration].
[[x3.0-redis]]
=== Redis Adapter Changes
The Redis inbound channel adapter can now use a `null` value for the `serializer` property, with the raw data being the message payload.
The Redis outbound channel adapter now has the `topic-expression` property to determine the Redis topic for the `Message` at runtime.
The Redis inbound channel adapter, in addition to the existing `topics` attribute, now has the `topic-patterns` attribute.
For more information, see xref:redis.adoc[Redis Support].
[[x3.0-advising-filters]]
=== Advising Filters
Previously, when a `<filter/>` had a `<request-handler-advice-chain/>`, the discard action was all performed within the scope of the advice chain (including any downstream flow on the `discard-channel`).
The filter element now has an attribute called `discard-within-advice` (default: `true`) to allow the discard action to be performed after the advice chain completes.
See xref:handler-advice/advising-filters.adoc[Advising Filters].
[[x3.0-annotation-advice]]
=== Advising Endpoints using Annotations
Request handler advice chains can now be configured using annotations.
See xref:handler-advice/advising-with-annotations.adoc[Advising Endpoints Using Annotations].
[[x3.0-o-t-s-t]]
=== ObjectToStringTransformer Improvements
This transformer now correctly transforms `byte[]` and `char[]` payloads to `String`.
For more information, see xref:transformer.adoc[Transformer].
[[x3.0-jpa-changes]]
=== JPA Support Changes
Payloads to persist or merge can now be of type `https://docs.oracle.com/javase/7/docs/api/java/lang/Iterable.html[java.lang.Iterable]`.
In that case, each object returned by the `Iterable` is treated as an entity and persisted or merged by using the underlying `EntityManager`.
Null values returned by the iterator are ignored.
The JPA adapters now have additional attributes to optionally flush and clear entities from the associated persistence context after performing persistence operations.
Retrieving gateways had no mechanism to specify the first record to be retrieved, which is a common use case.
The retrieving gateways now support specifying this parameter by adding the `first-result` and `first-result-expression` attributes to the gateway definition.
For more information, see xref:jpa/outbound-gateways.adoc#jpa-retrieving-outbound-gateway[Retrieving Outbound Gateway].
The JPA retrieving gateway and inbound adapter now have an attribute to specify the maximum number of results in a result set as an expression.
In addition, we introduced the `max-results` attribute to replace `max-number-of-results`, which has been deprecated.
`max-results` and `max-results-expression` are used to provide the maximum number of results or an expression to compute the maximum number of results, respectively, in the result set.
For more information, see xref:jpa.adoc[JPA Support].
[[x3.0-dalay-expression]]
=== Delayer: delay expression
Previously, the `<delayer>` provided a `delay-header-name` attribute to determine the delay value at runtime.
In complex cases, the `<delayer>` had to be preceded with a `<header-enricher>`.
Spring Integration 3.0 introduced the `expression` attribute and `expression` child element for dynamic delay determination.
The `delay-header-name` attribute is now deprecated, because you can specify the header evaluation in the `expression`.
In addition, we introduced the `ignore-expression-failures` to control the behavior when an expression evaluation fails.
For more information, see xref:delayer.adoc[Delayer].
[[x3.0-jdbc-mysql-v5_6_4]]
=== JDBC Message Store Improvements
Spring Integration 3.0 adds a new set of DDL scripts for MySQL version 5.6.4 and higher.
Now MySQL supports fractional seconds and is thus improving the FIFO ordering when polling from a MySQL-based message store.
For more information, see xref:jdbc/message-store.adoc#jdbc-message-store-generic[The Generic JDBC Message Store].
[[x3.0-event-for-imap-idle]]
=== IMAP Idle Connection Exceptions
Previously, if an IMAP idle connection failed, it was logged, but there was no mechanism to inform an application.
Such exceptions now generate `ApplicationEvent` instances.
Applications can obtain these events by using an `<int-event:inbound-channel-adapter>` or any `ApplicationListener` configured to receive an `ImapIdleExceptionEvent` (or one of its super classes).
[[x3.0-tcp-headers]]
=== Message Headers and TCP
The TCP connection factories now enable the configuration of a flexible mechanism to transfer selected headers (as well as the payload) over TCP.
A new `TcpMessageMapper` enables the selection of the headers, and you need to configure an appropriate serializer or deserializer to write the resulting `Map` to the TCP stream.
We added a `MapJsonSerializer` as a convenient mechanism to transfer headers and payload over TCP.
For more information, see xref:ip/correlation.adoc#ip-headers[Transferring Headers].
[[x3.0-jms-mdca-te]]
=== JMS Message Driven Channel Adapter
Previously, when configuring a `<message-driven-channel-adapter/>`, if you wished to use a specific `TaskExecutor`, you had to declare a container bean and provide it to the adapter by setting the `container` attribute.
We added the `task-executor`, letting it be set directly on the adapter.
This is in addition to several other container attributes that were already available.
[[x3.0-xslt-transformer]]
=== `XsltPayloadTransformer`
You can now specify the transformer factory class name by setting the `transformer-factory-class` attribute.
See `xref:xml/transformation.adoc#xml-xslt-payload-transformers[XsltPayloadTransformer]`.

View File

@@ -0,0 +1,264 @@
[[migration-3.0-4.0]]
= Changes between 3.0 and 4.0
See the https://github.com/spring-projects/spring-integration/wiki/Spring-Integration-3.0-to-4.0-Migration-Guide[Migration Guide] for important changes that might affect your applications.
You can find migration guides for all versions back to 2.1 on the https://github.com/spring-projects/spring-integration/wiki[wiki].
[[x4.0-new-components]]
== New Components
Version 4.0 added a number of new components.
[[x4.0-mqtt]]
=== MQTT Channel Adapters
The MQTT channel adapters (previously available in the Spring Integration Extensions repository) are now available as part of the normal Spring Integration distribution.
See xref:mqtt.adoc[MQTT Support].
[[x4.0-enable-configuration]]
=== `@EnableIntegration`
We added the `@EnableIntegration` annotation to permit declaration of standard Spring Integration beans when using `@Configuration` classes.
See xref:configuration/annotations.adoc[Annotation Support] for more information.
[[x4.0-component-scan]]
=== `@IntegrationComponentScan`
We added the `@IntegrationComponentScan` annotation to permit classpath scanning for Spring Integration-specific components.
See xref:configuration/annotations.adoc[Annotation Support] for more information.
[[x4.0-message-history]]
=== "`@EnableMessageHistory`"
You can now enable message history with the `@EnableMessageHistory` annotation in a `@Configuration` class.
In addition, a JMX MBean can modify the message history settings.
Also, `MessageHistory` can track auto-created `MessageHandler` instances for annotated endpoints (such as `@ServiceActivator`, `@Splitter`, and others).
For more information, see xref:message-history.adoc[Message History].
[[x4.0-messaging-gateway]]
=== `@MessagingGateway`
You can now configure messaging gateway interfaces with the `@MessagingGateway` annotation.
It is an analogue of the `<int:gateway/>` XML element.
For more information, see xref:gateway.adoc#messaging-gateway-annotation[`@MessagingGateway` Annotation].
[[x4.0-boot]]
=== Spring Boot `@EnableAutoConfiguration`
As well as the `@EnableIntegration` annotation mentioned earlier, we introduced a hook to allow the Spring Integration infrastructure beans to be configured with Spring Boot's `@EnableAutoConfiguration` annotation.
For more information, see https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-auto-configuration.html["`Auto-configuration`"] in the Spring Boot Reference Guide.
[[x4.0-global-channel-interceptor]]
=== `@GlobalChannelInterceptor`
As well as the `@EnableIntegration` annotation mentioned above, we introduced the `@GlobalChannelInterceptor` annotation.
For more information, see xref:configuration/annotations.adoc[Annotation Support].
[[x4.0-integration-converter]]
=== `@IntegrationConverter`
We introduced the `@IntegrationConverter` annotation as an analogue of the `<int:converter/>` component.
For more information, see xref:configuration/annotations.adoc[Annotation Support].
[[x4.0-enable-publisher]]
=== `@EnablePublisher`
We added the `@EnablePublisher` annotation to allow the specification of a `default-publisher-channel` for `@Publisher` annotations.
See xref:configuration/annotations.adoc[Annotation Support] for more information.
[[x4.0-redis-cms]]
=== Redis Channel Message Stores
We added a Redis `MessageGroupStore` that is optimized for use when backing a `QueueChannel` for persistence.
For more information, see xref:redis.adoc#redis-cms[Redis Channel Message Stores].
We added a Redis `ChannelPriorityMessageStore`.
You can use it to retrieve messages by priority.
For more information, see xref:redis.adoc#redis-cms[Redis Channel Message Stores].
[[x4.0-priority-channel-mondodb]]
=== MongodDB Channel Message Store
The MongoDB support now provides the `MongoDbChannelMessageStore`, which is a channel-specific `MessageStore` implementation.
With `priorityEnabled = true`, you can use it in `<int:priority-queue>` elements to achieve priority order polling of persisted messages.
For more information see xref:mongodb.adoc#mongodb-priority-channel-message-store[MongoDB Channel Message Store].
[[x4.0-MBeanExport-annotation]]
=== `@EnableIntegrationMBeanExport`
You can now enable the `IntegrationMBeanExporter` with the `@EnableIntegrationMBeanExport` annotation in a `@Configuration` class.
For more information, see xref:jmx.adoc#jmx-mbean-exporter[MBean Exporter].
[[x4.0-channel-security-interceptor]]
=== `ChannelSecurityInterceptorFactoryBean`
`ChannelSecurityInterceptorFactoryBean` now supports configuration of Spring Security for message channels that use `@Configuration` classes.
For more information, see xref:security.adoc[Security in Spring Integration].
[[x4.0-redis-outbound-gateway]]
=== Redis Command Gateway
The Redis support now provides the `<outbound-gateway>` component to perform generic Redis commands by using the `RedisConnection#execute` method.
For more information, see xref:redis.adoc#redis-outbound-gateway[Redis Outbound Command Gateway].
[[x4.0-redis-lock-registry]]
=== `RedisLockRegistry`
The `RedisLockRegistry` is now available to support global locks visible to multiple application instances and servers.
These can be used with aggregating message handlers across multiple application instances such that group release occurs on only one instance.
For more information, see xref:redis.adoc#redis-lock-registry[Redis Lock Registry] and xref:overview.adoc#overview-endpoints-aggregator[Aggregator].
[[x4.0-poller-annotation]]
=== `@Poller`
Annotation-based messaging configuration can now have a `poller` attribute.
This means that methods annotated with `@ServiceActivator`, `@Aggregator`, and similar annotations can now use an `inputChannel` that is a reference to a `PollableChannel`.
For more information, see xref:configuration/annotations.adoc[Annotation Support].
[[x4.0-inbound-channel-adapter-annotation]]
=== `@InboundChannelAdapter` and `SmartLifecycle` for Annotated Endpoints
We added the `@InboundChannelAdapter` method annotation.
It is an analogue of the `<int:inbound-channel-adapter>` XML component.
In addition, all messaging annotations now provide `SmartLifecycle` options.
For more information, see xref:configuration/annotations.adoc[Annotation Support].
[[x4.0-twitter-sog]]
=== Twitter Search Outbound Gateway
We added a new twitter endpoint: `<int-twitter-search-outbound-gateway/>`.
Unlike the search inbound adapter, which polls by using the same search query each time, the outbound gateway allows on-demand customized queries.
For more information, see https://github.com/spring-projects/spring-integration-extensions/tree/main/spring-integration-social-twitter[Spring Integration Social Twitter].
[[x4.0-bridge-annotations]]
=== `@BridgeFrom` and `@BridgeTo` Annotations
We introduced `@BridgeFrom` and `@BridgeTo` `@Bean` method annotations to mark `MessageChannel` beans in `@Configuration` classes.
For more information, see xref:configuration/annotations.adoc[Annotation Support].
[[x4.0-meta-messaging-annotations]]
=== Meta-messaging Annotations
Messaging annotations (`@ServiceActivator`, `@Router`, `@MessagingGateway`, and others) can now be configured as meta-annotations for user-defined messaging annotations.
In addition, the user-defined annotations can have the same attributes (`inputChannel`, `@Poller`, `autoStartup`, and others).
For more information, see xref:configuration/annotations.adoc[Annotation Support].
[[x4.0-general]]
== General Changes
This section describes general changes from version 3.0 to version 4.0.
[[requires-spring-framework-4-0]]
=== Requires Spring Framework 4.0
We moved the core messaging abstractions (`Message`, `MessageChannel`, and others) to the Spring Framework `spring-messaging` module.
Developers who reference these classes directly in their code need to make changes, as described in the first section of the https://github.com/spring-projects/spring-integration/wiki/Spring-Integration-3.0-to-4.0-Migration-Guide[3.0 to 4.0 Migration Guide].
[[x4.0-xpath-header-enricher-header-type]]
=== Header Type for XPath Header Enricher
We introduced the `header-type` attribute for the `header` child element of the `<int-xml:xpath-header-enricher>`.
This attribute provides the target type for the header value (to which the result of the XPath expression evaluation is converted).
For more information see xref:xml/xpath-header-enricher.adoc[XPath Header Enricher].
[[x4.0-object-to-json-transformer-result-type]]
=== Object To JSON Transformer: Node Result
We introduced the `result-type` attribute for the `<int:object-to-json-transformer>`.
This attribute provides the target type for the result of mapping an object to JSON.
It supports `STRING` (the default) and `NODE`.
For more information see xref:transformer.adoc#transformer-xpath-spel-function[Since version 3.0, Spring Integration also provides a built-in `#xpath` SpEL function for use in expressions.].
[[x4.0-jms-header-mapping]]
=== JMS Header Mapping
The `DefaultJmsHeaderMapper` now maps an incoming `JMSPriority` header to the Spring Integration `priority` header.
Previously, `priority` was only considered for outbound messages.
For more information, see xref:changes-3.0-4.0.adoc#x4.0-jms-header-mapping[Mapping Message Headers to and from JMS Message].
[[x4.0-jms-ob]]
=== JMS Outbound Channel Adapter
The JMS outbound channel adapter now supports the `session-transacted` attribute (default: `false`).
Previously, you had to inject a customized `JmsTemplate` to use transactions.
See xref:changes-3.0-4.0.adoc#x4.0-jms-ob[Outbound Channel Adapter].
[[x4.0-jms-ib]]
=== JMS Inbound Channel Adapter
The JMS inbound channel adapter now supports the `session-transacted` attribute (default: `false`).
Previously, you had to inject a customized `JmsTemplate` to use transactions.
The adapter allowed 'transacted' in the `acknowledgeMode`, which was incorrect and didn't work.
This value is no longer allowed.
See xref:changes-3.0-4.0.adoc#x4.0-jms-ib[Inbound Channel Adapter].
[[x4.0-datatype-channel]]
=== Datatype Channels
You can now specify a `MessageConverter` to be used when converting (if necessary) payloads to one of the accepted `datatype` instances in a Datatype channel.
For more information, see xref:channel/configuration.adoc#channel-datatype-channel[Datatype Channel Configuration].
[[x4.0-retry-config]]
=== Simpler Retry Advice Configuration
We added simplified namespace support to configure a `RequestHandlerRetryAdvice`.
For more information, see xref:handler-advice/classes.adoc#retry-config[Configuring the Retry Advice].
[[x4.0-release-strategy-group-timeout]]
=== Correlation Endpoint: Time-based Release Strategy
We added the mutually exclusive `group-timeout` and `group-timeout-expression` attributes to `<int:aggregator>` and `<int:resequencer>`.
These attributes allow forced completion of a partial `MessageGroup`, provided the `ReleaseStrategy` does not release a group and no further messages arrive within the time specified.
For more information, see xref:aggregator.adoc#aggregator-xml[Configuring an Aggregator with XML].
[[x4.0-redis-metadata]]
=== Redis Metadata Store
The `RedisMetadataStore` now implements `ConcurrentMetadataStore`, letting it be used, for example, in an `AbstractPersistentAcceptOnceFileListFilter` implementation in a multiple application instance or server environment.
For more information, see xref:redis.adoc#redis-metadata-store[Redis Metadata Store], xref:file/reading.adoc[Reading Files], xref:ftp/inbound.adoc[FTP Inbound Channel Adapter], and xref:sftp/inbound.adoc[SFTP Inbound Channel Adapter].
[[x4.0-jdbc-cs]]
=== `JdbcChannelMessageStore` and `PriorityChannel`
T`JdbcChannelMessageStore` now implements `PriorityCapableChannelMessageStore`, letting it be used as a `message-store` reference for `priority-queue` instances.
For more information, see xref:jdbc/message-store.adoc#jdbc-message-store-channels[Backing Message Channels].
[[x4.0-amqp]]
=== AMQP Endpoints Delivery Mode
Spring AMQP, by default, creates persistent messages on the broker.
You can override this behavior by setting the `amqp_deliveryMode` header or customizing the mappers.
We added a convenient `default-delivery-mode` attribute to the adapters to provide easier configuration of this important setting.
For more information, see xref:amqp/outbound-channel-adapter.adoc[Outbound Channel Adapter] and xref:amqp/outbound-gateway.adoc[Outbound Gateway].
[[x4.0-ftp]]
=== FTP Timeouts
The `DefaultFtpSessionFactory` now exposes the `connectTimeout`, `defaultTimeout`, and `dataTimeout` properties, avoiding the need to subclass the factory to set these common properties.
The `postProcess*` methods are still available for more advanced configuration.
See xref:ftp/session-factory.adoc[FTP Session Factory] for more information.
[[x4.0-twitter-status-updating]]
=== Twitter: `StatusUpdatingMessageHandler`
The `StatusUpdatingMessageHandler` (`<int-twitter:outbound-channel-adapter>`) now supports the `tweet-data-expression` attribute to build a `org.springframework.social.twitter.api.TweetData` object for updating the timeline status.
This feature allows, for example, attaching an image.
See https://github.com/spring-projects/spring-integration-extensions/tree/main/spring-integration-social-twitter[Spring Integration Social Twitter] for more information.
[[x4.0-jpa-id-expression]]
=== JPA Retrieving Gateway: `id-expression`
We introduced the `id-expression` attribute for `<int-jpa:retrieving-outbound-gateway>` to perform `EntityManager.find(Class entityClass, Object primaryKey)`.
See xref:jpa/outbound-gateways.adoc#jpa-retrieving-outbound-gateway[Retrieving Outbound Gateway] for more information.
[[x4.0-tcp-deserializer-events]]
=== TCP Deserialization Events
When one of the standard deserializers encounters a problem decoding the input stream to a message, it now emits a `TcpDeserializationExceptionEvent`, letting applications examine the data at the point at which the exception occurred.
See xref:changes-4.1-4.2.adoc#x4.2-tcp-events[TCP Connection Events] for more information.
[[x4.0-bean-messaging-annotations]]
=== Messaging Annotations on `@Bean` Definitions
You can now configure messaging annotations (`@ServiceActivator`, `@Router`, `@InboundChannelAdapter`, and others) on `@Bean` definitions in `@Configuration` classes.
For more information, see xref:configuration/annotations.adoc[Annotation Support].

View File

@@ -0,0 +1,230 @@
[[migration-4.0-4.1]]
= Changes between 4.0 and 4.1
See the https://github.com/spring-projects/spring-integration/wiki/Spring-Integration-4.0-to-4.1-Migration-Guide[Migration Guide] for important changes that might affect your applications.
You can find migration guides for all versions back to 2.1 on the https://github.com/spring-projects/spring-integration/wiki[wiki].
[[new-components]]
== New Components
Version 4.1 added a number of new components.
[[x4.1-promise-gateway]]
=== Promise<?> Gateway
The messaging gateway methods now support a Reactor `Promise` return type.
See xref:jms.adoc#jms-async-gateway[Asynchronous Gateway].
[[x4.1-web-socket-adapters]]
=== WebSocket support
The `WebSocket` module is now available.
It is fully based on the Spring WebSocket and Spring Messaging modules and provides an `<inbound-channel-adapter>` and an `<outbound-channel-adapter>`.
See xref:web-sockets.adoc[WebSockets Support] for more information.
[[x4.1-scatter-gather]]
=== Scatter-Gather Enterprise Integration Pattern
We implemented the scatter-gather enterprise integration pattern.
See xref:scatter-gather.adoc[Scatter-Gather] for more information.
[[x4.1-Routing-Slip]]
=== Routing Slip Pattern
We added the routing slip EIP pattern implementation.
See xref:router/routing-slip.adoc[Routing Slip] for more information.
[[x4.1-idempotent-receiver]]
=== Idempotent Receiver Pattern
We added the idempotent receiver enterprise integration pattern implementation by adding the `<idempotent-receiver>` component in XML or the `IdempotentReceiverInterceptor` and `IdempotentReceiver` annotations for Java configuration.
See xref:handler-advice/idempotent-receiver.adoc[Idempotent Receiver Enterprise Integration Pattern] and the https://docs.spring.io/spring-integration/api/index.html[Javadoc] for more information.
[[x4.1-BoonJsonObjectMapper]]
=== Boon `JsonObjectMapper`
We added the Boon `JsonObjectMapper` for the JSON transformers.
See xref:transformer.adoc[Transformer] for more information.
[[x4.1-redis-queue-gateways]]
=== Redis Queue Gateways
We added the `<redis-queue-inbound-gateway>` and `<redis-queue-outbound-gateway>` components.
See xref:redis.adoc#redis-queue-inbound-gateway[Redis Queue Inbound Gateway] and xref:redis.adoc#redis-queue-outbound-gateway[Redis Queue Outbound Gateway].
[[x4.1-PollSkipAdvice]]
=== `PollSkipAdvice`
We added the `PollSkipAdvice`, which you can use within the `<advice-chain>` of the `<poller>` to determine if the current poll should be suppressed (skipped) by some condition that you implement with `PollSkipStrategy`.
See xref:polling-consumer.adoc#polling-consumer[Poller] for more information.
[[x4.1-general]]
== General Changes
This section describes general changes from version 4.0 to version 4.1.
[[x4.1-amqp-inbound-missing-queues]]
=== AMQP Inbound Endpoints, Channel
Elements that use a message listener container (inbound endpoints and channel) now support the `missing-queues-fatal` attribute.
See xref:amqp.adoc[AMQP Support] for more information.
[[x4.1-amqp-outbound-lazy-connect]]
=== AMQP Outbound Endpoints
The AMQP outbound endpoints support a new property called `lazy-connect` (default: `true`).
When `true`, the connection to the broker is not established until the first message arrives (assuming there are no inbound endpoints, which always try to establish the connection during startup).
When set to `false`, an attempt to establish the connection is made during application startup.
See xref:amqp.adoc[AMQP Support] for more information.
[[x4.1-sms-copy-on-get]]
=== SimpleMessageStore
The `SimpleMessageStore` no longer makes a copy of the group when calling `getMessageGroup()`.
See xref:message-store.adoc#sms-caution[[WARNING]] for more information.
[[x4.1-ws-encode-uri]]
=== Web Service Outbound Gateway: `encode-uri`
The `<ws:outbound-gateway/>` now provides an `encode-uri` attribute to allow disabling the encoding of the URI object before sending the request.
[[x4.1-http-status-code]]
=== Http Inbound Channel Adapter and Status Code
The `<http:inbound-channel-adapter>` can now be configured with a `status-code-expression` to override the default `200 OK` status.
See xref:http/namespace.adoc[HTTP Namespace Support] for more information.
[[x4.1-mqtt]]
=== MQTT Adapter Changes
You can now configure the MQTT channel adapters to connect to multiple servers -- for example, to support High Availability (HA).
See xref:mqtt.adoc[MQTT Support] for more information.
The MQTT message-driven channel adapter now supports specifying the QoS setting for each subscription.
See xref:mqtt.adoc#mqtt-inbound[Inbound (Message-driven) Channel Adapter] for more information.
The MQTT outbound channel adapter now supports asynchronous sends, avoiding blocking until delivery is confirmed.
See xref:mqtt.adoc#mqtt-outbound[Outbound Channel Adapter] for more information.
It is now possible to programmatically subscribe to and unsubscribe from topics at runtime.
See xref:mqtt.adoc#mqtt-inbound[Inbound (Message-driven) Channel Adapter] for more information.
[[x4.1-sftp]]
=== FTP and SFTP Adapter Changes
The FTP and SFTP outbound channel adapters now support appending to remote files and taking specific actions when a remote file already exists.
The remote file templates now also supports this, as well as `rmdir()` and `exists()`.
In addition, the remote file templates provide access to the underlying client object, enabling access to low-level APIs.
See xref:ftp.adoc[FTP/FTPS Adapters] and xref:sftp.adoc[SFTP Adapters] for more information.
[[x4.1-splitter-iterator]]
=== Splitter and Iterator
`Splitter` components now support an `Iterator` as the result object for producing output messages.
See xref:splitter.adoc[Splitter] for more information.
[[x4.1-aggregator]]
=== Aggregator
`Aggregator` instancess now support a new attribute `expire-groups-upon-timeout`.
See xref:overview.adoc#overview-endpoints-aggregator[Aggregator] for more information.
[[x4.1-content-enricher-improvement]]
=== Content Enricher Improvements
We added a `null-result-expression` attribute, which is evaluated and returned if `<enricher>` returns `null`.
You can add it in `<header>` and `<property>`.
See xref:content-enrichment.adoc[Content Enricher] for more information.
We added an `error-channel` attribute, which is used to handle an error flow if an `Exception` occurs downstream of the `request-channel`.
This lets you return an alternative object to use for enrichment.
See xref:content-enrichment.adoc[Content Enricher] for more information.
[[x4.1-header-channel-registry]]
=== Header Channel Registry
The `<header-enricher/>` element's `<header-channels-to-string/>` child element can now override the header channel registry's default time for retaining channel mappings.
See xref:content-enrichment.adoc#header-channel-registry[Header Channel Registry] for more information.
[[x4.1-orderly-shutdown]]
=== Orderly Shutdown
We made improvements to the orderly shutdown algorithm.
See xref:shutdown.adoc[Orderly Shutdown] for more information.
[[x4.1-recipientListRouter]]
=== Management for `RecipientListRouter`
The `RecipientListRouter` now provides several management operations to configure recipients at runtime.
With that, you can now configure the `<recipient-list-router>` without any `<recipient>` from the start.
See xref:router/implementations.adoc#recipient-list-router-management[`RecipientListRouterManagement`] for more information.
[[x4.1-AbstractHeaderMapper-changes]]
=== AbstractHeaderMapper: NON_STANDARD_HEADERS token
The `AbstractHeaderMapper` implementation now provides the additional `NON_STANDARD_HEADERS` token to map any user-defined headers, which are not mapped by default.
See xref:amqp/message-headers.adoc[AMQP Message Headers] for more information.
[[x4.1-amqp-channels]]
=== AMQP Channels: `template-channel-transacted`
We introduced the `template-channel-transacted` attribute for AMQP `MessageChannel` instances.
See xref:amqp/channels.adoc[AMQP-backed Message Channels] for more information.
[[x4.1-syslog]]
=== Syslog Adapter
The default syslog message converter now has an option to retain the original message in the payload while still setting the headers.
See xref:syslog.adoc#syslog-inbound-adapter[Syslog Inbound Channel Adapter] for more information.
[[x4.1-async-gateway]]
=== Asynchronous Gateway
In addition to the `Promise` return type xref:changes-4.0-4.1.adoc#x4.1-promise-gateway[mentioned earlier], gateway methods may now return a `ListenableFuture`, introduced in Spring Framework 4.0.
You can also disable asynchronous processing in the gateway, letting a downstream flow directly return a `Future`.
See xref:jms.adoc#jms-async-gateway[Asynchronous Gateway].
[[x4.1-aggregator-advice-chain]]
=== Aggregator Advice Chain
`Aggregator` and `Resequencer` now support `<expire-advice-chain/>` and `<expire-transactional/>` child elements to advise the `forceComplete` operation.
See xref:aggregator.adoc#aggregator-xml[Configuring an Aggregator with XML] for more information.
[[x4.1-script-outbound-channel-adapter]]
=== Outbound Channel Adapter and Scripts
The `<int:outbound-channel-adapter/>` now supports the `<script/>` child element.
The underlying script must have a `void` return type or return `null`.
See xref:groovy.adoc[Groovy support] and xref:scripting.adoc[Scripting Support].
[[x4.1-reseq]]
=== Resequencer Changes
When a message group in a resequencer times out (using `group-timeout` or a `MessageGroupStoreReaper`), late arriving messages are now, by default, discarded immediately.
See xref:resequencer.adoc[Resequencer].
[[x4.1-Optional-Parameter]]
=== Optional POJO method parameter
Spring Integration now consistently handles the Java 8's `Optional` type.
See xref:service-activator.adoc#service-activator-namespace[Configuring Service Activator].
[[x4.1-queue-channel-queue.typ]]
=== `QueueChannel` backed Queue type
The `QueueChannel` backed `Queue type` has been changed from `BlockingQueue` to the more generic `Queue`.
This change allows the use of any external `Queue` implementation (for example, Reactor's `PersistentQueue`).
See xref:channel/configuration.adoc#channel-configuration-queuechannel[`QueueChannel` Configuration].
[[x4.1-channel-interceptor]]
=== `ChannelInterceptor` Changes
The `ChannelInterceptor` now supports additional `afterSendCompletion()` and `afterReceiveCompletion()` methods.
See xref:channel/interceptors.adoc[Channel Interceptors].
[[x4.1-mail-peek]]
=== IMAP PEEK
Since version 4.1.1 there is a change of behavior if you explicitly set the `mail.[protocol].peek` JavaMail property to `false` (where `[protocol]` is `imap` or `imaps`).
See xref:changes-4.0-4.1.adoc#x4.1-mail-peek[[IMPORTANT]].

View File

@@ -0,0 +1,408 @@
[[migration-4.1-4.2]]
= Changes between 4.1 and 4.2
See the https://github.com/spring-projects/spring-integration/wiki/Spring-Integration-4.1-to-4.2-Migration-Guide[Migration Guide] for important changes that might affect your applications.
You can find migration guides for all versions back to 2.1 on the https://github.com/spring-projects/spring-integration/wiki[wiki].
[[x4.2-new-components]]
== New Components
Version 4.2 added a number of new components.
[[x4.2-JMX]]
=== Major Management/JMX Rework
We added a new `MetricsFactory` strategy interface.
This change, together with other changes in the JMX and management infrastructure, provides much more control over management configuration and runtime performance.
However, this has some important implications for (some) user environments.
For complete details, see xref:metrics.adoc[Metrics and Management] and xref:jmx.adoc#jmx-42-improvements[JMX Improvements].
[[x4.2-mongodb-metadata-store]]
=== MongoDB Metadata Store
The `MongoDbMetadataStore` is now available.
For more information, see xref:mongodb.adoc#mongodb-metadata-store[MongoDB Metadata Store].
[[x4.2-secured-channel-annotation]]
=== SecuredChannel Annotation
We introduced the `@SecuredChannel` annotation, replacing the deprecated `ChannelSecurityInterceptorFactoryBean`.
For more information, see xref:security.adoc[Security in Spring Integration].
[[x4.2-security-context-propagation]]
=== `SecurityContext` Propagation
We introduced the `SecurityContextPropagationChannelInterceptor` for the `SecurityContext` propagation from one message flow's thread to another.
For more information, see xref:security.adoc[Security in Spring Integration].
[[x4.2-file-splitter]]
=== FileSplitter
In 4.1.2, we added `FileSplitter`, which splits text files into lines.
It now has full support in the `int-file:` namespace.
See xref:file/splitter.adoc[File Splitter] for more information.
[[x4.2-zk]]
=== Zookeeper Support
We added Zookeeper support to the framework to assist when running on a clustered or multi-host environment.
The change impacts the following features:
* `ZookeeperMetadataStore`
* `ZookeeperLockRegistry`
* Zookeeper Leadership
See xref:zookeeper.adoc[Zookeeper Support] for more information.
[[x4.2-barrier]]
=== Thread Barrier
A new thread `<int:barrier/>` component is available, letting a thread be suspended until some asynchronous event occurs.
See xref:barrier.adoc[Thread Barrier] for more information.
[[x4.2-stomp]]
=== STOMP Support
We added STOMP support to the framework as an inbound and outbound channel adapters pair.
See xref:stomp.adoc[STOMP Support] for more information.
[[x4.2-codec]]
=== Codec
A new `Codec` abstraction has been introduced, to encode and decode objects to and from `byte[]`.
We added an implementation that uses Kryo.
We also added codec-based transformers and message converters.
See xref:codec.adoc[Codec] for more information.
[[x4.2-prepared-statement-setter]]
=== Message PreparedStatement Setter
A new `MessagePreparedStatementSetter` functional interface callback is available for the `JdbcMessageHandler` (`<int-jdbc:outbound-gateway>` and `<int-jdbc:outbound-channel-adapter>`) as an alternative to using `SqlParameterSourceFactory` to populate parameters on the `PreparedStatement` with the `requestMessage` context.
See xref:jdbc/outbound-channel-adapter.adoc[Outbound Channel Adapter] for more information.
[[x4.2-general]]
== General Changes
This section describes general changes from version 4.1 to version 4.2.
[[x4.2-wire-tap]]
=== WireTap
As an alternative to the existing `selector` attribute, the `<wire-tap/>` element now supports the `selector-expression` attribute.
[[x4.2-file-changes]]
=== File Changes
See xref:file.adoc[File Support] for more information about these changes.
[[appending-new-lines]]
==== Appending New Lines
The `<int-file:outbound-channel-adapter>` and `<int-file:outbound-gateway>` now support an `append-new-line` attribute.
If set to `true`, a new line is appended to the file after a message is written.
The default attribute value is `false`.
[[ignoring-hidden-files]]
==== Ignoring Hidden Files
We added the `ignore-hidden` attribute for the `<int-file:inbound-channel-adapter>` to let you set whether to pick up hidden files from the source directory.
It defaults to `true`.
[[writing-inputstream-payloads]]
==== Writing `InputStream` Payloads
The `FileWritingMessageHandler` now also accepts `InputStream` as a valid message payload type.
[[headdirectoryscanner]]
==== `HeadDirectoryScanner`
You can now use the `HeadDirectoryScanner` with other `FileListFilter` implementations.
[[last-modified-filter]]
==== Last Modified Filter
We added the `LastModifiedFileListFilter`.
[[watch-service-directory-scanner]]
==== Watch Service Directory Scanner
We added the `WatchServiceDirectoryScanner`.
[[persistent-file-list-filter-changes]]
==== Persistent File List Filter Changes
The `AbstractPersistentFileListFilter` has a new property (`flushOnUpdate`) which, when set to `true`, calls `flush()` on the metadata store if it implements `Flushable` (for example, `PropertiesPersistingMetadataStore`).
[[x4.2-class-package-change]]
=== Class Package Change
We moved the `ScatterGatherHandler` class from the `org.springframework.integration.handler` to the `org.springframework.integration.scattergather`.
[[tcp-changes]]
=== TCP Changes
This section describes general changes to the Spring Integration TCP functionality.
[[x4.2-tcp-serializers]]
==== TCP Serializers
The TCP `Serializers` no longer `flush()` the `OutputStream`.
This is now done by the `TcpNxxConnection` classes.
If you use the serializers directly within your code, you may have to `flush()` the `OutputStream`.
[[x4.2-tcp-server-exceptions]]
==== Server Socket Exceptions
`TcpConnectionServerExceptionEvent` instances are now published whenever an unexpected exception occurs on a TCP server socket (also added to 4.1.3 and 4.0.7).
See xref:changes-4.1-4.2.adoc#x4.2-tcp-events[TCP Connection Events] for more information.
[[x4.2-tcp-server-port]]
==== TCP Server Port
If you configure a TCP server socket factory to listen on a random port, you can now obtain the actual port chosen by the OS by using `getPort()`.
`getServerSocketAddress()` is also available.
See "xref:ip/tcp-connection-factories.adoc[TCP Connection Factories]" for more information.
[[x4.2-tcp-gw-rto]]
==== TCP Gateway Remote Timeout
The `TcpOutboundGateway` now supports `remote-timeout-expression` as an alternative to the existing `remote-timeout` attribute.
This allows setting the timeout based on each message.
Also, the `remote-timeout` no longer defaults to the same value as `reply-timeout`, which has a completely different meaning.
See xref:ip/endpoint-reference.adoc#tcp-ob-gateway-attributes[.TCP Outbound Gateway Attributes] for more information.
[[x4.2-tcp-ssl]]
==== TCP SSLSession Available for Header Mapping
`TcpConnection` implementations now support `getSslSession()` to let you extract information from the session to add to message headers.
See xref:ip/msg-headers.adoc[IP Message Headers] for more information.
[[x4.2-tcp-events]]
==== TCP Events
New events are now published whenever a correlation exception occurs -- such as sending a message to a non-existent socket.
The `TcpConnectionEventListeningMessageProducer` is deprecated.
Use the generic event adapter instead.
See xref:changes-4.1-4.2.adoc#x4.2-tcp-events[TCP Connection Events] for more information.
[[x4.2-inbound-channel-adapter-annotation]]
=== `@InboundChannelAdapter` Changes
Previously, the `@Poller` on an inbound channel adapter defaulted the `maxMessagesPerPoll` attribute to `-1` (infinity).
This was inconsistent with the XML configuration of `<inbound-channel-adapter/>`, which defaults to `1`.
The annotation now defaults this attribute to `1`.
[[x4.2-api-changes]]
=== API Changes
`o.s.integration.util.FunctionIterator` now requires a `o.s.integration.util.Function` instead of a `reactor.function.Function`.
This was done to remove an unnecessary hard dependency on Reactor.
Any uses of this iterator need to change the import.
Reactor is still supported for functionality such as the `Promise` gateway.
The dependency was removed for those users who do not need it.
[[x4.2-jms-changes]]
=== JMS Changes
This section describes general changes to the Spring Integration TCP functionality.
[[reply-listener-lazy-initialization]]
==== Reply Listener Lazy Initialization
You can now configure the reply listener in JMS outbound gateways to be initialized on-demand and stopped after an idle period, instead of being controlled by the gateway's lifecycle.
See xref:jms.adoc#jms-outbound-gateway[Outbound Gateway] for more information.
[[conversion-errors-in-message-driven-endpoints]]
==== Conversion Errors in Message-Driven Endpoints
The `error-channel` is now used for the conversion errors.
In previous versions, they caused transaction rollback and message redelivery.
See xref:changes-2.2-3.0.adoc#x3.0-jms-mdca-te[Message-driven Channel Adapter] and xref:jms.adoc#jms-inbound-gateway[Inbound Gateway] for more information.
[[default-acknowledge-mode]]
==== Default Acknowledge Mode
When using an implicitly defined `DefaultMessageListenerContainer`, the default `acknowledge` is now `transacted`.
We recommend using `transacted` when using this container, to avoid message loss.
This default now applies to the message-driven inbound adapter and the inbound gateway.
It was already the default for JMS-backed channels.
See xref:changes-2.2-3.0.adoc#x3.0-jms-mdca-te[Message-driven Channel Adapter] and xref:jms.adoc#jms-inbound-gateway[Inbound Gateway] for more information.
[[shared-subscriptions]]
==== Shared Subscriptions
We added Namespace support for shared subscriptions (JMS 2.0) to message-driven endpoints and the `<int-jms:publish-subscribe-channel>`.
Previously, you had to wire up listener containers as `<bean/>` declarations to use shared connections.
See xref:jms.adoc[JMS Support] for more information.
[[x4.2-conditional-pollers]]
=== Conditional Pollers
We now provide much more flexibility for dynamic polling.
See xref:changes-4.1-4.2.adoc#x4.2-conditional-pollers[Conditional Pollers for Message Sources] for more information.
[[x4.2-amqp-changes]]
=== AMQP Changes
This section describes general changes to the Spring Integration AMQP functionality.
[[publisher-confirmations]]
==== Publisher Confirmations
The `<int-amqp:outbound-gateway>` now supports `confirm-correlation-expression`, `confirm-ack-channel`, and `confirm-nack-channel` attributes (which have a purpose similar to that of `<int-amqp:outbound-channel-adapter>`).
[[correlation-data]]
==== Correlation Data
For both the outbound channel adapter and the inbound gateway, if the correlation data is a `Message<?>`, it becomes the basis of the message on the ack or nack channel, with the additional header(s) added.
Previously, any correlation data (including `Message<?>`) was returned as the payload of the ack or nack message.
[[inbound-gateway-properties]]
==== Inbound Gateway Properties
The `<int-amqp:inbound-gateway>` now exposes the `amqp-template` attribute to allow more control over an external bean for the reply `RabbitTemplate`.
You can also provide your own `AmqpTemplate` implementation.
In addition, you can use `default-reply-to` if the request message does not have a `replyTo` property.
See xref:amqp.adoc[AMQP Support] for more information.
[[x4.2-xpath-splitter]]
=== XPath Splitter Improvements
The `XPathMessageSplitter` (`<int-xml:xpath-splitter>`) now allows the configuration of `output-properties` for the internal `javax.xml.transform.Transformer` and supports an `Iterator` mode (defaults to `true`) for the XPath evaluation `org.w3c.dom.NodeList` result.
See xref:xml/xpath-splitting.adoc[Splitting XML Messages] for more information.
[[x4.2-http-changes]]
=== HTTP Changes
This section describes general changes to the Spring Integration HTTP functionality.
[[cors]]
==== CORS
The HTTP inbound endpoints (`<int-http:inbound-channel-adapter>` and `<int-http:inbound-gateway>`) now allow the
configuration of Cross-origin Resource Sharing (CORS).
See xref:http/namespace.adoc#http-cors[Cross-origin Resource Sharing (CORS) Support] for more information.
[[inbound-gateway-timeout]]
==== Inbound Gateway Timeout
You can configure the HTTP inbound gate way to return a status code that you specify when a request times out.
The default is now `500 Internal Server Error` instead of `200 OK`.
See xref:http/namespace.adoc#http-response-statuscode[Response Status Code] for more information.
[[form-data]]
==== Form Data
We added documentation for proxying `multipart/form-data` requests.
See xref:http.adoc[HTTP Support] for more information.
[[x4.2-gw]]
=== Gateway Changes
This section describes general changes to the Spring Integration Gateway functionality.
[[gateway-methods-can-return-completablefuture<?>]]
==== Gateway Methods can Return `CompletableFuture<?>`
When using Java 8, gateway methods can now return `CompletableFuture<?>`.
See xref:gateway.adoc#gw-completable-future[`CompletableFuture`] for more information.
[[messaginggateway-annotation]]
==== MessagingGateway Annotation
The request and reply timeout properties are now `String` instead of `Long` to allow configuration with property placeholders or SpEL.
See xref:gateway.adoc#messaging-gateway-annotation[`@MessagingGateway` Annotation].
[[x4.2-aggregator-changes]]
=== Aggregator Changes
This section describes general changes to the Spring Integration aggregator functionality.
[[aggregator-performance]]
==== Aggregator Performance
This release includes some performance improvements for aggregating components (aggregator, resequencer, and others), by more efficiently removing messages from groups when they are released.
New methods (`removeMessagesFromGroup`) have been added to the message store.
Set the `removeBatchSize` property (default: `100`) to adjust the number of messages deleted in each operation.
Currently, the JDBC, Redis, and MongoDB message stores support this property.
[[output-message-group-processor]]
==== Output Message Group Processor
When using a `ref` or inner bean for the aggregator, you can now directly bind a `MessageGroupProcessor`.
In addition, we added a `SimpleMessageGroupProcessor` that returns the collection of messages in the group.
When an output processor produces a collection of `Message<?>`, the aggregator releases those messages individually.
Configuring the `SimpleMessageGroupProcessor` makes the aggregator a message barrier, where messages are held up until they all arrive and are then released individually.
See xref:overview.adoc#overview-endpoints-aggregator[Aggregator] for more information.
[[ftp-and-sftp-changes]]
=== FTP and SFTP Changes
This section describes general changes to the Spring Integration FTP and SFTP functionality.
[[inbound-channel-adapters]]
==== Inbound Channel Adapters
You can now specify a `remote-directory-expression` on the inbound channel adapters, to determine the directory at runtime.
See xref:ftp.adoc[FTP/FTPS Adapters] and xref:sftp.adoc[SFTP Adapters] for more information.
[[gateway-partial-results]]
==== Gateway Partial Results
When you use FTP or SFTP outbound gateways to operate on multiple files (with `mget` and `mput`), an exception can
occur after part of the request is completed.
If such a condition occurs, a `PartialSuccessException` that contains the partial results is thrown.
See xref:ftp/outbound-gateway.adoc[FTP Outbound Gateway] and xref:sftp/outbound-gateway.adoc[SFTP Outbound Gateway] for more information.
[[delegating-session-factory]]
==== Delegating Session Factory
We added a delegating session factory, enabling the selection of a particular session factory based on some thread context value.
See xref:ftp/dsf.adoc[Delegating Session Factory] and xref:sftp/dsf.adoc[Delegating Session Factory] for more information.
[[default-sftp-session-factory]]
==== Default Sftp Session Factory
Previously, the `DefaultSftpSessionFactory` unconditionally allowed connections to unknown hosts.
This is now configurable (default: `false`).
The factory now requires a configured `knownHosts`, file unless the `allowUnknownKeys` property is `true` (default: `false`).
See xref:sftp/session-factory.adoc#sftp-unk-keys[`allowUnknownKeys`::Set to `true` to allow connections to hosts with unknown (or changed) keys.] for more information.
[[message-session-callback]]
==== Message Session Callback
We introduced the `MessageSessionCallback<F, T>` to perform any custom `Session` operations with the `requestMessage` context in the `<int-(s)ftp:outbound-gateway/>`.
See xref:ftp/session-callback.adoc[Using `MessageSessionCallback`] and xref:sftp/session-callback.adoc[MessageSessionCallback] for more information.
[[websocket-changes]]
=== Websocket Changes
We added `WebSocketHandlerDecoratorFactory` support to the `ServerWebSocketContainer` to allow chained customization for the internal `WebSocketHandler`.
See xref:web-sockets.adoc#web-sockets-namespace[WebSockets Namespace Support] for more information.
[[application-event-adapters-changes]]
=== Application Event Adapters changes
The `ApplicationEvent` adapters can now operate with `payload` as an `event` to directly allow omitting custom `ApplicationEvent` extensions.
For this purpose, we introduced the `publish-payload` boolean attribute has been introduced on the `<int-event:outbound-channel-adapter>`.
See xref:event.adoc[Spring `ApplicationEvent` Support] for more information.

View File

@@ -0,0 +1,360 @@
[[migration-4.2-4.3]]
= Changes between 4.2 and 4.3
See the https://github.com/spring-projects/spring-integration/wiki/Spring-Integration-4.2-to-4.3-Migration-Guide[Migration Guide]
for important changes that might affect your applications.
You can find migration guides for all versions back to 2.1 on the https://github.com/spring-projects/spring-integration/wiki[Wiki].
[[x4.3-new-components]]
== New Components
Version 4.3 added a number of new components.
[[amqp-async-outbound-gateway]]
=== AMQP Async Outbound Gateway
See xref:amqp.adoc#amqp-async-outbound-gateway[Asynchronous Outbound Gateway].
[[messagegroupfactory]]
=== `MessageGroupFactory`
We introduced the `MessageGroupFactory` strategy to allow control over `MessageGroup` instances in `MessageGroupStore` logic.
We added `SimpleMessageGroupFactory` implementation for the `SimpleMessageGroup`, with the `GroupType.HASH_SET` as the default
factory for the standard `MessageGroupStore` implementations.
See xref:message-store.adoc[Message Store] for more information.
[[persistentmessagegroup]]
=== `PersistentMessageGroup`
We added the `PersistentMessageGroup` (lazy-load proxy) implementation for persistent `MessageGroupStore` instances,
which return this instance for the `getMessageGroup()` when their `lazyLoadMessageGroups` is `true` (the default).
See xref:message-store.adoc[Message Store] for more information.
[[ftp-and-sftp-streaming-inbound-channel-adapters]]
=== FTP and SFTP Streaming Inbound Channel Adapters
We added inbound channel adapters that return an `InputStream` for each file, letting you retrieve remote files without writing them to the local file system.
See xref:ftp/streaming.adoc[FTP Streaming Inbound Channel Adapter] and xref:sftp/streaming.adoc[SFTP Streaming Inbound Channel Adapter] for more information.
[[streamtransformer]]
=== `StreamTransformer`
We added `StreamTransformer` to transform an `InputStream` payload to either a `byte[]` or a `String`.
See xref:transformer.adoc#stream-transformer[Stream Transformer] for more information.
[[integration-graph]]
=== Integration Graph
We added `IntegrationGraphServer`, together with the `IntegrationGraphController` REST service, to expose the runtime model of a Spring Integration application as a graph.
See xref:graph.adoc#integration-graph[Integration Graph] for more information.
[[jdbc-lock-registry]]
=== JDBC Lock Registry
We added `JdbcLockRegistry` for distributed locks shared through a database table.
See xref:jdbc.adoc#jdbc-lock-registry[JDBC Lock Registry] for more information.
[[leaderinitiator-for-lockregistry]]
=== `LeaderInitiator` for `LockRegistry`
We added `LeaderInitiator` implementation based on the `LockRegistry` strategy.
See xref:endpoint.adoc#leadership-event-handling[Leadership Event Handling] for more information.
[[x4.3-general]]
== General Changes
This section describes general changes that version 4.3 brought to Spring Integration.
[[core-changes]]
=== Core Changes
This section describes general changes to the core of Spring Integration.
[[outbound-gateway-within-a-chain]]
==== Outbound Gateway within a Chain
Previously, you could specify a `reply-channel` on an outbound gateway within a chain.
It was completely ignored.
The gateway's reply goes to the next chain element or, if the gateway is the last element, to the chain's output channel.
This condition is now detected and disallowed.
If you have such a configuration, remove the `reply-channel`.
[[asynchronous-service-activator]]
==== Asynchronous Service Activator
We added an option to make the service activator be synchronous.
See xref:service-activator.adoc#async-service-activator[Asynchronous Service Activator] for more information.
[[messaging-annotation-support-changes]]
==== Messaging Annotation Support changes
The messaging annotation support does not require a `@MessageEndpoint` (or any other `@Component`) annotation declaration on the class level.
To restore the previous behavior, set the `spring.integration.messagingAnnotations.require.componentAnnotation` of
`spring.integration.properties` to `true`.
See xref:configuration/global-properties.adoc[Global Properties] and xref:configuration/annotations.adoc[Annotation Support] for more information.
[[mail-changes]]
=== Mail Changes
This section describes general changes to the Spring Integration Mail functionality.
[[customizable-user-flag]]
==== Customizable User Flag
The customizable `userFlag` (added in 4.2.2 to provide customization of the flag used to denote that the mail has been
seen) is now available in the XML namespace.
See xref:mail.adoc#imap-seen[Marking IMAP Messages When `Recent` Is Not Supported] for more information.
[[mail-message-mapping]]
==== Mail Message Mapping
You can now map inbound mail messages with the `MessageHeaders` containing the mail headers and the payload containing the email content.
Previously, the payload was always the raw `MimeMessage`.
See xref:mail.adoc#mail-mapping[Inbound Mail Message Mapping] for more information.
[[jms-changes]]
=== JMS Changes
This section describes general changes to the Spring Integration JMS functionality.
[[header-mapper]]
==== Header Mapper
The `DefaultJmsHeaderMapper` now maps the standard `correlationId` header as a message property by invoking its `toString()` method.
See xref:changes-3.0-4.0.adoc#x4.0-jms-header-mapping[Mapping Message Headers to and from JMS Message] for more information.
[[asynchronous-gateway]]
==== Asynchronous Gateway
The JMS outbound gateway now has an `async` property.
See xref:jms.adoc#jms-async-gateway[Async Gateway] for more information.
[[aggregator-changes]]
=== Aggregator Changes
There is a change in behavior when a POJO aggregator releases a collection of `Message<?>` objects.
This is rare, but, if your application does that, you need to make a small change to your POJO.
See this xref:aggregator.adoc#agg-message-collection[IMPORTANT: The `SimpleMessageGroup.getMessages()` method returns an `unmodifiableCollection`.] note for more information.
[[tcp/udp-changes]]
=== TCP/UDP Changes
This section describes general changes to the Spring Integration TCP/UDP functionality.
[[events]]
==== Events
A new `TcpConnectionServerListeningEvent` is emitted when a server connection factory is started.
See xref:changes-4.1-4.2.adoc#x4.2-tcp-events[TCP Connection Events] for more information.
You can now use the `destination-expression` and `socket-expression` attributes on `<int-ip:udp-outbound-channel-adapter>`.
See xref:ip/udp-adapters.adoc[UDP Adapters] for more information.
[[stream-deserializers]]
==== Stream Deserializers
The various deserializers that cannot allocate the final buffer until the whole message has been assembled now support pooling the raw buffer into which the data is received rather than creating and discarding a buffer for each message.
See xref:ip/tcp-connection-factories.adoc[TCP Connection Factories] for more information.
[[tcp-message-mapper]]
==== TCP Message Mapper
The message mapper now, optionally, sets a configured content type header.
See xref:ip/msg-headers.adoc[IP Message Headers] for more information.
[[file-changes]]
=== File Changes
This section describes general changes to the Spring Integration File functionality.
[[destination-directory-creation]]
==== Destination Directory Creation
The generated file name for the `FileWritingMessageHandler` can represent a sub-path to save the desired directory structure for a file in the target directory.
See xref:file/writing.adoc#file-writing-file-names[Generating File Names] for more information.
The `FileReadingMessageSource` now hides the `WatchService` directory scanning logic in the inner class.
We added the `use-watch-service` and `watch-events` options to enable this behavior.
We deprecated the top-level `WatchServiceDirectoryScanner` because of inconsistency around the API.
See xref:file.adoc#watch-service-directory-scanner[`WatchServiceDirectoryScanner`] for more information.
[[buffer-size]]
==== Buffer Size
When writing files, you can now specify the buffer size.
[[appending-and-flushing]]
==== Appending and Flushing
You can now avoid flushing files when appending and use a number of strategies to flush the data during idle periods.
See xref:file/writing.adoc#file-flushing[Flushing Files When Using `APPEND_NO_FLUSH`] for more information.
[[preserving-timestamps]]
==== Preserving Timestamps
You can now configure the outbound channel adapter to set the destination file's `lastmodified` timestamp.
See xref:file/writing.adoc#file-timestamps[File Timestamps] for more information.
[[splitter-changes]]
==== Splitter Changes
The `FileSplitter` now automatically closes an FTP or SFTP session when the file is completely read.
This applies when the outbound gateway returns an `InputStream` or when you use the new FTP or SFTP streaming channel adapters.
We also introduced a new `markers-json` option to convert `FileSplitter.FileMarker` to JSON `String` for relaxed downstream network interaction.
See xref:file/splitter.adoc[File Splitter] for more information.
[[file-filters]]
==== File Filters
We added `ChainFileListFilter` as an alternative to `CompositeFileListFilter`.
See xref:file/reading.adoc[Reading Files] for more information.
[[amqp-changes]]
=== AMQP Changes
This section describes general changes to the Spring Integration AMQP functionality.
[[content-type-message-converter]]
==== Content Type Message Converter
The outbound endpoints now support a `RabbitTemplate` configured with a `ContentTypeDelegatingMessageConverter` such
that you can choose the converter based on the message content type.
See xref:amqp/content-type-conversion-outbound.adoc[Outbound Message Conversion] for more information.
[[headers-for-delayed-message-handling]]
==== Headers for Delayed Message Handling
Spring AMQP 1.6 adds support for https://www.rabbitmq.com/blog/2015/04/16/scheduling-messages-with-rabbitmq/[delayed message exchanges].
Header mapping now supports the headers (`amqp_delay` and `amqp_receivedDelay`) used by this feature.
[[amqp-backed-channels]]
==== AMQP-Backed Channels
AMQP-backed channels now support message mapping.
See xref:amqp/channels.adoc[AMQP-backed Message Channels] for more information.
[[redis-changes]]
=== Redis Changes
This section describes general changes to the Spring Integration Redis functionality.
[[list-push/pop-direction]]
==== List Push/Pop Direction
Previously, the queue channel adapters always used the Redis list in a fixed direction, pushing to the left end and reading from the right end.
You can now configure the reading and writing direction with the `rightPop` and `leftPush` options for the
`RedisQueueMessageDrivenEndpoint` and `RedisQueueOutboundChannelAdapter`, respectively.
See xref:redis.adoc#redis-queue-inbound-channel-adapter[Redis Queue Inbound Channel Adapter] and xref:redis.adoc#redis-queue-outbound-channel-adapter[Redis Queue Outbound Channel Adapter] for more information.
[[queue-inbound-gateway-default-serializer]]
==== Queue Inbound Gateway Default Serializer
The default serializer in the inbound gateway has been changed to a `JdkSerializationRedisSerializer` for compatibility with the outbound gateway.
See xref:redis.adoc#redis-queue-inbound-gateway[Redis Queue Inbound Gateway] for more information.
[[http-changes]]
=== HTTP Changes
Previously, with requests that had a body (such as `POST`) that had no `content-type` header, the body was ignored.
With this release, the content type of such requests is considered to be `application/octet-stream` as recommended
by RFC 2616.
See xref:http/inbound.adoc[Http Inbound Components] for more information.
`uriVariablesExpression` now uses a `SimpleEvaluationContext` by default (since 4.3.15).
See xref:http/namespace.adoc#mapping-uri-variables[Mapping URI Variables] for more information.
[[sftp-changes]]
=== SFTP Changes
This section describes general changes to the Spring Integration SFTP functionality.
[[factory-bean]]
==== Factory Bean
We added a new factory bean to simplify the configuration of Jsch proxies for SFTP.
See `JschProxyFactoryBean` for more information.
[[chmod-changes]]
==== `chmod` Changes
The SFTP outbound gateway (for `put` and `mput` commands) and the SFTP outbound channel adapter now support the `chmod` attribute to change the remote file permissions after uploading.
See `xref:sftp/outbound.adoc[SFTP Outbound Channel Adapter]` and `xref:sftp/outbound-gateway.adoc[SFTP Outbound Gateway]` for more information.
[[ftp-changes]]
=== FTP Changes
This section describes general changes to the Spring Integration FTP functionality.
[[session-changes]]
==== Session Changes
The `FtpSession` now supports `null` for the `list()` and `listNames()` methods, since underlying FTP Client can use it.
With that, you can now configure the `FtpOutboundGateway` without the `remoteDirectory` expression.
You can also configure the `<int-ftp:inbound-channel-adapter>` without `remote-directory` or `remote-directory-expression`.
See xref:ftp.adoc[FTP/FTPS Adapters] for more information.
[[router-changes]]
=== Router Changes
The `ErrorMessageExceptionTypeRouter` now supports the `Exception` superclass mappings to avoid duplication for the same channel in case of multiple inheritors.
For this purpose, the `ErrorMessageExceptionTypeRouter` loads mapping classes during initialization to fail-fast for a `ClassNotFoundException`.
See xref:router.adoc[Routers] for more information.
[[header-mapping]]
=== Header Mapping
This section describes the changes to header mapping between version 4.2 and 4.3.
[[general]]
==== General
AMQP, WS, and XMPP header mappings (such as `request-header-mapping` and `reply-header-mapping`) now support negated patterns.
See xref:amqp/message-headers.adoc[AMQP Message Headers], xref:ws.adoc#ws-message-headers[WS Message Headers], and xref:xmpp.adoc#xmpp-message-headers[XMPP Message Headers] for more information.
[[amqp-header-mapping]]
==== AMQP Header Mapping
Previously, only standard AMQP headers were mapped by default.
You had to explicitly enable mapping of user-defined headers.
With this release, all headers are mapped by default.
In addition, the inbound `amqp_deliveryMode` header is no longer mapped by default.
See xref:amqp/message-headers.adoc[AMQP Message Headers] for more information.
[[groovy-scripts]]
=== Groovy Scripts
You can now configure groovy scripts with the `compile-static` hint or any other `CompilerConfiguration` options.
See xref:groovy.adoc#groovy-config[Groovy Configuration] for more information.
[[inboundchanneladapter-changes]]
=== `@InboundChannelAdapter` Changes
The `@InboundChannelAdapter` now has an alias `channel` attribute for the regular `value`.
In addition, the target `SourcePollingChannelAdapter` components can now resolve the target `outputChannel` bean from its provided name (`outputChannelName` options) in a late-binding manner.
See xref:configuration/annotations.adoc[Annotation Support] for more information.
[[xmpp-changes]]
=== XMPP Changes
The XMPP channel adapters now support the XMPP Extensions (XEP).
See xref:xmpp.adoc#xmpp-extensions[XMPP Extensions] for more information.
[[wiretap-late-binding]]
=== WireTap Late Binding
The `WireTap` `ChannelInterceptor` now can accept a `channelName` that is resolved to the target `MessageChannel`
later, during the first active interceptor operation.
See xref:channel/configuration.adoc#channel-wiretap[Wire Tap] for more information.
[[channelmessagestorequeryprovider-changes]]
=== `ChannelMessageStoreQueryProvider` Changes
The `ChannelMessageStoreQueryProvider` now supports H2 databases.
See xref:jdbc/message-store.adoc#jdbc-message-store-channels[Backing Message Channels] for more information.
[[websocket-changes]]
=== WebSocket Changes
The `ServerWebSocketContainer` now exposes an `allowedOrigins` option, and `SockJsServiceOptions` exposes a `suppressCors` option.
See xref:web-sockets.adoc[WebSockets Support] for more information.

View File

@@ -0,0 +1,325 @@
[[migration-4.3-5.0]]
= Changes between 4.3 and 5.0
See the https://github.com/spring-projects/spring-integration/wiki/Spring-Integration-4.3-to-5.0-Migration-Guide[Migration Guide] for important changes that might affect your applications.
You can find migration guides for all versions back to 2.1 on the https://github.com/spring-projects/spring-integration/wiki[wiki].
[[x5.0-new-components]]
== New Components
Version 5.0 added a number of new components.
[[java-dsl]]
=== Java DSL
The separate https://github.com/spring-projects/spring-integration-java-dsl[Spring Integration Java DSL] project has now been merged into the core Spring Integration project.
The `IntegrationComponentSpec` implementations for channel adapters and gateways are distributed to their specific modules.
See xref:dsl.adoc#java-dsl[Java DSL] for more information about Java DSL support.
See also the https://github.com/spring-projects/spring-integration/wiki/Spring-Integration-4.3-to-5.0-Migration-Guide#java-dsl[4.3 to 5.0 Migration Guide] for the required steps to move to Spring Integration 5.0.
[[testing-support]]
=== Testing Support
We created a new Spring Integration Test Framework to help with testing Spring Integration applications.
Now, with the `@SpringIntegrationTest` annotation on test classes and the `MockIntegration` factory, you can make your JUnit tests for integration flows somewhat easier.
See xref:testing.adoc[Testing support] for more information.
[[mongodb-outbound-gateway]]
=== MongoDB Outbound Gateway
The new `MongoDbOutboundGateway` lets you make queries to the database on demand by sending a message to its request channel.
See xref:mongodb.adoc#mongodb-outbound-gateway[MongoDB Outbound Gateway] for more information.
[[webflux-gateways-and-channel-adapters]]
=== WebFlux Gateways and Channel Adapters
We introduced the new WebFlux support module for Spring WebFlux Framework gateways and channel adapters.
See xref:webflux.adoc[WebFlux Support] for more information.
[[content-type-conversion]]
=== Content Type Conversion
Now that we use the new `InvocableHandlerMethod`-based infrastructure for service method invocations, we can perform `contentType` conversion from the payload to a target method argument.
See xref:endpoint.adoc#content-type-conversion[Content Type Conversion] for more information.
[[errormessagepublisher-and-errormessagestrategy]]
=== `ErrorMessagePublisher` and `ErrorMessageStrategy`
We added `ErrorMessagePublisher` and the `ErrorMessageStrategy` for creating `ErrorMessage` instances.
See xref:scatter-gather.adoc#scatter-gather-error-handling[Error Handling] for more information.
[[jdbc-metadata-store]]
=== JDBC Metadata Store
We added a JDBC implementation of the `MetadataStore` implementation.
This is useful when you need to ensure transactional boundaries for metadata.
See xref:jdbc.adoc#jdbc-metadata-store[JDBC Metadata Store] for more information.
[[x5.0-general]]
== General Changes
Spring Integration is now fully based on Spring Framework `5.0` and Project Reactor `3.1`.
Previous Project Reactor versions are no longer supported.
[[core-changes]]
=== Core Changes
The `@Poller` annotation now has the `errorChannel` attribute for easier configuration of the underlying `MessagePublishingErrorHandler`.
See xref:configuration/annotations.adoc[Annotation Support] for more information.
All the request-reply endpoints (based on `AbstractReplyProducingMessageHandler`) can now start transactions and, therefore, make the whole downstream flow transactional.
See xref:handler-advice/tx-handle-message.adoc[Transaction Support] for more information.
The `SmartLifecycleRoleController` now provides methods to obtain status of endpoints in roles.
See xref:endpoint.adoc#endpoint-roles[Endpoint Roles] for more information.
By default, POJO methods are now invoked by using an `InvocableHandlerMethod`, but you can configure them to use SpEL, as before.
See xref:overview.adoc#pojo-invocation[POJO Method invocation] for more information.
When targeting POJO methods as message handlers, you can now mark one of the service methods with the `@Default` annotation to provide a fallback mechanism for non-matched conditions.
See xref:service-activator.adoc#service-activator-namespace[Configuring Service Activator] for more information.
We added a simple `PassThroughTransactionSynchronizationFactory` to always store a polled message in the current transaction context.
That message is used as a `failedMessage` property of the `MessagingException`, which wraps any raw exception thrown during transaction completion.
See xref:transactions.adoc#transaction-synchronization[Transaction Synchronization] for more information.
The aggregator expression-based `ReleaseStrategy` now evaluates the expression against the `MessageGroup` instead of just the collection of `Message<?>`.
See xref:aggregator.adoc#aggregator-spel[Aggregators and Spring Expression Language (SpEL)] for more information.
You can now supply the `ObjectToMapTransformer` with a customized `JsonObjectMapper`.
See xref:aggregator.adoc#aggregator-spel[Aggregators and Spring Expression Language (SpEL)] for more information.
The `@GlobalChannelInterceptor` annotation and `<int:channel-interceptor>` now support negative patterns (via `!` prepending) for component names matching.
See xref:channel/configuration.adoc#global-channel-configuration-interceptors[Global Channel Interceptor Configuration] for more information.
When a candidate failed to acquire the lock, the `LockRegistryLeaderInitiator` now emits a new `OnFailedToAcquireMutexEvent` through `DefaultLeaderEventPublisher`.
See `xref:endpoint.adoc#leadership-event-handling[Leadership Event Handling]` for more information.
[[gateway-changes]]
=== Gateway Changes
When the gateway method has a `void` return type and an error channel is provided, the gateway now correctly sets the `errorChannel` header.
Previously, the header was not populated.
This caused synchronous downstream flows (running on the calling thread) to send the exception to the configured channel, but an exception on an asynchronous downstream flow would be sent to the default `errorChannel` instead.
The `RequestReplyExchanger` interface now has a `throws MessagingException` clause to meet the proposed messages exchange contract.
You can now specify the request and reply timeouts with SpEL expressions.
See xref:gateway.adoc[Messaging Gateways] for more information.
[[aggregator-performance-changes]]
=== Aggregator Performance Changes
By default, aggregators now use a `SimpleSequenceSizeReleaseStrategy`, which is more efficient, especially with large groups.
Empty groups are now scheduled for removal after `empty-group-min-timeout`.
See xref:overview.adoc#overview-endpoints-aggregator[Aggregator] for more information.
[[splitter-changes]]
=== Splitter Changes
The splitter component can now handle and split Java `Stream` and Reactive Streams `Publisher` objects.
If the output channel is a `ReactiveStreamsSubscribableChannel`, the `AbstractMessageSplitter` builds a `Flux` for subsequent iteration instead of a regular `Iterator`, independent of the object being split.
In addition, `AbstractMessageSplitter` provides `protected obtainSizeIfPossible()` methods to allow determination of the size of the `Iterable` and `Iterator` objects, if that is possible.
See xref:splitter.adoc[Splitter] for more information.
[[jms-changes]]
=== JMS Changes
Previously, Spring Integration JMS XML configuration used a default bean name of `connectionFactory` for the JMS connection factory, letting the property be omitted from component definitions.
We renamed it to `jmsConnectionFactory`, which is the bean name used by Spring Boot to auto-configure the JMS connection factory bean.
If your application relies on the previous behavior, you can rename your `connectionFactory` bean to `jmsConnectionFactory` or specifically configure your components to use your bean by using its current name.
See xref:jms.adoc[JMS Support] for more information.
[[mail-changes]]
=== Mail Changes
Some inconsistencies with rendering IMAP mail content have been resolved.
See xref:mail.adoc#imap-format-important[the note in the "`Mail-receiving Channel Adapter`" section] for more information.
[[feed-changes]]
=== Feed Changes
Instead of the `com.rometools.fetcher.FeedFetcher`, which is deprecated in ROME, we introduced a new `Resource` property for the `FeedEntryMessageSource`.
See xref:feed.adoc[Feed Adapter] for more information.
[[file-changes]]
=== File Changes
We introduced the new `FileHeaders.RELATIVE_PATH` message header to represent relative path in `FileReadingMessageSource`.
The tail adapter now supports `idleEventInterval` to emit events when there is no data in the file during that period.
The flush predicates for the `FileWritingMessageHandler` now have an additional parameter.
The file outbound channel adapter and gateway (`FileWritingMessageHandler`) now support the `REPLACE_IF_MODIFIED` `FileExistsMode`.
They also now support setting file permissions on the newly written file.
A new `FileSystemMarkerFilePresentFileListFilter` is now available.
See xref:file/reading.adoc#file-incomplete[Dealing With Incomplete Data] for more information.
The `FileSplitter` now provides a `firstLineAsHeader` option to carry the first line of content as a header in the messages emitted for the remaining lines.
See xref:file.adoc[File Support] for more information.
[[ftp-and-sftp-changes]]
=== FTP and SFTP Changes
The inbound channel adapters now have a property called `max-fetch-size`, which is used to limit the number of files fetched during a poll when no files are currently in the local directory.
By default, they also are configured with a `FileSystemPersistentAcceptOnceFileListFilter` in the `local-filter`.
You can also provide a custom `DirectoryScanner` implementation to inbound channel adapters by setting the newly introduced `scanner` attribute.
You can now configure the regex and pattern filters to always pass directories.
This can be useful when you use recursion in the outbound gateways.
By default, all the inbound channel adapters (streaming and synchronization-based) now use an appropriate `AbstractPersistentAcceptOnceFileListFilter` implementation to prevent duplicate downloads of remote files.
The FTP and SFTP outbound gateways now support the `REPLACE_IF_MODIFIED` `FileExistsMode` when fetching remote files.
The FTP and SFTP streaming inbound channel adapters now add remote file information in a message header.
The FTP and SFTP outbound channel adapters (as well as the `PUT` command for outbound gateways) now support `InputStream` as `payload`, too.
The inbound channel adapters can now build file trees locally by using a newly introduced `RecursiveDirectoryScanner`.
See the `scanner` option in the xref:ftp/inbound.adoc[FTP Inbound Channel Adapter] section for injection.
Also, you can now switch these adapters to the `WatchService` instead.
We added The `NLST` command to the `AbstractRemoteFileOutboundGateway` to perform the list files names remote command.
You can now supply the `FtpOutboundGateway` with `workingDirExpression` to change the FTP client working directory for the current request message.
The `RemoteFileTemplate` is supplied now with the `invoke(OperationsCallback<F, T> action)` to perform several `RemoteFileOperations` calls in the scope of the same, thread-bounded, `Session`.
We added new filters for detecting incomplete remote files.
The `FtpOutboundGateway` and `SftpOutboundGateway` now support an option to remove the remote file after a successful transfer by using the `GET` or `MGET` commands.
See xref:ftp.adoc[FTP/FTPS Adapters] and xref:sftp.adoc[SFTP Adapters] for more information.
[[integration-properties]]
=== Integration Properties
Version 4.3.2 added a new `spring.integration.readOnly.headers` global property to let you customize the list of headers that should not be copied to a newly created `Message` by the `MessageBuilder`.
See xref:configuration/global-properties.adoc[Global Properties] for more information.
[[stream-changes]]
=== Stream Changes
We added a new option on the `CharacterStreamReadingMessageSource` to let it be used to "`pipe`" stdin and publish an application event when the pipe is closed.
See xref:stream.adoc#stream-reading[Reading from Streams] for more information.
[[barrier-changes]]
=== Barrier Changes
The `BarrierMessageHandler` now supports a discard channel to which late-arriving trigger messages are sent.
See xref:barrier.adoc[Thread Barrier] for more information.
[[amqp-changes]]
=== AMQP Changes
The AMQP outbound endpoints now support setting a delay expression when you use the RabbitMQ Delayed Message Exchange plugin.
The inbound endpoints now support the Spring AMQP `DirectMessageListenerContainer`.
Pollable AMQP-backed channels now block the poller thread for the poller's configured `receiveTimeout` (default: one second).
Headers, such as `contentType`, that are added to message properties by the message converter are now used in the final message.
Previously, it depended on the converter type as to which headers and message properties appeared in the final message.
To override the headers set by the converter, set the `headersMappedLast` property to `true`.
See xref:amqp.adoc[AMQP Support] for more information.
[[http-changes]]
=== HTTP Changes
By default, the `DefaultHttpHeaderMapper.userDefinedHeaderPrefix` property is now an empty string instead of `X-`.
See xref:http/header-mapping.adoc[HTTP Header Mappings] for more information.
By default, `uriVariablesExpression` now uses a `SimpleEvaluationContext` (since 5.0.4).
See xref:http/namespace.adoc#mapping-uri-variables[Mapping URI Variables] for more information.
[[mqtt-changes]]
=== MQTT Changes
Inbound messages are now mapped with the `RECEIVED_TOPIC`, `RECEIVED_QOS`, and `RECEIVED_RETAINED` headers to avoid inadvertent propagation to outbound messages when an application relays messages.
The outbound channel adapter now supports expressions for the topic, qos, and retained properties.
The defaults remain the same.
See xref:mqtt.adoc[MQTT Support] for more information.
[[stomp-changes]]
=== STOMP Changes
We changed the STOMP module to use `ReactorNettyTcpStompClient`, based on the Project Reactor `3.1` and `reactor-netty` extension.
We renamed `Reactor2TcpStompSessionManager` to `ReactorNettyTcpStompSessionManager`, according to the `ReactorNettyTcpStompClient` foundation.
See xref:stomp.adoc[STOMP Support] for more information.
[[web-services-changes]]
=== Web Services Changes
You can now supply `WebServiceOutboundGateway` instances with an externally configured `WebServiceTemplate` instances.
`DefaultSoapHeaderMapper` can now map a `javax.xml.transform.Source` user-defined header to a SOAP header element.
Simple WebService inbound and outbound gateways can now deal with the complete `WebServiceMessage` as a `payload`, allowing the manipulation of MTOM attachments.
See xref:ws.adoc[Web Services Support] for more information.
[[redis-changes]]
=== Redis Changes
The `RedisStoreWritingMessageHandler` is supplied now with additional `String`-based setters for SpEL expressions (for convenience with Java configuration).
You can now configure the `zsetIncrementExpression` on the `RedisStoreWritingMessageHandler` as well.
In addition, this property has been changed from `true` to `false` since the `INCR` option on `ZADD` Redis command is optional.
You can now supply the `RedisInboundChannelAdapter` with an `Executor` for executing Redis listener invokers.
In addition, the received messages now contain a `RedisHeaders.MESSAGE_SOURCE` header to indicate the source of the message (topic or pattern).
See xref:redis.adoc[Redis Support] for more information.
[[tcp-changes]]
=== TCP Changes
We added a new `ThreadAffinityClientConnectionFactory` to bind TCP connections to threads.
You can now configure the TCP connection factories to support `PushbackInputStream` instances, letting deserializers "`unread`" (push back) bytes after "`reading ahead`".
We added a `ByteArrayElasticRawDeserializer` without `maxMessageSize` to control and buffer incoming data as needed.
See xref:ip.adoc[TCP and UDP Support] for more information.
[[jdbc-changes]]
=== JDBC Changes
The `JdbcMessageChannelStore` now provides a setter for `ChannelMessageStorePreparedStatementSetter`, letting you customize message insertion in the store.
The `ExpressionEvaluatingSqlParameterSourceFactory` now provides a setter for `sqlParameterTypes`, letting you customize the SQL types of the parameters.
See xref:jdbc.adoc[JDBC Support] for more information.
[[metrics-changes]]
=== Metrics Changes
https://micrometer.io/[Micrometer] application monitoring is now supported (since version 5.0.2).
See xref:metrics.adoc#micrometer-integration[Micrometer Integration] for more information.
IMPORTANT: Changes were made to the Micrometer `Meters` in version 5.0.3 to make them more suitable for use in dimensional systems.
Further changes were made in 5.0.4.
If you use Micrometer, we recommend a minimum of version 5.0.4.
[[endpointid-annotations]]
=== `@EndpointId` Annotations
Introduced in version 5.0.4, this annotation provides control over bean naming when you use Java configuration.
See xref:overview.adoc#endpoint-bean-names[Endpoint Bean Names] for more information.

View File

@@ -0,0 +1,240 @@
[[migration-5.0-5.1]]
= Changes between 5.0 and 5.1
[[x5.1-new-components]]
== New Components
The following components are new in 5.1:
* xref:changes-5.0-5.1.adoc#x5.1-AmqpDedicatedChannelAdvice[`AmqpDedicatedChannelAdvice`]
[[x5.1-AmqpDedicatedChannelAdvice]]
=== `AmqpDedicatedChannelAdvice`
See xref:amqp/strict-ordering.adoc[Strict Message Ordering].
[[x5.1-Functions]]
=== Improved Function Support
The `java.util.function` interfaces now have improved integration support in the Framework components.
Also Kotlin lambdas now can be used for handler and source methods.
See xref:functions-support.adoc[`java.util.function` Interfaces Support].
[[x5.1-LongRunningTest]]
=== `@LongRunningTest`
A JUnit 5 `@LongRunningTest` conditional annotation is provided to check the environment or system properties for the `RUN_LONG_INTEGRATION_TESTS` entry with the value of `true` to determine if test should be run or skipped.
See xref:testing.adoc#test-junit-rules[JUnit Rules and Conditions].
[[x5.1-general]]
== General Changes
The following changes have been made in version 5.1:
* xref:changes-5.0-5.1.adoc#x5.1-java-dsl[Java DSL]
* xref:changes-5.0-5.1.adoc#x5.1-dispatcher-exceptions[Dispatcher Exceptions]
* xref:changes-5.0-5.1.adoc#x5.1-global-channel-interceptors[Global Channel Interceptors]
* xref:changes-5.0-5.1.adoc#x5.1-object-to-json-transformer[`ObjectToJsonTransformer`]
* xref:changes-5.0-5.1.adoc#x5.1-integration-flows-generated-bean-names[Integration Flows: Generated Bean Names]
* xref:changes-5.0-5.1.adoc#x5.1-aggregator[Aggregator Changes]
* xref:changes-5.0-5.1.adoc#x5.1-publisher[@Publisher annotation changes]
* xref:changes-5.0-5.1.adoc#x51.-integration-graph[Integration Graph Customization]
* xref:changes-5.0-5.1.adoc#x51.-global-properties[Integration Global Properties]
* xref:changes-5.0-5.1.adoc#x51.-poller-annotation[The `receiveTimeout` for `@Poller`]
[[x5.1-java-dsl]]
=== Java DSL
The `IntegrationFlowContext` is now an interface and `IntegrationFlowRegistration` is an inner interface of `IntegrationFlowContext`.
A new `logAndReply()` operator has been introduced for convenience when you wish to log at the end of a flow for request-reply configurations.
This avoids confusion with `log()` which is treated as a one-way end flow component.
A generated bean name for any `NamedComponent` within an integration flow is now based on the component type for better readability from visual tools, logs analyzers and metrics collectors.
The `GenericHandler.handle()` now excepts a `MessageHeaders` type for the second argument.
[[x5.1-dispatcher-exceptions]]
=== Dispatcher Exceptions
Exceptions caught and re-thrown by `AbstractDispatcher` are now more consistent:
* A `MessagingException` of any kind that has a `failedMessage` property is re-thrown unchanged.
* All other exceptions are wrapped in a `MessageDeliveryException` with the `failedMessage` property set.
Previously:
* A `MessagingException` of any kind that has a `failedMessage` property was re-thrown unchanged
* A `MessagingException` that had no `failedMessage` property was wrapped in a `MessagingException` with the `failedMessage` property set.
* Other `RuntimeException` instances were re-thrown unchanged.
* Checked exceptions were wrapped in a `MessageDeliveryException` with the `failedMessage` property set.
[[x5.1-global-channel-interceptors]]
=== Global Channel Interceptors
Global channel interceptors now apply to dynamically registered channels, such as through the `IntegrationFlowContext` when using the Java DSL or beans that are initialized using `beanFactory.initializeBean()`.
Previously, when beans were created after the application context was refreshed, interceptors were not applied.
[[x5.1-channel-interceptors]]
=== Channel Interceptors
`ChannelInterceptor.postReceive()` is no longer called when no message is received; it is no longer necessary to check for a `null` `Message<?>`.
Previously, the method was called.
If you have an interceptor that relies on the previous behavior, implement `afterReceiveCompleted()` instead, since that method is invoked, regardless of whether a message is received or not.
Furthermore, the `PolledAmqpChannel` and `PolledJmsChannel` previously did not invoke `afterReceiveCompleted()` with `null`; they now do.
[[x5.1-object-to-json-transformer]]
=== `ObjectToJsonTransformer`
A new `ResultType.BYTES` mode is introduced for the `ObjectToJsonTransformer`.
See xref:transformer.adoc#json-transformers[JSON Transformers] for more information.
[[x5.1-integration-flows-generated-bean-names]]
=== Integration Flows: Generated Bean Names
Starting with version 5.0.5, generated bean names for the components in an `IntegrationFlow` include the flow bean name, followed by a dot, as a prefix.
For example, if a flow bean were named `flowBean`, a generated bean might be named `flowBean.generatedBean`.
See xref:dsl/java-flows.adoc[Working With Message Flows] for more information.
[[x5.1-aggregator]]
=== Aggregator Changes
If the `groupTimeout` is evaluated to a negative value, an aggregator now expires the group immediately.
Only `null` is considered as a signal to do nothing for the current message.
A new `popSequence` property has been introduced to allow (by default) to call a `MessageBuilder.popSequenceDetails()` for the output message.
Also an `AbstractAggregatingMessageGroupProcessor` returns now an `AbstractIntegrationMessageBuilder` instead of the whole `Message` for optimization.
See xref:overview.adoc#overview-endpoints-aggregator[Aggregator] for more information.
[[x5.1-publisher]]
=== @Publisher annotation changes
Starting with version 5.1, you must explicitly turn on the `@Publisher` AOP functionality by using `@EnablePublisher` or by using the `<int:enable-publisher>` child element on `<int:annotation-config>`.
Also the `proxy-target-class` and `order` attributes have been added for tuning the `ProxyFactory` configuration.
See xref:message-publishing.adoc#publisher-annotation[Annotation-driven Configuration with the `@Publisher` Annotation] for more information.
[[x5.1-files]]
== Files Changes
If you are using `FileExistsMode.APPEND` or `FileExistsMode.APPEND_NO_FLUSH` you can provide a `newFileCallback` that will be called when creating a new file.
This callback receives the newly created file and the message that triggered the callback.
This could be used to write a CSV header, for an example.
The `FileReadingMessageSource` now doesn't check and create a directory until its `start()` is called.
So, if an Inbound Channel Adapter for the `FileReadingMessageSource` has `autoStartup = false`, there are no failures against the file system during application start up.
See xref:file.adoc[File Support] for more information.
[[x5.1-amqp]]
== AMQP Changes
We have made `ID` and `Timestamp` header mapping changes in the `DefaultAmqpHeaderMapper`.
See the note near the bottom of xref:amqp/message-headers.adoc[AMQP Message Headers] for more information.
The `contentType` header is now correctly mapped as an entry in the general headers map.
See xref:amqp/message-headers.adoc#amqp-content-type[contentType Header] for more information.
Starting with version 5.1.3, if a message conversion exception occurs when using manual acknowledgments, and an error channel is defined, the payload is a `ManualAckListenerExecutionFailedException` with additional `channel` and `deliveryTag` properties.
This enables the error flow to ack/nack the original message.
See xref:amqp/conversion-inbound.adoc[Inbound Message Conversion] for more information.
[[x5.1-jdbc]]
== JDBC Changes
A confusing `max-rows-per-poll` property on the JDBC Inbound Channel Adapter and JDBC Outbound Gateway has been deprecated in favor of the newly introduced `max-rows` property.
The `JdbcMessageHandler` supports now a `batchUpdate` functionality when the payload of the request message is an instance of an `Iterable` type.
The indexes for the `INT_CHANNEL_MESSAGE` table (for the `JdbcChannelMessageStore`) have been optimized.
If you have large message groups in such a store, you may wish to alter the indexes.
See xref:jdbc.adoc[JDBC Support] for more information.
[[x5.1-ftp-sftp]]
== FTP and SFTP Changes
A `RotatingServerAdvice` is now available to poll multiple servers and directories with the inbound channel adapters.
See xref:ftp/rotating-server-advice.adoc[Inbound Channel Adapters: Polling Multiple Servers and Directories] and xref:sftp/rotating-server-advice.adoc[Inbound Channel Adapters: Polling Multiple Servers and Directories] for more information.
Also, inbound adapter `localFilenameExpression` instances can contain the `#remoteDirectory` variable, which contains the remote directory being polled.
The generic type of the comparators (used to sort the fetched file list for the streaming adapters) has changed from `Comparator<AbstractFileInfo<F>>` to `Comparator<F>`.
See xref:ftp/streaming.adoc[FTP Streaming Inbound Channel Adapter] and xref:sftp/streaming.adoc[SFTP Streaming Inbound Channel Adapter] for more information.
In addition, the synchronizers for inbound channel adapters can now be provided with a `Comparator`.
This is useful when using `maxFetchSize` to limit the files retrieved.
The `CachingSessionFactory` has a new property `testSession` which, when true, causes the factory to perform a `test()` operation on the `Session` when checking out an existing session from the cache.
See xref:sftp/session-caching.adoc[SFTP Session Caching] and xref:ftp/session-caching.adoc[FTP Session Caching] for more information.
The outbound gateway MPUT command now supports a message payload with a collection of files or strings.
See xref:sftp/outbound-gateway.adoc[SFTP Outbound Gateway] and xref:ftp/outbound-gateway.adoc[FTP Outbound Gateway] for more information.
[[x51.-tcp]]
== TCP Support
When using SSL, host verification is now enabled, by default, to prevent man-in-the-middle attacks with a trusted certificate.
See xref:ip/ssl-tls.adoc#tcp-ssl-host-verification[Host Verification] for more information.
In addition the key and trust store types can now be configured on the `DefaultTcpSSLContextSupport`.
[[x5.1-twitter]]
== Twitter Support
Since the Spring Social project has moved to https://spring.io/blog/2018/07/03/spring-social-end-of-life-announcement[end of life status], Twitter support in Spring Integration has been moved to the Extensions project.
See https://github.com/spring-projects/spring-integration-extensions/tree/main/spring-integration-social-twitter[Spring Integration Social Twitter] for more information.
[[x51.-jms]]
== JMS Support
The `JmsSendingMessageHandler` now provides `deliveryModeExpression` and `timeToLiveExpression` options to determine respective QoS options for JMS message to send at runtime.
The `DefaultJmsHeaderMapper` now allows to map inbound `JMSDeliveryMode` and `JMSExpiration` properties via setting to `true` respective `setMapInboundDeliveryMode()` and `setMapInboundExpiration()` options.
When a `JmsMessageDrivenEndpoint` or `JmsInboundGateway` is stopped, the associated listener container is now shut down; this closes its shared connection and any consumers.
You can configure the endpoints to revert to the previous behavior.
See xref:jms.adoc[JMS Support] for more information.
[[x51.-http]]
== HTTP/WebFlux Support
The `statusCodeExpression` (and `Function`) is now supplied with the `RequestEntity<?>` as a root object for evaluation context, so request headers, method, URI and body are available for target status code calculation.
See xref:http.adoc[HTTP Support] and xref:webflux.adoc[WebFlux Support] for more information.
[[x51.-jmx]]
== JMX Changes
Object name key values are now quoted if they contain any characters other than those allowed in a Java identifier (or period `.`).
For example `org.springframework.integration:type=MessageChannel,` `name="input:foo.myGroup.errors"`.
This has the side effect that previously "allowed" names, with such characters, will now be quoted.
For example `org.springframework.integration:type=MessageChannel,` `name="input#foo.myGroup.errors"`.
[[x51.-micrometer]]
== Micrometer Support Changes
It is now simpler to customize the standard Micrometer meters created by the framework.
See xref:metrics.adoc#micrometer-integration[Micrometer Integration] for more information.
[[x51.-integration-graph]]
== Integration Graph Customization
It is now possible to add additional properties to the `IntegrationNode` s via `Function<NamedComponent, Map<String, Object>> additionalPropertiesCallback` on the `IntegrationGraphServer`.
See xref:graph.adoc#integration-graph[Integration Graph] for more information.
[[x51.-global-properties]]
== Integration Global Properties
The Integration global properties (including defaults) can now be printed in the logs, when a `DEBUG` logic level is turned on for the `org.springframework.integration` category.
See xref:configuration/global-properties.adoc[Global Properties] for more information.
[[x51.-poller-annotation]]
== The `receiveTimeout` for `@Poller`
The `@Poller` annotation now provides a `receiveTimeout` option for convenience.
See xref:configuration/annotations.adoc#configuration-using-poller-annotation[Using the `@Poller` Annotation] for more information.

View File

@@ -0,0 +1,159 @@
[[migration-5.1-5.2]]
= Changes between 5.1 and 5.2
[[x5.2-package-class]]
== Package and Class Changes
`Pausable` has been moved from `o.s.i.endpoint` to `o.s.i.core`.
[[x5.2-behavior]]
== Behavior Changes
See the https://github.com/spring-projects/spring-integration/wiki/Spring-Integration-5.1-to-5.2-Migration-Guide[Migration Guide] about behavior changes in this version.
[[x5.2-new-components]]
== New Components
[[x5.2-rsocket-support]]
=== RSocket Support
The `spring-integration-rsocket` module is now available with channel adapter implementations for RSocket protocol support.
See xref:rsocket.adoc[RSocket Support] for more information.
[[x5.2-rate-limit-advice]]
=== Rate Limit Advice Support
The `RateLimiterRequestHandlerAdvice` is now available for limiting requests rate on handlers.
See xref:handler-advice/classes.adoc#rate-limiter-advice[Rate Limiter Advice] for more information.
[[x5.2-cache-advice]]
=== Caching Advice Support
The `CacheRequestHandlerAdvice` is now available for caching request results on handlers.
See xref:handler-advice/classes.adoc#cache-advice[Caching Advice] for more information.
[[x5.2-kotlin-scripts]]
=== Kotlin Scripts Support
The JSR223 scripting module now includes a support for Kotlin scripts.
See xref:scripting.adoc[Scripting Support] for more information.
[[x5.2-flux-aggregator]]
=== Flux Aggregator Support
The `FluxAggregatorMessageHandler` is now available for grouping and windowing messages logic based on the Project Reactor `Flux` operators.
See xref:aggregator.adoc#flux-aggregator[Flux Aggregator] for more information.
[[x5.2-sftp-events]]
=== FTP/SFTP Event Publisher
The FTP and SFTP modules now provide an event listener for certain Apache Mina FTP/SFTP server events.
See xref:ftp/server-events.adoc[Apache Mina FTP Server Events] and xref:sftp/server-events.adoc[Apache Mina SFTP Server Events] for more information.
[[x5.2-avro]]
=== Avro Transformers
Simple Apache Avro transformers are now provided.
See xref:changes-5.1-5.2.adoc#x5.2-avro[Avro Transformers] for more information.
[[x5.2-general]]
== General Changes
The `JsonToObjectTransformer` now supports generics for the target object to deserialize into.
See xref:transformer.adoc#json-transformers[JSON Transformers] for more information.
The `splitter` now supports a `discardChannel` configuration option.
See xref:splitter.adoc[Splitter] for more information.
The Control Bus can now handle `Pausable` (extension of `Lifecycle`) operations.
See xref:groovy.adoc#groovy-control-bus[Control Bus] for more information.
The `Function<MessageGroup, Map<String, Object>>` strategy has been introduced for the aggregator component to merge and compute headers for output messages.
See xref:aggregator.adoc#aggregator-api[Aggregator Programming Model] for more information.
All the `MessageHandlingException` s thrown in the framework, includes now a bean resource and source for back tracking a configuration part in case no end-user code involved.
See xref:scatter-gather.adoc#scatter-gather-error-handling[Error Handling] for more information.
For better end-user experience, Java DSL now provides a configurer variant for starting flow with a gateway interface.
See `IntegrationFlows.from(Class<?> serviceInterface, Consumer<GatewayProxySpec> endpointConfigurer)` JavaDocs for more information.
Also a `MethodArgsHolder` is now a root object for evaluation context for all the expressions in the `GatewayProxyFactoryBean`.
The `#args` and `#method` evaluation context variables are now deprecated.
See xref:gateway.adoc[Messaging Gateways] for more information.
[[x5.2-amqp]]
=== AMQP Changes
The outbound endpoints can now be configured to synthesize a "nack" if no publisher confirm is received within a timeout.
See xref:changes-4.0-4.1.adoc#x4.1-amqp-outbound-lazy-connect[Outbound Channel Adapter] for more information.
The inbound channel adapter can now receive batched messages as a `List<?>` payload instead of receiving a discrete message for each batch fragment.
See xref:amqp/inbound-channel-adapter.adoc#amqp-debatching[Batched Messages] for more information.
The outbound channel adapter can now be configured to block the calling thread until a publisher confirm (acknowledgment) is received.
See xref:amqp/outbound-channel-adapter.adoc[Outbound Channel Adapter] for more information.
[[x5.2-file]]
=== File Changes
Some improvements to filtering remote files have been made.
See xref:file/remote-persistent-flf.adoc[Remote Persistent File List Filters] for more information.
[[x5.2-tcp]]
=== TCP Changes
The length header used by the `ByteArrayLengthHeaderSerializer` can now include the length of the header in addition to the payload.
See xref:ip/tcp-connection-factories.adoc#tcp-codecs[Message Demarcation (Serializers and Deserializers)] for more information.
When using a `TcpNioServerConnectionFactory`, priority is now given to accepting new connections over reading from existing connections, but it is configurable.
See xref:ip/note-nio.adoc[About Non-blocking I/O (NIO)] for more information.
The outbound gateway has a new property `closeStreamAfterSend`; when used with a new connection for each request/reply it signals EOF to the server, without closing the connection.
This is useful for servers that use the EOF to signal end of message instead of some delimiter in the data.
See xref:ip/tcp-gateways.adoc[TCP Gateways] for more information.
The client connection factories now support `connectTimeout` which causes an exception to be thrown if the connection is not established in that time.
See xref:ip.adoc#tcp-connection-factory[ TCP Connection Factories] for more information.
`SoftEndOfStreamException` is now a `RuntimeException` instead of extending `IOException`.
[[x5.2-mail]]
=== Mail Changes
The `AbstractMailReceiver` has now an `autoCloseFolder` option (`true` by default), to disable an automatic folder close after a fetch, but populate `IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE` header instead for downstream interaction.
See xref:mail.adoc#mail-inbound[Mail-receiving Channel Adapter] for more information.
[[x5.2-http]]
=== HTTP Changes
The HTTP inbound endpoint now support a request payload validation.
See xref:http.adoc[HTTP Support] for more information.
[[x5.2-webflux]]
=== WebFlux Changes
The `WebFluxRequestExecutingMessageHandler` now supports a `Publisher`, `Resource` and `MultiValueMap` as a request message `payload`.
The `WebFluxInboundEndpoint` now supports a request payload validation.
See xref:webflux.adoc[WebFlux Support] for more information.
[[x5.2-mongodb]]
=== MongoDb Changes
The `MongoDbMessageStore` can now be configured with custom converters.
See xref:mongodb.adoc[MongoDB Support] for more information.
[[x5.2-routers]]
=== Router Changes
You can now disable falling back to the channel key as the channel bean name.
See xref:router/dynamic-routers.adoc[Dynamic Routers] for more information.
[[x5.2--ftp-sftp]]
=== FTP/SFTP Changes
The `RotatingServerAdvice` is decoupled now from the `RotationPolicy` and its `StandardRotationPolicy`.
The remote file information, including host/port and directory are included now into message headers in the `AbstractInboundFileSynchronizingMessageSource` and `AbstractRemoteFileStreamingMessageSource` implementations.
Also this information is included into headers in the read operations results of the `AbstractRemoteFileOutboundGateway` implementations.
The FTP outbound endpoints now support `chmod` to change permissions on the uploaded file.
(SFTP already supported it since version 4.3).
See xref:ftp.adoc[FTP(S) Support] and xref:sftp.adoc[SFTP Support] for more information.

View File

@@ -0,0 +1,162 @@
[[migration-5.2-5.3]]
= Changes between 5.2 and 5.3
[[x5.3-new-components]]
== New Components
[[x5.3-integration-pattern]]
=== Integration Pattern
The `IntegrationPattern` abstraction has been introduced to indicate which enterprise integration pattern (an `IntegrationPatternType`) and category a Spring Integration component belongs to.
See its JavaDocs and xref:graph.adoc#integration-graph[Integration Graph] for more information about this abstraction and its use-cases.
[[x5.3-reactive-message-handler]]
=== `ReactiveMessageHandler`
The `ReactiveMessageHandler` is now natively supported in the framework.
See xref:reactive-streams.adoc#reactive-message-handler[ReactiveMessageHandler] for more information.
[[x5.3-reactive-message-source-producer]]
=== `ReactiveMessageSourceProducer`
The `ReactiveMessageSourceProducer` is a reactive implementation of the `MessageProducerSupport` to wrap a provided `MessageSource` into a `Flux` for on demand `receive()` calls.
See xref:reactive-streams.adoc[Reactive Streams Support] for more information.
[[x5.3-java-dsl-extensions]]
=== Java DSL Extensions
A new `IntegrationFlowExtension` API has been introduced to allow extension of the existing Java DSL with custom or composed EIP-operators.
This also can be used to introduce customizers for any out-of-the-box `IntegrationComponentSpec` extensions.
See xref:changes-5.2-5.3.adoc#x5.3-java-dsl-extensions[DSL Extensions] for more information.
[[x5.3-kotlin-dsl]]
=== Kotlin DSL
The Kotlin DSL for integration flow configurations has been introduced.
See xref:kotlin-dsl.adoc[Kotlin DSL Chapter] for more information.
[[x5.3-reactive-request-handler-advice]]
=== ReactiveRequestHandlerAdvice
A `ReactiveRequestHandlerAdvice` is provided to customize `Mono` replies from message handlers.
See xref:handler-advice/reactive.adoc[Reactive Advice] for more information.
[[x5.3-handle-message-advice-adapter]]
=== HandleMessageAdviceAdapter
A `HandleMessageAdviceAdapter` is provided to wrap any `MethodInterceptor` for applying on the `MessageHandler.handleMessage()` instead of a default `AbstractReplyProducingMessageHandler.RequestHandler.handleRequestMessage()` behavior.
See xref:handler-advice/handle-message.adoc[Handling Message Advice] for more information.
[[x5.3-mongodb-reactive-channel-adapters]]
=== MongoDB Reactive Channel Adapters
The `spring-integration-mongodb` module now provides channel adapter implementations for the Reactive MongoDb driver support in Spring Data.
Also, a reactive implementation for MongoDb change stream support is present with the `MongoDbChangeStreamMessageProducer`.
See xref:mongodb.adoc[MongoDB Support] for more information.
[[x5.3-receive-message-advice]]
=== ReceiveMessageAdvice
A special `ReceiveMessageAdvice` has been introduced to proxy exactly `MessageSource.receive()` or `PollableChannel.receive()`.
See xref:polling-consumer.adoc#smart-polling[Smart Polling] for more information.
[[x5.3-general]]
== General Changes
The gateway proxy now doesn't proxy `default` methods by default.
See xref:gateway.adoc#gateway-calling-default-methods[Invoking `default` Methods] for more information.
Internal components (such as `_org.springframework.integration.errorLogger`) now have a shortened name when they are represented in the integration graph.
See xref:graph.adoc#integration-graph[Integration Graph] for more information.
In the aggregator, when the `MessageGroupProcessor` returns a `Message`, the `MessageBuilder.popSequenceDetails()` is performed on the output message if the `sequenceDetails` matches the header in the first message of the group.
See xref:aggregator.adoc#aggregator-api[Aggregator Programming Model] for more information.
A new `publishSubscribeChannel()` operator, based on the `BroadcastCapableChannel` and `BroadcastPublishSubscribeSpec`, was added into Java DSL.
This fluent API has its advantage when we configure sub-flows as pub-sub subscribers for broker-backed channels like `SubscribableJmsChannel`, `SubscribableRedisChannel` etc.
See xref:dsl/java-subflows.adoc[Sub-flows support] for more information.
Transactional support in Spring Integration now also includes options to configure a `ReactiveTransactionManager` if a `MessageSource` or `MessageHandler` implementation produces a reactive type for payload to send.
See `TransactionInterceptorBuilder` for more information.
See also xref:transactions.adoc#reactive-transactions[Reactive Transactions].
A new `intercept()` operator to register `ChannelInterceptor` instances without creating explicit channels was added into Java DSL.
See xref:dsl/java-intercept.adoc[Operator intercept()] for more information.
The `MessageStoreSelector` has a new mechanism to compare an old and new value.
See xref:handler-advice/idempotent-receiver.adoc[Idempotent Receiver Enterprise Integration Pattern] for more information.
The `MessageProducerSupport` base class now has a `subscribeToPublisher(Publisher<? extends Message<?>>)` API to allow implementation of message-driven producer endpoints which emit messages via reactive `Publisher`.
See xref:reactive-streams.adoc[Reactive Streams Support] for more information.
[[x5.3-amqp]]
== AMQP Changes
The outbound channel adapter has a new property `multiSend` allowing multiple messages to be sent within the scope of one `RabbitTemplate` invocation.
See xref:amqp/outbound-channel-adapter.adoc[AMQP Outbound Channel Adapter] for more information.
The inbound channel adapter now supports a listener container with the `consumerBatchEnabled` property set to `true`.
See xref:amqp/inbound-channel-adapter.adoc[AMQP Inbound Channel Adapter]
[[x5.3-http]]
== HTTP Changes
The `encodeUri` property on the `AbstractHttpRequestExecutingMessageHandler` has been deprecated in favor of newly introduced `encodingMode`.
See `DefaultUriBuilderFactory.EncodingMode` JavaDocs and xref:http/namespace.adoc#http-uri-encoding[Controlling URI Encoding] for more information.
This also affects `WebFluxRequestExecutingMessageHandler`, respective Java DSL and XML configuration.
The same option is added into an `AbstractWebServiceOutboundGateway`.
[[x5.3-ws]]
== Web Services Changes
Java DSL support has been added for Web Service components.
The `encodeUri` property on the `AbstractWebServiceOutboundGateway` has been deprecated in favor of newly introduced `encodingMode` - similar to HTTP changes above.
See xref:ws.adoc[Web Services Support] for more information.
[[x5.3-tcp]]
== TCP Changes
The `FailoverClientConnectionFactory` no longer fails back, by default, until the current connection fails.
See xref:ip/tcp-connection-factories.adoc#failover-cf[TCP Failover Client Connection Factory] for more information.
The `TcpOutboundGateway` now supports asynchronous request/reply.
See xref:ip/tcp-gateways.adoc[TCP Gateways] for more information.
You can now configure client connections to perform some arbitrary test on new connections.
See xref:ip/testing-connections.adoc[Testing Connections] for more information.
[[x5.3-rsocket]]
== RSocket Changes
A `decodeFluxAsUnit` option has been added to the `RSocketInboundGateway` with the meaning to decode incoming `Flux` as a single unit or apply decoding for each event in it.
See xref:rsocket.adoc#rsocket-inbound[RSocket Inbound Gateway] for more information.
[[x5.3-zookeeper]]
== Zookeeper Changes
A `LeaderInitiatorFactoryBean` (as well as its XML `<int-zk:leader-listener>`) exposes a `candidate` option for more control over a `Candidate` configuration.
See xref:zookeeper.adoc#zk-leadership[Leadership event handling] for more information.
[[x5.3-mqtt]]
== MQTT Changes
The inbound channel adapter can now be configured to provide user control over when a message is acknowledged as being delivered.
See xref:mqtt.adoc#mqtt-ack-mode[Manual Acks] for more information.
The outbound adapter now publishes a `MqttConnectionFailedEvent` when a connection can't be created, or is lost.
Previously, only the inbound adapter did so.
See xref:mqtt.adoc#mqtt-events[MQTT Events].
[[x5.3-sftp]]
== (S)FTP Changes
The `FileTransferringMessageHandler` (for FTP and SFTP, for example) in addition to `File`, `byte[]`, `String` and `InputStream` now also supports an `org.springframework.core.io.Resource`.
See xref:sftp.adoc[SFTP Support] and xref:ftp.adoc[FTP Support] for more information.
[[x5.3-file]]
== File Changes
The `FileSplitter` doesn't require a Jackson processor (or similar) dependency any more for the `markersJson` mode.
It uses a `SimpleJsonSerializer` for a straightforward string representation of the `FileSplitter.FileMarker` instances.
See xref:file/splitter.adoc[FileSplitter] for more information.

View File

@@ -0,0 +1,82 @@
[[migration-5.3-5.4]]
= Changes between 5.3 and 5.4
[[x5.4-new-components]]
== New Components
[[x5.4-sik]]
== Channel Adapters for Apache Kafka
The standalone https://projects.spring.io/spring-integration-kafka/[Spring Integration for Apache Kafka] project has been merged as a `spring-integration-kafka` module to this project.
The `KafkaProducerMessageHandler` `sendTimeoutExpression` default has changed.
You can now access the `Future<?>` for underlying `send()` operations.
See xref:kafka.adoc[Spring for Apache Kafka Support] for more information.
[[x5.4-r2dbc]]
== R2DBC Channel Adapters
The Channel Adapters for R2DBC database interaction have been introduced.
See xref:r2dbc.adoc[R2DBC Support] for more information.
[[x5.4-redis-stream]]
== Redis Stream Support
The Channel Adapters for Redis Stream support have been introduced.
See xref:redis.adoc#redis-stream-outbound[Redis Stream Outbound Channel Adapter] for more information.
[[x5.4-renewable-lock]]
== Renewable Lock Registry
A Renewable lock registry has been introduced to allow renew lease of a distributed lock.
See xref:jdbc.adoc#jdbc-lock-registry[JDBC implementation] for more information.
[[x5.4-zeromq]]
== ZeroMQ Support
`ZeroMqChannel`, `ZeroMqMessageHandler` and `ZeroMqMessageProducer` have been introduced.
See xref:zeromq.adoc[ZeroMQ Support] for more information.
[[x5.4-general]]
== General Changes
The one-way messaging gateway (the `void` method return type) now sets a `nullChannel` explicitly into the `replyChannel` header to ignore any possible downstream replies.
See xref:gateway.adoc#gateway-default-reply-channel[Setting the Default Reply Channel] for more information.
Also the gateway method invokers (`GatewayProxyFactoryBean.MethodInvocationGateway`) are now supplied with the managed bean name as a combination of gateway proxy bean name plus method signature.
For example: `sampleGateway#echo(String)`.
This effects message history and metrics exposed for the gateway method calls and also give fine-grained logs during start and close of application context.
The aggregator (and resequencer) can now expire orphaned groups (groups in a persistent store where no new messages arrive after an application restart).
See xref:aggregator.adoc#aggregator-expiring-groups[Aggregator Expiring Groups] for more information.
The legacy metrics that were replaced by Micrometer meters have been removed.
The xref:barrier.adoc[Thread Barrier] has now two separate timeout options: `requestTimeout` and `triggerTimeout`.
[[x5.4-tcp]]
== TCP/UDP Changes
Connection factories now support multiple sending components (`TcpSender`); they remain limited to one receiving component (`TcpListener`).
This allows, for example, an inbound gateway and outbound channel adapter to share the same factory, supporting both request/reply and arbitrary messaging from the server to the client.
Shared factories should not be used with outbound gateways, unless single-use connections or the `ThreadAffinityClientConnectionFactory` are being used.
See xref:ip/correlation.adoc#ip-collaborating-adapters[Collaborating Channel Adapters] and xref:ip/tcp-gateways.adoc[TCP Gateways] for more information.
The UDP channel adapters can now be configured with a `SocketCustomizer` which allows the setting of socket properties that are not directly supported by the adapters.
See xref:ip/udp-adapters.adoc[UDP Adapters] for more information.
[[x5.4-amqp]]
== AMQP Changes
The outbound endpoints now have a new mechanism for handling publisher confirms and returns.
See xref:amqp/alternative-confirms-returns.adoc[Alternative Mechanism for Publisher Confirms and Returns] for more information.
A new `BatchMode.EXTRACT_PAYLOAD_WITH_HEADERS` is supported by the `AmqpInboundChannelAdapter`.
See xref:amqp/inbound-channel-adapter.adoc[Inbound Channel Adapter] for more information.
[[x5.4-mail]]
== Mail Changes
The `AbstractMailReceiver` can now produce the `MimeMessage` as-is without eager fetching its content.
See xref:mail.adoc#mail-inbound[Mail-receiving Channel Adapter] for more information.

View File

@@ -0,0 +1,113 @@
[[migration-5.4-5.5]]
= Changes between 5.4 and 5.5
[[x5.5-new-components]]
== New Components
[[x5.5-file-aggregator]]
== File Aggregator
A `FileSplitter.FileMaker`-based implementation of `CorrelationStrategy`, `ReleaseStrategy` and `MessageGroupProcessor` as a `FileAggregator` component was introduced.
See xref:file/aggregator.adoc[File Aggregator] for more information.
[[x5.5-mqtt-v5]]
== MQTT v5 Support
The `Mqttv5PahoMessageDrivenChannelAdapter` and `Mqttv5PahoMessageHandler` (including respective `MqttHeaderMapper`) were introduced to support MQTT v5 protocol communication.
See xref:mqtt.adoc#mqtt-v5[MQTT v5 Support] for more information.
[[x5.5-general]]
== General Changes
All the persistent `MessageGroupStore` implementation provide a `streamMessagesForGroup(Object groupId)` contract based on the target database streaming API.
See xref:message-store.adoc[Message Store] for more information.
The `integrationGlobalProperties` bean (if declared) must be now an instance of `org.springframework.integration.context.IntegrationProperties` instead of `java.util.Properties`, which support is deprecated for backward compatibility.
The `spring.integration.channels.error.requireSubscribers=true` global property is added to indicate that the global default `errorChannel` must be configured with the `requireSubscribers` option (or not).
The `spring.integration.channels.error.ignoreFailures=true` global property is added to indicate that the global default `errorChannel` must ignore (or not) dispatching errors and pass the message to the next handler.
See xref:configuration/global-properties.adoc[Global Properties] for more information.
An `AbstractPollingEndpoint` (source polling channel adapter and polling consumer) treats `maxMessagesPerPoll == 0` as to skip calling the source.
It can be changed to different value later on, e.g. via a Control Bus.
See xref:endpoint.adoc#endpoint-pollingconsumer[Polling Consumer] for more information.
The `ConsumerEndpointFactoryBean` now accept a `reactiveCustomizer` `Function` to any input channel as reactive stream source and use a `ReactiveStreamsConsumer` underneath.
This is covered as a `ConsumerEndpointSpec.reactive()` option in Java DSL and as a `@Reactive` nested annotation for the messaging annotations.
See xref:reactive-streams.adoc[Reactive Streams Support] for more information.
The `groupTimeoutExpression` for a correlation message handler (an `Aggregator` and `Resequencer`) can now be evaluated to a `java.util.Date` for some fine-grained scheduling use-cases.
Also the `BiFunction groupConditionSupplier` option is added to the `AbstractCorrelatingMessageHandler` to supply a `MessageGroup` condition against a message to be added to the group.
See xref:overview.adoc#overview-endpoints-aggregator[Aggregator] for more information.
The `MessageGroup` abstraction can be supplied with a `condition` to evaluate later on to make a decision for the group.
See xref:message-store.adoc#message-group-condition[Message Group Condition] for more information.
[[x5.5-integration-flows-composition]]
== Integration Flows Composition
The new `IntegrationFlows.from(IntegrationFlow)` factory method has been added to allow starting the current `IntegrationFlow` from the output of an existing flow.
In addition, the `IntegrationFlowDefinition` has added a `to(IntegrationFlow)` terminal operator to continue the current flow at the input channel of some other flow.
See xref:dsl/integration-flows-composition.adoc[Integration Flows Composition] for more information.
[[x5.5-amqp]]
== AMQP Changes
The `AmqpInboundChannelAdapter` and `AmqpInboundGateway` (and the respective Java DSL builders) now support an `org.springframework.amqp.rabbit.retry.MessageRecoverer` as an AMQP-specific alternative to the general purpose `RecoveryCallback`.
See xref:amqp.adoc[AMQP Support] for more information.
[[x5.5-redis]]
== Redis Changes
The `ReactiveRedisStreamMessageProducer` has now setters for all the `StreamReceiver.StreamReceiverOptionsBuilder` options, including an `onErrorResume` function.
See xref:redis.adoc[Redis Support] for more information.
[[x5.5-http]]
== HTTP Changes
The `HttpRequestExecutingMessageHandler` doesn't fallback to the `application/x-java-serialized-object` content type any more and lets the `RestTemplate` make the final decision for the request body conversion based on the `HttpMessageConverter` provided.
It also has now an `extractResponseBody` flag (which is `true` by default) to return just the response body, or to return the whole `ResponseEntity` as the reply message payload, independently of the provided `expectedResponseType`.
Same option is presented for the `WebFluxRequestExecutingMessageHandler`, too.
See xref:http.adoc[HTTP Support] for more information.
[[x5.5-file]]
== File/FTP/SFTP Changes
The persistent file list filters now have a boolean property `forRecursion`.
Setting this property to `true`, also sets `alwaysAcceptDirectories`, which means that the recursive operation on the outbound gateways (`ls` and `mget`) will now always traverse the full directory tree each time.
This is to solve a problem where changes deep in the directory tree were not detected.
In addition, `forRecursion=true` causes the full path to files to be used as the metadata store keys; this solves a problem where the filter did not work properly if a file with the same name appears multiple times in different directories.
IMPORTANT: This means that existing keys in a persistent metadata store will not be found for files beneath the top level directory.
For this reason, the property is `false` by default; this may change in a future release.
The `FileInboundChannelAdapterSpec` has now a convenient `recursive(boolean)` option instead of requiring an explicit reference to the `RecursiveDirectoryScanner`.
The `remoteDirectoryExpression` can now be used in the `mv` command for convenience.
[[x5.5-mongodb]]
== MongoDb Changes
The `MongoDbMessageSourceSpec` was added into MongoDd Java DSL.
An `update` option is now exposed on both the `MongoDbMessageSource` and `ReactiveMongoDbMessageSource` implementations.
See xref:mongodb.adoc[MongoDb Support] for more information.
[[x5.5-websocket]]
== WebSockets Changes
The WebSocket channel adapters based on `ServerWebSocketContainer` can now be registered and removed at runtime.
See xref:web-sockets.adoc[WebSockets Support] for more information.
[[x5.5-jpa]]
== JPA Changes
The `JpaOutboundGateway` now supports an `Iterable` message payload for a `PersistMode.DELETE`.
See xref:jpa/outbound-channel-adapter.adoc[Outbound Channel Adapter] for more information.
[[x55-gw]]
== Gateway Changes
Previously, when using XML configuration, `@Gateway.payloadExpression` was ignored for no-argument methods.
There is one possible breaking change - if the method is annotated with `@Payload` as well as `@Gateway` (with a different expression) previously, the `@Payload` would be applied, now the `@Gateway.payloadExpression` is applied.
See xref:gateway.adoc#gateway-configuration-annotations[Gateway Configuration with Annotations and XML] and xref:gateway.adoc#gateway-calling-no-argument-methods[Invoking No-Argument Methods] for more information.

View File

@@ -0,0 +1,198 @@
[[migration-5.5-6.0]]
= Changes between 5.5 and 6.0
[[x6.0-new-components]]
== New Components
A Groovy DSL implementation for integration flow definitions has been added.
See xref:groovy-dsl.adoc[Groovy DSL] for more information.
[[x6.0-mqtt]]
=== MQTT ClientManager
A new MQTT `ClientManager` has been added to support a reusable MQTT connection across different channel adapters.
See xref:mqtt.adoc#mqtt-shared-client[Shared MQTT Client Support] for more information.
[[x6.0-graphql]]
=== GraphQL Support
The GraphQL support has been added.
See xref:graphql.adoc[GraphQL Support] for more information.
[[x6.0-camel]]
=== Apache Camel Support
Support for Apache Camel routes has been introduced.
See xref:camel.adoc[Apache Camel Support] for more information.
[[x6.0-hazelcast]]
=== Hazelcast Support
The Hazelcast Spring Integration Extensions project has been migrated as the `spring-integration-hazelcast` module.
See xref:hazelcast.adoc[Hazelcast Support] for more information.
[[x6.0-smb]]
=== SMB Support
SMB support has been added from the Spring Integration Extensions project.
The Java DSL (see `org.springframework.integration.smb.dsl.Smb` factory) also has been added to this module.
An `SmbStreamingMessageSource` and `SmbOutboundGateway` implementation are introduced.
See xref:smb.adoc[SMB Support] for more information.
[[x6.0-postgres]]
=== PostgreSQL Push Notification
A `PostgresSubscribableChannel` allows to receive push notifications via `PostgresChannelMessageTableSubscriber` upon new messages add to the `JdbcChannelMessageStore`.
See xref:jdbc/message-store.adoc#postgresql-push[PostgreSQL: Receiving Push Notifications] for more information.
[[x6.0-rmq]]
=== RabbitMQ Stream Queue Support
The AMQP module has been enhanced to provide support for inbound and outbound channel adapters using RabbitMQ Stream Queues.
See xref:amqp/rmq-streams.adoc[RabbitMQ Stream Queue Support] for more information.
[[x6.0-sftp]]
=== Apache MINA SFTP
The SFTP modules has been fully reworked from outdated JCraft JSch library to more robust and modern `org.apache.sshd:sshd-sftp` module of the Apache MINA project.
See xref:sftp.adoc[SFTP Adapters] for more information.
[[x6.0-micrometer-observation]]
=== Micrometer Observation
Enabling observation for timers and tracing using Micrometer is now supported.
See xref:metrics.adoc#micrometer-observation[Micrometer Observation] for more information.
[[x6.0-graalmv-polyglot]]
=== GraalVM Polyglot Support
The Scripting module now provides a `PolyglotScriptExecutor` implementation based on the GraalVM Polyglot support.
JavaScript support is now based on this executor since its JSR223 implementation has been removed from Java by itself.
See xref:scripting.adoc[Scripting Support] for more information.
[[x6.0-cassandra]]
=== Apache Cassandra Support
The Apache Cassandra Spring Integration Extensions project has been migrated as the `spring-integration-cassandra` module.
See xref:cassandra.adoc[Apache Cassandra Support] for more information.
[[x6.0-kotlin-coroutines]]
=== Kotlin Coroutines
Kotlin Coroutines support has been introduced to the framework.
See xref:kotlin-functions.adoc#kotlin-coroutines[Kotlin Coroutines] for more information.
[[x6.0-aot]]
=== Native Images
Support for creating GraalVM native images is provided.
See xref:native-aot.adoc[Native Images Support] for more information.
[[x6.0-general]]
== General Changes
The messaging annotations are now `@Repeatable` and the same type can be declared several times on the same service method.
The messaging annotations don't require a `poller` attribute as an array of `@Poller` anymore.
See xref:configuration/annotations.adoc[Annotation Support] for more information.
For convenience, the XML and Java DSL for Scatter-Gather, based on the `RecipientListRouter`, now sets an `applySequence = true`, so the gatherer part can rely on the default correlation strategies.
See xref:scatter-gather.adoc[Scatter-Gather] for more information.
Another convenient behavior change has been made to the `AbstractMappingMessageRouter`.
Now, setting a `defaultOutputChannel` also resets the `channelKeyFallback` property to `false`, so no attempts will be made to resolve a channel from its key, but the logic immediately falls back to sending the message to the `defaultOutputChannel`.
See xref:router/common-parameters.adoc#router-common-parameters-all[Router Options] for more information.
The `AggregatingMessageHandler` now does not split a `Collection<Message<?>>` result of the `MessageGroupProcessor` (unless it is a `SimpleMessageGroupProcessor`) on the output, but emits a single message containing this whole collection as a payload.
See xref:overview.adoc#overview-endpoints-aggregator[Aggregator] for more information.
The `IntegrationFlows` factory is now marked as deprecated in favor of the fluent API available in the `IntegrationFlow` interface itself.
The factory class will be removed in the future releases.
See xref:dsl.adoc#java-dsl[Java DSL] for more information.
The `org.springframework.util.concurrent.ListenableFuture` has been deprecated starting with Spring Framework `6.0`.
All Spring Integration async API has been migrated to the `CompletableFuture`.
The Messaging Gateway interface method can now return `Future<Void>` and `Mono<Void>` with a proper asynchronous execution of the downstream flow.
Alongside with a `@MessagingGateway` annotation the interface can also be marked with a `@Primary`.
`@MessagingGateway` interfaces can now be use as an `@Import` resources for configuration.
The default naming strategy for gateway proxy beans can be customized via `@IntegrationComponentScan.nameGenerator()` attribute.
If `AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR` bean is present, it is consulted otherwise before falling back to the `AnnotationBeanNameGenerator`.
See xref:gateway.adoc[Messaging Gateway] for more information.
The `integrationGlobalProperties` bean is now declared by the framework as an instance of `org.springframework.integration.context.IntegrationProperties` instead of the previously deprecated `java.util.Properties`.
Message handlers which produce a collection as a reply (e.g. `JpaOutboundGateway`, `JdbcOutboundGateway` and other DB-based gateways) now return an empty result list if no records are returned by the query.
Previously, `null` was returned ending the flow, or throwing an exception, depending on `requiresReply`.
[[x6.0-rmi]]
== RMI Removal
The `spring-integration-rmi` module has been removed altogether after being deprecated in previous versions.
There is no replacement: it is recommended to migrate to more secure network and application protocols, such as WebSockets, RSockets, gRPC or REST.
[[x6.0-gemfire]]
== GemFire Removal
The `spring-integration-gemfire` module has been removed altogether since there is no Spring Data `2022.0.0` support for VMware GemFire or Apache Geode.
[[x6.0-http]]
== HTTP Changes
The `#cookies` variable for expression evaluation context, exposed in the `HttpRequestHandlingEndpointSupport`, is now a `MultiValueMap` to carry all the values for cookies set by the client.
See xref:http.adoc[HTTP Support] for more information.
[[x6.0-kafka]]
== Apache Kafka Changes
When providing a `RetryTemplate` on the inbound gateway or message-driven channel adapter, if an `errorChannel` is also provided, an `ErrorMessageSendingRecoverer` is automatically configured.
In addition, the new `KafkaErrorMessageSendingRecoverer` is provided; this can be used with a `DefaultErrorHandler` to avoid issues with long aggregated retry delays causing partitions rebalances.
See xref:kafka.adoc[Spring for Apache Kafka Support] for more information.
[[x6.0-jdbc]]
== JDBC Changes
The `DefaultLockRepository` can now be supplied with a `PlatformTransactionManager` instead of relying on the primary bean from the application context.
See xref:jdbc.adoc#jdbc-lock-registry[JDBC Lock Registry] for more information.
[[x6.0-tcp]]
== TCP/IP Changes
The `lookupHost` property of the `AbstractConnectionFactory` and `DatagramPacketMessageMapper` is now set to `false` by default to avoid delays in the environments where DNS is not configured.
See xref:ip.adoc[TCP and UDP Support] for more information.
[[x6.0-jms]]
== JMS Changes
The `JmsOutboundGateway` now creates a `TemporaryTopic` instead of `TemporaryQueue` if `replyPubSubDomain` option is set to `true`.
See xref:jms.adoc[JMS Support] for more information.
[[x6.0-security]]
== Security Changes
The `ChannelSecurityInterceptor` and its annotation `@SecuredChannel` and XML `<secured-channels>` configurations have been deprecated in favor of `AuthorizationChannelInterceptor`.
See xref:security.adoc[Security Support] for more information.
[[x6.0-webflux]]
== Webflux Request Attributes Support
Webclient Request attributes support has been added for `WebFluxRequestExecutingMessageHandler`.
See xref:webflux.adoc#webflux-request-attributes[WebFlux Request Attributes] for more information.

View File

@@ -0,0 +1,84 @@
[[migration-6.0-6.1]]
= Changes between 6.0 and 6.1
[[x6.1-new-components]]
== New Components
[[x6.1-zip]]
=== Zip Support
The Zip Spring Integration Extension project has been migrated as the `spring-integration-zip` module.
See xref:zip.adoc[Zip Support] for more information.
[[x6.1-context-holder-advice]]
=== `ContextHolderRequestHandlerAdvice`
The `ContextHolderRequestHandlerAdvice` allows to store a value from a request message into some context around `MessageHandler` execution.
See xref:handler-advice/context-holder.adoc[Context Holder Advice] for more information.
[[x6.1-handle-reactive]]
=== The `handleReactive()` operator for Java DSL
The `IntegrationFlow` can now end with a convenient `handleReactive(ReactiveMessageHandler)` operator.
See xref:reactive-streams.adoc#reactive-message-handler[`ReactiveMessageHandler`] for more information.
[[x6.1-partitioned-channel]]
== `PartitionedChannel`
A new `PartitionedChannel` has been introduced to process messages with the same partition key in the same thread.
See xref:channel/implementations.adoc#partitioned-channel[`PartitionedChannel`] for more information.
[[x6.1-general]]
== General Changes
- Added support for transforming to/from Protocol Buffers.
See xref:transformer.adoc#Protobuf-transformers[Protocol Buffers Transformers] for more information.
- The `MessageFilter` now emits a warning into logs when message is silently discarded and dropped.
See xref:filter.adoc[Filter] for more information.
- The default timeout for send and receive operations in gateways and replying channel adapters has been changed from infinity to `30` seconds.
Only one left as a `1` second is a `receiveTimeout` for `PollingConsumer` to not block a scheduler thread too long and let other queued tasks to be performed with the `TaskScheduler`.
- The `IntegrationComponentSpec.get()` method has been deprecated with removal planned for the next version.
Since `IntegrationComponentSpec` is a `FactoryBean`, its bean definition must stay as is without any target object resolutions.
The Java DSL and the framework by itself will manage the `IntegrationComponentSpec` lifecycle.
See xref:dsl.adoc#java-dsl[ Java DSL] for more information.
- The `AbstractMessageProducingHandler` is marked as an `async` by default if its output channel is configured to a `ReactiveStreamsSubscribableChannel`.
See xref:service-activator.adoc#async-service-activator[Asynchronous Service Activator] for more information.
[[x6.1-web-sockets]]
== Web Sockets Changes
A `ClientWebSocketContainer` can now be configured with a predefined `URI` instead of a combination of `uriTemplate` and `uriVariables`.
See xref:web-sockets.adoc#web-socket-overview[WebSocket Overview] for more information.
[[x6.1-jms]]
== JMS Changes
The `JmsInboundGateway`, via its `ChannelPublishingJmsMessageListener`, can now be configured with a `replyToExpression` to resolve a reply destination against the request message at runtime.
See xref:jms.adoc#jms-inbound-gateway[JMS Inbound Gateway] for more information.
[[x6.1-mail]]
== Mail Changes
The (previously deprecated) `ImapIdleChannelAdapter.sendingTaskExecutor` property has been removed in favor of an asynchronous message process downstream in the flow.
See xref:mail.adoc#mail-inbound[Mail-receiving Channel Adapter] for more information.
[[x6.1-file]]
== Files Changes
The `FileReadingMessageSource` now exposes `watchMaxDepth` and `watchDirPredicate` options for the `WatchService`.
See xref:file.adoc#watch-service-directory-scanner[ `WatchServiceDirectoryScanner`] for more information.
[[x6.1-amqp]]
== AMQP Changes
The Java DSL API for Rabbit Streams (the `RabbitStream` factory) exposes additional properties for simple configurations.
See xref:amqp/rmq-streams.adoc[`RabbitMQ Stream Queue Support`] for more information.
[[x6.1-jdbc]]
== JDBC Changes
The `DefaultLockRepository` now exposes setters for `insert`, `update` and `renew` queries.
See xref:jdbc.adoc#jdbc-lock-registry[ JDBC Lock Registry] for more information.

View File

@@ -0,0 +1,262 @@
[[channel-adapter]]
= Channel Adapter
A channel adapter is a message endpoint that enables connecting a single sender or receiver to a message channel.
Spring Integration provides a number of adapters to support various transports, such as JMS, file, HTTP, web services, mail, and more.
Upcoming chapters of this reference guide discuss each adapter.
However, this chapter focuses on the simple but flexible method-invoking channel adapter support.
There are both inbound and outbound adapters, and each may be configured with XML elements provided in the core namespace.
These provide an easy way to extend Spring Integration, as long as you have a method that can be invoked as either a source or a destination.
[[channel-adapter-namespace-inbound]]
== Configuring An Inbound Channel Adapter
An `inbound-channel-adapter` element (a `SourcePollingChannelAdapter` in Java configuration) can invoke any method on a Spring-managed object and send a non-null return value to a `MessageChannel` after converting the method's output to a `Message`.
When the adapter's subscription is activated, a poller tries to receive messages from the source.
The poller is scheduled with the `TaskScheduler` according to the provided configuration.
To configure the polling interval or cron expression for an individual channel adapter, you can provide a 'poller' element with one of the scheduling attributes, such as 'fixed-rate' or 'cron'.
The following example defines two `inbound-channel-adapter` instances:
[tabs]
======
Java DSL::
+
[source, java, role="primary"]
----
@Bean
public IntegrationFlow source1() {
return IntegrationFlow.from(() -> new GenericMessage<>(...),
e -> e.poller(p -> p.fixedRate(5000)))
...
.get();
}
@Bean
public IntegrationFlow source2() {
return IntegrationFlow.from(() -> new GenericMessage<>(...),
e -> e.poller(p -> p.cron("30 * 9-17 * * MON-FRI")))
...
.get();
}
----
Java::
+
[source, java, role="secondary"]
----
public class SourceService {
@InboundChannelAdapter(channel = "channel1", poller = @Poller(fixedRate = "5000"))
Object method1() {
...
}
@InboundChannelAdapter(channel = "channel2", poller = @Poller(cron = "30 * 9-17 * * MON-FRI"))
Object method2() {
...
}
}
----
Kotlin DSL::
+
[source, kotlin, role="secondary"]
----
@Bean
fun messageSourceFlow() =
integrationFlow( { GenericMessage<>(...) },
{ poller { it.fixedRate(5000) } }) {
...
}
----
XML::
+
[source, xml, role="secondary"]
----
<int:inbound-channel-adapter ref="source1" method="method1" channel="channel1">
<int:poller fixed-rate="5000"/>
</int:inbound-channel-adapter>
<int:inbound-channel-adapter ref="source2" method="method2" channel="channel2">
<int:poller cron="30 * 9-17 * * MON-FRI"/>
</int:channel-adapter>
----
======
See also xref:channel-adapter.adoc#channel-adapter-expressions-and-scripts[Channel Adapter Expressions and Scripts].
NOTE: If no poller is provided, then a single default poller must be registered within the context.
See xref:endpoint.adoc#endpoint-namespace[Endpoint Namespace Support] for more detail.
[IMPORTANT]
.Important: Poller Configuration
=====
All the `inbound-channel-adapter` types are backed by a `SourcePollingChannelAdapter`, which means they contain a poller configuration that polls the `MessageSource` (to invoke a custom method that produces the value that becomes a `Message` payload) based on the configuration specified in the Poller.
The following example shows the configuration of two pollers:
[source,xml]
----
<int:poller max-messages-per-poll="1" fixed-rate="1000"/>
<int:poller max-messages-per-poll="10" fixed-rate="1000"/>
----
In the first configuration, the polling task is invoked once per poll, and, during each task (poll), the method (which results in the production of the message) is invoked once, based on the `max-messages-per-poll` attribute value.
In the second configuration, the polling task is invoked 10 times per poll or until it returns 'null', thus possibly producing ten messages per poll while each poll happens at one-second intervals.
However, what happens if the configuration looks like the following example:
[source,xml]
----
<int:poller fixed-rate="1000"/>
----
Note that there is no `max-messages-per-poll` specified.
As we cover later, the identical poller configuration in the `PollingConsumer` (for example, `service-activator`, `filter`, `router`, and others) would have a default value of `-1` for `max-messages-per-poll`, which means "`execute the polling task non-stop unless the polling method returns null (perhaps because there are no more messages in the `QueueChannel`)`" and then sleep for one second.
However, in the `SourcePollingChannelAdapter`, it is a bit different.
The default value for `max-messages-per-poll` is `1`, unless you explicitly set it to a negative value (such as `-1`).
This makes sure that the poller can react to lifecycle events (such as start and stop) and prevents it from potentially spinning in an infinite loop if the implementation of the custom method of the `MessageSource` has a potential to never return null and happens to be non-interruptible.
However, if you are sure that your method can return null, and you need to poll for as many sources as available per each poll, you should explicitly set `max-messages-per-poll` to a negative value, as the following example shows:
[source,xml]
----
<int:poller max-messages-per-poll="-1" fixed-rate="1000"/>
----
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.
Starting with version 6.2, the `fixed-delay` and `fixed-rate` can be configured in https://en.wikipedia.org/wiki/ISO_8601#Durations[ISO 8601 Duration] format, e.g. `PT10S`, `P1D` etc.
In addition, an `initial-interval` of the underlying `PeriodicTrigger` is also exposed with similar value formats as `fixed-delay` and `fixed-rate`.
Also see xref:endpoint.adoc#global-default-poller[Global Default Poller] for more information.
=====
[[channel-adapter-namespace-outbound]]
== Configuring An Outbound Channel Adapter
An `outbound-channel-adapter` element (a `@ServiceActivator` for Java configuration) can also connect a `MessageChannel` to any POJO consumer method that should be invoked with the payload of messages sent to that channel.
The following example shows how to define an outbound channel adapter:
[tabs]
======
Java DSL::
+
[source, java, role="primary"]
----
@Bean
public IntegrationFlow outboundChannelAdapterFlow(MyPojo myPojo) {
return f -> f
.handle(myPojo, "handle");
}
----
Java::
+
[source, java, role="secondary"]
----
public class MyPojo {
@ServiceActivator(channel = "channel1")
void handle(Object payload) {
...
}
}
----
Kotlin DSL::
+
[source, kotlin, role="secondary"]
----
@Bean
fun outboundChannelAdapterFlow(myPojo: MyPojo) =
integrationFlow {
handle(myPojo, "handle")
}
----
XML::
+
[source, xml, role="secondary"]
----
<int:outbound-channel-adapter channel="channel1" ref="target" method="handle"/>
<beans:bean id="target" class="org.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:
[tabs]
======
Java::
+
[source, java, role="primary"]
----
public class MyPojo {
@ServiceActivator(channel = "channel1", poller = @Poller(fixedRate = "3000"))
void handle(Object payload) {
...
}
}
----
XML::
+
[source, xml, role="secondary"]
----
<int:outbound-channel-adapter channel="channel2" ref="target" method="handle">
<int:poller fixed-rate="3000" />
</int:outbound-channel-adapter>
<beans:bean id="target" class="org.MyPojo"/>
----
======
You should use a `ref` attribute if the POJO consumer implementation can be reused in other `<outbound-channel-adapter>` definitions.
However, if the consumer implementation is referenced by only a single definition of the `<outbound-channel-adapter>`, you can define it as an inner bean, as the following example shows:
[source,xml]
----
<int:outbound-channel-adapter channel="channel" method="handle">
<beans:bean class="org.Foo"/>
</int:outbound-channel-adapter>
----
NOTE: Using both the `ref` attribute and an inner handler definition in the same `<outbound-channel-adapter>` configuration is not allowed, as it creates an ambiguous condition.
Such a configuration results in an exception being thrown.
Any channel adapter can be created without a `channel` reference, in which case it implicitly creates an instance of `DirectChannel`.
The created channel's name matches the `id` attribute of the `<inbound-channel-adapter>` or `<outbound-channel-adapter>` element.
Therefore, if `channel` is not provided, `id` is required.
[[channel-adapter-expressions-and-scripts]]
== Channel Adapter Expressions and Scripts
Like many other Spring Integration components, the `<inbound-channel-adapter>` and `<outbound-channel-adapter>` also provide support for SpEL expression evaluation.
To use SpEL, provide the expression string in the 'expression' attribute instead of providing the 'ref' and 'method' attributes that are used for method-invocation on a bean.
When an expression is evaluated, it follows the same contract as method-invocation where: the expression for an `<inbound-channel-adapter>` generates a message any time the evaluation result is a non-null value, while the expression for an `<outbound-channel-adapter>` must be the equivalent of a void-returning method invocation.
Starting with Spring Integration 3.0, an `<int:inbound-channel-adapter/>` can also be configured with a SpEL `<expression/>` (or even with a `<script/>`) sub-element, for when more sophistication is required than can be achieved with the simple 'expression' attribute.
If you provide a script as a `Resource` by using the `location` attribute, you can also set `refresh-check-delay`, which allows the resource to be periodically refreshed.
If you want the script to be checked on each poll, you would need to coordinate this setting with the poller's trigger, as the following example shows:
[source,xml]
----
<int:inbound-channel-adapter ref="source1" method="method1" channel="channel1">
<int:poller max-messages-per-poll="1" fixed-delay="5000"/>
<script:script lang="ruby" location="Foo.rb" refresh-check-delay="5000"/>
</int:inbound-channel-adapter>
----
See also the `cacheSeconds` property on the `ReloadableResourceBundleExpressionSource` when using the `<expression/>` sub-element.
For more information regarding expressions, see xref:spel.adoc[Spring Expression Language (SpEL)].
For scripts, see xref:groovy.adoc[Groovy support] and xref:scripting.adoc[Scripting Support].
IMPORTANT: The `<int:inbound-channel-adapter/>` (`SourcePollingChannelAdapter`) is an endpoint which starts a message flow by periodically triggering to poll some underlying `MessageSource`.
Since, at the time of polling, there is no message object, expressions and scripts do not have access to a root `Message`, so there are no payload or headers properties that are available in most other messaging SpEL expressions.
The script can generate and return a complete `Message` object with headers and payload or only a payload, which is added to a message with basic headers by the framework.

View File

@@ -0,0 +1,6 @@
[[channel]]
= Message Channels
:page-section-summary-toc: 1
While the `Message` plays the crucial role of encapsulating data, it is the `MessageChannel` that decouples message producers from message consumers.

View File

@@ -0,0 +1,818 @@
[[channel-configuration]]
= Configuring Message Channels
To create a message channel instance, you can use the `<channel/>` element for xml or `DirectChannel` instance for Java configuration, as follows:
[tabs]
======
Java::
+
[source, java, role="primary"]
----
@Bean
public MessageChannel exampleChannel() {
return new DirectChannel();
}
----
XML::
+
[source, xml, role="secondary"]
----
<int:channel id="exampleChannel"/>
----
======
When you use the `<channel/>` element without any sub-elements, it creates a `DirectChannel` instance (a `SubscribableChannel`).
To create a publish-subscribe channel, use the `<publish-subscribe-channel/>` element (the `PublishSubscribeChannel` in Java), as follows:
[tabs]
======
Java::
+
[source, java, role="primary"]
----
@Bean
public MessageChannel exampleChannel() {
return new PublishSubscribeChannel();
}
----
XML::
+
[source, xml, role="secondary"]
----
<int:publish-subscribe-channel id="exampleChannel"/>
----
======
You can alternatively provide a variety of `<queue/>` sub-elements to create any of the pollable channel types (as described in xref:channel/implementations.adoc[Message Channel Implementations]).
The following sections shows examples of each channel type.
[[channel-configuration-directchannel]]
== `DirectChannel` Configuration
As mentioned earlier, `DirectChannel` is the default type.
The following listing shows who to define one:
[tabs]
======
Java::
+
[source, java, role="primary"]
----
@Bean
public MessageChannel directChannel() {
return new DirectChannel();
}
----
XML::
+
[source, xml, role="secondary"]
----
<int:channel id="directChannel"/>
----
======
A default channel has a round-robin load-balancer and also has failover enabled (see xref:channel/implementations.adoc#channel-implementations-directchannel[`DirectChannel`] for more detail).
To disable one or both of these, add a `<dispatcher/>` sub-element (a `LoadBalancingStrategy` constructor of the `DirectChannel`) and configure the attributes as follows:
[tabs]
======
Java::
+
[source, java, role="primary"]
----
@Bean
public MessageChannel failFastChannel() {
DirectChannel channel = new DirectChannel();
channel.setFailover(false);
return channel;
}
@Bean
public MessageChannel failFastChannel() {
return new DirectChannel(null);
}
----
XML::
+
[source, xml, role="secondary"]
----
<int:channel id="failFastChannel">
<int:dispatcher failover="false"/>
</channel>
<int:channel id="channelWithFixedOrderSequenceFailover">
<int:dispatcher load-balancer="none"/>
</int:channel>
----
======
[[channel-datatype-channel]]
== Datatype Channel Configuration
Sometimes, a consumer can process only a particular type of payload, forcing you to ensure the payload type of the input messages.
The first thing that comes to mind may be to use a message filter.
However, all that message filter can do is filter out messages that are not compliant with the requirements of the consumer.
Another way would be to use a content-based router and route messages with non-compliant data-types to specific transformers to enforce transformation and conversion to the required data type.
This would work, but a simpler way to accomplish the same thing is to apply the https://www.enterpriseintegrationpatterns.com/DatatypeChannel.html[Datatype Channel] pattern.
You can use separate datatype channels for each specific payload data type.
To create a datatype channel that accepts only messages that contain a certain payload type, provide the data type's fully-qualified class name in the channel element's `datatype` attribute, as the following example shows:
[tabs]
======
Java::
+
[source, java, role="primary"]
----
@Bean
public MessageChannel numberChannel() {
DirectChannel channel = new DirectChannel();
channel.setDatatypes(Number.class);
return channel;
}
----
XML::
+
[source, xml, role="secondary"]
----
<int:channel id="numberChannel" datatype="java.lang.Number"/>
----
======
Note that the type check passes for any type that is assignable to the channel's datatype.
In other words, the `numberChannel` in the preceding example would accept messages whose payload is `java.lang.Integer` or `java.lang.Double`.
Multiple types can be provided as a comma-delimited list, as the following example shows:
[tabs]
======
Java::
+
[source, java, role="primary"]
----
@Bean
public MessageChannel numberChannel() {
DirectChannel channel = new DirectChannel();
channel.setDatatypes(String.class, Number.class);
return channel;
}
----
XML::
+
[source, xml, role="secondary"]
----
<int:channel id="stringOrNumberChannel" datatype="java.lang.String,java.lang.Number"/>
----
======
So the 'numberChannel' in the preceding example accepts only messages with a data type of `java.lang.Number`.
But what happens if the payload of the message is not of the required type?
It depends on whether you have defined a bean named `integrationConversionService` that is an instance of Spring's https://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert-ConversionService-API[Conversion Service].
If not, then an `Exception` would be thrown immediately.
However, if you have defined an `integrationConversionService` bean, it is used in an attempt to convert the message's payload to the acceptable type.
You can even register custom converters.
For example, suppose you send a message with a `String` payload to the 'numberChannel' we configured above.
You might handle the message as follows:
[source,java]
----
MessageChannel inChannel = context.getBean("numberChannel", MessageChannel.class);
inChannel.send(new GenericMessage<String>("5"));
----
Typically, this would be a perfectly legal operation.
However, since we use Datatype Channel, the result of such operation would generate an exception similar to the following:
[source]
----
Exception in thread "main" org.springframework.integration.MessageDeliveryException:
Channel 'numberChannel'
expected one of the following datataypes [class java.lang.Number],
but received [class java.lang.String]
----
The exception happens because we require the payload type to be a `Number`, but we sent a `String`.
So we need something to convert a `String` to a `Number`.
For that, we can implement a converter similar to the following example:
[source,java]
----
public static class StringToIntegerConverter implements Converter<String, Integer> {
public Integer convert(String source) {
return Integer.parseInt(source);
}
}
----
Then we can register it as a converter with the Integration Conversion Service, as the following example shows:
[tabs]
======
Java::
+
[source, java, role="primary"]
----
@Bean
@IntegrationConverter
public StringToIntegerConverter strToInt {
return new StringToIntegerConverter();
}
----
XML::
+
[source, xml, role="secondary"]
----
<int:converter ref="strToInt"/>
<bean id="strToInt" class="org.springframework.integration.util.Demo.StringToIntegerConverter"/>
----
======
Or on the `StringToIntegerConverter` class when it is marked with the `@Component` annotation for auto-scanning.
When the 'converter' element is parsed, it creates the `integrationConversionService` bean if one is not already defined.
With that converter in place, the `send` operation would now be successful, because the datatype channel uses that converter to convert the `String` payload to an `Integer`.
For more information regarding payload type conversion, see xref:endpoint.adoc#payload-type-conversion[Payload Type Conversion].
Beginning with version 4.0, the `integrationConversionService` is invoked by the `DefaultDatatypeChannelMessageConverter`, which looks up the conversion service in the application context.
To use a different conversion technique, you can specify the `message-converter` attribute on the channel.
This must be a reference to a `MessageConverter` implementation.
Only the `fromMessage` method is used.
It provides the converter with access to the message headers (in case the conversion might need information from the headers, such as `content-type`).
The method can return only the converted payload or a full `Message` object.
If the latter, the converter must be careful to copy all the headers from the inbound message.
Alternatively, you can declare a `<bean/>` of type `MessageConverter` with an ID of `datatypeChannelMessageConverter`, and that converter is used by all channels with a `datatype`.
[[channel-configuration-queuechannel]]
== `QueueChannel` Configuration
To create a `QueueChannel`, use the `<queue/>` sub-element.
You may specify the channel's capacity as follows:
[tabs]
======
Java::
+
[source, java, role="primary"]
----
@Bean
public PollableChannel queueChannel() {
return new QueueChannel(25);
}
----
XML::
+
[source, xml, role="secondary"]
----
<int:channel id="queueChannel">
<queue capacity="25"/>
</int:channel>
----
======
NOTE: If you do not provide a value for the 'capacity' attribute on this `<queue/>` sub-element, the resulting queue is unbounded.
To avoid issues such as running out of memory, we highly recommend that you set an explicit value for a bounded queue.
[[persistent-queuechannel-configuration]]
=== Persistent `QueueChannel` Configuration
Since a `QueueChannel` provides the capability to buffer messages but does so in-memory only by default, it also introduces a possibility that messages could be lost in the event of a system failure.
To mitigate this risk, a `QueueChannel` may be backed by a persistent implementation of the `MessageGroupStore` strategy interface.
For more details on `MessageGroupStore` and `MessageStore`, see xref:message-store.adoc[Message Store].
IMPORTANT: The `capacity` attribute is not allowed when the `message-store` attribute is used.
When a `QueueChannel` receives a `Message`, it adds the message to the message store.
When a `Message` is polled from a `QueueChannel`, it is removed from the message store.
By default, a `QueueChannel` stores its messages in an in-memory queue, which can lead to the lost message scenario mentioned earlier.
However, Spring Integration provides persistent stores, such as the `JdbcChannelMessageStore`.
You can configure a message store for any `QueueChannel` by adding the `message-store` attribute, as the following example shows:
[source,xml]
----
<int:channel id="dbBackedChannel">
<int:queue message-store="channelStore"/>
</int:channel>
<bean id="channelStore" class="o.s.i.jdbc.store.JdbcChannelMessageStore">
<property name="dataSource" ref="dataSource"/>
<property name="channelMessageStoreQueryProvider" ref="queryProvider"/>
</bean>
----
(See samples below for Java/Kotlin Configuration options.)
The Spring Integration JDBC module also provides a schema Data Definition Language (DDL) for a number of popular databases.
These schemas are located in the org.springframework.integration.jdbc.store.channel package of that module (`spring-integration-jdbc`).
IMPORTANT: One important feature is that, with any transactional persistent store (such as `JdbcChannelMessageStore`), as long as the poller has a transaction configured, a message removed from the store can be permanently removed only if the transaction completes successfully.
Otherwise, the transaction rolls back, and the `Message` is not lost.
Many other implementations of the message store are available as the growing number of Spring projects related to "`NoSQL`" data stores come to provide underlying support for these stores.
You can also provide your own implementation of the `MessageGroupStore` interface if you cannot find one that meets your particular needs.
Since version 4.0, we recommend that `QueueChannel` instances be configured to use a `ChannelMessageStore`, if possible.
These are generally optimized for this use, as compared to a general message store.
If the `ChannelMessageStore` is a `ChannelPriorityMessageStore`, the messages are received in FIFO within priority order.
The notion of priority is determined by the message store implementation.
For example, the following example shows the Java configuration for the xref:mongodb.adoc#mongodb-priority-channel-message-store[MongoDB Channel Message Store]:
[tabs]
======
Java::
+
[source, java, role="primary"]
----
@Bean
public BasicMessageGroupStore mongoDbChannelMessageStore(MongoDbFactory mongoDbFactory) {
MongoDbChannelMessageStore store = new MongoDbChannelMessageStore(mongoDbFactory);
store.setPriorityEnabled(true);
return store;
}
@Bean
public PollableChannel priorityQueue(BasicMessageGroupStore mongoDbChannelMessageStore) {
return new PriorityChannel(new MessageGroupQueue(mongoDbChannelMessageStore, "priorityQueue"));
}
----
Java DSL::
+
[source, java, role="secondary"]
----
@Bean
public IntegrationFlow priorityFlow(PriorityCapableChannelMessageStore mongoDbChannelMessageStore) {
return IntegrationFlow.from((Channels c) ->
c.priority("priorityChannel", mongoDbChannelMessageStore, "priorityGroup"))
....
.get();
}
----
Kotlin DSL::
+
[source, kotlin, role="secondary"]
----
@Bean
fun priorityFlow(mongoDbChannelMessageStore: PriorityCapableChannelMessageStore) =
integrationFlow {
channel { priority("priorityChannel", mongoDbChannelMessageStore, "priorityGroup") }
}
----
======
NOTE: Pay attention to the `MessageGroupQueue` class.
That is a `BlockingQueue` implementation to use the `MessageGroupStore` operations.
Another option to customize the `QueueChannel` environment is provided by the `ref` attribute of the `<int:queue>` sub-element or its particular constructor.
This attribute supplies the reference to any `java.util.Queue` implementation.
For example, a Hazelcast distributed https://hazelcast.com/use-cases/imdg/imdg-messaging/[`IQueue`] can be configured as follows:
[source,java]
----
@Bean
public HazelcastInstance hazelcastInstance() {
return Hazelcast.newHazelcastInstance(new Config()
.setProperty("hazelcast.logging.type", "log4j"));
}
@Bean
public PollableChannel distributedQueue() {
return new QueueChannel(hazelcastInstance()
.getQueue("springIntegrationQueue"));
}
----
[[channel-configuration-pubsubchannel]]
== `PublishSubscribeChannel` Configuration
To create a `PublishSubscribeChannel`, use the <publish-subscribe-channel/> element.
When using this element, you can also specify the `task-executor` used for publishing messages (if none is specified, it publishes in the sender's thread), as follows:
[tabs]
======
Java::
+
[source, java, role="primary"]
----
@Bean
public MessageChannel pubsubChannel() {
return new PublishSubscribeChannel(someExecutor());
}
----
XML::
+
[source, xml, role="secondary"]
----
<int:publish-subscribe-channel id="pubsubChannel" task-executor="someExecutor"/>
----
======
If you provide a resequencer or aggregator downstream from a `PublishSubscribeChannel`, you can set the 'apply-sequence' property on the channel to `true`.
Doing so indicates that the channel should set the `sequence-size` and `sequence-number` message headers as well as the correlation ID prior to passing along the messages.
For example, if there are five subscribers, the `sequence-size` would be set to `5`, and the messages would have `sequence-number` header values ranging from `1` to `5`.
Along with the `Executor`, you can also configure an `ErrorHandler`.
By default, the `PublishSubscribeChannel` uses a `MessagePublishingErrorHandler` implementation to send an error to the `MessageChannel` from the `errorChannel` header or into the global `errorChannel` instance.
If an `Executor` is not configured, the `ErrorHandler` is ignored and exceptions are thrown directly to the caller's thread.
If you provide a `Resequencer` or `Aggregator` downstream from a `PublishSubscribeChannel`, you can set the 'apply-sequence' property on the channel to `true`.
Doing so indicates that the channel should set the sequence-size and sequence-number message headers as well as the correlation ID prior to passing along the messages.
For example, if there are five subscribers, the sequence-size would be set to `5`, and the messages would have sequence-number header values ranging from `1` to `5`.
The following example shows how to set the `apply-sequence` header to `true`:
[tabs]
======
Java::
+
[source, java, role="primary"]
----
@Bean
public MessageChannel pubsubChannel() {
PublishSubscribeChannel channel = new PublishSubscribeChannel();
channel.setApplySequence(true);
return channel;
}
----
XML::
+
[source, xml, role="secondary"]
----
<int:publish-subscribe-channel id="pubsubChannel" apply-sequence="true"/>
----
======
NOTE: The `apply-sequence` value is `false` by default so that a publish-subscribe channel can send the exact same message instances to multiple outbound channels.
Since Spring Integration enforces immutability of the payload and header references, when the flag is set to `true`, the channel creates new `Message` instances with the same payload reference but different header values.
Starting with version 5.4.3, the `PublishSubscribeChannel` can also be configured with the `requireSubscribers` option of its `BroadcastingDispatcher` to indicate that this channel will not ignore a message silently when it has no subscribers.
A `MessageDispatchingException` with a `Dispatcher has no subscribers` message is thrown when there are no subscribers and this option is set to `true`.
[[channel-configuration-executorchannel]]
== `ExecutorChannel`
To create an `ExecutorChannel`, add the `<dispatcher>` sub-element with a `task-executor` attribute.
The attribute's value can reference any `TaskExecutor` within the context.
For example, doing so enables configuration of a thread pool for dispatching messages to subscribed handlers.
As mentioned earlier, doing so breaks the single-threaded execution context between sender and receiver so that any active transaction context is not shared by the invocation of the handler (that is, the handler may throw an `Exception`, but the `send` invocation has already returned successfully).
The following example shows how to use the `dispatcher` element and specify an executor in the `task-executor` attribute:
[tabs]
======
Java::
+
[source, java, role="primary"]
----
@Bean
public MessageChannel executorChannel() {
return new ExecutorChannel(someExecutor());
}
----
XML::
+
[source, xml, role="secondary"]
----
<int:channel id="executorChannel">
<int:dispatcher task-executor="someExecutor"/>
</int:channel>
----
======
[NOTE]
=====
The `load-balancer` and `failover` options are also both available on the <dispatcher/> sub-element, as described earlier in xref:channel/configuration.adoc#channel-configuration-directchannel[`DirectChannel` Configuration].
The same defaults apply.
Consequently, the channel has a round-robin load-balancing strategy with failover enabled unless explicit configuration is provided for one or both of those attributes, as the following example shows:
[source,xml]
----
<int:channel id="executorChannelWithoutFailover">
<int:dispatcher task-executor="someExecutor" failover="false"/>
</int:channel>
----
=====
[[channel-configuration-prioritychannel]]
== `PriorityChannel` Configuration
To create a `PriorityChannel`, use the `<priority-queue/>` sub-element, as the following example shows:
[tabs]
======
Java::
+
[source, java, role="primary"]
----
@Bean
public PollableChannel priorityChannel() {
return new PriorityChannel(20);
}
----
XML::
+
[source, xml, role="secondary"]
----
<int:channel id="priorityChannel">
<int:priority-queue capacity="20"/>
</int:channel>
----
======
By default, the channel consults the `priority` header of the message.
However, you can instead provide a custom `Comparator` reference.
Also, note that the `PriorityChannel` (like the other types) does support the `datatype` attribute.
As with the `QueueChannel`, it also supports a `capacity` attribute.
The following example demonstrates all of these:
[tabs]
======
Java::
+
[source, java, role="primary"]
----
@Bean
public PollableChannel priorityChannel() {
PriorityChannel channel = new PriorityChannel(20, widgetComparator());
channel.setDatatypes(example.Widget.class);
return channel;
}
----
XML::
+
[source, xml, role="secondary"]
----
<int:channel id="priorityChannel" datatype="example.Widget">
<int:priority-queue comparator="widgetComparator"
capacity="10"/>
</int:channel>
----
======
Since version 4.0, the `priority-channel` child element supports the `message-store` option (`comparator` and `capacity` are not allowed in that case).
The message store must be a `PriorityCapableChannelMessageStore`.
Implementations of the `PriorityCapableChannelMessageStore` are currently provided for `Redis`, `JDBC`, and `MongoDB`.
See xref:channel/configuration.adoc#channel-configuration-queuechannel[`QueueChannel` Configuration] and xref:message-store.adoc[Message Store] for more information.
You can find sample configuration in xref:jdbc/message-store.adoc#jdbc-message-store-channels[Backing Message Channels].
[[channel-configuration-rendezvouschannel]]
== `RendezvousChannel` Configuration
A `RendezvousChannel` is created when the queue sub-element is a `<rendezvous-queue>`.
It does not provide any additional configuration options to those described earlier, and its queue does not accept any capacity value, since it is a zero-capacity direct handoff queue.
The following example shows how to declare a `RendezvousChannel`:
[tabs]
======
Java::
+
[source, java, role="primary"]
----
@Bean
public PollableChannel rendezvousChannel() {
return new RendezvousChannel();
}
----
XML::
+
[source, xml, role="secondary"]
----
<int:channel id="rendezvousChannel"/>
<int:rendezvous-queue/>
</int:channel>
----
======
[[channel-configuration-threadlocalchannel]]
== Scoped Channel Configuration
Any channel can be configured with a `scope` attribute, as the following example shows:
[source,xml]
----
<int:channel id="threadLocalChannel" scope="thread"/>
----
// TODO What scope values are available? Can I create my own?
[[channel-configuration-interceptors]]
== Channel Interceptor Configuration
Message channels may also have interceptors, as described in xref:channel/interceptors.adoc[Channel Interceptors].
The `<interceptors/>` sub-element can be added to a `<channel/>` (or the more specific element types).
You can provide the `ref` attribute to reference any Spring-managed object that implements the `ChannelInterceptor` interface, as the following example shows:
[source,xml]
----
<int:channel id="exampleChannel">
<int:interceptors>
<ref bean="trafficMonitoringInterceptor"/>
</int:interceptors>
</int:channel>
----
In general, we recommend defining the interceptor implementations in a separate location, since they usually provide common behavior that can be reused across multiple channels.
[[global-channel-configuration-interceptors]]
== Global Channel Interceptor Configuration
Channel interceptors provide a clean and concise way of applying cross-cutting behavior per individual channel.
If the same behavior should be applied on multiple channels, configuring the same set of interceptors for each channel would not be the most efficient way.
To avoid repeated configuration while also enabling interceptors to apply to multiple channels, Spring Integration provides global interceptors.
Consider the following pair of examples:
[source,xml]
----
<int:channel-interceptor pattern="input*, thing2*, thing1, !cat*" order="3">
<bean class="thing1.thing2SampleInterceptor"/>
</int:channel-interceptor>
----
[source,xml]
----
<int:channel-interceptor ref="myInterceptor" pattern="input*, thing2*, thing1, !cat*" order="3"/>
<bean id="myInterceptor" class="thing1.thing2SampleInterceptor"/>
----
Each `<channel-interceptor/>` element lets you define a global interceptor, which is applied on all channels that match any patterns defined by the `pattern` attribute.
In the preceding case, the global interceptor is applied on the 'thing1' channel and all other channels that begin with 'thing2' or 'input' but not to channels starting with 'thing3' (since version 5.0).
WARNING: The addition of this syntax to the pattern causes one possible (though perhaps unlikely) problem.
If you have a bean named `!thing1` and you included a pattern of `!thing1` in your channel interceptor's `pattern` patterns, it no longer matches.
The pattern now matches all beans not named `thing1`.
In this case, you can escape the `!` in the pattern with `\`.
The pattern `\!thing1` matches a bean named `!thing1`.
The order attribute lets you manage where this interceptor is injected when there are multiple interceptors on a given channel.
For example, channel 'inputChannel' could have individual interceptors configured locally (see below), as the following example shows:
[source,xml]
----
<int:channel id="inputChannel">
<int:interceptors>
<int:wire-tap channel="logger"/>
</int:interceptors>
</int:channel>
----
A reasonable question is "`how is a global interceptor injected in relation to other interceptors configured locally or through other global interceptor definitions?`"
The current implementation provides a simple mechanism for defining the order of interceptor execution.
A positive number in the `order` attribute ensures interceptor injection after any existing interceptors, while a negative number ensures that the interceptor is injected before existing interceptors.
This means that, in the preceding example, the global interceptor is injected after (since its `order` is greater than `0`) the 'wire-tap' interceptor configured locally.
If there were another global interceptor with a matching `pattern`, its order would be determined by comparing the values of both interceptors' `order` attributes.
To inject a global interceptor before the existing interceptors, use a negative value for the `order` attribute.
NOTE: Note that both the `order` and `pattern` attributes are optional.
The default value for `order` will be 0 and for `pattern`, the default is '*' (to match all channels).
[[channel-wiretap]]
== Wire Tap
As mentioned earlier, Spring Integration provides a simple wire tap interceptor.
You can configure a wire tap on any channel within an `<interceptors/>` element.
Doing so is especially useful for debugging and can be used in conjunction with Spring Integration's logging channel adapter as follows:
[source,xml]
----
<int:channel id="in">
<int:interceptors>
<int:wire-tap channel="logger"/>
</int:interceptors>
</int:channel>
<int:logging-channel-adapter id="logger" level="DEBUG"/>
----
TIP: The 'logging-channel-adapter' also accepts an 'expression' attribute so that you can evaluate a SpEL expression against the 'payload' and 'headers' variables.
Alternatively, to log the full message `toString()` result, provide a value of `true` for the 'log-full-message' attribute.
By default, it is `false` so that only the payload is logged.
Setting it to `true` enables logging of all headers in addition to the payload.
The 'expression' option provides the most flexibility (for example, `expression="payload.user.name"`).
One of the common misconceptions about the wire tap and other similar components (xref:message-publishing.adoc#message-publishing-config[Message Publishing Configuration]) is that they are automatically asynchronous in nature.
By default, wire tap as a component is not invoked asynchronously.
Instead, Spring Integration focuses on a single unified approach to configuring asynchronous behavior: the message channel.
What makes certain parts of the message flow synchronous or asynchronous is the type of Message Channel that has been configured within that flow.
That is one of the primary benefits of the message channel abstraction.
From the inception of the framework, we have always emphasized the need and the value of the message channel as a first-class citizen of the framework.
It is not just an internal, implicit realization of the EIP pattern.
It is fully exposed as a configurable component to the end user.
So, the wire tap component is only responsible for performing the following tasks:
* Intercept a message flow by tapping into a channel (for example, `channelA`)
* Grab each message
* Send the message to another channel (for example, `channelB`)
It is essentially a variation of the bridge pattern, but it is encapsulated within a channel definition (and hence easier to enable and disable without disrupting a flow).
Also, unlike the bridge, it basically forks another message flow.
Is that flow synchronous or asynchronous? The answer depends on the type of message channel that 'channelB' is.
We have the following options: direct channel, pollable channel, and executor channel.
The last two break the thread boundary, making communication over such channels asynchronous, because the dispatching of the message from that channel to its subscribed handlers happens on a different thread than the one used to send the message to that channel.
That is what is going to make your wire-tap flow synchronous or asynchronous.
It is consistent with other components within the framework (such as message publisher) and adds a level of consistency and simplicity by sparing you from worrying in advance (other than writing thread-safe code) about whether a particular piece of code should be implemented as synchronous or asynchronous.
The actual wiring of two pieces of code (say, component A and component B) over a message channel is what makes their collaboration synchronous or asynchronous.
You may even want to change from synchronous to asynchronous in the future, and message channel lets you do it swiftly without ever touching the code.
One final point regarding the wire tap is that, despite the rationale provided above for not being asynchronous by default, you should keep in mind that it is usually desirable to hand off the message as soon as possible.
Therefore, it would be quite common to use an asynchronous channel option as the wire tap's outbound channel.
However, the asynchronous behavior is not enforced by default.
There are a number of use cases that would break if we did, including that you might not want to break a transactional boundary.
Perhaps you use the wire tap pattern for auditing purposes, and you do want the audit messages to be sent within the original transaction.
As an example, you might connect the wire tap to a JMS outbound channel adapter.
That way, you get the best of both worlds: 1) the sending of a JMS Message can occur within the transaction while 2) it is still a "`fire-and-forget`" action, thereby preventing any noticeable delay in the main message flow.
IMPORTANT: Starting with version 4.0, it is important to avoid circular references when an interceptor (such as the https://docs.spring.io/autorepo/docs/spring-integration/current/api/org/springframework/integration/channel/interceptor/WireTap.html[`WireTap` class]) references a channel.
You need to exclude such channels from those being intercepted by the current interceptor.
This can be done with appropriate patterns or programmatically.
If you have a custom `ChannelInterceptor` that references a `channel`, consider implementing `VetoCapableInterceptor`.
That way, the framework asks the interceptor if it is OK to intercept each channel that is a candidate, based on the supplied pattern.
You can also add runtime protection in the interceptor methods to ensure that the channel is not one that is referenced by the interceptor.
The `WireTap` uses both of these techniques.
Starting with version 4.3, the `WireTap` has additional constructors that take a `channelName` instead of a
`MessageChannel` instance.
This can be convenient for Java configuration and when channel auto-creation logic is being used.
The target `MessageChannel` bean is resolved from the provided `channelName` later, on the first interaction with the
interceptor.
IMPORTANT: Channel resolution requires a `BeanFactory`, so the wire tap instance must be a Spring-managed bean.
This late-binding approach also allows simplification of typical wire-tapping patterns with Java DSL configuration, as the following example shows:
[source,java]
----
@Bean
public PollableChannel myChannel() {
return MessageChannels.queue()
.wireTap("loggingFlow.input")
.get();
}
@Bean
public IntegrationFlow loggingFlow() {
return f -> f.log();
}
----
[[conditional-wiretap]]
== Conditional Wire Taps
Wire taps can be made conditional by using the `selector` or `selector-expression` attributes.
The `selector` references a `MessageSelector` bean, which can determine at runtime whether the message should go to the tap channel.
Similarly, the `selector-expression` is a boolean SpEL expression that performs the same purpose: If the expression evaluates to `true`, the message is sent to the tap channel.
[[channel-global-wiretap]]
== Global Wire Tap Configuration
It is possible to configure a global wire tap as a special case of the xref:channel/configuration.adoc#global-channel-configuration-interceptors[Global Channel Interceptor Configuration].
To do so, configure a top level `wire-tap` element.
Now, in addition to the normal `wire-tap` namespace support, the `pattern` and `order` attributes are supported and work in exactly the same way as they do for the `channel-interceptor`.
The following example shows how to configure a global wire tap:
[tabs]
======
Java::
+
[source, java, role="primary"]
----
@Bean
@GlobalChannelInterceptor(patterns = "input*,thing2*,thing1", order = 3)
public WireTap wireTap(MessageChannel wiretapChannel) {
return new WireTap(wiretapChannel);
}
----
XML::
+
[source, xml, role="secondary"]
----
<int:wire-tap pattern="input*, thing2*, thing1" order="3" channel="wiretapChannel"/>
----
======
TIP: A global wire tap provides a convenient way to configure a single-channel wire tap externally without modifying the existing channel configuration.
To do so, set the `pattern` attribute to the target channel name.
For example, you can use this technique to configure a test case to verify messages on a channel.

View File

@@ -0,0 +1,197 @@
[[channel-implementations]]
= Message Channel Implementations
Spring Integration provides different message channel implementations.
The following sections briefly describe each one.
[[channel-implementations-publishsubscribechannel]]
== `PublishSubscribeChannel`
The `PublishSubscribeChannel` implementation broadcasts any `Message` sent to it to all of its subscribed handlers.
This is most often used for sending event messages, whose primary role is notification (as opposed to document messages, which are generally intended to be processed by a single handler).
Note that the `PublishSubscribeChannel` is intended for sending only.
Since it broadcasts to its subscribers directly when its `send(Message)` method is invoked, consumers cannot poll for messages (it does not implement `PollableChannel` and therefore has no `receive()` method).
Instead, any subscriber must itself be a `MessageHandler`, and the subscriber's `handleMessage(Message)` method is invoked in turn.
Prior to version 3.0, invoking the `send` method on a `PublishSubscribeChannel` that had no subscribers returned `false`.
When used in conjunction with a `MessagingTemplate`, a `MessageDeliveryException` was thrown.
Starting with version 3.0, the behavior has changed such that a `send` is always considered successful if at least the minimum subscribers are present (and successfully handle the message).
This behavior can be modified by setting the `minSubscribers` property, which defaults to `0`.
NOTE: If you use a `TaskExecutor`, only the presence of the correct number of subscribers is used for this determination, because the actual handling of the message is performed asynchronously.
[[channel-implementations-queuechannel]]
== `QueueChannel`
The `QueueChannel` implementation wraps a queue.
Unlike the `PublishSubscribeChannel`, the `QueueChannel` has point-to-point semantics.
In other words, even if the channel has multiple consumers, only one of them should receive any `Message` sent to that channel.
It provides a default no-argument constructor (providing an essentially unbounded capacity of `Integer.MAX_VALUE`) as well as a constructor that accepts the queue capacity, as the following listing shows:
[source,java]
----
public QueueChannel(int capacity)
----
A channel that has not reached its capacity limit stores messages in its internal queue, and the `send(Message<?>)` method returns immediately, even if no receiver is ready to handle the message.
If the queue has reached capacity, the sender blocks until room is available in the queue.
Alternatively, if you use the send method that has an additional timeout parameter, the queue blocks until either room is available or the timeout period elapses, whichever occurs first.
Similarly, a `receive()` call returns immediately if a message is available on the queue, but, if the queue is empty, then a receive call may block until either a message is available or the timeout, if provided, elapses.
In either case, it is possible to force an immediate return regardless of the queue's state by passing a timeout value of 0.
Note, however, that calls to the versions of `send()` and `receive()` with no `timeout` parameter block indefinitely.
[[channel-implementations-prioritychannel]]
== `PriorityChannel`
Whereas the `QueueChannel` enforces first-in-first-out (FIFO) ordering, the `PriorityChannel` is an alternative implementation that allows for messages to be ordered within the channel based upon a priority.
By default, the priority is determined by the `priority` header within each message.
However, for custom priority determination logic, a comparator of type `Comparator<Message<?>>` can be provided to the `PriorityChannel` constructor.
[[channel-implementations-rendezvouschannel]]
== `RendezvousChannel`
The `RendezvousChannel` enables a "`direct-handoff`" scenario, wherein a sender blocks until another party invokes the channel's `receive()` method.
The other party blocks until the sender sends the message.
Internally, this implementation is quite similar to the `QueueChannel`, except that it uses a `SynchronousQueue` (a zero-capacity implementation of `BlockingQueue`).
This works well in situations where the sender and receiver operate in different threads, but asynchronously dropping the message in a queue is not appropriate.
In other words, with a `RendezvousChannel`, the sender knows that some receiver has accepted the message, whereas with a `QueueChannel`, the message would have been stored to the internal queue and potentially never received.
TIP: Keep in mind that all of these queue-based channels are storing messages in-memory only by default.
When persistence is required, you can either provide a 'message-store' attribute within the 'queue' element to reference a persistent `MessageStore` implementation or you can replace the local channel with one that is backed by a persistent broker, such as a JMS-backed channel or channel adapter.
The latter option lets you take advantage of any JMS provider's implementation for message persistence, as discussed in xref:jms.adoc[JMS Support].
However, when buffering in a queue is not necessary, the simplest approach is to rely upon the `DirectChannel`, discussed in the next section.
The `RendezvousChannel` is also useful for implementing request-reply operations.
The sender can create a temporary, anonymous instance of `RendezvousChannel`, which it then sets as the 'replyChannel' header when building a `Message`.
After sending that `Message`, the sender can immediately call `receive` (optionally providing a timeout value) in order to block while waiting for a reply `Message`.
This is very similar to the implementation used internally by many of Spring Integration's request-reply components.
[[channel-implementations-directchannel]]
== `DirectChannel`
The `DirectChannel` has point-to-point semantics but otherwise is more similar to the `PublishSubscribeChannel` than any of the queue-based channel implementations described earlier.
It implements the `SubscribableChannel` interface instead of the `PollableChannel` interface, so it dispatches messages directly to a subscriber.
As a point-to-point channel, however, it differs from the `PublishSubscribeChannel` in that it sends each `Message` to a single subscribed `MessageHandler`.
In addition to being the simplest point-to-point channel option, one of its most important features is that it enables a single thread to perform the operations on "`both sides`" of the channel.
For example, if a handler subscribes to a `DirectChannel`, then sending a `Message` to that channel triggers invocation of that handler's `handleMessage(Message)` method directly in the sender's thread, before the `send()` method invocation can return.
The key motivation for providing a channel implementation with this behavior is to support transactions that must span across the channel while still benefiting from the abstraction and loose coupling that the channel provides.
If the `send()` call is invoked within the scope of a transaction, the outcome of the handler's invocation (for example, updating a database record) plays a role in determining the ultimate result of that transaction (commit or rollback).
NOTE: Since the `DirectChannel` is the simplest option and does not add any additional overhead that would be required for scheduling and managing the threads of a poller, it is the default channel type within Spring Integration.
The general idea is to define the channels for an application, consider which of those need to provide buffering or to throttle input, and modify those to be queue-based `PollableChannels`.
Likewise, if a channel needs to broadcast messages, it should not be a `DirectChannel` but rather a `PublishSubscribeChannel`.
Later, we show how each of these channels can be configured.
The `DirectChannel` internally delegates to a message dispatcher to invoke its subscribed message handlers, and that dispatcher can have a load-balancing strategy exposed by `load-balancer` or `load-balancer-ref` attributes (mutually exclusive).
The load balancing strategy is used by the message dispatcher to help determine how messages are distributed amongst message handlers when multiple message handlers subscribe to the same channel.
As a convenience, the `load-balancer` attribute exposes an enumeration of values pointing to pre-existing implementations of `LoadBalancingStrategy`.
A `round-robin` (load-balances across the handlers in rotation) and `none` (for the cases where one wants to explicitly disable load balancing) are the only available values.
Other strategy implementations may be added in future versions.
However, since version 3.0, you can provide your own implementation of the `LoadBalancingStrategy` and inject it by using the `load-balancer-ref` attribute, which should point to a bean that implements `LoadBalancingStrategy`, as the following example shows:
A `FixedSubscriberChannel` is a `SubscribableChannel` that only supports a single `MessageHandler` subscriber that cannot be unsubscribed.
This is useful for high-throughput performance use-cases when no other subscribers are involved and no channel interceptors are needed.
[source,xml]
----
<int:channel id="lbRefChannel">
<int:dispatcher load-balancer-ref="lb"/>
</int:channel>
<bean id="lb" class="foo.bar.SampleLoadBalancingStrategy"/>
----
Note that the `load-balancer` and `load-balancer-ref` attributes are mutually exclusive.
The load-balancing also works in conjunction with a boolean `failover` property.
If the `failover` value is true (the default), the dispatcher falls back to any subsequent handlers (as necessary) when preceding handlers throw exceptions.
The order is determined by an optional order value defined on the handlers themselves or, if no such value exists, the order in which the handlers subscribed.
If a certain situation requires that the dispatcher always try to invoke the first handler and then fall back in the same fixed order sequence every time an error occurs, no load-balancing strategy should be provided.
In other words, the dispatcher still supports the `failover` boolean property even when no load-balancing is enabled.
Without load-balancing, however, the invocation of handlers always begins with the first, according to their order.
For example, this approach works well when there is a clear definition of primary, secondary, tertiary, and so on.
When using the namespace support, the `order` attribute on any endpoint determines the order.
NOTE: Keep in mind that load-balancing and `failover` apply only when a channel has more than one subscribed message handler.
When using the namespace support, this means that more than one endpoint shares the same channel reference defined in the `input-channel` attribute.
Starting with version 5.2, when `failover` is true, a failure of the current handler together with the failed message is logged under `debug` or `info` if configured respectively.
[[executor-channel]]
== `ExecutorChannel`
The `ExecutorChannel` is a point-to-point channel that supports the same dispatcher configuration as `DirectChannel` (load-balancing strategy and the `failover` boolean property).
The key difference between these two dispatching channel types is that the `ExecutorChannel` delegates to an instance of `TaskExecutor` to perform the dispatch.
This means that the send method typically does not block, but it also means that the handler invocation may not occur in the sender's thread.
It therefore does not support transactions that span the sender and receiving handler.
CAUTION: The sender can sometimes block.
For example, when using a `TaskExecutor` with a rejection policy that throttles the client (such as the `ThreadPoolExecutor.CallerRunsPolicy`), the sender's thread can execute the method any time the thread pool is at its maximum capacity and the executor's work queue is full.
Since that situation would only occur in a non-predictable way, you should not rely upon it for transactions.
[[partitioned-channel]]
== `PartitionedChannel`
Starting with version 6.1, a `PartitionedChannel` implementation is provided.
This is an extension of `AbstractExecutorChannel` and represents point-to-point dispatching logic where the actual consumption is processed on a specific thread, determined by the partition key evaluated from a message sent to this channel.
This channel is similar to the `ExecutorChannel` mentioned above, but with the difference that messages with the same partition key are always handled in the same thread, preserving ordering.
It does not require an external `TaskExecutor`, but can be configured with a custom `ThreadFactory` (e.g. `Thread.ofVirtual().name("partition-", 0).factory()`).
This factory is used to populate single-thread executors into a `MessageDispatcher` delegate, per partition.
By default, the `IntegrationMessageHeaderAccessor.CORRELATION_ID` message header is used as the partition key.
This channel can be configured as a simple bean:
[source,java]
----
@Bean
PartitionedChannel somePartitionedChannel() {
return new PartitionedChannel(3, (message) -> message.getHeaders().get("partitionKey"));
}
----
The channel will have `3` partitions - dedicated threads; will use the `partitionKey` header to determine in which partition the message will be handled.
See `PartitionedChannel` class Javadocs for more information.
[[flux-message-channel]]
== `FluxMessageChannel`
The `FluxMessageChannel` is an `org.reactivestreams.Publisher` implementation for `"sinking"` sent messages into an internal `reactor.core.publisher.Flux` for on demand consumption by reactive subscribers downstream.
This channel implementation is neither a `SubscribableChannel`, nor a `PollableChannel`, so only `org.reactivestreams.Subscriber` instances can be used to consume from this channel honoring back-pressure nature of reactive streams.
On the other hand, the `FluxMessageChannel` implements a `ReactiveStreamsSubscribableChannel` with its `subscribeTo(Publisher<Message<?>>)` contract allowing receiving events from reactive source publishers, bridging a reactive stream into the integration flow.
To achieve fully reactive behavior for the whole integration flow, such a channel must be placed between all the endpoints in the flow.
See xref:reactive-streams.adoc[Reactive Streams Support] for more information about interaction with Reactive Streams.
[[channel-implementations-threadlocalchannel]]
== Scoped Channel
Spring Integration 1.0 provided a `ThreadLocalChannel` implementation, but that has been removed as of 2.0.
Now the more general way to handle the same requirement is to add a `scope` attribute to a channel.
The value of the attribute can be the name of a scope that is available within the context.
For example, in a web environment, certain scopes are available, and any custom scope implementations can be registered with the context.
The following example shows a thread-local scope being applied to a channel, including the registration of the scope itself:
[source,xml]
----
<int:channel id="threadScopedChannel" scope="thread">
<int:queue />
</int:channel>
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="thread" value="org.springframework.context.support.SimpleThreadScope" />
</map>
</property>
</bean>
----
The channel defined in the previous example also delegates to a queue internally, but the channel is bound to the current thread, so the contents of the queue are similarly bound.
That way, the thread that sends to the channel can later receive those same messages, but no other thread would be able to access them.
While thread-scoped channels are rarely needed, they can be useful in situations where `DirectChannel` instances are being used to enforce a single thread of operation but any reply messages should be sent to a "`terminal`" channel.
If that terminal channel is thread-scoped, the original sending thread can collect its replies from the terminal channel.
Now, since any channel can be scoped, you can define your own scopes in addition to thread-Local.

View File

@@ -0,0 +1,70 @@
[[channel-interceptors]]
= Channel Interceptors
One of the advantages of a messaging architecture is the ability to provide common behavior and capture meaningful information about the messages passing through the system in a non-invasive way.
Since the `Message` instances are sent to and received from `MessageChannel` instances, those channels provide an opportunity for intercepting the send and receive operations.
The `ChannelInterceptor` strategy interface, shown in the following listing, provides methods for each of those operations:
[source,java]
----
public interface ChannelInterceptor {
Message<?> preSend(Message<?> message, MessageChannel channel);
void postSend(Message<?> message, MessageChannel channel, boolean sent);
void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, Exception ex);
boolean preReceive(MessageChannel channel);
Message<?> postReceive(Message<?> message, MessageChannel channel);
void afterReceiveCompletion(Message<?> message, MessageChannel channel, Exception ex);
}
----
After implementing the interface, registering the interceptor with a channel is just a matter of making the following call:
[source,java]
----
channel.addInterceptor(someChannelInterceptor);
----
The methods that return a `Message` instance can be used for transforming the `Message` or can return 'null' to prevent further processing (of course, any of the methods can throw a `RuntimeException`).
Also, the `preReceive` method can return `false` to prevent the receive operation from proceeding.
NOTE: Keep in mind that `receive()` calls are only relevant for `PollableChannels`.
In fact, the `SubscribableChannel` interface does not even define a `receive()` method.
The reason for this is that when a `Message` is sent to a `SubscribableChannel`, it is sent directly to zero or more subscribers, depending on the type of channel (for example,
a `PublishSubscribeChannel` sends to all of its subscribers).
Therefore, the `preReceive(...)`, `postReceive(...)`, and `afterReceiveCompletion(...)` interceptor methods are invoked only when the interceptor is applied to a `PollableChannel`.
Spring Integration also provides an implementation of the https://www.enterpriseintegrationpatterns.com/WireTap.html[Wire Tap] pattern.
It is a simple interceptor that sends the `Message` to another channel without otherwise altering the existing flow.
It can be very useful for debugging and monitoring.
An example is shown in xref:channel/configuration.adoc#channel-wiretap[Wire Tap].
Because it is rarely necessary to implement all of the interceptor methods, the interface provides no-op methods (those returning `void` method have no code, the `Message`-returning methods return the `Message` as-is, and the `boolean` method returns `true`).
TIP: The order of invocation for the interceptor methods depends on the type of channel.
As described earlier, the queue-based channels are the only ones where the `receive()` method is intercepted in the first place.
Additionally, the relationship between send and receive interception depends on the timing of the separate sender and receiver threads.
For example, if a receiver is already blocked while waiting for a message, the order could be as follows: `preSend`, `preReceive`, `postReceive`, `postSend`.
However, if a receiver polls after the sender has placed a message on the channel and has already returned, the order would be as follows: `preSend`, `postSend` (some-time-elapses), `preReceive`, `postReceive`.
The time that elapses in such a case depends on a number of factors and is therefore generally unpredictable (in fact, the receive may never happen).
The type of queue also plays a role (for example, rendezvous versus priority).
In short, you cannot rely on the order beyond the fact that `preSend` precedes `postSend` and `preReceive` precedes `postReceive`.
Starting with Spring Framework 4.1 and Spring Integration 4.1, the `ChannelInterceptor` provides new methods: `afterSendCompletion()` and `afterReceiveCompletion()`.
They are invoked after `send()' and 'receive()` calls, regardless of any exception that is raised, which allow for resource cleanup.
Note that the channel invokes these methods on the `ChannelInterceptor` list in the reverse order of the initial `preSend()` and `preReceive()` calls.
Starting with version 5.1, global channel interceptors now apply to dynamically registered channels - such as through beans that are initialized by using `beanFactory.initializeBean()` or `IntegrationFlowContext` when using the Java DSL.
Previously, interceptors were not applied when beans were created after the application context was refreshed.
Also, starting with version 5.1, `ChannelInterceptor.postReceive()` is no longer called when no message is received; it is no longer necessary to check for a `null` `Message<?>`.
Previously, the method was called.
If you have an interceptor that relies on the previous behavior, implement `afterReceiveCompleted()` instead, since that method is invoked, regardless of whether a message is received or not.
NOTE: Starting with version 5.2, the `ChannelInterceptorAware` is deprecated in favor of `InterceptableChannel` from the Spring Messaging module, which it extends now for backward compatibility.

View File

@@ -0,0 +1,56 @@
[[channel-interfaces]]
= The MessageChannel Interface
Spring Integration's top-level `MessageChannel` interface is defined as follows:
[source,java]
----
public interface MessageChannel {
boolean send(Message message);
boolean send(Message message, long timeout);
}
----
When sending a message, the return value is `true` if the message is sent successfully.
If the send call times out or is interrupted, it returns `false`.
[[channel-interfaces-pollablechannel]]
== `PollableChannel`
Since message channels may or may not buffer messages (as discussed in the xref:overview.adoc[Spring Integration Overview]), two sub-interfaces define the buffering (pollable) and non-buffering (subscribable) channel behavior.
The following listing shows the definition of the `PollableChannel` interface:
[source,java]
----
public interface PollableChannel extends MessageChannel {
Message<?> receive();
Message<?> receive(long timeout);
}
----
As with the send methods, when receiving a message, the return value is null in the case of a timeout or interrupt.
[[channel-interfaces-subscribablechannel]]
== `SubscribableChannel`
The `SubscribableChannel` base interface is implemented by channels that send messages directly to their subscribed `MessageHandler` instances.
Therefore, they do not provide receive methods for polling.
Instead, they define methods for managing those subscribers.
The following listing shows the definition of the `SubscribableChannel` interface:
[source,java]
----
public interface SubscribableChannel extends MessageChannel {
boolean subscribe(MessageHandler handler);
boolean unsubscribe(MessageHandler handler);
}
----

View File

@@ -0,0 +1,16 @@
[[channel-special-channels]]
= Special Channels
:page-section-summary-toc: 1
Two special channels are defined within the application context by default: `errorChannel` and `nullChannel`.
The 'nullChannel' (an instance of `NullChannel`) acts like `/dev/null`, logging any message sent to it at the `DEBUG` level and returning immediately.
The special treatment is applied for an `org.reactivestreams.Publisher` payload of a transmitted message: it is subscribed to in this channel immediately, to initiate reactive stream processing, although the data is discarded.
An error thrown from a reactive stream processing (see `Subscriber.onError(Throwable)`) is logged under the `warn` level for possible investigation.
If there is need to do anything with such an error, the `xref:handler-advice/reactive.adoc[ReactiveRequestHandlerAdvice]` with a `Mono.doOnError()` customization can be applied to the message handler producing `Mono` reply into this `nullChannel`.
Any time you face channel resolution errors for a reply that you do not care about, you can set the affected component's `output-channel` attribute to 'nullChannel' (the name, 'nullChannel', is reserved within the application context).
The 'errorChannel' is used internally for sending error messages and may be overridden with a custom configuration.
This is discussed in greater detail in xref:scatter-gather.adoc#scatter-gather-error-handling[Error Handling].
See also xref:dsl/java-channels.adoc[Message Channels] in the Java DSL chapter for more information about message channel and interceptors.

View File

@@ -0,0 +1,33 @@
[[channel-template]]
= `MessagingTemplate`
When the endpoints and their various configuration options are introduced, Spring Integration provides a foundation for messaging components that enables non-invasive invocation of your application code from the messaging system.
However, it is sometimes necessary to invoke the messaging system from your application code.
For convenience when implementing such use cases, Spring Integration provides a `MessagingTemplate` that supports a variety of operations across the message channels, including request and reply scenarios.
For example, it is possible to send a request and wait for a reply, as follows:
[source,java]
----
MessagingTemplate template = new MessagingTemplate();
Message reply = template.sendAndReceive(someChannel, new GenericMessage("test"));
----
In the preceding example, a temporary anonymous channel would be created internally by the template.
The 'sendTimeout' and 'receiveTimeout' properties may also be set on the template, and other exchange types are also supported.
The following listing shows the signatures for such methods:
[source,java]
----
public boolean send(final MessageChannel channel, final Message<?> message) { ...
}
public Message<?> sendAndReceive(final MessageChannel channel, final Message<?> request) { ...
}
public Message<?> receive(final PollableChannel<?> channel) { ...
}
----
NOTE: A less invasive approach that lets you invoke simple interfaces with payload or header values instead of `Message` instances is described in xref:gateway.adoc#gateway-proxy[Enter the `GatewayProxyFactoryBean`].

View File

@@ -0,0 +1,186 @@
[[claim-check]]
= Claim Check
In earlier sections, we covered several content enricher components that can help you deal with situations where a message is missing a piece of data.
We also discussed content filtering, which lets you remove data items from a message.
However, there are times when we want to hide data temporarily.
For example, in a distributed system, we may receive a message with a very large payload.
Some intermittent message processing steps may not need access to this payload and some may only need to access certain headers, so carrying the large message payload through each processing step may cause performance degradation, may produce a security risk, and may make debugging more difficult.
The https://www.enterpriseintegrationpatterns.com/StoreInLibrary.html[store in library] (or claim check) pattern describes a mechanism that lets you store data in a well known place while maintaining only a pointer (a claim check) to where that data is located.
You can pass that pointer around as the payload of a new message, thereby letting any component within the message flow get the actual data as soon as it needs it.
This approach is very similar to the certified mail process, where you get a claim check in your mailbox and then have to go to the post office to claim your actual package.
It is also the same idea as baggage claim after a flight or in a hotel.
Spring Integration provides two types of claim check transformers:
* Incoming Claim Check Transformer
* Outgoing Claim Check Transformer
Convenient namespace-based mechanisms are available to configure them.
[[claim-check-in]]
== Incoming Claim Check Transformer
An incoming claim check transformer transforms an incoming message by storing it in the message store identified by its `message-store` attribute.
The following example defines an incoming claim check transformer:
[source,xml]
----
<int:claim-check-in id="checkin"
input-channel="checkinChannel"
message-store="testMessageStore"
output-channel="output"/>
----
In the preceding configuration, the message that is received on the `input-channel` is persisted to the message store identified with the `message-store` attribute and indexed with a generated ID.
That ID is the claim check for that message.
The claim check also becomes the payload of the new (transformed) message that is sent to the `output-channel`.
Now, assume that at some point you do need access to the actual message.
You can access the message store manually and get the contents of the message, or you can use the same approach (creating a transformer) except that now you transform the Claim Check to the actual message by using an outgoing claim check transformer.
The following listing provides an overview of all available parameters of an incoming claim check transformer:
[source,xml]
----
<int:claim-check-in auto-startup="true" <1>
id="" <2>
input-channel="" <3>
message-store="messageStore" <4>
order="" <5>
output-channel="" <6>
send-timeout=""> <7>
<int:poller></int:poller> <8>
</int:claim-check-in>
----
<1> Lifecycle attribute signaling whether this component should be started during application context startup.
It defaults to `true`.
This attribute is not available inside a `Chain` element.
Optional.
<2> ID identifying the underlying bean definition (`MessageTransformingHandler`).
This attribute is not available inside a `Chain` element.
Optional.
<3> The receiving message channel of this endpoint.
This attribute is not available inside a `Chain` element.
Optional.
<4> Reference to the `MessageStore` to be used by this claim check transformer.
If not specified, the default reference is to a bean named `messageStore`.
Optional.
<5> Specifies the order for invocation when this endpoint is connected as a subscriber to a channel.
This is particularly relevant when that channel uses a `failover` dispatching strategy.
It has no effect when this endpoint is itself a polling consumer for a channel with a queue.
This attribute is not available inside a `Chain` element.
Optional.
<6> Identifies the message channel where the message is sent after being processed by this endpoint.
This attribute is not available inside a `Chain` element.
Optional.
<7> Specifies the maximum amount of time (in milliseconds) to wait when sending a reply message to the output channel.
Defaults to `30` seconds.
This attribute is not available inside a `Chain` element.
Optional.
<8> Defines a poller.
This element is not available inside a `Chain` element.
Optional.
[[claim-check-out]]
== Outgoing Claim Check Transformer
An outgoing claim check transformer lets you transform a message with a claim check payload into a message with the original content as its payload.
[source,xml]
----
<int:claim-check-out id="checkout"
input-channel="checkoutChannel"
message-store="testMessageStore"
output-channel="output"/>
----
In the preceding configuration, the message received on the `input-channel` should have a claim check as its payload.
The outgoing claim check transformer transforms it into a message with the original payload by querying the message store for a message identified by the provided claim check.
It then sends the newly checked-out message to the `output-channel`.
The following listing provides an overview of all available parameters of an outgoing claim check transformer:
[source,xml]
----
<int:claim-check-out auto-startup="true" <1>
id="" <2>
input-channel="" <3>
message-store="messageStore" <4>
order="" <5>
output-channel="" <6>
remove-message="false" <7>
send-timeout=""> <8>
<int:poller></int:poller> <9>
</int:claim-check-out>
----
<1> Lifecycle attribute signaling whether this component should be started during application context startup.
It defaults to `true`.
This attribute is not available inside a `Chain` element.
Optional.
<2> ID identifying the underlying bean definition (`MessageTransformingHandler`).
This attribute is not available inside a `Chain` element.
Optional.
<3> The receiving message channel of this endpoint.
This attribute is not available inside a `Chain` element.
Optional.
<4> Reference to the `MessageStore` to be used by this claim check transformer.
If not specified, the default reference is to a bean named `messageStore`.
Optional.
<5> Specifies the order for invocation when this endpoint is connected as a subscriber to a channel.
This is particularly relevant when that channel is using a `failover` dispatching strategy.
It has no effect when this endpoint is itself a polling consumer for a channel with a queue.
This attribute is not available inside a `Chain` element.
Optional.
<6> Identifies the message channel where the message is sent after being processed by this endpoint.
This attribute is not available inside a `Chain` element.
Optional.
<7> If set to `true`, the message is removed from the `MessageStore` by this transformer.
This setting is useful when Message can be "`claimed`" only once.
It defaults to `false`.
Optional.
<8> Specifies the maximum amount of time (in milliseconds) to wait when sending a reply message to the output channel.
It defaults to `30` seconds.
This attribute is not available inside a `Chain` element.
Optional.
<9> Defines a poller.
This element is not available inside a `Chain` element.
Optional.
[[claim-once]]
== Claim Once
Sometimes, a particular message must be claimed only once.
As an analogy, consider process of handling airplane luggage.
You're checking in your luggage on departure and claiming it on arrival.
Once the luggage has been claimed, it can not be claimed again without first checking it back in.
To accommodate such cases, we introduced a `remove-message` boolean attribute on the `claim-check-out` transformer.
This attribute is set to `false` by default.
However, if set to `true`, the claimed message is removed from the `MessageStore` so that it cannot be claimed again.
This feature has an impact in terms of storage space, especially in the case of the in-memory `Map`-based `SimpleMessageStore`, where failing to remove messages could ultimately lead to an `OutOfMemoryException`.
Therefore, if you do not expect multiple claims to be made, we recommend that you set the `remove-message` attribute's value to `true`.
The following example show how to use the `remove-message` attribute:
[source,xml]
----
<int:claim-check-out id="checkout"
input-channel="checkoutChannel"
message-store="testMessageStore"
output-channel="output"
remove-message="true"/>
----
[[a-word-on-message-store]]
== A Word on Message Store
Although we rarely care about the details of the claim checks (as long as they work), you should know that the current implementation of the actual claim check (the pointer) in Spring Integration uses a UUID to ensure uniqueness.
`org.springframework.integration.store.MessageStore` is a strategy interface for storing and retrieving messages.
Spring Integration provides two convenient implementations of it:
* `SimpleMessageStore`: An in-memory, `Map`-based implementation (the default, good for testing)
* `JdbcMessageStore`: An implementation that uses a relational database over JDBC

View File

@@ -0,0 +1,144 @@
[[codec]]
= Codec
Version 4.2 of Spring Integration introduced the `Codec` abstraction.
Codecs encode and decode objects to and from `byte[]`.
They offer an alternative to Java serialization.
One advantage is that, typically, objects need not implement `Serializable`.
We provide one implementation that uses https://github.com/EsotericSoftware/kryo[Kryo] for serialization, but you can provide your own implementation for use in any of the following components:
* `EncodingPayloadTransformer`
* `DecodingTransformer`
* `CodecMessageConverter`
[[encodingpayloadtransformer]]
== `EncodingPayloadTransformer`
This transformer encodes the payload to a `byte[]` by using the codec.
It does not affect message headers.
See the https://docs.spring.io/spring-integration/api/org/springframework/integration/transformer/EncodingPayloadTransformer.html[Javadoc] for more information.
[[decodingtransformer]]
== `DecodingTransformer`
This transformer decodes a `byte[]` by using the codec.
It needs to be configured with the `Class` to which the object should be decoded (or an expression that resolves to a `Class`).
If the resulting object is a `Message<?>`, inbound headers are not retained.
See the https://docs.spring.io/spring-integration/api/org/springframework/integration/transformer/DecodingTransformer.html[Javadoc] for more information.
[[codecmessageconverter]]
== `CodecMessageConverter`
Certain endpoints (such as TCP and Redis) have no concept of message headers.
They support the use of a `MessageConverter`, and the `CodecMessageConverter` can be used to convert a message to or from a `byte[]` for transmission.
See the https://docs.spring.io/spring-integration/api/org/springframework/integration/codec/CodecMessageConverter.html[Javadoc] for more information.
[[kryo]]
== Kryo
Currently, this is the only implementation of `Codec`, and it provides two kinds of `Codec`:
* `PojoCodec`: Used in the transformers
* `MessageCodec`: Used in the `CodecMessageConverter`
The framework provides several custom serializers:
* `FileSerializer`
* `MessageHeadersSerializer`
* `MutableMessageHeadersSerializer`
The first can be used with the `PojoCodec` by initializing it with the `FileKryoRegistrar`.
The second and third are used with the `MessageCodec`, which is initialized with the `MessageKryoRegistrar`.
[[customizing-kryo]]
=== Customizing Kryo
By default, Kryo delegates unknown Java types to its `FieldSerializer`.
Kryo also registers default serializers for each primitive type, along with `String`, `Collection`, and `Map`.
`FieldSerializer` uses reflection to navigate the object graph.
A more efficient approach is to implement a custom serializer that is aware of the object's structure and can directly serialize selected primitive fields.
The following example shows such a serializer:
[source,java]
----
public class AddressSerializer extends Serializer<Address> {
@Override
public void write(Kryo kryo, Output output, Address address) {
output.writeString(address.getStreet());
output.writeString(address.getCity());
output.writeString(address.getCountry());
}
@Override
public Address read(Kryo kryo, Input input, Class<Address> type) {
return new Address(input.readString(), input.readString(), input.readString());
}
}
----
The `Serializer` interface exposes `Kryo`, `Input`, and `Output`, which provide complete control over which fields are included and other internal settings, as described in the https://github.com/EsotericSoftware/kryo[Kryo documentation].
NOTE: When registering your custom serializer, you need a registration ID.
The registration IDs are arbitrary.
However, in our case, the IDs must be explicitly defined, because each Kryo instance across the distributed application must use the same IDs.
Kryo recommends small positive integers and reserves a few ids (value < 10).
Spring Integration currently defaults to using 40, 41, and 42 (for the file and message header serializers mentioned earlier).
We recommend you start at 60, to allow for expansion in the framework.
You can override these framework defaults by configuring the registrars mentioned earlier.
[[using-a-custom-kryo-serializer]]
==== Using a Custom Kryo Serializer
If you need custom serialization, see the https://github.com/EsotericSoftware/kryo[Kryo] documentation, because you need to use the native API to do the customization.
For an example, see the https://github.com/spring-projects/spring-integration/blob/main/spring-integration-core/src/main/java/org/springframework/integration/codec/kryo/MessageCodec.java[`MessageCodec`] implementation.
[[implementing-kryoserializable]]
==== Implementing KryoSerializable
If you have `write` access to the domain object source code, you can implement `KryoSerializable` as described https://github.com/EsotericSoftware/kryo#kryoserializable[here].
In this case, the class provides the serialization methods itself and no further configuration is required.
However, benchmarks have shown this is not quite as efficient as registering a custom serializer explicitly.
The following example shows a custom Kryo serializer:
[source,java]
----
public class Address implements KryoSerializable {
...
@Override
public void write(Kryo kryo, Output output) {
output.writeString(this.street);
output.writeString(this.city);
output.writeString(this.country);
}
@Override
public void read(Kryo kryo, Input input) {
this.street = input.readString();
this.city = input.readString();
this.country = input.readString();
}
}
----
You can also use this technique to wrap a serialization library other than Kryo.
[[using-the-defaultserializer-annotation]]
==== Using the `@DefaultSerializer` Annotation
Kryo also provides a `@DefaultSerializer` annotation, as described https://github.com/EsotericSoftware/kryo#default-serializers[here].
[source,java]
----
@DefaultSerializer(SomeClassSerializer.class)
public class SomeClass {
// ...
}
----
If you have `write` access to the domain object, this may be a simpler way to specify a custom serializer.
Note that this does not register the class with an ID, which may make the technique unhelpful for certain situations.

View File

@@ -0,0 +1,12 @@
[[configuration]]
= Configuration
:page-section-summary-toc: 1
Spring Integration offers a number of configuration options.
Which option you choose depends upon your particular needs and at what level you prefer to work.
As with the Spring framework in general, you can mix and match the various techniques to suit the problem at hand.
For example, you can choose the XSD-based namespace for the majority of configuration and combine it with a handful of objects that you configure with annotations.
As much as possible, the two provide consistent naming.
The XML elements defined by the XSD schema match the names of the annotations, and the attributes of those XML elements match the names of annotation properties.
You can also use the API directly, but we expect most developers to choose one of the higher-level options or a combination of the namespace-based and annotation-driven configuration.

View File

@@ -0,0 +1,309 @@
[[annotations]]
= Annotation Support
In addition to the XML namespace support for configuring message endpoints, you can also use annotations.
First, Spring Integration provides the class-level `@MessageEndpoint` as a stereotype annotation, meaning that it is itself annotated with Spring's `@Component` annotation and is therefore automatically recognized as a bean definition by Spring's component scanning.
Even more important are the various method-level annotations.
They indicate that the annotated method is capable of handling a message.
The following example demonstrates both class-level and method-level annotations:
[source,java]
----
@MessageEndpoint
public class FooService {
@ServiceActivator
public void processMessage(Message message) {
...
}
}
----
Exactly what it means for the method to "`handle`" the Message depends on the particular annotation.
Annotations available in Spring Integration include:
* `@Aggregator` (see xref:overview.adoc#overview-endpoints-aggregator[Aggregator])
* `@Filter` (see xref:filter.adoc[Filter])
* `@Router` (see xref:router.adoc[Routers])
* `@ServiceActivator` (see xref:service-activator.adoc[Service Activator])
* `@Splitter` (see xref:splitter.adoc[Splitter])
* `@Transformer` (see xref:transformer.adoc[Transformer])
* `@InboundChannelAdapter` (see xref:overview.adoc#overview-endpoints-channeladapter[Channel Adapter])
* `@BridgeFrom` (see xref:bridge.adoc#bridge-annot[Configuring a Bridge with Java Configuration])
* `@BridgeTo` (see xref:bridge.adoc#bridge-annot[Configuring a Bridge with Java Configuration])
* `@MessagingGateway` (see xref:gateway.adoc[Messaging Gateways])
* `@IntegrationComponentScan` (see xref:overview.adoc#configuration-enable-integration[Configuration and `@EnableIntegration`])
NOTE: If you use XML configuration in combination with annotations, the `@MessageEndpoint` annotation is not required.
If you want to configure a POJO reference from the `ref` attribute of a `<service-activator/>` element, you can provide only the method-level annotations.
In that case, the annotation prevents ambiguity even when no method-level attribute exists on the `<service-activator/>` element.
In most cases, the annotated handler method should not require the `Message` type as its parameter.
Instead, the method parameter type can match the message's payload type, as the following example shows:
[source,java]
----
public class ThingService {
@ServiceActivator
public void bar(Thing thing) {
...
}
}
----
When the method parameter should be mapped from a value in the `MessageHeaders`, another option is to use the parameter-level `@Header` annotation.
In general, methods annotated with the Spring Integration annotations can accept the `Message` itself, the message payload, or a header value (with `@Header`) as the parameter.
In fact, the method can accept a combination, as the following example shows:
[source,java]
----
public class ThingService {
@ServiceActivator
public void otherThing(String payload, @Header("x") int valueX, @Header("y") int valueY) {
...
}
}
----
You can also use the `@Headers` annotation to provide all the message headers as a `Map`, as the following example shows:
[source,java]
----
public class ThingService {
@ServiceActivator
public void otherThing(String payload, @Headers Map<String, Object> headerMap) {
...
}
}
----
NOTE: The value of the annotation can also be a SpEL expression (for example, `someHeader.toUpperCase()`), which is useful when you wish to manipulate the header value before injecting it.
It also provides an optional `required` property, which specifies whether the attribute value must be available within the headers.
The default value for the `required` property is `true`.
For several of these annotations, when a message-handling method returns a non-null value, the endpoint tries to send a reply.
This is consistent across both configuration options (namespace and annotations) in that such an endpoint's output channel is used (if available), and the `REPLY_CHANNEL` message header value is used as a fallback.
TIP: The combination of output channels on endpoints and the reply channel message header enables a pipeline approach, where multiple components have an output channel and the final component allows the reply message to be forwarded to the reply channel (as specified in the original request message).
In other words, the final component depends on the information provided by the original sender and can dynamically support any number of clients as a result.
This is an example of the https://www.enterpriseintegrationpatterns.com/ReturnAddress.html[return address] pattern.
In addition to the examples shown here, these annotations also support the `inputChannel` and `outputChannel` properties, as the following example shows:
[source,java]
----
@Service
public class ThingService {
@ServiceActivator(inputChannel="input", outputChannel="output")
public void otherThing(String payload, @Headers Map<String, Object> headerMap) {
...
}
}
----
The processing of these annotations creates the same beans as the corresponding XML components -- `AbstractEndpoint` instances and `MessageHandler` instances (or `MessageSource` instances for the inbound channel adapter).
See xref:configuration/meta-annotations.adoc#annotations_on_beans[Annotations on `@Bean` Methods].
The bean names are generated from the following pattern: `[componentName].[methodName].[decapitalizedAnnotationClassShortName]`.
In the preceding example the bean name is `thingService.otherThing.serviceActivator` for the `AbstractEndpoint` and the same name with an additional `.handler` (`.source`) suffix for the `MessageHandler` (`MessageSource`) bean.
Such a name can be customized using an `@EndpointId` annotation alongside with these messaging annotations.
The `MessageHandler` instances (`MessageSource` instances) are also eligible to be tracked by xref:message-history.adoc[the message history].
Starting with version 4.0, all messaging annotations provide `SmartLifecycle` options (`autoStartup` and `phase`) to allow endpoint lifecycle control on application context initialization.
They default to `true` and `0`, respectively.
To change the state of an endpoint (such as `start()` or `stop()`), you can obtain a reference to the endpoint bean by using the `BeanFactory` (or autowiring) and invoke the methods.
Alternatively, you can send a command message to the `Control Bus` (see xref:groovy.adoc#groovy-control-bus[Control Bus]).
For these purposes, you should use the `beanName` mentioned earlier in the preceding paragraph.
[IMPORTANT]
=====
Channels automatically created after parsing the mentioned annotations (when no specific channel bean is configured), and the corresponding consumer endpoints, are declared as beans near the end of the context initialization.
These beans **can** be autowired in other services, but they have to be marked with the `@Lazy` annotation because the definitions, typically, won't yet be available during normal autowiring processing.
[source, java]
----
@Autowired
@Lazy
@Qualifier("someChannel")
MessageChannel someChannel;
...
@Bean
Thing1 dependsOnSPCA(@Qualifier("someInboundAdapter") @Lazy SourcePollingChannelAdapter someInboundAdapter) {
...
}
----
=====
Starting with version 6.0, all the messaging annotations are `@Repeatable` now, so several of the same type can be declared on the same service method with the meaning to create as many endpoints as those annotations are repeated:
[source, java]
----
@Transformer(inputChannel = "inputChannel1", outputChannel = "outputChannel1")
@Transformer(inputChannel = "inputChannel2", outputChannel = "outputChannel2")
public String transform(String input) {
return input.toUpperCase();
}
----
[[configuration-using-poller-annotation]]
== Using the `@Poller` Annotation
Before Spring Integration 4.0, messaging annotations required that the `inputChannel` be a reference to a `SubscribableChannel`.
For `PollableChannel` instances, an `<int:bridge/>` element was needed to configure an `<int:poller/>` and make the composite endpoint be a `PollingConsumer`.
Version 4.0 introduced the `@Poller` annotation to allow the configuration of `poller` attributes directly on the messaging annotations, as the following example shows:
[source,java]
----
public class AnnotationService {
@Transformer(inputChannel = "input", outputChannel = "output",
poller = @Poller(maxMessagesPerPoll = "${poller.maxMessagesPerPoll}", fixedDelay = "${poller.fixedDelay}"))
public String handle(String payload) {
...
}
}
----
The `@Poller` annotation provides only simple `PollerMetadata` options.
You can configure the `@Poller` annotation's attributes (`maxMessagesPerPoll`, `fixedDelay`, `fixedRate`, and `cron`) with property placeholders.
Also, starting with version 5.1, the `receiveTimeout` option for `PollingConsumer` s is also provided.
If it is necessary to provide more polling options (for example, `transaction`, `advice-chain`, `error-handler`, and others), you should configure the `PollerMetadata` as a generic bean and use its bean name as the `@Poller` 's `value` attribute.
In this case, no other attributes are allowed (they must be specified on the `PollerMetadata` bean).
Note, if `inputChannel` is a `PollableChannel` and no `@Poller` is configured, the default `PollerMetadata` is used (if it is present in the application context).
To declare the default poller by using a `@Configuration` annotation, use code similar to the following example:
[source,java]
----
@Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata defaultPoller() {
PollerMetadata pollerMetadata = new PollerMetadata();
pollerMetadata.setTrigger(new PeriodicTrigger(10));
return pollerMetadata;
}
----
The following example shows how to use the default poller:
[source,java]
----
public class AnnotationService {
@Transformer(inputChannel = "aPollableChannel", outputChannel = "output")
public String handle(String payload) {
...
}
}
----
The following example shows how to use a named poller:
[source,java]
----
@Bean
public PollerMetadata myPoller() {
PollerMetadata pollerMetadata = new PollerMetadata();
pollerMetadata.setTrigger(new PeriodicTrigger(1000));
return pollerMetadata;
}
----
The following example shows an endpoint that uses the default poller:
[source,java]
----
public class AnnotationService {
@Transformer(inputChannel = "aPollableChannel", outputChannel = "output"
poller = @Poller("myPoller"))
public String handle(String payload) {
...
}
}
----
Starting with version 4.3.3, the `@Poller` annotation has the `errorChannel` attribute for easier configuration of the underlying `MessagePublishingErrorHandler`.
This attribute plays the same role as `error-channel` in the `<poller>` XML component.
See xref:endpoint.adoc#endpoint-namespace[Endpoint Namespace Support] for more information.
The `poller()` attribute on the messaging annotations is mutually exclusive with the `reactive()` attribute.
See next section for more information.
[[configuration-using-reactive-annotation]]
== Using `@Reactive` Annotation
The `ReactiveStreamsConsumer` has been around since version 5.0, but it was applied only when an input channel for the endpoint is a `FluxMessageChannel` (or any `org.reactivestreams.Publisher` implementation).
Starting with version 5.3, its instance is also created by the framework when the target message handler is a `ReactiveMessageHandler` independently of the input channel type.
The `@Reactive` sub-annotation (similar to mentioned above `@Poller`) has been introduced for all the messaging annotations starting with version 5.5.
It accepts an optional `Function<? super Flux<Message<?>>, ? extends Publisher<Message<?>>>` bean reference and, independently of the input channel type and message handler, turns the target endpoint into the `ReactiveStreamsConsumer` instance.
The function is used from the `Flux.transform()` operator to apply some customization (`publishOn()`, `doOnNext()`, `log()`, `retry()` etc.) on a reactive stream source from the input channel.
The following example demonstrates how to change the publishing thread from the input channel independently of the final subscriber and producer to that `DirectChannel`:
[source,java]
----
@Bean
public Function<Flux<?>, Flux<?>> publishOnCustomizer() {
return flux -> flux.publishOn(Schedulers.parallel());
}
@ServiceActivator(inputChannel = "directChannel", reactive = @Reactive("publishOnCustomizer"))
public void handleReactive(String payload) {
...
}
----
The `reactive()` attribute on the messaging annotations is mutually exclusive with the `poller()` attribute.
See xref:configuration/annotations.adoc#configuration-using-poller-annotation[Using the `@Poller` Annotation] and xref:reactive-streams.adoc[Reactive Streams Support] for more information.
[[using-the-inboundchanneladapter-annotation]]
== Using the `@InboundChannelAdapter` Annotation
Version 4.0 introduced the `@InboundChannelAdapter` method-level annotation.
It produces a `SourcePollingChannelAdapter` integration component based on a `MethodInvokingMessageSource` for the annotated method.
This annotation is an analogue of the `<int:inbound-channel-adapter>` XML component and has the same restrictions: The method cannot have parameters, and the return type must not be `void`.
It has two attributes: `value` (the required `MessageChannel` bean name) and `poller` (an optional `@Poller` annotation, as xref:configuration/annotations.adoc#configuration-using-poller-annotation[described earlier]).
If you need to provide some `MessageHeaders`, use a `Message<?>` return type and use a `MessageBuilder` to build the `Message<?>`.
Using a `MessageBuilder` lets you configure the `MessageHeaders`.
The following example shows how to use an `@InboundChannelAdapter` annotation:
[source,java]
----
@InboundChannelAdapter("counterChannel")
public Integer count() {
return this.counter.incrementAndGet();
}
@InboundChannelAdapter(value = "fooChannel", poller = @Poller(fixed-rate = "5000"))
public String foo() {
return "foo";
}
----
Version 4.3 introduced the `channel` alias for the `value` annotation attribute, to provide better source code readability.
Also, the target `MessageChannel` bean is resolved in the `SourcePollingChannelAdapter` by the provided name (set by the `outputChannelName` option) on the first `receive()` call, not during the initialization phase.
It allows "`late binding`" logic: The target `MessageChannel` bean from the consumer perspective is created and registered a bit later than the `@InboundChannelAdapter` parsing phase.
The first example requires that the default poller has been declared elsewhere in the application context.
Using the `@MessagingGateway` Annotation
See xref:gateway.adoc#messaging-gateway-annotation[`@MessagingGateway` Annotation].
[[using-the-integrationcomponentscan-annotation]]
== Using the `@IntegrationComponentScan` Annotation
The standard Spring Framework `@ComponentScan` annotation does not scan interfaces for stereotype `@Component` annotations.
To overcome this limitation and allow the configuration of `@MessagingGateway` (see xref:gateway.adoc#messaging-gateway-annotation[`@MessagingGateway` Annotation]), we introduced the `@IntegrationComponentScan` mechanism.
This annotation must be placed with a `@Configuration` annotation and be customized to define its scanning options,
such as `basePackages` and `basePackageClasses`.
In this case, all discovered interfaces annotated with `@MessagingGateway` are parsed and registered as `GatewayProxyFactoryBean` instances.
All other class-based components are parsed by the standard `@ComponentScan`.

View File

@@ -0,0 +1,71 @@
[[global-properties]]
= Global Properties
Certain global framework properties can be overridden by providing a properties file on the classpath.
The default properties can be found in `org.springframework.integration.context.IntegrationProperties` class.
The following listing shows the default values:
[source]
----
spring.integration.channels.autoCreate=true <1>
spring.integration.channels.maxUnicastSubscribers=0x7fffffff <2>
spring.integration.channels.maxBroadcastSubscribers=0x7fffffff <3>
spring.integration.taskScheduler.poolSize=10 <4>
spring.integration.messagingTemplate.throwExceptionOnLateReply=false <5>
spring.integration.readOnly.headers= <6>
spring.integration.endpoints.noAutoStartup= <7>
spring.integration.channels.error.requireSubscribers=true <8>
spring.integration.channels.error.ignoreFailures=true <9>
----
<1> When true, `input-channel` instances are automatically declared as `DirectChannel` instances when not explicitly found in the application context.
<2> Sets the default number of subscribers allowed on, for example, a `DirectChannel`.
It can be used to avoid inadvertently subscribing multiple endpoints to the same channel.
You can override it on individual channels by setting the `max-subscribers` attribute.
<3> This property provides the default number of subscribers allowed on, for example, a `PublishSubscribeChannel`.
It can be used to avoid inadvertently subscribing more than the expected number of endpoints to the same channel.
You can override it on individual channels by setting the `max-subscribers` attribute.
<4> The number of threads available in the default `taskScheduler` bean.
See xref:configuration/namespace-taskscheduler.adoc[Configuring the Task Scheduler].
<5> When `true`, messages that arrive at a gateway reply channel throw an exception when the gateway is not expecting a reply (because the sending thread has timed out or already received a reply).
<6> A comma-separated list of message header names that should not be populated into `Message` instances during a header copying operation.
The list is used by the `DefaultMessageBuilderFactory` bean and propagated to the `IntegrationMessageHeaderAccessor` instances (see xref:message.adoc#message-header-accessor[`MessageHeaderAccessor` API]) used to build messages via `MessageBuilder` (see xref:message.adoc#message-builder[The `MessageBuilder` Helper Class]).
By default, only `MessageHeaders.ID` and `MessageHeaders.TIMESTAMP` are not copied during message building.
Since version 4.3.2.
<7> A comma-separated list of `AbstractEndpoint` bean names patterns (`xxx*`, `*xxx`, `*xxx*` or `xxx*yyy`) that should not be started automatically during application startup.
You can manually start these endpoints later by their bean name through a `Control Bus` (see xref:groovy.adoc#groovy-control-bus[Control Bus]), by their role with the `SmartLifecycleRoleController` (see xref:endpoint.adoc#endpoint-roles[Endpoint Roles]), or by `Lifecycle` bean injection.
You can explicitly override the effect of this global property by specifying `auto-startup` XML annotation or the `autoStartup` annotation attribute or by calling `AbstractEndpoint.setAutoStartup()` in the bean definition.
Since version 4.3.12.
<8> A boolean flag to indicate that default global `errorChannel` must be configured with the `requireSubscribers` option.
Since version 5.4.3.
See xref:scatter-gather.adoc#scatter-gather-error-handling[Error Handling] for more information.
<9> A boolean flag to indicate that default global `errorChannel` must ignore dispatching errors and pass the message to the next handler.
Since version 5.5.
These properties can be overridden by adding a `/META-INF/spring.integration.properties` file to the classpath or an `IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME` bean for the `org.springframework.integration.context.IntegrationProperties` instance.
You need not provide all the properties -- only those that you want to override.
Starting with version 5.1, all the merged global properties are printed in the logs after application context startup when a `DEBUG` logic level is turned on for the `org.springframework.integration` category.
The output looks like this:
[source]
----
Spring Integration global properties:
spring.integration.endpoints.noAutoStartup=fooService*
spring.integration.taskScheduler.poolSize=20
spring.integration.channels.maxUnicastSubscribers=0x7fffffff
spring.integration.channels.autoCreate=true
spring.integration.channels.maxBroadcastSubscribers=0x7fffffff
spring.integration.readOnly.headers=
spring.integration.messagingTemplate.throwExceptionOnLateReply=true
----

View File

@@ -0,0 +1,245 @@
[[message-mapping-rules]]
= Message Mapping Rules and Conventions
Spring Integration implements a flexible facility to map messages to methods and their arguments without providing extra configuration, by relying on some default rules and defining certain conventions.
The examples in the following sections articulate the rules.
[[sample-scenarios]]
== Sample Scenarios
The following example shows a single un-annotated parameter (object or primitive) that is not a `Map` or a `Properties` object with a non-void return type:
[source,java]
----
public String doSomething(Object o);
----
The input parameter is a message payload.
If the parameter type is not compatible with a message payload, an attempt is made to convert it by using a conversion service provided by Spring 3.0.
The return value is incorporated as a payload of the returned message.
The following example shows a single un-annotated parameter (object or primitive)that is not a `Map` or a `Properties` with a `Message` return type:
[source,java]
----
public Message doSomething(Object o);
----
The input parameter is a message payload.
If the parameter type is not compatible with a message payload, an attempt is made to convert it by using a conversion service provided by Spring 3.0.
The return value is a newly constructed message that is sent to the next destination.
The following example shows a single parameter that is a message (or one of its subclasses) with an arbitrary object or primitive return type:
[source,java]
----
public int doSomething(Message msg);
----
The input parameter is itself a `Message`.
The return value becomes a payload of the `Message` that is sent to the next destination.
The following example shows a single parameter that is a `Message` (or one of its subclasses) with a `Message` (or one of its subclasses) as the return type:
[source,java]
----
public Message doSomething(Message msg);
----
The input parameter is itself a `Message`.
The return value is a newly constructed `Message` that is sent to the next destination.
The following example shows a single parameter of type `Map` or `Properties` with a `Message` as the return type:
[source,java]
----
public Message doSomething(Map m);
----
This one is a bit interesting.
Although, at first, it might seem like an easy mapping straight to message headers, preference is always given to a `Message` payload.
This means that if a `Message` payload is of type `Map`, this input argument represents a `Message` payload.
However, if the `Message` payload is not of type `Map`, the conversion service does not try to convert the payload, and the input argument is mapped to message headers.
The following example shows two parameters, where one of them is an arbitrary type (an object or a primitive) that is not a `Map` or a `Properties` object and the other is of type `Map` or `Properties` type (regardless of the return):
[source,java]
----
public Message doSomething(Map h, <T> t);
----
This combination contains two input parameters where one of them is of type `Map`.
The non-`Map` parameters (regardless of the order) are mapped to a `Message` payload and the `Map` or `Properties` (regardless of the order) is mapped to message headers, giving you a nice POJO way of interacting with `Message` structure.
The following example shows no parameters (regardless of the return):
[source,java]
----
public String doSomething();
----
This message handler method is invoked based on the Message sent to the input channel to which this handler is connected.
However, no `Message` data is mapped, thus making the `Message` act as event or trigger to invoke the handler.
The output is mapped according to the rules xref:configuration/message-mapping-rules.adoc[described earlier].
The following example shows no parameters and a void return:
[source,java]
----
public void soSomething();
----
This example is the same as the previous example, but it produces no output.
[[annotation-based-mapping]]
== Annotation-based Mapping
Annotation-based mapping is the safest and least ambiguous approach to map messages to methods.
The following example shows how to explicitly map a method to a header:
[source,java]
----
public String doSomething(@Payload String s, @Header("someheader") String b)
----
As you can see later on, without an annotation this signature would result in an ambiguous condition.
However, by explicitly mapping the first argument to a `Message` payload and the second argument to a value of the `someheader` message header, we avoid any ambiguity.
The following example is nearly identical to the preceding example:
[source,java]
----
public String doSomething(@Payload String s, @RequestParam("something") String b)
----
`@RequestMapping` or any other non-Spring Integration mapping annotation is irrelevant and is therefore ignored, leaving the second parameter unmapped.
Although the second parameter could easily be mapped to a payload, there can only be one payload.
Therefore, the annotations keep this method from being ambiguous.
The following example shows another similar method that would be ambiguous were it not for annotations to clarify the intent:
[source,java]
----
public String foo(String s, @Header("foo") String b)
----
The only difference is that the first argument is implicitly mapped to the message payload.
The following example shows yet another signature that would definitely be treated as ambiguous without annotations, because it has more than two arguments:
[source,java]
----
public String soSomething(@Headers Map m, @Header("something") Map f, @Header("someotherthing") String bar)
----
This example would be especially problematic, because two of its arguments are `Map` instances.
However, with annotation-based mapping, the ambiguity is easily avoided.
In this example the first argument is mapped to all the message headers, while the second and third argument map to the values of the message headers named 'something' and 'someotherthing'.
The payload is not being mapped to any argument.
[[complex-scenarios]]
== Complex Scenarios
The following example uses multiple parameters:
Multiple parameters can create a lot of ambiguity in regards to determining the appropriate mappings.
The general advice is to annotate your method parameters with `@Payload`, `@Header`, and `@Headers`.
The examples in this section show ambiguous conditions that result in an exception being raised.
[source,java]
----
public String doSomething(String s, int i)
----
The two parameters are equal in weight.
Therefore, there is no way to determine which one is a payload.
The following example shows a similar problem, only with three parameters:
[source,java]
----
public String foo(String s, Map m, String b)
----
Although the Map could be easily mapped to message headers, there is no way to determine what to do with the two String parameters.
The following example shows another ambiguous method:
[source,java]
----
public String foo(Map m, Map f)
----
Although one might argue that one `Map` could be mapped to the message payload and the other one to the message headers, we cannot rely on the order.
TIP: Any method signature with more than one method argument that is not (`Map`, `<T>`) and with unannotated parameters results in an ambiguous condition and triggers an exception.
The next set of examples each show multiple methods that result in ambiguity.
Message handlers with multiple methods are mapped based on the same rules that are described earlier (in the examples).
However, some scenarios might still look confusing.
The following example shows multiple methods with legal (mappable and unambiguous) signatures:
[source,java]
----
public class Something {
public String doSomething(String str, Map m);
public String doSomething(Map m);
}
----
(Whether the methods have the same name or different names makes no difference).
The `Message` could be mapped to either method.
The first method would be invoked when the message payload could be mapped to `str` and the message headers could be mapped to `m`.
The second method could also be a candidate by mapping only the message headers to `m`.
To make matters worse, both methods have the same name.
At first, that might look ambiguous because of the following configuration:
[source,xml]
----
<int:service-activator input-channel="input" output-channel="output" method="doSomething">
<bean class="org.things.Something"/>
</int:service-activator>
----
It works because mappings are based on the payload first and everything else next.
In other words, the method whose first argument can be mapped to a payload takes precedence over all other methods.
Now consider an alternate example, which produces a truly ambiguous condition:
[source,java]
----
public class Something {
public String doSomething(String str, Map m);
public String doSomething(String str);
}
----
Both methods have signatures that could be mapped to a message payload.
They also have the same name.
Such handler methods will trigger an exception.
However, if the method names were different, you could influence the mapping with a `method` attribute (shown in the next example).
The following example shows the same example with two different method names:
[source,java]
----
public class Something {
public String doSomething(String str, Map m);
public String doSomethingElse(String str);
}
----
The following example shows how to use the `method` attribute to dictate the mapping:
[source,xml]
----
<int:service-activator input-channel="input" output-channel="output" method="doSomethingElse">
<bean class="org.bar.Foo"/>
</int:service-activator>
----
Because the configuration explicitly maps the `doSomethingElse` method, we have eliminated the ambiguity.

View File

@@ -0,0 +1,176 @@
[[meta-annotations]]
= Messaging Meta-Annotations
Starting with version 4.0, all messaging annotations can be configured as meta-annotations and all user-defined messaging annotations can define the same attributes to override their default values.
In addition, meta-annotations can be configured hierarchically, as the following example shows:
[source,java]
----
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@ServiceActivator(inputChannel = "annInput", outputChannel = "annOutput")
public @interface MyServiceActivator {
String[] adviceChain = { "annAdvice" };
}
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@MyServiceActivator
public @interface MyServiceActivator1 {
String inputChannel();
String outputChannel();
}
...
@MyServiceActivator1(inputChannel = "inputChannel", outputChannel = "outputChannel")
public Object service(Object payload) {
...
}
----
Configuring meta-annotations hierarchically lets users set defaults for various attributes and enables isolation of framework Java dependencies to user annotations, avoiding their use in user classes.
If the framework finds a method with a user annotation that has a framework meta-annotation, it is treated as if the method were annotated directly with the framework annotation.
[[annotations_on_beans]]
== Annotations on `@Bean` Methods
Starting with version 4.0, you can configure messaging annotations on `@Bean` method definitions in `@Configuration` classes, to produce message endpoints based on the beans, not the methods.
It is useful when `@Bean` definitions are "`out-of-the-box`" `MessageHandler` instances (`AggregatingMessageHandler`, `DefaultMessageSplitter`, and others), `Transformer` instances (`JsonToObjectTransformer`, `ClaimCheckOutTransformer`, and others), and `MessageSource` instances (`FileReadingMessageSource`, `RedisStoreMessageSource`, and others).
The following example shows how to use messaging annotations with `@Bean` annotations:
[source,java]
----
@Configuration
@EnableIntegration
public class MyFlowConfiguration {
@Bean
@InboundChannelAdapter(value = "inputChannel", poller = @Poller(fixedDelay = "1000"))
public MessageSource<String> consoleSource() {
return CharacterStreamReadingMessageSource.stdin();
}
@Bean
@Transformer(inputChannel = "inputChannel", outputChannel = "httpChannel")
public ObjectToMapTransformer toMapTransformer() {
return new ObjectToMapTransformer();
}
@Bean
@ServiceActivator(inputChannel = "httpChannel")
public HttpRequestExecutingMessageHandler httpHandler() {
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("https://foo/service");
handler.setExpectedResponseType(String.class);
handler.setOutputChannelName("outputChannel");
return handler;
}
@Bean
@ServiceActivator(inputChannel = "outputChannel")
public LoggingHandler loggingHandler() {
return new LoggingHandler("info");
}
}
----
Version 5.0 introduced support for a `@Bean` annotated with `@InboundChannelAdapter` that returns `java.util.function.Supplier`, which can produce either a POJO or a `Message`.
The following example shows how to use that combination:
[source,java]
----
@Configuration
@EnableIntegration
public class MyFlowConfiguration {
@Bean
@InboundChannelAdapter(value = "inputChannel", poller = @Poller(fixedDelay = "1000"))
public Supplier<String> pojoSupplier() {
return () -> "foo";
}
@Bean
@InboundChannelAdapter(value = "inputChannel", poller = @Poller(fixedDelay = "1000"))
public Supplier<Message<String>> messageSupplier() {
return () -> new GenericMessage<>("foo");
}
}
----
The meta-annotation rules work on `@Bean` methods as well (the `@MyServiceActivator` annotation xref:configuration/meta-annotations.adoc[described earlier] can be applied to a `@Bean` definition).
NOTE: When you use these annotations on consumer `@Bean` definitions, if the bean definition returns an appropriate `MessageHandler` (depending on the annotation type), you must set attributes (such as `outputChannel`, `requiresReply`, `order`, and others), on the `MessageHandler` `@Bean` definition itself.
Only the following annotation attributes are used: `adviceChain`, `autoStartup`, `inputChannel`, `phase`, and `poller`.
All other attributes are for the handler.
NOTE: The bean names are generated with the following algorithm:
* The `MessageHandler` (`MessageSource`) `@Bean` gets its own standard name from the method name or `name` attribute on the `@Bean`.
This works as though there were no messaging annotation on the `@Bean` method.
* The `AbstractEndpoint` bean name is generated with the following pattern: `[@Bean name].[decapitalizedAnnotationClassShortName]`.
For example, the `SourcePollingChannelAdapter` endpoint for the `consoleSource()` definition xref:configuration/meta-annotations.adoc#annotations_on_beans[shown earlier] gets a bean name of `consoleSource.inboundChannelAdapter`.
Unlike with POJO methods, the bean method name is not included in the endpoint bean name.
See also xref:overview.adoc#endpoint-bean-names[Endpoint Bean Names].
* If `@Bean` cannot be used directly in the target endpoint (not an instance of a `MessageSource`, `AbstractReplyProducingMessageHandler` or `AbstractMessageRouter`), a respective `AbstractStandardMessageHandlerFactoryBean` is registered to delegate to this `@Bean`.
The bean name for this wrapper is generated with the following pattern: `[@Bean name].[decapitalizedAnnotationClassShortName].[handler (or source)]`.
IMPORTANT: When using these annotations on `@Bean` definitions, the `inputChannel` must reference a declared bean.
Channels are automatically declared if not present in the application context yet.
[NOTE]
=====
With Java configuration, you can use any `@Conditional` (for example, `@Profile`) definition on the `@Bean` method level to skip the bean registration for some conditional reason.
The following example shows how to do so:
[source,java]
----
@Bean
@ServiceActivator(inputChannel = "skippedChannel")
@Profile("thing")
public MessageHandler skipped() {
return System.out::println;
}
----
Together with the existing Spring container logic, the messaging endpoint bean (based on the `@ServiceActivator` annotation), is also not registered.
=====
[[creating-a-bridge-with-annotations]]
== Creating a Bridge with Annotations
Starting with version 4.0, Java configuration provides the `@BridgeFrom` and `@BridgeTo` `@Bean` method annotations to mark `MessageChannel` beans in `@Configuration` classes.
These really exists for completeness, providing a convenient mechanism to declare a `BridgeHandler` and its message endpoint configuration:
[source,java]
----
@Bean
public PollableChannel bridgeFromInput() {
return new QueueChannel();
}
@Bean
@BridgeFrom(value = "bridgeFromInput", poller = @Poller(fixedDelay = "1000"))
public MessageChannel bridgeFromOutput() {
return new DirectChannel();
}
@Bean
public QueueChannel bridgeToOutput() {
return new QueueChannel();
}
@Bean
@BridgeTo("bridgeToOutput")
public MessageChannel bridgeToInput() {
return new DirectChannel();
}
----
You can use these annotations as meta-annotations as well.
[[advising-annotated-endpoints]]
== Advising Annotated Endpoints
See xref:handler-advice/advising-with-annotations.adoc[Advising Endpoints Using Annotations].

View File

@@ -0,0 +1,49 @@
[[namespace-taskscheduler]]
= Configuring the Task Scheduler
In Spring Integration, the `ApplicationContext` plays the central role of a message bus, and you need to consider only a couple of configuration options.
First, you may want to control the central `TaskScheduler` instance.
You can do so by providing a single bean named `taskScheduler`.
This is also defined as a constant, as follows:
[source,java]
----
IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME
----
By default, Spring Integration relies on an instance of `ThreadPoolTaskScheduler`, as described in the https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#scheduling[Task Execution and Scheduling] section of the Spring Framework reference manual.
That default `TaskScheduler` starts up automatically with a pool of ten threads, but see xref:configuration/global-properties.adoc[Global Properties].
If you provide your own `TaskScheduler` instance instead, you can set the 'autoStartup' property to `false` or provide your own pool size value.
When polling consumers provide an explicit task executor reference in their configuration, the invocation of the handler methods happens within that executor's thread pool and not the main scheduler pool.
However, when no task executor is provided for an endpoint's poller, it is invoked by one of the main scheduler's threads.
CAUTION: Do not run long-running tasks on poller threads.
Use a task executor instead.
If you have a lot of polling endpoints, you can cause thread starvation, unless you increase the pool size.
Also, polling consumers have a default `receiveTimeout` of one second.
Since the poller thread blocks for this time, we recommend that you use a task executor when many such endpoints exist, again to avoid starvation.
Alternatively, you can reduce the `receiveTimeout`.
NOTE: An endpoint is a Polling Consumer if its input channel is one of the queue-based (that is, pollable) channels.
Event-driven consumers are those having input channels that have dispatchers instead of queues (in other words, they are subscribable).
Such endpoints have no poller configuration, since their handlers are invoked directly.
[IMPORTANT]
=====
When running in a JEE container, you may need to use Spring's `TimerManagerTaskScheduler`, as described https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#scheduling-task-scheduler-implementations[here], instead of the default `taskScheduler`.
To do so, define a bean with the appropriate JNDI name for your environment, as the following example shows:
[source,xml]
----
<bean id="taskScheduler" class="org.springframework.scheduling.concurrent.DefaultManagedTaskScheduler">
<property name="jndiName" value="tm/MyTimerManager" />
<property name="resourceRef" value="true" />
</bean>
----
=====
IMPORTANT: When a custom `TaskScheduler` is configured in the application context (like the above mentioned `DefaultManagedTaskScheduler`), it is recommended to supply it with a `MessagePublishingErrorHandler` (`integrationMessagePublishingErrorHandler` bean) to be able to handle exceptions as `ErrorMessage`s sent to the error channel, as is done with the default `TaskScheduler` bean provided by the framework.
See also xref:scatter-gather.adoc#scatter-gather-error-handling[Error Handling] for more information.

View File

@@ -0,0 +1,80 @@
[[configuration-namespace]]
= Namespace Support
You can configure Spring Integration components with XML elements that map directly to the terminology and concepts of enterprise integration.
In many cases, the element names match those of the https://www.enterpriseintegrationpatterns.com/[_Enterprise Integration Patterns_] book.
To enable Spring Integration's core namespace support within your Spring configuration files, add the following namespace reference and schema mapping in your top-level 'beans' element:
// We lose coloring here, but we want to bold the lines we're talking about...
[subs="+quotes"]
----
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
*xmlns:int="http://www.springframework.org/schema/integration"*
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
*http://www.springframework.org/schema/integration*
*https://www.springframework.org/schema/integration/spring-integration.xsd*">
----
(We have emphasized the lines that are particular to Spring Integration.)
You can choose any name after "xmlns:".
We use `int` (short for Integration) for clarity, but you might prefer another abbreviation.
On the other hand, if you use an XML editor or IDE support, the availability of auto-completion may convince you to keep the longer name for clarity.
Alternatively, you can create configuration files that use the Spring Integration schema as the primary namespace, as the following example shows:
// We lose coloring here, but we want to bold the lines we're talking about...
[subs=+quotes]
----
*<beans:beans xmlns="http://www.springframework.org/schema/integration"*
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
*http://www.springframework.org/schema/integration*
*https://www.springframework.org/schema/integration/spring-integration.xsd*">
----
(We have emphasized the lines that are particular to Spring Integration.)
When using this alternative, no prefix is necessary for the Spring Integration elements.
On the other hand, if you define a generic Spring bean within the same configuration file, the bean element requires a prefix (`<beans:bean .../>`).
Since it is generally a good idea to modularize the configuration files themselves (based on responsibility or architectural layer), you may find it appropriate to use the latter approach in the integration-focused configuration files, since generic beans are seldom necessary within those files.
For the purposes of this documentation, we assume the integration namespace is the primary.
Spring Integration provides many other namespaces.
In fact, each adapter type (JMS, file, and so on) that provides namespace support defines its elements within a separate schema.
In order to use these elements, add the necessary namespaces with an `xmlns` entry and the corresponding `schemaLocation` mapping.
For example, the following root element shows several of these namespace declarations:
[source,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-file="http://www.springframework.org/schema/integration/file"
xmlns:int-jms="http://www.springframework.org/schema/integration/jms"
xmlns:int-mail="http://www.springframework.org/schema/integration/mail"
xmlns:int-ws="http://www.springframework.org/schema/integration/ws"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
https://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/file
https://www.springframework.org/schema/integration/file/spring-integration-file.xsd
http://www.springframework.org/schema/integration/jms
https://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd
http://www.springframework.org/schema/integration/mail
https://www.springframework.org/schema/integration/mail/spring-integration-mail.xsd
http://www.springframework.org/schema/integration/ws
https://www.springframework.org/schema/integration/ws/spring-integration-ws.xsd">
...
</beans>
----
This reference manual provides specific examples of the various elements in their corresponding chapters.
Here, the main thing to recognize is the consistency of the naming for each namespace URI and schema location.

View File

@@ -0,0 +1,423 @@
[[content-enricher]]
= Content Enricher
At times, you may have a requirement to enhance a request with more information than was provided by the target system.
The https://www.enterpriseintegrationpatterns.com/DataEnricher.html[data enricher] pattern describes various scenarios as well as the component (Enricher) that lets you address such requirements.
The Spring Integration `Core` module includes two enrichers:
* xref:content-enrichment.adoc#header-enricher[Header Enricher]
* xref:content-enrichment.adoc#payload-enricher[Payload Enricher]
It also includes three adapter-specific header enrichers:
* xref:xml/xpath-header-enricher.adoc[XPath Header Enricher (XML Module)]
* xref:mail.adoc#mail-namespace[Mail Header Enricher (Mail Module)]
* xref:xmpp.adoc#xmpp-message-outbound-channel-adapter[XMPP Header Enricher (XMPP Module)]
See the adapter-specific sections of this reference manual to learn more about those adapters.
For more information regarding expressions support, see xref:spel.adoc[Spring Expression Language (SpEL)].
[[header-enricher]]
== Header Enricher
If you need do nothing more than add headers to a message and the headers are not dynamically determined by the message content, referencing a custom implementation of a transformer may be overkill.
For that reason, Spring Integration provides support for the header enricher pattern.
It is exposed through the `<header-enricher>` element.
The following example shows how to use it:
[source,xml]
----
<int:header-enricher input-channel="in" output-channel="out">
<int:header name="foo" value="123"/>
<int:header name="bar" ref="someBean"/>
</int:header-enricher>
----
The header enricher also provides helpful sub-elements to set well known header names, as the following example shows:
[source,xml]
----
<int:header-enricher input-channel="in" output-channel="out">
<int:error-channel ref="applicationErrorChannel"/>
<int:reply-channel ref="quoteReplyChannel"/>
<int:correlation-id value="123"/>
<int:priority value="HIGHEST"/>
<routing-slip value="channel1; routingSlipRoutingStrategy; request.headers[myRoutingSlipChannel]"/>
<int:header name="bar" ref="someBean"/>
</int:header-enricher>
----
The preceding configuration shows that, for well known headers (such as `errorChannel`, `correlationId`, `priority`, `replyChannel`, `routing-slip`, and others), instead of using generic `<header>` sub-elements where you would have to provide both header 'name' and 'value', you can use convenient sub-elements to set those values directly.
Starting with version 4.1, the header enricher provides a `routing-slip` sub-element.
See xref:router/routing-slip.adoc[Routing Slip] for more information.
[[pojo-support]]
=== POJO Support
Often, a header value cannot be defined statically and has to be determined dynamically based on some content in the message.
That is why the header enricher lets you also specify a bean reference by using the `ref` and `method` attributes.
The specified method calculates the header value.
Consider the following configuration and a bean with a method that modifies a `String`:
[source,xml]
----
<int:header-enricher input-channel="in" output-channel="out">
<int:header name="something" method="computeValue" ref="myBean"/>
</int:header-enricher>
<bean id="myBean" class="thing1.thing2.MyBean"/>
----
[source,java]
----
public class MyBean {
public String computeValue(String payload){
return payload.toUpperCase() + "_US";
}
}
----
You can also configure your POJO as an inner bean, as the following example shows:
[source,xml]
----
<int:header-enricher input-channel="inputChannel" output-channel="outputChannel">
<int:header name="some_header">
<bean class="org.MyEnricher"/>
</int:header>
</int:header-enricher>
----
You can similarly point to a Groovy script, as the following example shows:
[source,xml]
----
<int:header-enricher input-channel="inputChannel" output-channel="outputChannel">
<int:header name="some_header">
<int-groovy:script location="org/SampleGroovyHeaderEnricher.groovy"/>
</int:header>
</int:header-enricher>
----
[[spel-support]]
=== SpEL Support
In Spring Integration 2.0, we introduced the convenience of the https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#expressions[Spring Expression Language (SpEL)] to help configure many different components.
The header enricher is one of them.
Look again at the POJO example shown earlier.
You can see that the computation logic to determine the header value is pretty simple.
A natural question would be: "Is there an even simpler way to accomplish this?".
That is where SpEL shows its true power.
Consider the following example:
[source,xml]
----
<int:header-enricher input-channel="in" output-channel="out">
<int:header name="foo" expression="payload.toUpperCase() + '_US'"/>
</int:header-enricher>
----
By using SpEL for such simple cases, you no longer have to provide a separate class and configure it in the application context.
All you need do is configured the `expression` attribute with a valid SpEL expression.
The 'payload' and 'headers' variables are bound to the SpEL evaluation context, giving you full access to the incoming message.
[[configuring-a-header-enricher-with-java-configuration]]
=== Configuring a Header Enricher with Java Configuration
The following two examples show how to use Java Configuration for header enrichers:
[source, java]
----
@Bean
@Transformer(inputChannel = "enrichHeadersChannel", outputChannel = "emailChannel")
public HeaderEnricher enrichHeaders() {
Map<String, ? extends HeaderValueMessageProcessor<?>> headersToAdd =
Collections.singletonMap("emailUrl",
new StaticHeaderValueMessageProcessor<>(this.imapUrl));
HeaderEnricher enricher = new HeaderEnricher(headersToAdd);
return enricher;
}
@Bean
@Transformer(inputChannel="enrichHeadersChannel", outputChannel="emailChannel")
public HeaderEnricher enrichHeaders() {
Map<String, HeaderValueMessageProcessor<?>> headersToAdd = new HashMap<>();
headersToAdd.put("emailUrl", new StaticHeaderValueMessageProcessor<String>(this.imapUrl));
Expression expression = new SpelExpressionParser().parseExpression("payload.from[0].toString()");
headersToAdd.put("from",
new ExpressionEvaluatingHeaderValueMessageProcessor<>(expression, String.class));
HeaderEnricher enricher = new HeaderEnricher(headersToAdd);
return enricher;
}
----
The first example adds a single literal header.
The second example adds two headers, a literal header and one based on a SpEL expression.
[[configuring-a-header-enricher-with-the-java-dsl]]
=== Configuring a Header Enricher with the Java DSL
The following example shows Java DSL Configuration for a header enricher:
[source, java]
----
@Bean
public IntegrationFlow enrichHeadersInFlow() {
return f -> f
...
.enrichHeaders(h -> h.header("emailUrl", this.emailUrl)
.headerExpression("from", "payload.from[0].toString()"))
.handle(...);
}
----
[[header-channel-registry]]
=== Header Channel Registry
Starting with Spring Integration 3.0, a new sub-element `<int:header-channels-to-string/>` is available.
It has no attributes.
This new sub-element converts existing `replyChannel` and `errorChannel` headers (when they are a `MessageChannel`) to a `String` and stores the channels in a registry for later resolution, when it is time to send a reply or handle an error.
This is useful for cases where the headers might be lost -- for example, when serializing a message into a message store or when transporting the message over JMS.
If the header does not already exist or it is not a `MessageChannel`, no changes are made.
Using this functionality requires the presence of a `HeaderChannelRegistry` bean.
By default, the framework creates a `DefaultHeaderChannelRegistry` with the default expiry (60 seconds).
Channels are removed from the registry after this time.
To change this behavior, define a bean with an `id` of `integrationHeaderChannelRegistry` and configure the required default delay by using a constructor argument (in milliseconds).
Since version 4.1, you can set a property called `removeOnGet` to `true` on the `<bean/>` definition, and the mapping entry is removed immediately on first use.
This might be useful in a high-volume environment and when the channel is only used once, rather than waiting for the reaper to remove it.
The `HeaderChannelRegistry` has a `size()` method to determine the current size of the registry.
The `runReaper()` method cancels the current scheduled task and runs the reaper immediately.
The task is then scheduled to run again based on the current delay.
These methods can be invoked directly by getting a reference to the registry, or you can send a message with, for example, the following content to a control bus:
[source]
----
"@integrationHeaderChannelRegistry.runReaper()"
----
This sub-element is a convenience, and is the equivalent of specifying the following configuration:
[source,xml]
----
<int:reply-channel
expression="@integrationHeaderChannelRegistry.channelToChannelName(headers.replyChannel)"
overwrite="true" />
<int:error-channel
expression="@integrationHeaderChannelRegistry.channelToChannelName(headers.errorChannel)"
overwrite="true" />
----
Starting with version 4.1, you can now override the registry's configured reaper delay so that the channel mapping is retained for at least the specified time, regardless of the reaper delay.
The following example shows how to do so:
[source,xml]
----
<int:header-enricher input-channel="inputTtl" output-channel="next">
<int:header-channels-to-string time-to-live-expression="120000" />
</int:header-enricher>
<int:header-enricher input-channel="inputCustomTtl" output-channel="next">
<int:header-channels-to-string
time-to-live-expression="headers['channelTTL'] ?: 120000" />
</int:header-enricher>
----
In the first case, the time to live for every header channel mapping will be two minutes.
In the second case, the time to live is specified in the message header and uses an Elvis operator to use two minutes if there is no header.
[[payload-enricher]]
== Payload Enricher
In certain situations, the header enricher, as discussed earlier, may not be sufficient and payloads themselves may have to be enriched with additional information.
For example, order messages that enter the Spring Integration messaging system have to look up the order's customer based on the provided customer number and then enrich the original payload with that information.
Spring Integration 2.1 introduced the payload enricher.
The payload enricher defines an endpoint that passes a `Message` to the exposed request channel and then expects a reply message.
The reply message then becomes the root object for evaluation of expressions to enrich the target payload.
The payload enricher provides full XML namespace support through the `enricher` element.
In order to send request messages, the payload enricher has a `request-channel` attribute that lets you dispatch messages to a request channel.
Basically, by defining the request channel, the payload enricher acts as a gateway, waiting for the message sent to the request channel to return.
The enricher then augments the message's payload with the data provided by the reply message.
When sending messages to the request channel, you also have the option to send only a subset of the original payload by using the `request-payload-expression` attribute.
The enriching of payloads is configured through SpEL expressions, providing a maximum degree of flexibility.
Therefore, you can not only enrich payloads with direct values from the reply channel's `Message`, but you can use SpEL expressions to extract a subset from that message or to apply additional inline transformations, letting you further manipulate the data.
If you need only to enrich payloads with static values, you need not provide the `request-channel` attribute.
NOTE: Enrichers are a variant of transformers.
In many cases, you could use a payload enricher or a generic transformer implementation to add additional data to your message payloads.
You should familiarize yourself with all transformation-capable components that are provided by Spring Integration and carefully select the implementation that semantically fits your business case best.
[[payload-enricher-configuration]]
=== Configuration
The following example shows all available configuration options for the payload enricher:
[source,xml]
----
<int:enricher request-channel="" <1>
auto-startup="true" <2>
id="" <3>
order="" <4>
output-channel="" <5>
request-payload-expression="" <6>
reply-channel="" <7>
error-channel="" <8>
send-timeout="" <9>
should-clone-payload="false"> <10>
<int:poller></int:poller> <11>
<int:property name="" expression="" null-result-expression="'Could not determine the name'"/> <12>
<int:property name="" value="23" type="java.lang.Integer" null-result-expression="'0'"/>
<int:header name="" expression="" null-result-expression=""/> <13>
<int:header name="" value="" overwrite="" type="" null-result-expression=""/>
</int:enricher>
----
<1> Channel to which a message is sent to get the data to use for enrichment.
Optional.
<2> Lifecycle attribute signaling whether this component should be started during the application context startup.
Defaults to true.
Optional.
<3> ID of the underlying bean definition, which is either an `EventDrivenConsumer` or a `PollingConsumer`.
Optional.
<4> Specifies the order for invocation when this endpoint is connected as a subscriber to a channel.
This is particularly relevant when that channel is using a "`failover`" dispatching strategy.
It has no effect when this endpoint is itself a polling consumer for a channel with a queue.
Optional.
<5> Identifies the message channel where a message is sent after it is being processed by this endpoint.
Optional.
<6> By default, the original message's payload is used as payload that is sent to the `request-channel`.
By specifying a SpEL expression as the value for the `request-payload-expression` attribute, you can use a subset of the original payload, a header value, or any other resolvable SpEL expression as the basis for the payload that is sent to the request-channel.
For the expression evaluation, the full message is available as the 'root object'.
For instance, the following SpEL expressions (among others) are possible: `payload.something`, `headers.something`, `new java.util.Date()`, `'thing1' + 'thing2'`
<7> Channel where a reply message is expected.
This is optional.
Typically, the auto-generated temporary reply channel suffices.
Optional.
<8> The channel to which an `ErrorMessage` is sent if an `Exception` occurs downstream of the `request-channel`.
This enables you to return an alternative object to use for enrichment.
If it is not set, an `Exception` is thrown to the caller.
Optional.
<9> Maximum amount of time in milliseconds to wait when sending a message to the channel, if the channel might block.
For example, a queue channel can block until space is available, if its maximum capacity has been reached.
Internally, the `send()` timeout is set on the `MessagingTemplate` and ultimately applied when invoking the send operation on the `MessageChannel`.
By default, the `send()` timeout is set to '30'.
Optional.
<10> Boolean value indicating whether any payload that implements `Cloneable` should be cloned prior to sending the message to the request channel for acquiring the enriching data.
The cloned version would be used as the target payload for the ultimate reply.
The default is `false`.
Optional.
<11> Lets you configure a message poller if this endpoint is a polling consumer.
Optional.
<12> Each `property` sub-element provides the name of a property (through the mandatory `name` attribute).
That property should be settable on the target payload instance.
Exactly one of the `value` or `expression` attributes must be provided as well -- the former for a literal value to set and the latter for a SpEL expression to be evaluated.
The root object of the evaluation context is the message that was returned from the flow initiated by this enricher -- the input message if there is no request channel or the application context (using the `@<beanName>.<beanProperty>` SpEL syntax).
Starting with version 4.0, when specifying a `value` attribute, you can also specify an optional `type` attribute.
When the destination is a typed setter method, the framework coerces the value appropriately (as long as a `PropertyEditor`) exists to handle the conversion.
If, however, the target payload is a `Map`, the entry is populated with the value without conversion.
The `type` attribute lets you, for example, convert a `String` containing a number to an `Integer` value in the target payload.
Starting with version 4.1, you can also specify an optional `null-result-expression` attribute.
When the `enricher` returns null, it is evaluated, and the output of the evaluation is returned instead.
<13> Each `header` sub-element provides the name of a message header (through the mandatory `name` attribute).
Exactly one of the `value` or `expression` attributes must also be provided -- the former for a literal value to set and the latter for a SpEL expression to be evaluated.
The root object of the evaluation context is the message that was returned from the flow initiated by this enricher -- the input message if there is no request channel or the application context (using the '@<beanName>.<beanProperty>' SpEL syntax).
Note that, similarly to the `<header-enricher>`, the `<enricher>` element's `header` element has `type` and `overwrite` attributes.
However, a key difference is that, with the `<enricher>`, the `overwrite` attribute is `true` by default, to be consistent with the `<enricher>` element's `<property>` sub-element.
Starting with version 4.1, you can also specify an optional `null-result-expression` attribute.
When the `enricher` returns null, it is evaluated, and the output of the evaluation is returned instead.
[[payload-enricher-examples]]
=== Examples
This section contains several examples of using a payload enricher in various situations.
TIP: The code samples shown here are part of the Spring Integration Samples project.
See xref:samples.adoc#samples-impl[Spring Integration Samples].
In the following example, a `User` object is passed as the payload of the `Message`:
[source,xml]
----
<int:enricher id="findUserEnricher"
input-channel="findUserEnricherChannel"
request-channel="findUserServiceChannel">
<int:property name="email" expression="payload.email"/>
<int:property name="password" expression="payload.password"/>
</int:enricher>
----
The `User` has several properties, but only the `username` is set initially.
The enricher's `request-channel` attribute is configured to pass the `User` to the `findUserServiceChannel`.
Through the implicitly set `reply-channel`, a `User` object is returned and, by using the `property` sub-element, properties from the reply are extracted and used to enrich the original payload.
[[how-do-i-pass-only-a-subset-of-data-to-the-request-channel?]]
=== How Do I Pass Only a Subset of Data to the Request Channel?
When using a `request-payload-expression` attribute, a single property of the payload instead of the full message can be passed on to the request channel.
In the following example, the username property is passed on to the request channel:
[source,xml]
----
<int:enricher id="findUserByUsernameEnricher"
input-channel="findUserByUsernameEnricherChannel"
request-channel="findUserByUsernameServiceChannel"
request-payload-expression="payload.username">
<int:property name="email" expression="payload.email"/>
<int:property name="password" expression="payload.password"/>
</int:enricher>
----
Keep in mind that, although only the username is passed, the resulting message to the request channel contains the full set of `MessageHeaders`.
[[how-can-i-enrich-payloads-that-consist-of-collection-data?]]
==== How Can I Enrich Payloads that Consist of Collection Data?
In the following example, instead of a `User` object, a `Map` is passed in:
[source,xml]
----
<int:enricher id="findUserWithMapEnricher"
input-channel="findUserWithMapEnricherChannel"
request-channel="findUserByUsernameServiceChannel"
request-payload-expression="payload.username">
<int:property name="user" expression="payload"/>
</int:enricher>
----
The `Map` contains the username under the `username` map key.
Only the `username` is passed on to the request channel.
The reply contains a full `User` object, which is ultimately added to the `Map` under the `user` key.
[[how-can-i-enrich-payloads-with-static-information-without-using-a-request-channel?]]
=== How Can I Enrich Payloads with Static Information without Using a Request Channel?
The following example does not use a request channel at all but solely enriches the message's payload with static values:
[source,xml]
----
<int:enricher id="userEnricher"
input-channel="input">
<int:property name="user.updateDate" expression="new java.util.Date()"/>
<int:property name="user.firstName" value="William"/>
<int:property name="user.lastName" value="Shakespeare"/>
<int:property name="user.age" value="42"/>
</int:enricher>
----
Note that the word, 'static', is used loosely here.
You can still use SpEL expressions for setting those values.

View File

@@ -0,0 +1,70 @@
[[control-bus]]
= Control Bus
As described in the https://www.enterpriseintegrationpatterns.com/[_Enterprise Integration Patterns_] (EIP) book, the idea behind the control bus is that the same messaging system can be used for monitoring and managing the components within the framework as is used for "`application-level`" messaging.
In Spring Integration, we build upon the adapters described above so that you can send messages as a means of invoking exposed operations.
The following example shows how to configure a control bus with XML:
[source,xml]
----
<int:control-bus input-channel="operationChannel"/>
----
The control bus has an input channel that can be accessed for invoking operations on the beans in the application context.
It also has all the common properties of a service activating endpoint.
For example, you can specify an output channel if the result of the operation has a return value that you want to send on to a downstream channel.
The control bus runs messages on the input channel as Spring Expression Language (SpEL) expressions.
It takes a message, compiles the body to an expression, adds some context, and then runs it.
The default context supports any method that has been annotated with `@ManagedAttribute` or `@ManagedOperation`.
It also supports the methods on Spring's `Lifecycle` interface (and its `Pausable` extension since version 5.2), and it supports methods that are used to configure several of Spring's `TaskExecutor` and `TaskScheduler` implementations.
The simplest way to ensure that your own methods are available to the control bus is to use the `@ManagedAttribute` or `@ManagedOperation` annotations.
Since those annotations are also used for exposing methods to a JMX MBean registry, they offer a convenient by-product: Often, the same types of operations you want to expose to the control bus are reasonable for exposing through JMX).
Resolution of any particular instance within the application context is achieved in the typical SpEL syntax.
To do so, provide the bean name with the SpEL prefix for beans (`@`).
For example, to execute a method on a Spring Bean, a client could send a message to the operation channel as follows:
[source,java]
----
Message operation = MessageBuilder.withPayload("@myServiceBean.shutdown()").build();
operationChannel.send(operation)
----
The root of the context for the expression is the `Message` itself, so you also have access to the `payload` and `headers` as variables within your expression.
This is consistent with all the other expression support in Spring Integration endpoints.
With Java annotations, you can configured the control bus as follows:
[source,java]
----
@Bean
@ServiceActivator(inputChannel = "operationChannel")
public ExpressionControlBusFactoryBean controlBus() {
return new ExpressionControlBusFactoryBean();
}
----
Similarly, you can configure Java DSL flow definitions as follows:
[source,java]
----
@Bean
public IntegrationFlow controlBusFlow() {
return IntegrationFlow.from("controlBus")
.controlBus()
.get();
}
----
If you prefer to use lambdas with automatic `DirectChannel` creation, you can create a control bus as follows:
[source,java]
----
@Bean
public IntegrationFlow controlBus() {
return IntegrationFlowDefinition::controlBus;
}
----
In this case, the channel is named `controlBus.input`.

View File

@@ -0,0 +1,14 @@
[[spring-integration-core-messaging]]
= Core Messaging
:page-section-summary-toc: 1
[[spring-integration-core-msg]]
This section covers all aspects of the core messaging API in Spring Integration.
It covers messages, message channels, and message endpoints.
It also covers many of the enterprise integration patterns, such as filter, router, transformer, service activator, splitter, and aggregator.
This section also contains material about system management, including the control bus and message history support.

View File

@@ -0,0 +1,228 @@
[[debezium]]
= Debezium Support
https://debezium.io/documentation/reference/development/engine.html[Debezium Engine], Change Data Capture (CDC) inbound channel adapter.
The `DebeziumMessageProducer` allows capturing database change events, converting them into messages and streaming later to the outbound channels.
You need to include the spring integration Debezium dependency to your project:
[tabs]
======
Maven::
+
[source, xml, subs="normal", role="primary"]
----
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-debezium</artifactId>
<version>{project-version}</version>
</dependency>
----
Gradle::
+
[source, groovy, subs="normal", role="secondary"]
----
compile "org.springframework.integration:spring-integration-debezium:{project-version}"
----
======
You also need to include a https://debezium.io/documentation/reference/connectors/index.html[debezium connector] dependency for your input Database.
For example to use Debezium with PostgreSQL you will need the postgres debezium connector:
[tabs]
======
Maven::
+
[source, xml, role="primary"]
----
<dependency>
<groupId>io.debezium</groupId>
<artifactId>debezium-connector-postgres</artifactId>
<version>${debezium-version}</version>
</dependency>
----
Gradle::
+
[source, groovy, role="secondary"]
----
compile "io.debezium:debezium-connector-postgres:{debezium-version}"
----
======
[NOTE]
====
Replace the `debezium-version` with the version compatible with the `spring-integration-debezium` version being used.
====
[[debezium-inbound]]
== Inbound Debezium Channel Adapter
The Debezium adapter expects a pre-configured `DebeziumEngine.Builder<ChangeEvent<byte[], byte[]>>` instance.
[TIP]
====
The https://github.com/spring-cloud/stream-applications/tree/main/functions/supplier/debezium-supplier[debezium-supplier] provides an out of the box `DebeziumEngine.Builder` Spring Boot auto-configuration with a handy https://github.com/spring-cloud/stream-applications/blob/main/functions/supplier/debezium-supplier/src/main/java/org/springframework/cloud/fn/supplier/debezium/DebeziumProperties.java[DebeziumProperties] configuration abstraction.
====
[TIP]
====
The xref:debezium.adoc#debezium-java-dsl[Debezium Java DSL] can create a `DebeziumMessageProducer` instance from a provided `DebeziumEngine.Builder`, as well as from a plain Debezium configuration (e.g. `java.util.Properties`).
Later can be handy for some common use-cases with opinionated configuration and serialization formats.
====
Additionally, the `DebeziumMessageProducer` can be tuned with the following configuration properties:
- `contentType` - allows handling for `JSON` (default), `AVRO` and `PROTOBUF` message contents.
The contentType `must` be be aligned with the `SerializationFormat` configured for the provided `DebeziumEngine.Builder`.
- `enableBatch` - when set to `false` (default), the debezium adapter would send new `Message` for every `ChangeEvent` data change event received from the source database.
If set to `true` then the adapter sends downstream a single `Message` for each batch of `ChangeEvent` received from the Debezium engine.
Such a payload is not serializable and would require a custom serialization/deserialization implementation.
- `enableEmptyPayload` - Enables support for tombstone (aka delete) messages.
On a database row delete, Debezium can send a tombstone change event that has the same key as the deleted row and a value of `Optional.empty`.
Defaults to `false`.
- `headerMapper` - custom `HeaderMapper` implementation that allows for selecting and converting the `ChangeEvent` headers into `Message` headers.
The default `DefaultDebeziumHeaderMapper` implementation provides a setter for `setHeaderNamesToMap`.
By default, all headers are mapped.
- `taskExecutor` - Set a custom `TaskExecutor` for the Debezium engine.
The following code snippets demonstrate various configuration for this channel adapter:
[[configuring-with-java-configuration]]
=== Configuring with Java Configuration
The following Spring Boot application shows an example of how to configure the inbound adapter with Java configuration:
[source, java]
----
@SpringBootApplication
public class DebeziumJavaApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(DebeziumJavaApplication.class)
.web(WebApplicationType.NONE)
.run(args);
}
@Bean
public MessageChannel debeziumInputChannel() {
return new DirectChannel();
}
@Bean
public MessageProducer debeziumMessageProducer(
DebeziumEngine.Builder<ChangeEvent<byte[], byte[]>> debeziumEngineBuilder,
MessageChannel debeziumInputChannel) {
DebeziumMessageProducer debeziumMessageProducer =
new DebeziumMessageProducer(debeziumEngineBuilder);
debeziumMessageProducer.setOutputChannel(debeziumInputChannel);
return debeziumMessageProducer;
}
@ServiceActivator(inputChannel = "debeziumInputChannel")
public void handler(Message<?> message) {
Object destination = message.getHeaders().get(DebeziumHeaders.DESTINATION); # <1>
String key = new String((byte[]) message.getHeaders().get(DebeziumHeaders.KEY)); # <2>
String payload = new String((byte[]) message.getPayload()); # <3>
System.out.println("KEY: " + key + ", DESTINATION: " + destination + ", PAYLOAD: " + payload);
}
}
----
<1> A name of the logical destination for which the event is intended.
Usually the destination is composed of the `topic.prefix` configuration option, the database name and the table name. For example: `my-topic.inventory.orders`.
<2> Contains the schema for the changed table's key and the changed row's actual key.
Both the key schema and its corresponding key payload contain a field for each column in the changed table's `PRIMARY KEY` (or unique constraint) at the time the connector created the event.
<3> Like the key, the payload has a schema section and a payload value section.
The schema section contains the schema that describes the Envelope structure of the payload value section, including its nested fields.
Change events for operations that create, update or delete data all have a value payload with an envelope structure.
[TIP]
====
The `key.converter.schemas.enable=false` and/or `value.converter.schemas.enable=false` permit disabling the in-message schema content for key or payload respectively.
====
Similarly, we can configure the `DebeziumMessageProducer` to process the incoming change events in batches:
[source, java]
----
@Bean
public MessageProducer debeziumMessageProducer(
DebeziumEngine.Builder<ChangeEvent<byte[], byte[]>> debeziumEngineBuilder,
MessageChannel debeziumInputChannel) {
DebeziumMessageProducer debeziumMessageProducer = new DebeziumMessageProducer(debeziumEngineBuilder);
debeziumMessageProducer.setEnableBatch(true);
debeziumMessageProducer.setOutputChannel(debeziumInputChannel);
return debeziumMessageProducer;
}
@ServiceActivator(inputChannel = "debeziumInputChannel")
public void handler(List<ChangeEvent<Object, Object>> payload) {
System.out.println(payload);
}
----
[[debezium-java-dsl]]
== Debezium Java DSL Support
The `spring-integration-debezium` provides a convenient Java DSL fluent API via the `Debezium` factory and the `DebeziumMessageProducerSpec` implementations.
The Inbound Channel Adapter for Debezium Java DSL is:
[source, java]
----
DebeziumEngine.Builder<ChangeEvent<byte[], byte[]>> debeziumEngineBuilder = ...
IntegrationFlow.from(
Debezium.inboundChannelAdapter(debeziumEngineBuilder)
.headerNames("special*")
.contentType("application/json")
.enableBatch(false))
.handle(m -> System.out.println(new String((byte[]) m.getPayload())))
----
Or create an `DebeziumMessageProducerSpec` instance from native debezium configuration properties and default to `JSON` serialization formats.
[source, java]
----
Properties debeziumConfig = ...
IntegrationFlow
.from(Debezium.inboundChannelAdapter(debeziumConfig))
.handle(m -> System.out.println(new String((byte[]) m.getPayload())))
----
The following Spring Boot application provides an example of configuring the inbound adapter with the Java DSL:
[source, java]
----
@SpringBootApplication
public class DebeziumJavaApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(DebeziumJavaApplication.class)
.web(false)
.run(args);
}
@Bean
public IntegrationFlow debeziumInbound(
DebeziumEngine.Builder<ChangeEvent<byte[], byte[]>> debeziumEngineBuilder) {
return IntegrationFlow
.from(Debezium
.inboundChannelAdapter(debeziumEngineBuilder)
.headerNames("special*")
.contentType("application/json")
.enableBatch(false))
.handle(m -> System.out.println(new String((byte[]) m.getPayload())))
.get();
}
}
----

View File

@@ -0,0 +1,220 @@
[[delayer]]
= Delayer
A delayer is a simple endpoint that lets a message flow be delayed by a certain interval.
When a message is delayed, the original sender does not block.
Instead, the delayed messages are scheduled with an instance of `org.springframework.scheduling.TaskScheduler` to be sent to the output channel after the delay has passed.
This approach is scalable even for rather long delays, since it does not result in a large number of blocked sender threads.
On the contrary, in the typical case, a thread pool is used for the actual execution of releasing the messages.
This section contains several examples of configuring a delayer.
[[delayer-namespace]]
== Configuring a Delayer
The `<delayer>` element is used to delay the message flow between two message channels.
As with the other endpoints, you can provide the 'input-channel' and 'output-channel' attributes, but the delayer also has 'default-delay' and 'expression' attributes (and the 'expression' element) that determine the number of milliseconds by which each message should be delayed.
The following example delays all messages by three seconds:
[source,xml]
----
<int:delayer id="delayer" input-channel="input"
default-delay="3000" output-channel="output"/>
----
If you need to determine the delay for each message, you can also provide the SpEL expression by using the 'expression' attribute, as the following expression shows:
[tabs]
======
Java DSL::
+
[source, java, role="primary"]
----
@Bean
public IntegrationFlow flow() {
return IntegrationFlow.from("input")
.delay(d -> d
.messageGroupId("delayer.messageGroupId")
.defaultDelay(3_000L)
.delayExpression("headers['delay']"))
.channel("output")
.get();
}
----
Kotlin DSL::
+
[source, kotlin, role="secondary"]
----
@Bean
fun flow() =
integrationFlow("input") {
delay {
messageGroupId("delayer.messageGroupId")
defaultDelay(3000L)
delayExpression("headers['delay']")
}
channel("output")
}
----
Java::
+
[source, java, role="secondary"]
----
@ServiceActivator(inputChannel = "input")
@Bean
public DelayHandler delayer() {
DelayHandler handler = new DelayHandler("delayer.messageGroupId");
handler.setDefaultDelay(3_000L);
handler.setDelayExpressionString("headers['delay']");
handler.setOutputChannelName("output");
return handler;
}
----
XML::
+
[source, xml, role="secondary"]
----
<int:delayer id="delayer" input-channel="input" output-channel="output"
default-delay="3000" expression="headers['delay']"/>
----
======
In the preceding example, the three-second delay applies only when the expression evaluates to null for a given inbound message.
If you want to apply a delay only to messages that have a valid result of the expression evaluation, you can use a 'default-delay' of `0` (the default).
For any message that has a delay of `0` (or less), the message is sent immediately, on the calling thread.
NOTE: The XML parser uses a message group ID of `<beanName>.messageGroupId`.
TIP: The delay handler supports expression evaluation results that represent an interval in milliseconds (any `Object` whose `toString()` method produces a value that can be parsed into a `Long`) as well as `java.util.Date` instances representing an absolute time.
In the first case, the milliseconds are counted from the current time (for example a value of `5000` would delay the message for at least five seconds from the time it is received by the delayer).
With a `Date` instance, the message is not released until the time represented by that `Date` object.
A value that equates to a non-positive delay or a Date in the past results in no delay.
Instead, it is sent directly to the output channel on the original sender's thread.
If the expression evaluation result is not a `Date` and can not be parsed as a `Long`, the default delay (if any -- the default is `0`) is applied.
IMPORTANT: The expression evaluation may throw an evaluation exception for various reasons, including an invalid expression or other conditions.
By default, such exceptions are ignored (though logged at the DEBUG level) and the delayer falls back to the default delay (if any).
You can modify this behavior by setting the `ignore-expression-failures` attribute.
By default, this attribute is set to `true` and the delayer behavior is as described earlier.
However, if you wish to not ignore expression evaluation exceptions and throw them to the delayer's caller, set the `ignore-expression-failures` attribute to `false`.
[TIP]
=====
In the preceding example, the delay expression is specified as `headers['delay']`.
This is the SpEL `Indexer` syntax to access a `Map` element (`MessageHeaders` implements `Map`).
It invokes: `headers.get("delay")`.
For simple map element names (that do not contain '.') you can also use the SpEL "`dot accessor`" syntax, where the header expression shown earlier can be specified as `headers.delay`.
However, different results are achieved if the header is missing.
In the first case, the expression evaluates to `null`.
The second results in something similar to the following:
[source,java]
----
org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 8):
Field or property 'delay' cannot be found on object of type 'org.springframework.messaging.MessageHeaders'
----
Consequently, if there is a possibility of the header being omitted and you want to fall back to the default delay, it is generally more efficient (and recommended) using the indexer syntax instead of dot property accessor syntax, because detecting the null is faster than catching an exception.
=====
The delayer delegates to an instance of Spring's `TaskScheduler` abstraction.
The default scheduler used by the delayer is the `ThreadPoolTaskScheduler` instance provided by Spring Integration on startup.
See xref:configuration/namespace-taskscheduler.adoc[Configuring the Task Scheduler].
If you want to delegate to a different scheduler, you can provide a reference through the delayer element's 'scheduler' attribute, as the following example shows:
[source,xml]
----
<int:delayer id="delayer" input-channel="input" output-channel="output"
expression="headers.delay"
scheduler="exampleTaskScheduler"/>
<task:scheduler id="exampleTaskScheduler" pool-size="3"/>
----
TIP: If you configure an external `ThreadPoolTaskScheduler`, you can set `waitForTasksToCompleteOnShutdown = true` on this property.
It allows successful completion of 'delay' tasks that are already in the execution state (releasing the message) when the application is shutdown.
Before Spring Integration 2.2, this property was available on the `<delayer>` element, because `DelayHandler` could create its own scheduler on the background.
Since 2.2, the delayer requires an external scheduler instance and `waitForTasksToCompleteOnShutdown` was deleted.
You should use the scheduler's own configuration.
TIP: `ThreadPoolTaskScheduler` has a property `errorHandler`, which can be injected with some implementation of `org.springframework.util.ErrorHandler`.
This handler allows processing an `Exception` from the thread of the scheduled task sending the delayed message.
By default, it uses an `org.springframework.scheduling.support.TaskUtils$LoggingErrorHandler`, and you can see a stack trace in the logs.
You might want to consider using an `org.springframework.integration.channel.MessagePublishingErrorHandler`, which sends an `ErrorMessage` into an `error-channel`, either from the failed message's header or into the default `error-channel`.
This error handling is performed after a transaction rolls back (if present).
See xref:delayer.adoc#delayer-release-failures[Release Failures].
[[delayer-message-store]]
== Delayer and a Message Store
The `DelayHandler` persists delayed messages into the message group in the provided `MessageStore`.
(The 'groupId' is based on the required 'id' attribute of the `<delayer>` element.
See also `DelayHandler.setMessageGroupId(String)`.)
A delayed message is removed from the `MessageStore` by the scheduled task immediately before the `DelayHandler` sends the message to the `output-channel`.
If the provided `MessageStore` is persistent (such as `JdbcMessageStore`), it provides the ability to not lose messages on the application shutdown.
After application startup, the `DelayHandler` reads messages from its message group in the `MessageStore` and reschedules them with a delay based on the original arrival time of the message (if the delay is numeric).
For messages where the delay header was a `Date`, that `Date` is used when rescheduling.
If a delayed message remains in the `MessageStore` more than its 'delay', it is sent immediately after startup.
The `messageGroupId` is required and cannot rely on a `DelayHandler` bean name which can be generated.
That way, after application restart, a `DelayHandler` may get a new generated bean name.
Therefore, delayed messages might be lost from rescheduling since their group is not managed by the application anymore.
The `<delayer>` can be enriched with either of two mutually exclusive elements: `<transactional>` and `<advice-chain>`.
The `List` of these AOP advices is applied to the proxied internal `DelayHandler.ReleaseMessageHandler`, which has the responsibility to release the message, after the delay, on a `Thread` of the scheduled task.
It might be used, for example, when the downstream message flow throws an exception and the transaction of the `ReleaseMessageHandler` is rolled back.
In this case, the delayed message remains in the persistent `MessageStore`.
You can use any custom `org.aopalliance.aop.Advice` implementation within the `<advice-chain>`.
The `<transactional>` element defines a simple advice chain that has only the transactional advice.
The following example shows an `advice-chain` within a `<delayer>`:
[source,xml]
----
<int:delayer id="delayer" input-channel="input" output-channel="output"
expression="headers.delay"
message-store="jdbcMessageStore">
<int:advice-chain>
<beans:ref bean="customAdviceBean"/>
<tx:advice>
<tx:attributes>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
</int:advice-chain>
</int:delayer>
----
The `DelayHandler` can be exported as a JMX `MBean` with managed operations (`getDelayedMessageCount` and `reschedulePersistedMessages`), which allows the rescheduling of delayed persisted messages at runtime -- for example, if the `TaskScheduler` has previously been stopped.
These operations can be invoked through a `Control Bus` command, as the following example shows:
[source,java]
----
Message<String> delayerReschedulingMessage =
MessageBuilder.withPayload("@'delayer.handler'.reschedulePersistedMessages()").build();
controlBusChannel.send(delayerReschedulingMessage);
----
NOTE: For more information regarding the message store, JMX, and the control bus, see xref:system-management.adoc[System Management].
Starting with version 5.3.7, if a transaction is active when a message is stored into a `MessageStore`, the release task is scheduled in a `TransactionSynchronization.afterCommit()` callback.
This is necessary to prevent a race condition, where the scheduled release could run before the transaction has committed, and the message is not found.
In this case, the message will be released after the delay, or after the transaction commits, whichever is later.
[[delayer-release-failures]]
== Release Failures
Starting with version 5.0.8, there are two new properties on the delayer:
- `maxAttempts` (default 5)
- `retryDelay` (default 1 second)
When a message is released, if the downstream flow fails, the release will be attempted after the `retryDelay`.
If the `maxAttempts` is reached, the message is discarded (unless the release is transactional, in which case the message will remain in the store, but will no longer be scheduled for release, until the application is restarted, or the `reschedulePersistedMessages()` method is invoked, as discussed above).
In addition, you can configure a `delayedMessageErrorChannel`; when a release fails, an `ErrorMessage` is sent to that channel with the exception as the payload and has the `originalMessage` property.
The `ErrorMessage` contains a header `IntegrationMessageHeaderAccessor.DELIVERY_ATTEMPT` containing the current count.
If the error flow consumes the error message and exits normally, no further action is taken; if the release is transactional, the transaction will commit and the message deleted from the store.
If the error flow throws an exception, the release will be retried up to `maxAttempts` as discussed above.

View File

@@ -0,0 +1,57 @@
[[java-dsl]]
= Java DSL
The Spring Integration Java configuration and DSL provides a set of convenient builders and a fluent API that lets you configure Spring Integration message flows from Spring `@Configuration` classes.
(See also xref:kotlin-dsl.adoc[Kotlin DSL].)
(See also xref:groovy-dsl.adoc[Groovy DSL].)
The Java DSL for Spring Integration is essentially a facade for Spring Integration.
The DSL provides a simple way to embed Spring Integration Message Flows into your application by using the fluent `Builder` pattern together with existing Java configuration from Spring Framework and Spring Integration.
We also use and support lambdas (available with Java 8) to further simplify Java configuration.
The https://github.com/spring-projects/spring-integration-samples/tree/main/dsl/cafe-dsl[cafe] offers a good example of using the DSL.
The DSL is presented by the `IntegrationFlow` fluent API (see `IntegrationFlowBuilder`).
This produces the `IntegrationFlow` component, which should be registered as a Spring bean (by using the `@Bean` annotation).
The builder pattern is used to express arbitrarily complex structures as a hierarchy of methods that can accept lambdas as arguments.
The `IntegrationFlowBuilder` only collects integration components (`MessageChannel` instances, `AbstractEndpoint` instances, and so on) in the `IntegrationFlow` bean for further parsing and registration of concrete beans in the application context by the `IntegrationFlowBeanPostProcessor`.
The Java DSL uses Spring Integration classes directly and bypasses any XML generation and parsing.
However, the DSL offers more than syntactic sugar on top of XML.
One of its most compelling features is the ability to define inline lambdas to implement endpoint logic, eliminating the need for external classes to implement custom logic.
In some sense, Spring Integration's support for the Spring Expression Language (SpEL) and inline scripting address this, but lambdas are easier and much more powerful.
The following example shows how to use Java Configuration for Spring Integration:
[source,java]
----
@Configuration
@EnableIntegration
public class MyConfiguration {
@Bean
public AtomicInteger integerSource() {
return new AtomicInteger();
}
@Bean
public IntegrationFlow myFlow(AtomicInteger integerSource) {
return IntegrationFlow.fromSupplier(integerSource::getAndIncrement,
c -> c.poller(Pollers.fixedRate(100)))
.channel("inputChannel")
.filter((Integer p) -> p > 0)
.transform(Object::toString)
.channel(MessageChannels.queue())
.get();
}
}
----
The result of the preceding configuration example is that it creates, after `ApplicationContext` start up, Spring Integration endpoints and message channels.
Java configuration can be used both to replace and augment XML configuration.
You need not replace all of your existing XML configuration to use Java configuration.

View File

@@ -0,0 +1,56 @@
[[integration-flow-as-gateway]]
= `IntegrationFlow` as a Gateway
The `IntegrationFlow` can start from the service interface that provides a `GatewayProxyFactoryBean` component, as the following example shows:
[source,java]
----
public interface ControlBusGateway {
void send(String command);
}
...
@Bean
public IntegrationFlow controlBusFlow() {
return IntegrationFlow.from(ControlBusGateway.class)
.controlBus()
.get();
}
----
All the proxy for interface methods are supplied with the channel to send messages to the next integration component in the `IntegrationFlow`.
You can mark the service interface with the `@MessagingGateway` annotation and mark the methods with the `@Gateway` annotations.
Nevertheless, the `requestChannel` is ignored and overridden with that internal channel for the next component in the `IntegrationFlow`.
Otherwise, creating such a configuration by using `IntegrationFlow` does not make sense.
By default, a `GatewayProxyFactoryBean` gets a conventional bean name, such as `[FLOW_BEAN_NAME.gateway]`.
You can change that ID by using the `@MessagingGateway.name()` attribute or the overloaded `IntegrationFlow.from(Class<?> serviceInterface, Consumer<GatewayProxySpec> endpointConfigurer)` factory method.
Also, all the attributes from the `@MessagingGateway` annotation on the interface are applied to the target `GatewayProxyFactoryBean`.
When annotation configuration is not applicable, the `Consumer<GatewayProxySpec>` variant can be used for providing appropriate option for the target proxy.
This DSL method is available starting with version 5.2.
With Java 8, you can even create an integration gateway with the `java.util.function` interfaces, as the following example shows:
[source,java]
----
@Bean
public IntegrationFlow errorRecovererFlow() {
return IntegrationFlow.from(Function.class, (gateway) -> gateway.beanName("errorRecovererFunction"))
.<Object>handle((p, h) -> {
throw new RuntimeException("intentional");
}, e -> e.advice(retryAdvice()))
.get();
}
----
That `errorRecovererFlow` can be used as follows:
[source,java]
----
@Autowired
@Qualifier("errorRecovererFunction")
private Function<String, String> errorRecovererFlowGateway;
----

View File

@@ -0,0 +1,50 @@
[[integration-flows-composition]]
= Integration Flows Composition
With the `MessageChannel` abstraction as a first class citizen in Spring Integration, the composition of integration flows was always assumed.
The input channel of any endpoint in the flow can be used to send messages from any other endpoint and not only from the one which has this channel as an output.
Furthermore, with a `@MessagingGateway` contract, Content Enricher components, composite endpoints like a `<chain>`, and now with `IntegrationFlow` beans (e.g. `IntegrationFlowAdapter`), it is straightforward enough to distribute the business logic between shorter, reusable parts.
All that is needed for the final composition is knowledge about a `MessageChannel` to send to or receive from.
Starting with version `5.5.4`, to abstract more from `MessageChannel` and hide implementation details from the end-user, the `IntegrationFlow` introduces the `from(IntegrationFlow)` factory method to allow starting the current `IntegrationFlow` from the output of an existing flow:
[source,java]
----
@Bean
IntegrationFlow templateSourceFlow() {
return IntegrationFlow.fromSupplier(() -> "test data")
.channel("sourceChannel")
.get();
}
@Bean
IntegrationFlow compositionMainFlow(IntegrationFlow templateSourceFlow) {
return IntegrationFlow.from(templateSourceFlow)
.<String, String>transform(String::toUpperCase)
.channel(c -> c.queue("compositionMainFlowResult"))
.get();
}
----
On the other hand, the `IntegrationFlowDefinition` has added a `to(IntegrationFlow)` terminal operator to continue the current flow at the input channel of some other flow:
[source,java]
----
@Bean
IntegrationFlow mainFlow(IntegrationFlow otherFlow) {
return f -> f
.<String, String>transform(String::toUpperCase)
.to(otherFlow);
}
@Bean
IntegrationFlow otherFlow() {
return f -> f
.<String, String>transform(p -> p + " from other flow")
.channel(c -> c.queue("otherFlowResultChannel"));
}
----
The composition in the middle of the flow is simply achievable with an existing `gateway(IntegrationFlow)` EIP-method.
This way we can build flows with any complexity by composing them from simpler, reusable logical blocks.
For example, you may add a library of `IntegrationFlow` beans as a dependency, and it is just enough to have their configuration classes imported to the final project and autowired for your `IntegrationFlow` definitions.

View File

@@ -0,0 +1,41 @@
[[java-dsl-aggregators]]
= Aggregators and Resequencers
An `Aggregator` is conceptually the opposite of a `Splitter`.
It aggregates a sequence of individual messages into a single message and is necessarily more complex.
By default, an aggregator returns a message that contains a collection of payloads from incoming messages.
The same rules are applied for the `Resequencer`.
The following example shows a canonical example of the splitter-aggregator pattern:
[source,java]
----
@Bean
public IntegrationFlow splitAggregateFlow() {
return IntegrationFlow.from("splitAggregateInput")
.split()
.channel(MessageChannels.executor(this.taskExecutor()))
.resequence()
.aggregate()
.get();
}
----
The `split()` method splits the list into individual messages and sends them to the `ExecutorChannel`.
The `resequence()` method reorders messages by sequence details found in the message headers.
The `aggregate()` method collects those messages.
However, you can change the default behavior by specifying a release strategy and correlation strategy, among other things.
Consider the following example:
[source,java]
----
.aggregate(a ->
a.correlationStrategy(m -> m.getHeaders().get("myCorrelationKey"))
.releaseStrategy(g -> g.size() > 10)
.messageStore(messageStore()))
----
The preceding example correlates messages that have `myCorrelationKey` headers and releases the messages once at least ten have been accumulated.
Similar lambda configurations are provided for the `resequence()` EIP method.

View File

@@ -0,0 +1,95 @@
[[java-dsl-basics]]
= DSL Basics
The `org.springframework.integration.dsl` package contains the `IntegrationFlowBuilder` API mentioned earlier and a number of `IntegrationComponentSpec` implementations, which are also builders and provide the fluent API to configure concrete endpoints.
The `IntegrationFlowBuilder` infrastructure provides common https://www.enterpriseintegrationpatterns.com/[enterprise integration patterns] (EIP) for message-based applications, such as channels, endpoints, pollers, and channel interceptors.
IMPORTANT:: The `IntegrationComponentSpec` is a `FactoryBean` implementation, therefore its `getObject()` method must not be called from bean definitions.
The `IntegrationComponentSpec` implementation must be left as is for bean definitions and the framework will manage its lifecycle.
Bean method parameter injection for the target `IntegrationComponentSpec` type (a `FactoryBean` value) must be used for `IntegrationFlow` bean definitions instead of bean method references.
Endpoints are expressed as verbs in the DSL to improve readability.
The following list includes the common DSL method names and the associated EIP endpoint:
* transform -> `Transformer`
* filter -> `Filter`
* handle -> `ServiceActivator`
* split -> `Splitter`
* aggregate -> `Aggregator`
* route -> `Router`
* bridge -> `Bridge`
Conceptually, integration processes are constructed by composing these endpoints into one or more message flows.
Note that EIP does not formally define the term 'message flow', but it is useful to think of it as a unit of work that uses well known messaging patterns.
The DSL provides an `IntegrationFlow` component to define a composition of channels and endpoints between them, but now `IntegrationFlow` plays only the configuration role to populate real beans in the application context and is not used at runtime.
However, the bean for `IntegrationFlow` can be autowired as a `Lifecycle` to control `start()` and `stop()` for the whole flow which is delegated to all the Spring Integration components associated with this `IntegrationFlow`.
The following example uses the `IntegrationFlow` fluent API to define an `IntegrationFlow` bean by using EIP-methods from `IntegrationFlowBuilder`:
[source,java]
----
@Bean
public IntegrationFlow integerFlow() {
return IntegrationFlow.from("input")
.<String, Integer>transform(Integer::parseInt)
.get();
}
----
The `transform` method accepts a lambda as an endpoint argument to operate on the message payload.
The real argument of this method is a `GenericTransformer<S, T>` instance.
Consequently, any of the provided transformers (`ObjectToJsonTransformer`, `FileToStringTransformer`, and other) can be used here.
Under the covers, `IntegrationFlowBuilder` recognizes the `MessageHandler` and the endpoint for it, with `MessageTransformingHandler` and `ConsumerEndpointFactoryBean`, respectively.
Consider another example:
[source,java]
----
@Bean
public IntegrationFlow myFlow() {
return IntegrationFlow.from("input")
.filter("World"::equals)
.transform("Hello "::concat)
.handle(System.out::println)
.get();
}
----
The preceding example composes a sequence of `Filter -> Transformer -> Service Activator`.
The flow is "'one way'".
That is, it does not provide a reply message but only prints the payload to STDOUT.
The endpoints are automatically wired together by using direct channels.
[[java-dsl-class-cast]]
.Lambdas And `Message<?>` Arguments
[IMPORTANT]
====
When using lambdas in EIP methods, the "input" argument is generally the message payload.
If you wish to access the entire message, use one of the overloaded methods that take a `Class<?>` as the first parameter.
For example, this won't work:
[source, java]
----
.<Message<?>, Foo>transform(m -> newFooFromMessage(m))
----
This will fail at runtime with a `ClassCastException` because the lambda doesn't retain the argument type and the framework will attempt to cast the payload to a `Message<?>`.
Instead, use:
[source, java]
----
.(Message.class, m -> newFooFromMessage(m))
----
====
[[bean-definitions-override]]
.Bean Definitions override
[IMPORTANT]
====
The Java DSL can register beans for the object defined in-line in the flow definition, as well as can reuse existing, injected beans.
In case of the same bean name defined for in-line object and existing bean definition, a `BeanDefinitionOverrideException` is thrown indicating that such a configuration is wrong.
However, when you deal with `prototype` beans, there is no way to detect from the integration flow processor an existing bean definition because every time we call a `prototype` bean from the `BeanFactory` we get a new instance.
This way a provided instance is used in the `IntegrationFlow` as is without any bean registration and any possible check against existing `prototype` bean definition.
However `BeanFactory.initializeBean()` is called for this object if it has an explicit `id` and bean definition for this name is in `prototype` scope.
====

View File

@@ -0,0 +1,89 @@
[[java-dsl-channels]]
= Message Channels
In addition to the `IntegrationFlowBuilder` with EIP methods, the Java DSL provides a fluent API to configure `MessageChannel` instances.
For this purpose the `MessageChannels` builder factory is provided.
The following example shows how to use it:
[source,java]
----
@Bean
public PriorityChannelSpec priorityChannel() {
return MessageChannels.priority(this.mongoDbChannelMessageStore, "priorityGroup")
.interceptor(wireTap());
}
----
The same `MessageChannels` builder factory can be used in the `channel()` EIP method from `IntegrationFlowBuilder` to wire endpoints, similar to wiring an `input-channel`/`output-channel` pair in the XML configuration.
By default, endpoints are wired with `DirectChannel` instances where the bean name is based on the following pattern: `[IntegrationFlow.beanName].channel#[channelNameIndex]`.
This rule is also applied for unnamed channels produced by inline `MessageChannels` builder factory usage.
However, all `MessageChannels` methods have a variant that is aware of the `channelId` that you can use to set the bean names for `MessageChannel` instances.
The `MessageChannel` references and `beanName` can be used as bean-method invocations.
The following example shows the possible ways to use the `channel()` EIP method:
[source,java]
----
@Bean
public QueueChannelSpec queueChannel() {
return MessageChannels.queue();
}
@Bean
public PublishSubscribeChannelSpec<?> publishSubscribe() {
return MessageChannels.publishSubscribe();
}
@Bean
public IntegrationFlow channelFlow() {
return IntegrationFlow.from("input")
.fixedSubscriberChannel()
.channel("queueChannel")
.channel(publishSubscribe())
.channel(MessageChannels.executor("executorChannel", this.taskExecutor))
.channel("output")
.get();
}
----
* `from("input")` means "'find and use the `MessageChannel` with the "input" id, or create one'".
* `fixedSubscriberChannel()` produces an instance of `FixedSubscriberChannel` and registers it with a name of `channelFlow.channel#0`.
* `channel("queueChannel")` works the same way but uses an existing `queueChannel` bean.
* `channel(publishSubscribe())` is the bean-method reference.
* `channel(MessageChannels.executor("executorChannel", this.taskExecutor))` is the `IntegrationFlowBuilder` that exposes `IntegrationComponentSpec` to the `ExecutorChannel` and registers it as `executorChannel`.
* `channel("output")` registers the `DirectChannel` bean with `output` as its name, as long as no beans with this name already exist.
Note: The preceding `IntegrationFlow` definition is valid, and all of its channels are applied to endpoints with `BridgeHandler` instances.
IMPORTANT: Be careful to use the same inline channel definition through `MessageChannels` factory from different `IntegrationFlow` instances.
Even if the DSL parser registers non-existent objects as beans, it cannot determine the same object (`MessageChannel`) from different `IntegrationFlow` containers.
The following example is wrong:
[source,java]
----
@Bean
public IntegrationFlow startFlow() {
return IntegrationFlow.from("input")
.transform(...)
.channel(MessageChannels.queue("queueChannel"))
.get();
}
@Bean
public IntegrationFlow endFlow() {
return IntegrationFlow.from(MessageChannels.queue("queueChannel"))
.handle(...)
.get();
}
----
The result of that bad example is the following exception:
```
Caused by: java.lang.IllegalStateException:
Could not register object [queueChannel] under bean name 'queueChannel':
there is already object [queueChannel] bound
at o.s.b.f.s.DefaultSingletonBeanRegistry.registerSingleton(DefaultSingletonBeanRegistry.java:129)
```
To make it work, you need to declare `@Bean` for that channel and use its bean method from different `IntegrationFlow` instances.

View File

@@ -0,0 +1,46 @@
[[java-dsl-endpoints]]
= DSL and Endpoint Configuration
All `IntegrationFlowBuilder` EIP methods have a variant that applies the lambda parameter to provide options for `AbstractEndpoint` instances: `SmartLifecycle`, `PollerMetadata`, `request-handler-advice-chain`, and others.
Each of them has generic arguments, so it lets you configure an endpoint and even its `MessageHandler` in the context, as the following example shows:
[source,java]
----
@Bean
public IntegrationFlow flow2() {
return IntegrationFlow.from(this.inputChannel)
.transformWith(t -> t
.transformer(new PayloadSerializingTransformer())
.autoStartup(false)
.id("payloadSerializingTransformer"))
.transformWith(t -> t
.transformer((Integer p) -> p * 2)
.advice(expressionAdvice()))
.get();
}
----
In addition, the `EndpointSpec` provides an `id()` method to let you register an endpoint bean with a given bean name, rather than a generated one.
If the `MessageHandler` is referenced as a bean, then any existing `adviceChain` configuration will be overridden if the `.advice()` method is present in the DSL definition:
[source,java]
----
@Bean
public TcpOutboundGateway tcpOut() {
TcpOutboundGateway gateway = new TcpOutboundGateway();
gateway.setConnectionFactory(cf());
gateway.setAdviceChain(Collections.singletonList(fooAdvice()));
return gateway;
}
@Bean
public IntegrationFlow clientTcpFlow() {
return f -> f
.handle(tcpOut(), e -> e.advice(testAdvice()))
.transform(Transformers.objectToString());
}
----
They are not merged, only the `testAdvice()` bean is used in this case.

View File

@@ -0,0 +1,56 @@
[[java-dsl-extensions]]
= DSL Extensions
Starting with version 5.3, an `IntegrationFlowExtension` has been introduced to allow extension of the existing Java DSL with custom or composed EIP-operators.
All that is needed is an extension of this class that provides methods which can be used in the `IntegrationFlow` bean definitions.
The extension class can also be used for custom `IntegrationComponentSpec` configuration; for example, missed or default options can be implemented in the existing `IntegrationComponentSpec` extension.
The sample below demonstrates a composite custom operator and usage of an `AggregatorSpec` extension for a default custom `outputProcessor`:
[source,java]
----
public class CustomIntegrationFlowDefinition
extends IntegrationFlowExtension<CustomIntegrationFlowDefinition> {
public CustomIntegrationFlowDefinition upperCaseAfterSplit() {
return split()
.transform("payload.toUpperCase()");
}
public CustomIntegrationFlowDefinition customAggregate(Consumer<CustomAggregatorSpec> aggregator) {
return register(new CustomAggregatorSpec(), aggregator);
}
}
public class CustomAggregatorSpec extends AggregatorSpec {
CustomAggregatorSpec() {
outputProcessor(group ->
group.getMessages()
.stream()
.map(Message::getPayload)
.map(String.class::cast)
.collect(Collectors.joining(", ")));
}
}
----
For a method chain flow the new DSL operator in these extensions must return the extension class.
This way a target `IntegrationFlow` definition will work with new and existing DSL operators:
[source,java]
----
@Bean
public IntegrationFlow customFlowDefinition() {
return
new CustomIntegrationFlowDefinition()
.log()
.upperCaseAfterSplit()
.channel("innerChannel")
.customAggregate(customAggregatorSpec ->
customAggregatorSpec.expireGroupsUponCompletion(true))
.logAndReply();
}
----

View File

@@ -0,0 +1,79 @@
[[java-dsl-flow-adapter]]
= `IntegrationFlowAdapter`
The `IntegrationFlow` interface can be implemented directly and specified as a component for scanning, as the following example shows:
[source,java]
----
@Component
public class MyFlow implements IntegrationFlow {
@Override
public void configure(IntegrationFlowDefinition<?> f) {
f.<String, String>transform(String::toUpperCase);
}
}
----
It is picked up by the `IntegrationFlowBeanPostProcessor` and correctly parsed and registered in the application context.
For convenience and to gain the benefits of loosely coupled architecture, we provide the `IntegrationFlowAdapter` base class implementation.
It requires a `buildFlow()` method implementation to produce an `IntegrationFlowDefinition` by using one of `from()` methods, as the following example shows:
[source,java]
----
@Component
public class MyFlowAdapter extends IntegrationFlowAdapter {
private final AtomicBoolean invoked = new AtomicBoolean();
public Instant nextExecutionTime(TriggerContext triggerContext) {
return this.invoked.getAndSet(true) ? null : Instant.now();
}
@Override
protected IntegrationFlowDefinition<?> buildFlow() {
return fromSupplier(this::messageSource,
e -> e.poller(p -> p.trigger(this::nextExecutionTime)))
.split(this)
.transform(this)
.aggregate(this)
.enrichHeaders(Collections.singletonMap("thing1", "THING1"))
.filter(this)
.handle(this)
.channel(c -> c.queue("myFlowAdapterOutput"));
}
public String messageSource() {
return "T,H,I,N,G,2";
}
@Splitter
public String[] split(String payload) {
return StringUtils.commaDelimitedListToStringArray(payload);
}
@Transformer
public String transform(String payload) {
return payload.toLowerCase();
}
@Aggregator
public String aggregate(List<String> payloads) {
return payloads.stream().collect(Collectors.joining());
}
@Filter
public boolean filter(@Header Optional<String> thing1) {
return thing1.isPresent();
}
@ServiceActivator
public String handle(String payload, @Header String thing1) {
return payload + ":" + thing1;
}
}
----

View File

@@ -0,0 +1,43 @@
[[java-dsl-flows]]
= Working With Message Flows
`IntegrationFlowBuilder` provides a top-level API to produce integration components wired to message flows.
When your integration may be accomplished with a single flow (which is often the case), this is convenient.
Alternately `IntegrationFlow` instances can be joined via `MessageChannel` instances.
By default, `MessageFlow` behaves as a "`chain`" in Spring Integration parlance.
That is, the endpoints are automatically and implicitly wired by `DirectChannel` instances.
The message flow is not actually constructed as a chain, which offers much more flexibility.
For example, you may send a message to any component within the flow, if you know its `inputChannel` name (that is, if you explicitly define it).
You may also reference externally defined channels within a flow to allow the use of channel adapters (to enable remote transport protocols, file I/O, and so on), instead of direct channels.
As such, the DSL does not support the Spring Integration `chain` element, because it does not add much value in this case.
Since the Spring Integration Java DSL produces the same bean definition model as any other configuration options and is based on the existing Spring Framework `@Configuration` infrastructure, it can be used together with XML definitions and wired with Spring Integration messaging annotation configuration.
You can also define direct `IntegrationFlow` instances by using a lambda.
The following example shows how to do so:
[source,java]
----
@Bean
public IntegrationFlow lambdaFlow() {
return f -> f.filter("World"::equals)
.transform("Hello "::concat)
.handle(System.out::println);
}
----
The result of this definition is the same set of integration components that are wired with an implicit direct channel.
The only limitation here is that this flow is started with a named direct channel - `lambdaFlow.input`.
Also, a Lambda flow cannot start from `MessageSource` or `MessageProducer`.
Starting with version 5.1, this kind of `IntegrationFlow` is wrapped to the proxy to expose lifecycle control and provide access to the `inputChannel` of the internally associated `StandardIntegrationFlow`.
Starting with version 5.0.6, the generated bean names for the components in an `IntegrationFlow` include the flow bean followed by a dot (`.`) as a prefix.
For example, the `ConsumerEndpointFactoryBean` for the `.transform("Hello "::concat)` in the preceding sample results in a bean name of `lambdaFlow.o.s.i.config.ConsumerEndpointFactoryBean#0`.
(The `o.s.i` is a shortened from `org.springframework.integration` to fit on the page.)
The `Transformer` implementation bean for that endpoint has a bean name of `lambdaFlow.transformer#0` (starting with version 5.1), where instead of a fully qualified name of the `MethodInvokingTransformer` class, its component type is used.
The same pattern is applied for all the `NamedComponent` s when the bean name has to be generated within the flow.
These generated bean names are prepended with the flow ID for purposes such as parsing logs or grouping components together in some analysis tool, as well as to avoid a race condition when we concurrently register integration flows at runtime.
See xref:dsl/java-runtime-flows.adoc[Dynamic and Runtime Integration Flows] for more information.

View File

@@ -0,0 +1,17 @@
[[java-dsl-function-expression]]
= `FunctionExpression`
:page-section-summary-toc: 1
We introduced the `FunctionExpression` class (an implementation of SpEL's `Expression` interface) to let us use lambdas and `generics`.
The `Function<T, R>` option is provided for the DSL components, along with an `expression` option, when there is the implicit `Strategy` variant from Core Spring Integration.
The following example shows how to use a function expression:
[source,java]
----
.enrich(e -> e.requestChannel("enrichChannel")
.requestPayload(Message::getPayload)
.propertyFunction("date", m -> new Date()))
----
The `FunctionExpression` also supports runtime type conversion, as is done in `SpelExpression`.

View File

@@ -0,0 +1,36 @@
[[java-dsl-gateway]]
= Operator gateway()
The `gateway()` operator in an `IntegrationFlow` definition is a special service activator implementation, to call some other endpoint or integration flow via its input channel and wait for reply.
Technically it plays the same role as a nested `<gateway>` component in a `<chain>` definition (see xref:chain.adoc#chain-gateway[Calling a Chain from within a Chain]) and allows a flow to be cleaner and more straightforward.
Logically, and from business perspective, it is a messaging gateway to allow the distribution and reuse of functionality between different parts of the target integration solution (see xref:gateway.adoc[Messaging Gateways]).
This operator has several overloads for different goals:
- `gateway(String requestChannel)` to send a message to some endpoint's input channel by its name;
- `gateway(MessageChannel requestChannel)` to send a message to some endpoint's input channel by its direct injection;
- `gateway(IntegrationFlow flow)` to send a message to the input channel of the provided `IntegrationFlow`.
All of these have a variant with the second `Consumer<GatewayEndpointSpec>` argument to configure the target `GatewayMessageHandler` and respective `AbstractEndpoint`.
Also, the `IntegrationFlow`-based methods allows calling existing `IntegrationFlow` bean or declare the flow as a sub-flow via an in-place lambda for an `IntegrationFlow` functional interface or have it extracted in a `private` method cleaner code style:
[source,java]
----
@Bean
IntegrationFlow someFlow() {
return IntegrationFlow
.from(...)
.gateway(subFlow())
.handle(...)
.get();
}
private static IntegrationFlow subFlow() {
return f -> f
.scatterGather(s -> s.recipientFlow(...),
g -> g.outputProcessor(MessageGroup::getOne))
}
----
IMPORTANT: If the downstream flow does not always return a reply, you should set the `requestTimeout` to 0 to prevent hanging the calling thread indefinitely.
In that case, the flow will end at that point and the thread released for further work.

View File

@@ -0,0 +1,57 @@
[[java-dsl-handle]]
= Service Activators and the `.handle()` method
The `.handle()` EIP method's goal is to invoke any `MessageHandler` implementation or any method on some POJO.
Another option is to define an "`activity`" by using lambda expressions.
Consequently, we introduced a generic `GenericHandler<P>` functional interface.
Its `handle` method requires two arguments: `P payload` and `MessageHeaders headers` (starting with version 5.1).
Having that, we can define a flow as follows:
[source,java]
----
@Bean
public IntegrationFlow myFlow() {
return IntegrationFlow.from("flow3Input")
.<Integer>handle((p, h) -> p * 2)
.get();
}
----
The preceding example doubles any integer it receives.
However, one main goal of Spring Integration is `loose coupling`, through runtime type conversion from message payload to the target arguments of the message handler.
Since Java does not support generic type resolution for lambda classes, we introduced a workaround with an additional `payloadType` argument for the most EIP methods and `LambdaMessageProcessor`.
Doing so delegates the hard conversion work to Spring's `ConversionService`, which uses the provided `type` and the requested message to target method arguments.
The following example shows what the resulting `IntegrationFlow` might look like:
[source,java]
----
@Bean
public IntegrationFlow integerFlow() {
return IntegrationFlow.from("input")
.<byte[], String>transform(p - > new String(p, "UTF-8"))
.handle(Integer.class, (p, h) -> p * 2)
.get();
}
----
We also can register some `BytesToIntegerConverter` within `ConversionService` to get rid of that additional `.transform()`:
[source,java]
----
@Bean
@IntegrationConverter
public BytesToIntegerConverter bytesToIntegerConverter() {
return new BytesToIntegerConverter();
}
@Bean
public IntegrationFlow integerFlow() {
return IntegrationFlow.from("input")
.handle(Integer.class, (p, h) -> p * 2)
.get();
}
----
Also see xref:dsl/java-basics.adoc#java-dsl-class-cast[Lambdas And `Message<?>` Arguments].

View File

@@ -0,0 +1,31 @@
[[java-dsl-inbound-adapters]]
= Inbound Channel Adapters
Typically, message flows start from an inbound channel adapter (such as `<int-jdbc:inbound-channel-adapter>`).
The adapter is configured with `<poller>`, and it asks a `MessageSource<?>` to periodically produce messages.
Java DSL allows for starting `IntegrationFlow` from a `MessageSource<?>`, too.
For this purpose, the `IntegrationFlow` fluent API provides an overloaded `IntegrationFlow.from(MessageSource<?> messageSource)` method.
You can configure the `MessageSource<?>` as a bean and provide it as an argument for that method.
The second parameter of `IntegrationFlow.from()` is a `Consumer<SourcePollingChannelAdapterSpec>` lambda that lets you provide options (such as `PollerMetadata` or `SmartLifecycle`) for the `SourcePollingChannelAdapter`.
The following example shows how to use the fluent API and a lambda to create an `IntegrationFlow`:
[source,java]
----
@Bean
public MessageSource<Object> jdbcMessageSource() {
return new JdbcPollingChannelAdapter(this.dataSource, "SELECT * FROM something");
}
@Bean
public IntegrationFlow pollingFlow() {
return IntegrationFlow.from(jdbcMessageSource(),
c -> c.poller(Pollers.fixedRate(100).maxMessagesPerPoll(1)))
.transform(Transformers.toJson())
.channel("furtherProcessChannel")
.get();
}
----
For those cases that have no requirements to build `Message` objects directly, you can use a `IntegrationFlow.fromSupplier()` variant that is based on the `java.util.function.Supplier` .
The result of the `Supplier.get()` is automatically wrapped in a `Message` (if it is not already a `Message`).

View File

@@ -0,0 +1,15 @@
[[java-dsl-intercept]]
= Operator intercept()
:page-section-summary-toc: 1
Starting with version 5.3, the `intercept()` operator allows to register one or more `ChannelInterceptor` instances at the current `MessageChannel` in the flow.
This is an alternative to creating an explicit `MessageChannel` via the `MessageChannels` API.
The following example uses a `MessageSelectingInterceptor` to reject certain messages with an exception:
[source,java]
----
.transform(...)
.intercept(new MessageSelectingInterceptor(m -> m.getPayload().isValid()))
.handle(...)
----

View File

@@ -0,0 +1,22 @@
[[java-dsl-log]]
= Operator log()
:page-section-summary-toc: 1
For convenience, to log the message journey through the Spring Integration flow (`<logging-channel-adapter>`), a `log()` operator is presented.
Internally, it is represented by the `WireTap` `ChannelInterceptor` with a `LoggingHandler` as its subscriber.
It is responsible for logging the incoming message into the next endpoint or the current channel.
The following example shows how to use `LoggingHandler`:
[source,java]
----
.filter(...)
.log(LoggingHandler.Level.ERROR, "test.category", m -> m.getHeaders().getId())
.route(...)
----
In the preceding example, an `id` header is logged at the `ERROR` level onto `test.category` only for messages that passed the filter and before routing.
Starting with version 6.0, the behavior of this operator in the end of flow is aligned with its usage in the middle.
In other words the behavior of the flow remains the same even if the `log()` operator is removed.
So, if a reply is not expected to be produced in the end of the flow, the `nullChannel()` is recommended to be used after the last `log()`.

View File

@@ -0,0 +1,21 @@
[[java-dsl-pollers]]
= Pollers
:page-section-summary-toc: 1
Spring Integration also provides a fluent API that lets you configure `PollerMetadata` for `AbstractPollingEndpoint` implementations.
You can use the `Pollers` builder factory to configure common bean definitions or those created from `IntegrationFlowBuilder` EIP methods, as the following example shows:
[source,java]
----
@Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerSpec poller() {
return Pollers.fixedRate(500)
.errorChannel("myErrors");
}
----
See https://docs.spring.io/spring-integration/api/org/springframework/integration/dsl/Pollers.html[`Pollers`] and https://docs.spring.io/spring-integration/api/org/springframework/integration/dsl/PollerSpec.html[`PollerSpec`] in the Javadoc for more information.
IMPORTANT: If you use the DSL to construct a `PollerSpec` as a `@Bean`, do not call the `getObject()` method in the bean definition.
The `PollerSpec` is a `FactoryBean` that generates the `PollerMetadata` object from the specification and initializes all of its properties.

View File

@@ -0,0 +1,91 @@
[[java-dsl-protocol-adapters]]
= Using Protocol Adapters
All the examples shown so far illustrate how the DSL supports a messaging architecture by using the Spring Integration programming model.
However, we have yet to do any real integration.
Doing so requires access to remote resources over HTTP, JMS, AMQP, TCP, JDBC, FTP, SMTP, and so on or access to the local file system.
Spring Integration supports all of these and more.
Ideally, the DSL should offer first class support for all of them, but it is a daunting task to implement all of these and keep up as new adapters are added to Spring Integration.
So the expectation is that the DSL is continually catching up with Spring Integration.
Consequently, we provide the high-level API to seamlessly define protocol-specific messaging.
We do so with the factory and builder patterns and with lambdas.
You can think of the factory classes as "`Namespace Factories`", because they play the same role as the XML namespace for components from the concrete protocol-specific Spring Integration modules.
Currently, Spring Integration Java DSL supports the `Amqp`, `Feed`, `Jms`, `Files`, `(S)Ftp`, `Http`, `JPA`, `MongoDb`, `TCP/UDP`, `Mail`, `WebFlux`, and `Scripts` namespace factories.
The following example shows how to use three of them (`Amqp`, `Jms`, and `Mail`):
[source,java]
----
@Bean
public IntegrationFlow amqpFlow() {
return IntegrationFlow.from(Amqp.inboundGateway(this.rabbitConnectionFactory, queue()))
.transform("hello "::concat)
.transform(String.class, String::toUpperCase)
.get();
}
@Bean
public IntegrationFlow jmsOutboundGatewayFlow() {
return IntegrationFlow.from("jmsOutboundGatewayChannel")
.handle(Jms.outboundGateway(this.jmsConnectionFactory)
.replyContainer(c ->
c.concurrentConsumers(3)
.sessionTransacted(true))
.requestDestination("jmsPipelineTest"))
.get();
}
@Bean
public IntegrationFlow sendMailFlow() {
return IntegrationFlow.from("sendMailChannel")
.handle(Mail.outboundAdapter("localhost")
.port(smtpPort)
.credentials("user", "pw")
.protocol("smtp")
.javaMailProperties(p -> p.put("mail.debug", "true")),
e -> e.id("sendMailEndpoint"))
.get();
}
----
The preceding example shows how to use the "`namespace factories`" as inline adapters declarations.
However, you can use them from `@Bean` definitions to make the `IntegrationFlow` method chain more readable.
NOTE: We are soliciting community feedback on these namespace factories before we spend effort on others.
We also appreciate any input into prioritization for which adapters and gateways we should support next.
You can find more Java DSL samples in the protocol-specific chapters throughout this reference manual.
All other protocol channel adapters may be configured as generic beans and wired to the `IntegrationFlow`, as the following examples show:
[source,java]
----
@Bean
public QueueChannelSpec wrongMessagesChannel() {
return MessageChannels
.queue()
.wireTap("wrongMessagesWireTapChannel");
}
@Bean
public IntegrationFlow xpathFlow(MessageChannel wrongMessagesChannel) {
return IntegrationFlow.from("inputChannel")
.filter(new StringValueTestXPathMessageSelector("namespace-uri(/*)", "my:namespace"),
e -> e.discardChannel(wrongMessagesChannel))
.log(LoggingHandler.Level.ERROR, "test.category", m -> m.getHeaders().getId())
.route(xpathRouter(wrongMessagesChannel))
.get();
}
@Bean
public AbstractMappingMessageRouter xpathRouter(MessageChannel wrongMessagesChannel) {
XPathRouter router = new XPathRouter("local-name(/*)");
router.setEvaluateAsString(true);
router.setResolutionRequired(false);
router.setDefaultOutputChannel(wrongMessagesChannel);
router.setChannelMapping("Tags", "splittingChannel");
router.setChannelMapping("Tag", "receivedChannel");
return router;
}
----

Some files were not shown because too many files have changed in this diff Show More