Full editing pass for the Reference Guide
I went through all the Asciidoc files to correct spelling, grammar, punctuation, and usage. Wherever possible, I also made it more clear and more concise. I also added links wherever they were reasonable. Accommodating changes that came while I was editing Various folks made changes (one of which I recommended) to the documents while I spent a month editing. I had to edit that new and changed content as well, so I need this second commit. Accommodating Artem's requested changes for `aggregator.adoc` Artem reviewed `aggregator.adoc` as a starting point, so that he could get some questions answered. I have made the changes he requested (and answered his questions on Github).
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -6,9 +6,10 @@ 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.
|
||||
|
||||
Spring Integration _version 4.2_ introduced the `<barrier/>` component for this purpose.
|
||||
The underlying `MessageHandler` is the `BarrierMessageHandler`; this class also implements
|
||||
`MessageTriggerAction` where a message passed to the `trigger()` method releases a corresponding thread in the
|
||||
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.
|
||||
@@ -16,29 +17,27 @@ When a message is sent to the `input-channel`, the thread is suspended for up to
|
||||
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 using a `MessageGroupProcessor`.
|
||||
By default, the message is a `Collection<?>` of the two payloads and the headers are merged, using a
|
||||
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 will be suspended
|
||||
for up to `timeout` 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 its thread
|
||||
will be suspended instead.
|
||||
CAUTION: If the `trigger()` method is invoked first (or after the main thread times out), it is suspended for up to `timeout` 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.
|
||||
|
||||
The `requires-reply` property determines the action if the suspended thread times out before the trigger message
|
||||
arrives.
|
||||
By default, it is `false` which means the endpoint simply returns `null`, the flow ends and the thread returns to the
|
||||
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 using the name `barrier.handler`
|
||||
- where _barrier_ is the bean name of the barrier endpoint) or you can configure
|
||||
an `<outbound-channel-adapter/>` to trigger the release.
|
||||
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.
|
||||
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:
|
||||
|
||||
[source, xml]
|
||||
----
|
||||
<int:barrier id="barrier1" input-channel="in" output-channel="out"
|
||||
@@ -51,13 +50,10 @@ An exception is thrown if a second thread arrives with the same correlation.
|
||||
<int:outbound-channel-adapter channel="release" ref="barrier1.handler" method="trigger" />
|
||||
----
|
||||
|
||||
In this example, a custom header is used for correlation.
|
||||
Either the thread sending a message to `in` or the one sending a message to `release` will wait for
|
||||
up to 10 seconds until the other arrives.
|
||||
When the message is released, the `out` channel will be sent a message combining the result of invoking the
|
||||
custom `MessageGroupProcessor` bean `myOutputProcessor`.
|
||||
If the main thread times out and a trigger arrives later, you can configure a discard channel to which the late trigger will be sent.
|
||||
Java configuration is shown below.
|
||||
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.
|
||||
The following example shows the Java configuration to do so:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@@ -90,6 +86,4 @@ public class Config {
|
||||
}
|
||||
----
|
||||
|
||||
See the
|
||||
https://github.com/spring-projects/spring-integration-samples/tree/master/basic/barrier[barrier sample application]
|
||||
for an example of this component.
|
||||
For an example of this component, see the https://github.com/spring-projects/spring-integration-samples/tree/master/basic/barrier[barrier sample application].
|
||||
|
||||
@@ -1,41 +1,48 @@
|
||||
[[bridge]]
|
||||
=== Messaging Bridge
|
||||
|
||||
[[bridge-introduction]]
|
||||
==== Introduction
|
||||
|
||||
A Messaging Bridge is a relatively trivial endpoint that simply connects two Message Channels or Channel Adapters.
|
||||
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.
|
||||
Instead, the messaging bridge provides the polling configuration.
|
||||
|
||||
By providing an intermediary poller between two channels, a Messaging Bridge can be used to throttle inbound Messages.
|
||||
The poller's trigger will determine the rate at which messages arrive on the second channel, and the poller's "maxMessagesPerPoll" property will enforce a limit on the throughput.
|
||||
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 on 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 would be 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, and in that case, the channels would 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.
|
||||
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
|
||||
|
||||
The <bridge> element is used to create a Messaging Bridge between two Message Channels or Channel Adapters.
|
||||
Simply provide the "input-channel" and "output-channel" attributes:
|
||||
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`, and when performing this role, the Messaging Bridge may also serve as a throttler:
|
||||
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>
|
||||
----
|
||||
====
|
||||
|
||||
Connecting Channel Adapters is just as easy.
|
||||
Here is a simple echo example between the "stdin" and "stdout" adapters from Spring Integration's "stream" namespace.
|
||||
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"/>
|
||||
@@ -44,16 +51,20 @@ Here is a simple echo example between the "stdin" and "stdout" adapters from Spr
|
||||
|
||||
<int:bridge id="echo" input-channel="stdin" output-channel="stdout"/>
|
||||
----
|
||||
====
|
||||
|
||||
Of course, the configuration would be similar for other (potentially more useful) Channel Adapter bridges, such as File to JMS, or Mail to File.
|
||||
The various Channel Adapters will be discussed in upcoming chapters.
|
||||
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 will be used, if available.
|
||||
If neither output or reply channel is available, an Exception will be thrown.
|
||||
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
|
||||
@@ -67,8 +78,9 @@ public SubscribableChannel direct() {
|
||||
return new DirectChannel();
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
or
|
||||
The following example shows how to configure a bridge in Java by using the `@BridgeTo` annotation:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@@ -84,7 +96,7 @@ public SubscribableChannel direct() {
|
||||
}
|
||||
----
|
||||
|
||||
Or, using a `BridgeHandler`:
|
||||
Alternately, you can use a `BridgeHandler`, as the following example shows:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@@ -101,6 +113,8 @@ public BridgeHandler 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
|
||||
|
||||
@@ -1,126 +1,137 @@
|
||||
[[chain]]
|
||||
=== Message Handler Chain
|
||||
|
||||
[[chain-introduction]]
|
||||
==== Introduction
|
||||
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 http://www.eaipatterns.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.
|
||||
|
||||
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.
|
||||
This can lead to a much simpler configuration when several handlers need to be connected in a fixed, linear progression.
|
||||
For example, it is fairly common to provide a Transformer before other components.
|
||||
Similarly, when providing a _Filter_ before some other component in a chain, you are essentially creating a http://www.eaipatterns.com/MessageSelector.html[Selective Consumer].
|
||||
In either case, the chain only requires a single `input-channel` and a single `output-channel` eliminating the need to define channels for each individual component.
|
||||
|
||||
TIP: Spring Integration's `Filter` provides a boolean property `throwExceptionOnRejection`.
|
||||
When providing multiple Selective Consumers on the same point-to-point channel with different acceptance criteria, this value should be set to 'true' (the default is false) so that the dispatcher will know that the Message was rejected and as a result will attempt to pass the Message on to other subscribers.
|
||||
If the Exception were not thrown, then it would appear to the dispatcher as if 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, then the Filter's 'discard-channel' might be useful since it does give you a chance to perform some operation with the dropped message (e.g.
|
||||
send to a JMS queue or simply write to a log).
|
||||
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 will be expanded into a linear setup of the listed endpoints, separated by anonymous channels.
|
||||
The reply channel header will not be taken into account within the chain: only after the last handler is invoked will the resulting message be forwarded on to the reply channel or the chain's output channel.
|
||||
Because of this setup all handlers except the last required to implement the MessageProducer interface (which provides a 'setOutputChannel()' method).
|
||||
The last handler only needs an output channel if the outputChannel on the MessageHandlerChain is set.
|
||||
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, but if not available, the chain handler will check for a reply channel header on the inbound Message as a fallback.
|
||||
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 there is no need to implement MessageHandlers yourself.
|
||||
The next section will focus on namespace support for the chain element.
|
||||
Most Spring Integration endpoints, like Service Activators and Transformers, are suitable for use within a `MessageHandlerChain`.
|
||||
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, and if the last element in the chain is capable of producing reply messages (optional), it also supports an `output-channel` attribute.
|
||||
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 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="foo" value="bar"/>
|
||||
<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 above example will set a message header named "foo" with a value of "bar" on the message.
|
||||
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 obviously a simpler option.
|
||||
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 'black-box' consumer of the message flow.
|
||||
For this solution it is enough to put at the end of the <chain> some <outbound-channel-adapter>:
|
||||
The `<chain>` can be configured as the last 'black-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="foo" value="bar"/>
|
||||
<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_
|
||||
|
||||
It is important to note that 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.
|
||||
|
||||
.Disallowed Attributes and Elements
|
||||
[IMPORTANT]
|
||||
=====
|
||||
For the _Spring Integration_ core components, the XML Schema itself will enforce 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.
|
||||
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.
|
||||
|
||||
These XML namespace parser constraints were added with _Spring Integration 2.2_.
|
||||
The XML namespace parser will throw an `BeanDefinitionParsingException` if you try to use disallowed attributes and elements.
|
||||
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`.
|
||||
=====
|
||||
|
||||
_'id' Attribute_
|
||||
==== Using the 'id' Attribute
|
||||
|
||||
Beginning with Spring Integration 3.0, if a chain element is given an _id_, the bean name for the element is a combination of the chain's _id_ and the _id_ of the element itself.
|
||||
Elements without an _id_ are not registered as beans, but they are given `componentName` s that include the chain id.
|
||||
For example:
|
||||
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="fooChain" input-channel="input">
|
||||
<int:service-activator id="fooService" ref="someService" method="someMethod"/>
|
||||
<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 `<chain>` root element has an _id_ 'fooChain'.
|
||||
So, the `AbstractEndpoint` implementation (`PollingConsumer` or `EventDrivenConsumer`, depending on the _input-channel_ type) bean takes this value as it's bean name.
|
||||
* The `MessageHandlerChain` bean acquires a bean alias ('somethingChain.handler'), which allows direct access to this bean from the `BeanFactory`.
|
||||
|
||||
* The `MessageHandlerChain` bean acquires a bean alias 'fooChain.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 `<service-activator>` is not a fully-fledged Messaging Endpoint (`PollingConsumer` or `EventDrivenConsumer`) - it is simply a `MessageHandler` within the `<chain>`.
|
||||
In this case, the bean name registered with the `BeanFactory` is 'fooChain$child.fooService.handler'.
|
||||
* The `componentName` of this `ServiceActivatingHandler` takes the same value but without the '.handler' suffix.
|
||||
It becomes 'somethingChain$child.somethingService'.
|
||||
|
||||
* The _componentName_ of this `ServiceActivatingHandler` takes the same value, but without the '.handler' suffix - 'fooChain$child.fooService'.
|
||||
|
||||
* The last `<chain>` sub-component, `<object-to-json-transformer>`, doesn't have an _id_ attribute.
|
||||
Its _componentName_ is based on its position in the `<chain>`.
|
||||
In this case, it is 'fooChain$child#1'.
|
||||
* 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 isn't registered as a bean within the application context, so, it doesn't get a _beanName_, however its _componentName_ has a value which is useful for logging etc.
|
||||
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 allows them to be eligible for <<jmx-mbean-exporter,JMX export>> and they are trackable via <<message-history,Message History>>.
|
||||
They can also be accessed from the `BeanFactory` using the appropriate bean name as discussed above.
|
||||
The `id` attribute for `<chain>` elements lets them be eligible for <<jmx-mbean-exporter,JMX export>>, and they are trackable in the <<message-history,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>` s to simplify the identification of sub-components in logs, and to provide access to them from the `BeanFactory` etc.
|
||||
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.
|
||||
|
||||
_Calling a Chain from within a Chain_
|
||||
==== 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 utilize a Messaging Gateway by including a <gateway> element.
|
||||
For example:
|
||||
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">
|
||||
@@ -152,10 +163,12 @@ For example:
|
||||
</int:service-activator>
|
||||
</int:chain>
|
||||
----
|
||||
====
|
||||
|
||||
In the above example the _nested-chain-a_ will be 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_ will be made after header enrichment and then it will come back to finish execution in _nested-chain-b_.
|
||||
Finally the flow returns to the _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 simple takes the message in its current state and places it on the channel defined via the `request-channel` attribute.
|
||||
When the downstream flow initiated by that gateway completes, a `Message` will be returned to the gateway and continue its journey within the current 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.
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
[[migration-1.0-2.0]]
|
||||
=== Changes between 1.0 and 2.0
|
||||
=== Changes between Versions 1.0 and 2.0
|
||||
|
||||
For a detailed migration guide in regards to upgrading an existing application that uses Spring Integration older than version 2.0, please see:
|
||||
|
||||
https://github.com/spring-projects/spring-integration/wiki/Spring-Integration-1.0-to-2.0-Migration-Guide[Spring Integration 1.0 to 2.0 Migration Guide]
|
||||
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
|
||||
@@ -13,166 +11,164 @@ Spring Integration 2.0 is built on top of Spring 3.0.5 and makes many of its fea
|
||||
[[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 various adapters.
|
||||
There are many samples provided throughout this manual.
|
||||
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]]
|
||||
===== ConversionService and Converter
|
||||
===== Conversion Service and Converter
|
||||
|
||||
You can now benefit from _Conversion Service_ support provided with Spring while configuring many Spring Integration components such as http://www.eaipatterns.com/DatatypeChannel.html[Datatype Channel].
|
||||
See <<channel-implementations>> as well <<service-activator-introduction>>.
|
||||
Also, the SpEL support mentioned in the previous point also relies upon the ConversionService.
|
||||
Therefore, you can register Converters once, and take advantage of them anywhere you are using SpEL expressions.
|
||||
You can now benefit from the conversion service support provided with Spring while configuring many Spring Integration components, such as a http://www.eaipatterns.com/DatatypeChannel.html[Datatype channel].
|
||||
See "`<<channel-implementations>>`" and "`<<service-activator-introduction>>`".
|
||||
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
|
||||
===== `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 (e.g.
|
||||
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).
|
||||
Configuration of Spring Integration Pollers has been greatly simplified as well by providing attributes for directly configuring rates, delays, cron expressions, and trigger references.
|
||||
See <<channel-adapter>> for sample configurations.
|
||||
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 "`<<channel-adapter>>`" for sample configurations.
|
||||
|
||||
[[rest-support]]
|
||||
===== RestTemplate and HttpMessageConverter
|
||||
===== `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 <<http-outbound>> for more details.
|
||||
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 "`<<http-outbound>>`" 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 http://www.eaipatterns.com/[Enterprise Integration Patterns] book.
|
||||
Also in 2.0, we have added support for even more of the patterns described in Hohpe and Woolf's http://www.eaipatterns.com/[Enterprise Integration Patterns] book.
|
||||
|
||||
[[new-message-history]]
|
||||
===== Message History
|
||||
|
||||
We now provide support for the http://www.eaipatterns.com/MessageHistory.html[Message History] pattern allowing you to keep track of all traversed components, including the name of each channel and endpoint as well as the timestamp of that traversal.
|
||||
See <<message-history>> for more details.
|
||||
We now provide support for the http://www.eaipatterns.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 "`<<message-history>>`" for more details.
|
||||
|
||||
[[new-message-store]]
|
||||
===== Message Store
|
||||
|
||||
We now provide support for the http://www.eaipatterns.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 Resequencer.
|
||||
Many sections of this document provide samples on how to use a Message Store as it affects several areas of Spring Integration.
|
||||
See <<message-store>>, <<claim-check>>, <<channel>>, <<aggregator>>, <<jdbc>>, and <<resequencer>> for more details
|
||||
We now provide support for the http://www.eaipatterns.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 "`<<message-store>>`", "`<<claim-check>>`", "`<<channel>>`", "`<<aggregator>>`", "`<<jdbc>>`", and "`<<resequencer>>`" for more details.
|
||||
|
||||
[[new-claim-check]]
|
||||
===== Claim Check
|
||||
|
||||
We have added an implementation of the http://www.eaipatterns.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" and vice-versa.
|
||||
This allows you to reduce bandwidth and/or avoid potential security issues when sending Messages across channels.
|
||||
See <<claim-check>> for more details.
|
||||
We have added an implementation of the http://www.eaipatterns.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 "`<<claim-check>>`" for more details.
|
||||
|
||||
[[new-control-bus]]
|
||||
===== Control Bus
|
||||
|
||||
We have provided implementations of the http://www.eaipatterns.com/ControlBus.html[Control Bus] pattern which allows you to use messaging to manage and monitor endpoints and channels.
|
||||
The implementations include both a SpEL-based approach and one that executes Groovy scripts.
|
||||
See <<control-bus>> and <<groovy-control-bus>> for more details.
|
||||
We have provided implementations of the http://www.eaipatterns.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 "`<<control-bus>>`" and "`<<groovy-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.
|
||||
We have added several new channel adapters and messaging gateways in Spring Integration 2.0.
|
||||
|
||||
[[new-ip]]
|
||||
===== TCP/UDP Adapters
|
||||
===== TCP and UDP Adapters
|
||||
|
||||
We have added Channel Adapters for receiving and sending messages over the TCP and UDP internet protocols.
|
||||
See <<ip>> for more details.
|
||||
Also, you can checkout the following blog: http://blog.springsource.com/2010/03/29/using-udp-and-tcp-adapters-in-spring-integration-2-0-m3/[TCP/UDP support]
|
||||
We have added channel adapters for receiving and sending messages over the TCP and UDP internet protocols.
|
||||
See "`<<ip>>`" for more details.
|
||||
See also the following blog: http://blog.springsource.com/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 <<twitter>> for more details.
|
||||
Twitter adapters provide support for sending and receiving Twitter status updates and direct messages.
|
||||
You can also perform Twitter searches with an inbound channel adapter.
|
||||
See "`<<twitter>>`" for more details.
|
||||
|
||||
[[new-xmpp]]
|
||||
===== XMPP Adapters
|
||||
|
||||
The new XMPP adapters support both Chat Messages and Presence events.
|
||||
See <<xmpp>> for more details.
|
||||
The new XMPP adapters support both chat messages and presence events.
|
||||
See "`<<xmpp>>`" for more details.
|
||||
|
||||
[[new-ftp]]
|
||||
===== FTP/FTPS Adapters
|
||||
===== FTP and FTPS Adapters
|
||||
|
||||
Inbound and outbound File transfer support over FTP/FTPS is now available.
|
||||
See <<ftp>> for more details.
|
||||
Inbound and outbound file transfer support over FTP and FTPS is now available.
|
||||
See "`<<ftp>>`" for more details.
|
||||
|
||||
[[new-sftp]]
|
||||
===== SFTP Adapters
|
||||
|
||||
Inbound and outbound File transfer support over SFTP is now available.
|
||||
See <<sftp>> for more details.
|
||||
Inbound and outbound file transfer support over SFTP is now available.
|
||||
See "`<<sftp>>`" for more details.
|
||||
|
||||
[[new-feed]]
|
||||
===== Feed Adapters
|
||||
|
||||
We have also added Channel Adapters for receiving news feeds (ATOM/RSS).
|
||||
See <<feed>> for more details.
|
||||
We have also added channel adapters for receiving news feeds (ATOM and RSS).
|
||||
See "`<<feed>>`" for more details.
|
||||
|
||||
[[new-other]]
|
||||
==== Other Additions
|
||||
Spring Integration adds a number of other features. This section describes them.
|
||||
|
||||
[[new-groovy]]
|
||||
===== Groovy Support
|
||||
|
||||
With Spring Integration 2.0 we've added Groovy support allowing you to use Groovy scripting language to provide integration and/or business logic.
|
||||
See <<groovy>> for more details.
|
||||
Spring Integration 2.0 added Groovy support, letting you use the Groovy scripting language to provide integration and business logic.
|
||||
See "`<<groovy>>`" for more details.
|
||||
|
||||
[[new-map-transformer]]
|
||||
===== Map Transformers
|
||||
|
||||
These symmetrical transformers convert payload objects to and from a Map.
|
||||
See <<transformer>> for more details.
|
||||
These symmetrical transformers convert payload objects to and from `Map` objects.
|
||||
See "`<<transformer>>`" for more details.
|
||||
|
||||
[[new-json-transformer]]
|
||||
===== JSON Transformers
|
||||
|
||||
These symmetrical transformers convert payload objects to and from JSON.
|
||||
See <<transformer>> for more details.
|
||||
See "`<<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 have been added as of Spring 3.0.5.
|
||||
See <<transformer>> for more details.
|
||||
They also support the serializer and deserializer strategy interfaces that Spring 3.0.5 added.
|
||||
See "`<<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 the end user should be minimal, please read through this document to find what was changed.
|
||||
Especially, visit <<dynamic-routers>> , <<gateway>>, <<http-outbound>>, <<message>>, and <<aggregator>> for more details.
|
||||
If you are depending directly on some of the core components (Message, MessageHeaders, MessageChannel, MessageBuilder, etc.), you will notice that 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").
|
||||
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 "`<<dynamic-routers>>`", "`<<gateway>>`", "`<<http-outbound>>`", "`<<message>>`", and "`<<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 have switched our build environment to use Git for source control.
|
||||
To access our repository simply follow this URL: http://git.springsource.org/spring-integration[http://git.springsource.org/spring-integration].
|
||||
With Spring Integration 2.0, we switched our build environment to use Git for source control.
|
||||
To access our repository, visit http://git.springsource.org/spring-integration.
|
||||
We have also switched our build system to http://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 this blog to get more info http://blog.springsource.com/2010/09/29/new-spring-integration-samples/[New Spring Integration Samples] We have also created many new samples, including samples for every new Adapter.
|
||||
With Spring Integration 2.0, we have decoupled the samples from our main release distribution.
|
||||
Please read the following blog to get more information: http://blog.springsource.com/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, please download it here:
|
||||
|
||||
https://spring.io/tools/sts[Spring Tool Suite]
|
||||
If you are not already using STS, you can download it athttps://spring.io/tools/sts[Spring Tool Suite].
|
||||
|
||||
@@ -1,68 +1,70 @@
|
||||
[[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, support for http://groovy.codehaus.org/[Groovy] was added.
|
||||
With Spring Integration 2.1 we expanded support for additional languages substantially by implementing support for http://www.jcp.org/en/jsr/detail?id=223[JSR-223] (Scripting for the Java™ Platform).
|
||||
In Spring Integration 2.0, we added support for http://groovy.codehaus.org/[Groovy].
|
||||
With Spring Integration 2.1, we expanded support for additional languages substantially by implementing support for http://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/JRuby
|
||||
* Python/Jython
|
||||
* Ruby and JRuby
|
||||
* Python and Jython
|
||||
* Groovy
|
||||
|
||||
|
||||
|
||||
For further details please see <<scripting>>.
|
||||
For further details, see "`<<scripting>>`".
|
||||
|
||||
[[x2.1-new-gemfire-support]]
|
||||
===== GemFire Support
|
||||
|
||||
Spring Integration provides support for http://www.vmware.com/products/application-platform/vfabric-gemfire/overview.html[GemFire] by providing inbound adapters for entry and continuous query events, an outbound adapter to write entries to the cache, and http://static.springsource.org/spring-integration/api/org/springframework/integration/store/MessageStore.html[`MessageStore`] and http://static.springsource.org/spring-integration/api/org/springframework/integration/store/MessageGroupStore.html[`MessageGroupStore`] implementations.
|
||||
Spring integration leverages the http://www.springsource.org/spring-gemfire[_Spring Gemfire_] project, providing a thin wrapper over its components.
|
||||
Spring integration leverages the http://www.springsource.org/spring-gemfire[Spring Gemfire] project, providing a thin wrapper over its components.
|
||||
|
||||
For further details please see <<gemfire>>.
|
||||
For further details, see "`<<gemfire>>`".
|
||||
|
||||
[[x2.1-new-amqp-support]]
|
||||
===== AMQP Support
|
||||
|
||||
Spring Integration 2.1 adds several Channel Adapters for receiving and sending messages using thehttp://www.amqp.org/[_Advanced Message Queuing Protocol_] (AMQP).
|
||||
Furthermore, Spring Integration also provides a point-to-point Message Channel, as well as a publish/subscribe Message Channel that are backed by AMQP Exchanges and Queues.
|
||||
Spring Integration 2.1 added several channel adapters for receiving and sending messages by using the http://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 please see <<amqp>>.
|
||||
For further details, see "`<<amqp>>`".
|
||||
|
||||
[[x2.1-new-mongodb-support]]
|
||||
===== MongoDB Support
|
||||
|
||||
As of version 2.1 Spring Integration provides support for http://www.mongodb.org/[MongoDB] by providing a MongoDB-based MessageStore.
|
||||
As of version 2.1, Spring Integration provides support for http://www.mongodb.org/[MongoDB] by providing a MongoDB-based `MessageStore`.
|
||||
|
||||
For further details please see <<mongodb>>.
|
||||
For further details, see "`<<mongodb>>`".
|
||||
|
||||
[[x2.1-new-redis-support]]
|
||||
===== Redis Support
|
||||
|
||||
As of version 2.1 Spring Integration supports http://redis.io/[Redis], an advanced key-value store, by providing a Redis-based MessageStore as well as Publish-Subscribe Messaging adapters.
|
||||
As of version 2.1, Spring Integration supports http://redis.io/[Redis], an advanced key-value store, by providing a Redis-based `MessageStore` as well as publish-subscribe messaging adapters.
|
||||
|
||||
For further details please see <<redis>>.
|
||||
For further details, see "`<<redis>>`".
|
||||
|
||||
[[x2.1-new-resource-support]]
|
||||
===== Support for Spring's Resource abstraction
|
||||
|
||||
As of version 2.1, we've 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 class path resource.
|
||||
Therefore, it's similar to but more generic than the _File Inbound Channel Adapter_.
|
||||
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 please see <<resource-inbound-channel-adapter>>.
|
||||
For further details, see "`<<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/outbound channel adapters and an Outbound Gateway.
|
||||
The Stored Procedure support leverages Spring'shttp://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/jdbc/core/simple/SimpleJdbcCall.html[`SimpleJdbcCall`] class and consequently supports stored procedures for:
|
||||
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 http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/jdbc/core/simple/SimpleJdbcCall.html[`SimpleJdbcCall`] class and consequently supports stored procedures for:
|
||||
|
||||
* Apache Derby
|
||||
* DB2
|
||||
@@ -72,89 +74,89 @@ The Stored Procedure support leverages Spring'shttp://static.springsource.org/sp
|
||||
* PostgreSQL
|
||||
* Sybase
|
||||
|
||||
|
||||
|
||||
The Stored Procedure components also support Sql Functions for the following databases:
|
||||
The stored procedure components also support SQL functions for the following databases:
|
||||
|
||||
* MySQL
|
||||
* Microsoft SQL Server
|
||||
* Oracle
|
||||
* PostgreSQL
|
||||
|
||||
|
||||
|
||||
For further details please see <<stored-procedures>>.
|
||||
For further details, see "`<<stored-procedures>>`".
|
||||
|
||||
[[x2.1-new-xpath-filter-support]]
|
||||
===== XPath and XML Validating Filter
|
||||
|
||||
Spring Integration 2.1 provides a new XPath-based Message Filter, that is part of the `XML` module.
|
||||
The XPath Filter allows you to filter messages using provided XPath Expressions.
|
||||
Furthermore, documentation was added for the 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 please see <<xml-xpath-filter>> and <<xml-validating-filter>>.
|
||||
For more details, see "`<<xml-xpath-filter>>`" and "`<<xml-validating-filter>>`".
|
||||
|
||||
[[x2.1-new-payload-enricher-support]]
|
||||
===== Payload Enricher
|
||||
|
||||
Since Spring Integration 2.1, the Payload Enricher is provided.
|
||||
A Payload Enricher defines an endpoint that typically passes ahttp://static.springsource.org/spring-integration/api/org/springframework/integration/Message.html[`Message`] to the exposed request channel and then expects a reply message.
|
||||
Since Spring Integration 2.1, we added the payload enricher.
|
||||
A payload enricher defines an endpoint that typically passes a http://static.springsource.org/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 please see <<payload-enricher>>.
|
||||
For further details, see "`<<payload-enricher>>`".
|
||||
|
||||
[[x2.1-new-ftp-outbound-gateway]]
|
||||
===== FTP and SFTP Outbound Gateways
|
||||
|
||||
Spring Integration 2.1 provides two new Outbound Gateways in order to interact with remote File Transfer Protocol (FTP) or Secure File Transfer Protocol (SFT) servers.
|
||||
These two gateways allow you to directly execute a limited set of remote commands.
|
||||
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 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 please see <<ftp-outbound-gateway>> and <<sftp-outbound-gateway>>.
|
||||
For further details, see "`<<ftp-outbound-gateway>>`" and "`<<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 (e.g., FTP, SFTP etc).
|
||||
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, the `cache-sessions` attribute, which is available via the XML namespace support, is now _deprecated_.
|
||||
Alternatively, we added the `sessionCacheSize` and `sessionWaitTimeout` attributes on the `CachingSessionFactory`.
|
||||
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 please see <<ftp-session-caching>> and <<sftp-session-caching>>.
|
||||
For further details, see "`<<ftp-session-caching>>`" and "`<<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
|
||||
|
||||
Router parameters have been standardized across all router implementations with Spring Integration 2.1 providing a more consistent user experience.
|
||||
We standardized router parameters across all router implementations with Spring Integration 2.1 to provide a more consistent user experience.
|
||||
|
||||
With Spring Integration 2.1 the `ignore-channel-name-resolution-failures` attribute has been removed in favor of consolidating its behavior with the `resolution-required` attribute.
|
||||
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 will 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 will throw a `MessageDeliveryException` if no channel was determined (or an attempt to send was not successful).
|
||||
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 desire to drop messages silently, simply set `default-output-channel="nullChannel"`.
|
||||
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 above, there is the possibility of breaking older Spring Integration based applications.
|
||||
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 please see <<router>>
|
||||
For further details, see `<<router>>`.
|
||||
|
||||
[[x2.1-schema-updated]]
|
||||
===== XML Schemas updated to 2.1
|
||||
|
||||
Spring Integration 2.1 ships with an updated XML Schema (version 2.1), providing many improvements, e.g.
|
||||
the Router standardizations discussed above.
|
||||
Spring Integration 2.1 ships with an updated XML Schema (version 2.1).
|
||||
It provides many improvements, such as the Router standardizations <<x2.1-router-standardization,discussed earlier>>.
|
||||
|
||||
From now on, users _must_ always declare the latest XML schema (currently version 2.1).
|
||||
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 will automatically use the latest available version of Spring Integration.
|
||||
Generally, the best option is to use version-less namespaces, as these automatically use the latest available version of Spring Integration.
|
||||
|
||||
Declaring a version-less Spring Integration namespace:
|
||||
The following example declares a version-less Spring Integration namespace:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
@@ -168,9 +170,11 @@ Declaring a version-less Spring Integration namespace:
|
||||
...
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
Declaring a Spring Integration namespace using an explicit version:
|
||||
The following example declares a Spring Integration namespace with an explicit version:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
@@ -184,37 +188,37 @@ Declaring a Spring Integration namespace using an explicit version:
|
||||
...
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
The old 1.0 and 2.0 schemas are still there, but if an Application Context still references one of those deprecated schemas, the validator will fail on initialization.
|
||||
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
|
||||
===== Source Code Now Hosted on Github
|
||||
|
||||
Since version 2.0, the Spring Integration project uses http://git-scm.com/[Git] for version control.
|
||||
In order to increase community visibility even further, the project was moved from SpringSource hosted Git repositories to http://www.github.com/[Github].
|
||||
To increase community visibility even further, the project was moved from SpringSource hosted Git repositories to http://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 and we ensure that every commit is peer-reviewed.
|
||||
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 please see:
|
||||
|
||||
https://github.com/spring-projects/spring-integration/blob/master/CONTRIBUTING.adoc[Contributing].
|
||||
For more details, see https://github.com/spring-projects/spring-integration/blob/master/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, an instance of http://www.sonarsource.org/[Sonar] was setup and metrics are gathered nightly and made available at:
|
||||
|
||||
https://sonar.spring.io/dashboard?id=org.springframework.integration%3Aspring-integration%3Amaster[sonar.spring.io].
|
||||
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 http://www.sonarsource.org/[Sonar].
|
||||
We gather metrics nightly and make them available at https://sonar.spring.io/dashboard?id=org.springframework.integration%3Aspring-integration%3Amaster[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, e.g.
|
||||
samples covering AMQP support, the new payload enricher, a sample illustrating techniques for testing Spring Integration flow fragments, as well as an example for executing Stored Procedures against Oracle.
|
||||
For details please visit:
|
||||
|
||||
https://github.com/spring-projects/spring-integration-samples[spring-integration-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].
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
[[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
|
||||
|
||||
[[x2.2-redis-store-adapters]]
|
||||
===== RedisStore Inbound and Outbound Channel Adapters
|
||||
Version 2.2 added a number of new components.
|
||||
|
||||
Spring Integration now has RedisStore Inbound and Outbound Channel Adapters allowing you to write and read Message payloads to/from Redis collection(s).
|
||||
For more information please see <<redis-store-outbound-channel-adapter>> and <<redis-store-inbound-channel-adapter>>.
|
||||
[[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 "`<<redis-store-outbound-channel-adapter>>`" and "`<<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 allowing you to write and read Message payloads to/from a MongoDB document store.
|
||||
For more information please see <<mongodb-outbound-channel-adapter>> and <<mongodb-inbound-channel-adapter>>.
|
||||
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 "`<<mongodb-outbound-channel-adapter>>`" and "`<<mongodb-inbound-channel-adapter>>`".
|
||||
|
||||
[[x2.2-jpa]]
|
||||
===== JPA Endpoints
|
||||
@@ -22,18 +27,18 @@ For more information please see <<mongodb-outbound-channel-adapter>> and <<mongo
|
||||
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:
|
||||
|
||||
* _<<jpa-inbound-channel-adapter,Inbound Channel Adapter>>_
|
||||
* _<<jpa-outbound-channel-adapter,Outbound Channel Adapter>>_
|
||||
* _<<jpa-updating-outbound-gateway,Updating Outbound Gateway>>_
|
||||
* _<<jpa-retrieving-outbound-gateway,Retrieving Outbound Gateway>>_
|
||||
* <<jpa-inbound-channel-adapter,Inbound channel adapter>>
|
||||
* <<jpa-outbound-channel-adapter,Outbound channel adapter>>
|
||||
* <<jpa-updating-outbound-gateway,Updating outbound gateway>>
|
||||
* <<jpa-retrieving-outbound-gateway,Retrieving outbound gateway>>
|
||||
|
||||
|
||||
|
||||
For more information please see <<jpa>>
|
||||
For more information, see "`<<jpa>>`".
|
||||
|
||||
[[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
|
||||
|
||||
@@ -42,134 +47,124 @@ 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.
|
||||
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, say, retry, to an individual endpoint.
|
||||
The 2.2.
|
||||
release introduces the <request-handler-advice-chain/> to many endpoints.
|
||||
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, 3 standard Advice classes have been provided for this purpose:
|
||||
In addition, we added three standard advice classes for this purpose:
|
||||
|
||||
* MessageHandlerRetryAdvice
|
||||
* MessageHandlerCircuitBreakerAdvice
|
||||
* ExpressionEvaluatingMessageHandlerAdvice
|
||||
* `MessageHandlerRetryAdvice`
|
||||
* `MessageHandlerCircuitBreakerAdvice`
|
||||
* `ExpressionEvaluatingMessageHandlerAdvice`
|
||||
|
||||
|
||||
|
||||
For more information, see <<message-handler-advice-chain>>.
|
||||
For more information, see "`<<message-handler-advice-chain>>`".
|
||||
|
||||
[[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.
|
||||
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, these features can be enabled when there is not a 'real' transaction present, by means of a `PseudoTransactionManager`.
|
||||
In addition, you can enable these features when no "`real`" transaction is present, by means of a `PseudoTransactionManager`.
|
||||
|
||||
For more information see <<transaction-synchronization>>.
|
||||
For more information, see "`<<transaction-synchronization>>`".
|
||||
|
||||
[[x2.2-file-adapter]]
|
||||
===== File Adapter - Improved File Overwrite/Append Handling
|
||||
===== File Adapter: Improved File Overwrite and Append Handling
|
||||
|
||||
When using the _File Oubound Channel Adapter_ or the _File Outbound Gateway_, a new _mode_ property was added.
|
||||
Prior to _Spring Integration 2.2_, target files were replaced when they existed.
|
||||
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
|
||||
* `REPLACE` (default)
|
||||
* `APPEND`
|
||||
* `FAIL`
|
||||
* `IGNORE`
|
||||
|
||||
|
||||
|
||||
For more information please see <<file-writing-destination-exists>>.
|
||||
For more information, see "`<<file-writing-destination-exists>>`".
|
||||
|
||||
[[x2.2-outbound-gateways]]
|
||||
===== Reply-Timeout added to more Outbound Gateways
|
||||
===== Reply-Timeout Added to More Outbound Gateways
|
||||
|
||||
The XML Namespace support adds the _reply-timeout_ attribute to the following _Outbound Gateways_:
|
||||
The XML Namespace support adds the reply-timeout attribute to the following outbound gateways:
|
||||
|
||||
* Amqp Outbound Gateway
|
||||
* AMQP Outbound Gateway
|
||||
* File Outbound Gateway
|
||||
* Ftp Outbound Gateway
|
||||
* Sftp Outbound Gateway
|
||||
* Ws 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...
|
||||
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 Confirms
|
||||
* Returned Messages
|
||||
* Support for Dead Letter Exchanges/Dead Letter 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 using Spring Expression Language (SpEL).
|
||||
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).
|
||||
|
||||
This allows you to specify the Stored Procedures to be invoked at runtime.
|
||||
For example, you can provide Stored Procedure names that you would like to execute via Message Headers.
|
||||
For more information please see <<stored-procedures>>.
|
||||
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 "`<<stored-procedures>>`".
|
||||
|
||||
_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
|
||||
====== 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
|
||||
===== JDBC Support: Outbound Gateway
|
||||
|
||||
When using the JDBC Outbound Gateway, the update query is no longer mandatory.
|
||||
You can now provide solely a select query using the request message as a source of parameters.
|
||||
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
|
||||
===== JDBC Support: Channel-specific Message Store Implementation
|
||||
|
||||
A new _Message Channel_-specific Message Store Implementation has been added, providing a more scalable solution using database-specific SQL queries.
|
||||
For more information please see: <<jdbc-message-store-channels>>.
|
||||
We added a new message channel-specific message store implementation, providing a more scalable solution using database-specific SQL queries.
|
||||
For more information, see "`<<jdbc-message-store-channels>>`".
|
||||
|
||||
[[x2.2-shutdown]]
|
||||
===== Orderly Shutdown
|
||||
|
||||
A method `stopActiveComponents()` has been added to the IntegrationMBeanExporter.
|
||||
This 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.
|
||||
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 Oubound Gateway Improvements
|
||||
===== JMS Outbound Gateway Improvements
|
||||
|
||||
The JMS Outbound Gateway can now be configured to use a `MessageListener` container to receive replies.
|
||||
This can improve performance of the gateway.
|
||||
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]]
|
||||
===== object-to-json-transformer
|
||||
===== `ObjectToJsonTransformer`
|
||||
|
||||
The `ObjectToJsonTransformer` now sets the _content-type_ header to _application/json_ by default.
|
||||
For more information see <<transformer>>.
|
||||
By default, the `ObjectToJsonTransformer` now sets the `content-type` header to `application/json`.
|
||||
For more information, see "`<<transformer>>`".
|
||||
|
||||
[[httpChanges]]
|
||||
===== HTTP Support
|
||||
|
||||
Java serialization over HTTP is no longer enabled by default.
|
||||
Previously, when setting a `expected-response-type` to a `Serializable` object, the `Accept` header was not properly set up.
|
||||
The `SerializingHttpMessageConverter` has now been updated to set the Accept header to `application/x-java-serialized-object`.
|
||||
However, because this could cause incompatibility with existing applications, it was decided to no longer automatically add this converter to the HTTP endpoints.
|
||||
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 will need to add the `SerializingHttpMessageConverter` to the appropriate endpoints, using the `message-converters` attribute, when using XML configuration, or using the `setMessageConverters()` method.
|
||||
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 which is enabled by simply having `Jackson` on the classpath.
|
||||
Alternatively, you may wish to consider using JSON instead.
|
||||
It is enabled by having `Jackson` on the classpath.
|
||||
|
||||
@@ -1,160 +1,173 @@
|
||||
[[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.
|
||||
Class `UriPathHandlerMapping` was replaced by `IntegrationRequestMappingHandlerMapping`, which is registered under the bean name `integrationRequestMappingHandlerMapping` in the application context.
|
||||
Upon parsing of the HTTP Inbound Endpoint, a new `IntegrationRequestMappingHandlerMapping` bean is either registered or an existing bean is being reused.
|
||||
To achieve flexible Request Mapping configuration, Spring Integration provides the `<request-mapping/>` sub-element for the `<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.
|
||||
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 <<http-namespace>>.
|
||||
For more information see "`<<http-namespace>>`".
|
||||
|
||||
[[x3.0-spel-customization]]
|
||||
===== Spring Expression Language (SpEL) Configuration
|
||||
|
||||
A new `IntegrationEvaluationContextFactoryBean` is provided to allow configuration of custom `PropertyAccessor` s and functions for use in SpEL expressions throughout the framework.
|
||||
For more information see <<spel>>.
|
||||
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 "`<<spel>>`".
|
||||
|
||||
[[x3.0-spel-functions]]
|
||||
===== SpEL Functions Support
|
||||
|
||||
To customize the SpEL `EvaluationContext` with static `Method` functions, the new `<spel-function/>` component is introduced.
|
||||
Two built-in functions are also provided (`#jsonPath` and `#xpath`).
|
||||
For more information see <<spel-functions>>.
|
||||
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 "`<<spel-functions>>`".
|
||||
|
||||
[[x3.0-spel-property-accessors]]
|
||||
===== SpEL PropertyAccessors Support
|
||||
|
||||
To customize the SpEL `EvaluationContext` with `PropertyAccessor` implementations the new `<spel-property-accessors/>` component is introduced.
|
||||
For more information see <<spel-property-accessors>>.
|
||||
To customize the SpEL `EvaluationContext` with `PropertyAccessor` implementations, we added the `<spel-property-accessors/>` component.
|
||||
For more information, see "`<<spel-property-accessors>>`".
|
||||
|
||||
[[x3.0-redis-new-components]]
|
||||
===== Redis: New Components
|
||||
|
||||
A new Redis-based http://docs.spring.io/spring-integration/docs/latest-ga/api/org/springframework/integration/store/MetadataStore.html[MetadataStore] implementation has been added.
|
||||
The `RedisMetadataStore` can be used to maintain state of a `MetadataStore` across application restarts.
|
||||
This new `MetadataStore` implementation can be used with adapters such as:
|
||||
We added a new Redis-based http://docs.spring.io/spring-integration/docs/latest-ga/api/org/springframework/integration/store/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
|
||||
* 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.
|
||||
|
||||
|
||||
New queue-based components have been added.
|
||||
The `<int-redis:queue-inbound-channel-adapter/>` and the `<int-redis:queue-outbound-channel-adapter/>` components are provided to perform 'right pop' and 'left push' operations on a Redis List, respectively.
|
||||
|
||||
For more information see <<redis>>.
|
||||
For more information, "`see <<redis>>`".
|
||||
|
||||
[[x3.0-hcr]]
|
||||
===== Header Channel Registry
|
||||
|
||||
It is now possible to instruct the framework to store reply 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 <<header-enricher>> for more information.
|
||||
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 "`<<header-enricher>>`" for more information.
|
||||
|
||||
[[x3.0-configurable-mongo-MS]]
|
||||
===== MongoDB support: New ConfigurableMongoDbMessageStore
|
||||
===== MongoDB support: New `ConfigurableMongoDbMessageStore`
|
||||
|
||||
In addition to the existing `eMongoDbMessageStore`, a new `ConfigurableMongoDbMessageStore` has been introduced.
|
||||
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 it is recommended to use it for new applications.
|
||||
Existing applications can use it, but messages in the old store will not be available.
|
||||
See <<mongodb>> for more information.
|
||||
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 "`<<mongodb>>`" for more information.
|
||||
|
||||
[[x3.0-syslog]]
|
||||
===== Syslog Support
|
||||
|
||||
Building on the 2.2 `SyslogToMapTransformer` Spring Integration 3.0 now introduces `UDP` and `TCP` inbound channel adapters especially tailored for receiving SYSLOG messages.
|
||||
For more information, see <<syslog>>.
|
||||
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 "`<<syslog>>`".
|
||||
|
||||
[[x3.0-tail]]
|
||||
===== 'Tail' Support
|
||||
===== `tail` Support
|
||||
|
||||
File 'tail'ing inbound channel adapters are now provided to generate messages when lines are added to the end of text files; see <<file-tailing>>.
|
||||
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 "`<<file-tailing>>`".
|
||||
|
||||
[[x3.0-jmx]]
|
||||
===== JMX Support
|
||||
|
||||
* A new `<int-jmx:tree-polling-channel-adapter/>` is provided; this adapter queries the JMX MBean tree and sends a message with a payload that is the graph of objects that matches the query.
|
||||
By default the MBeans are mapped to primitives and simple Objects like Map, List and arrays - permitting simple transformation, for example, to JSON.
|
||||
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` using the `naming-strategy` attribute.
|
||||
The `IntegrationMBeanExporter` now allows the configuration of a custom `ObjectNamingStrategy` by using the `naming-strategy` attribute.
|
||||
|
||||
For more information, see <<jmx>>.
|
||||
For more information, see "`<<jmx>>`".
|
||||
|
||||
[[x3.0-tcp-events]]
|
||||
===== TCP/IP Connection Events and Connection Management
|
||||
|
||||
`TcpConnection` s now emit `ApplicationEvent` s (specifically `TcpConnectionEvent` s) when connections are opened, closed, or an exception occurs.
|
||||
This allows applications to be informed of changes to TCP connections using the normal Spring `ApplicationListener` mechanism.
|
||||
`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.
|
||||
|
||||
`AbstractTcpConnection` has been renamed `TcpConnectionSupport`; custom connections that are subclasses of this class, can use its methods to publish events.
|
||||
Similarly, `AbstractTcpConnectionInterceptor` has been renamed to `TcpConnectionInterceptorSupport`.
|
||||
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, a new `<int-ip:tcp-connection-event-inbound-channel-adapter/>` is provided; by default, this adapter sends all `TcpConnectionEvent` s to a `Channel`.
|
||||
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 `getOpenConnectionIds()`, which returns a list of identifiers for all open connections; this allows applications, for example, to broadcast to all open connections.
|
||||
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 `closeConnection(String connectionId)` which allows applications to explicitly close a connection using its ID.
|
||||
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 <<tcp-events>>.
|
||||
For more information see "`<<tcp-events>>`".
|
||||
|
||||
[[x3.0-inbound-script]]
|
||||
===== Inbound Channel Adapter Script Support
|
||||
|
||||
The `<int:inbound-channel-adapter/>` now supports `<expression/>` and `<script/>` sub-elements to create a `MessageSource`; see <<channel-adapter-expressions-and-scripts>>.
|
||||
The `<int:inbound-channel-adapter/>` now supports using `<expression/>` and `<script/>` child elements to create a `MessageSource`.
|
||||
See "`<<channel-adapter-expressions-and-scripts>>`".
|
||||
|
||||
[[x3.0-content-enricher-headers]]
|
||||
===== Content Enricher: Headers Enrichment Support
|
||||
|
||||
The Content Enricher now provides configuration for `<header/>` sub-elements, to enrich the outbound Message with headers based on the reply Message from the underlying message flow.
|
||||
For more information see <<payload-enricher>>.
|
||||
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 "`<<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 using the JDK `UUID.randomUUID()` method.
|
||||
With this release, the default mechanism has been changed to use a more efficient algorithm which is significantly faster.
|
||||
In addition, the ability to change the strategy used to generate message ids has been added.
|
||||
For more information see <<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 "`<<message-id-generation>>`".
|
||||
|
||||
[[x3.0-gateway]]
|
||||
===== <gateway> Changes
|
||||
===== "`<gateway>`" Changes
|
||||
|
||||
* It is now possible to set common headers across all gateway methods, and more options are provided for adding, to the message, information about which method was invoked.
|
||||
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.
|
||||
|
||||
* It is now possible to entirely customize the way that gateway method calls are mapped to messages.
|
||||
You can now entirely customize the way that gateway method calls are mapped to messages.
|
||||
|
||||
* The `GatewayMethodMetadata` is now public class and it makes possible flexibly to configure the `GatewayProxyFactoryBean` programmatically from Java code.
|
||||
The `GatewayMethodMetadata` is now a public class.
|
||||
It lets you programmatically configure the `GatewayProxyFactoryBean` from Java.
|
||||
|
||||
For more information see <<gateway>>.
|
||||
For more information, see "`<<gateway>>`".
|
||||
|
||||
[[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.
|
||||
* *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` s after the custom message converters.
|
||||
* *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-(Un)Modified-Since' HTTP Headers* - previously, 'If-Modified-Since' and 'If-Unmodified-Since' HTTP headers were incorrectly processed within from/to HTTP headers mapping in the `DefaultHttpHeaderMapper`.
|
||||
Now, in addition correcting that issue, `DefaultHttpHeaderMapper` provides date parsing from formatted strings for any HTTP headers that accept date-time values.
|
||||
* *`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_.
|
||||
* *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.
|
||||
* *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 <<http>>.
|
||||
For more information, see "`<<http>>`".
|
||||
|
||||
[[x3.0-json-transformers]]
|
||||
===== Jackson Support (JSON)
|
||||
@@ -165,122 +178,125 @@ 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 <<transformer>>.
|
||||
For more information, see "`JSON Transformers`" in "`<<transformer>>`".
|
||||
|
||||
[[x3.0-id-for-chain-sub-components]]
|
||||
===== Chain Elements 'id' Attribute
|
||||
===== 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: 'fooChain$child.fooTransformer.handler'.
|
||||
For more information see <<chain>>.
|
||||
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, "`<<chain>>`".
|
||||
|
||||
[[x3.0-corr-endpoint-empty-groups]]
|
||||
===== Aggregator 'empty-group-min-timeout' property
|
||||
|
||||
The `AbstractCorrelatingMessageHandler` provides a new property `empty-group-min-timeout` to allow empty group expiry to run on a longer schedule than expiring partial groups.
|
||||
Empty groups will not be removed from the `MessageStore` until they have not been modified for at least this number of milliseconds.
|
||||
For more information see <<aggregator-config>>.
|
||||
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 "`<<aggregator-config>>`".
|
||||
|
||||
[[x3.0-filelistfilter]]
|
||||
===== Persistent File List Filters (file, (S)FTP)
|
||||
|
||||
New `FileListFilter` s that use a persistent `MetadataStore` are now available.
|
||||
These can be used to prevent duplicate files after a system restart.
|
||||
See <<file-reading>>, <<ftp-inbound>>, and <<sftp-inbound>> for more information.
|
||||
New `FileListFilter` implementations that use a persistent `MetadataStore` are now available.
|
||||
You can use these to prevent duplicate files after a system restart.
|
||||
See "`<<file-reading>>`", "`<<ftp-inbound>>`", and "`<<sftp-inbound>>`" for more information.
|
||||
|
||||
[[x3.0-scripting-variables]]
|
||||
===== Scripting Support: Variables Changes
|
||||
|
||||
A new `variables` attribute has been introduced for scripting components.
|
||||
We introduced a new `variables` attribute for scripting components.
|
||||
In addition, variable bindings are now allowed for inline scripts.
|
||||
See <<groovy>> and <<scripting>> for more information.
|
||||
See "`<<groovy>>`" and "`<<scripting>>`" for more information.
|
||||
|
||||
[[x3.0-direct-channel-lb-ref]]
|
||||
===== Direct Channel Load Balancing configuration
|
||||
|
||||
Previously, when configuring `LoadBalancingStrategy` on the channel's 'dispatcher' sub-element, the only available option was to use a pre-defined enumeration of values which did not allow one to set a custom implementation of the `LoadBalancingStrategy`.
|
||||
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 <<channel-implementations-directchannel>>.
|
||||
For more information, see "`<<channel-implementations-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 `minSubscribers` (default 0).
|
||||
If the message is sent to at least the minimum number of subscribers, the send is deemed to be successful (even if zero).
|
||||
If an application is expecting to get an exception under these conditions, set the minimum subscribers to at least 1.
|
||||
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*
|
||||
The FTP, SFTP and FTPS endpoints no longer cache sessions by default.
|
||||
|
||||
The deprecated `cached-sessions` attribute has been removed from all endpoints.
|
||||
Previously, the embedded caching mechanism controlled by this attribute's value didn't provide a way to limit the size of the cache, which could grow indefinitely.
|
||||
The `CachingConnectionFactory` was introduced in release 2.1 and it became the preferred (and is now the only) way to cache sessions.
|
||||
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.
|
||||
|
||||
The `CachingConnectionFactory` now provides a new method `resetCache()`.
|
||||
This immediately closes idle sessions and causes in-use sessions to be closed as and when they are returned to the cache.
|
||||
`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.
|
||||
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` allows you to override the default filter, for example with an `AcceptAllFileListFilter`, or some other custom filter.
|
||||
With this release, a new attribute `local-filter` lets you override the default filter (for example, with an `AcceptAllFileListFilter` or some other custom filter).
|
||||
|
||||
For users that wish the behavior of the `AcceptOnceFileListFilter` to be maintained across JVM executions, a custom filter that retains state, perhaps on the file system, can now be configured.
|
||||
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).
|
||||
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 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 recursive `ls` and `mget` commands, enabling the retrieval of a remote file tree.
|
||||
|
||||
* The gateways now support *put* and *mput* commands, enabling sending file(s) to the remote server.
|
||||
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.
|
||||
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.
|
||||
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, this abstraction can also be used programmatically and, like all Spring `*Template` implementations, reliably closes the underlying session while allowing low level access to the session when needed.
|
||||
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 <<ftp>> and <<sftp>>.
|
||||
For more information, see "`<<ftp>>`" and "`<<sftp>>`".
|
||||
|
||||
[[x3.0-outbound-gateway-requires-reply]]
|
||||
===== 'requires-reply' Attribute for Outbound Gateways
|
||||
|
||||
All Outbound Gateways (e.g.
|
||||
`<jdbc:outbound-gateway/>` or `<jms:outbound-gateway/>`) are designed for 'request-reply' scenarios.
|
||||
A response is expected from the external service and will be 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, e.g.
|
||||
a `<jdbc:outbound-gateway/>`, when a SELECT ends with an empty `ResultSet` or, say, a Web Service is One-Way.
|
||||
An option is therefore needed to configure whether or not a _reply_ is required.
|
||||
For this purpose, the _requires-reply_ attribute has been introduced for Outbound Gateway components.
|
||||
In most cases, the default value for _requires-reply_ is `true` and, if there is not any result, a `ReplyRequiredException` will be thrown.
|
||||
Changing the value to `false` means that, if an external service doesn't return anything, the message-flow will end at that point, similar to an Outbound Channel Adapter.
|
||||
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 `ignore-empty-responses`; this is used to treat an empty String response as if no response was received.
|
||||
It is true by default but can be set 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.
|
||||
_requires-reply_ is false by default for the WebService outbound gateway.
|
||||
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, the `requiresReply` property was previously present in the `AbstractReplyProducingMessageHandler` but set to `false`, and there wasn't any way to configure it on Outbound Gateways using the XML namespace.
|
||||
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); with this change an exception will now be thrown by default by most gateways.
|
||||
To revert to the previous behavior, set `requires-reply` to false.
|
||||
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
|
||||
@@ -296,126 +312,119 @@ 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, 2 new additional attributes were introduced to the `<sql-parameter-definition/>` element with OUT-direction:
|
||||
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_
|
||||
* `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).
|
||||
|
||||
_return-type_
|
||||
|
||||
|
||||
|
||||
The `row-mapper` attribute of the Stored Procedure Inbound Channel Adapter `<returning-resultset/>` sub-element now supports a reference to a `RowMapper` bean definition.
|
||||
Previously, it contained just a class name (which is still supported).
|
||||
|
||||
For more information see <<stored-procedures>>.
|
||||
For more information, see "`<<stored-procedures>>`".
|
||||
|
||||
[[x3.0-ws-outbound-uri-substitution]]
|
||||
===== Web Service Outbound URI Configuration
|
||||
|
||||
Web Service Outbound Gateway 'uri' attribute now supports `<uri-variable/>` substitution for all URI-schemes supported by Spring Web Services.
|
||||
For more information see <<outbound-uri>>.
|
||||
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 "`<<outbound-uri>>`".
|
||||
|
||||
[[x3.0-redis]]
|
||||
===== Redis Adapter Changes
|
||||
|
||||
The Redis Inbound Channel Adapter can now use a `null` value for `serializer` property, with the raw data being the message payload.
|
||||
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 Outbound Channel Adapter now has the `topic-expression` property to determine the Redis topic against the Message at runtime.
|
||||
The Redis inbound channel adapter, in addition to the existing `topics` attribute, now has the `topic-patterns` attribute.
|
||||
|
||||
|
||||
The Redis Inbound Channel Adapter, in addition to the existing `topics` attribute, now has the `topic-patterns` attribute.
|
||||
|
||||
For more information, see <<redis>>.
|
||||
For more information, see "`<<redis>>`".
|
||||
|
||||
[[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 `discard-within-advice` (default `true`), to allow the discard action to be performed after the advice chain completes.
|
||||
See <<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 "`<<advising-filters>>`".
|
||||
|
||||
[[x3.0-annotation-advice]]
|
||||
===== Advising Endpoints using Annotations
|
||||
|
||||
Request Handler Advice Chains can now be configured using annotations.
|
||||
See <<advising-with-annotations>>.
|
||||
Request handler advice chains can now be configured using annotations.
|
||||
See "`<<advising-with-annotations>>`".
|
||||
|
||||
[[x3.0-o-t-s-t]]
|
||||
===== ObjectToStringTransformer Improvements
|
||||
|
||||
This transformer now correctly transforms `byte[]` and `char[]` payloads to `String`.
|
||||
For more information see <<transformer>>.
|
||||
For more information, see "`<<transformer>>`".
|
||||
|
||||
[[x3.0-jpa-changes]]
|
||||
===== JPA Support Changes
|
||||
|
||||
Payloads to _persist_ or _merge_ can now be of type `http://docs.oracle.com/javase/7/docs/api/java/lang/Iterable.html[java.lang.Iterable]`.
|
||||
Payloads to persist or merge can now be of type `http://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 using the underlying `EntityManager`.
|
||||
_NULL_ values returned by the iterator are ignored.
|
||||
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.
|
||||
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 using a `first-result` and `first-result-expression` attributes to the gateway definition.
|
||||
<<jpa-retrieving-outbound-gateway>>.
|
||||
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 "`<<jpa-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, the `max-results` attribute has been introduced 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.
|
||||
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 <<jpa>>.
|
||||
For more information, see "`<<jpa>>`".
|
||||
|
||||
[[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 it was necessary to precede the `<delayer>` with a `<header-enricher>`.
|
||||
Spring Integration 3.0 introduced the `expression` attribute and `expression` sub-element for dynamic delay determination.
|
||||
The `delay-header-name` attribute is now deprecated because the header evaluation can be specified in the `expression`.
|
||||
In addition, the `ignore-expression-failures` was introduced to control the behavior when an expression evaluation fails.
|
||||
For more information see <<delayer>>.
|
||||
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 "`<<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, please see <<jdbc-message-store-generic>>.
|
||||
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 "`<<jdbc-message-store-generic>>`".
|
||||
|
||||
[[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` s.
|
||||
Applications can obtain these events using an `<int-event:inbound-channel-adapter>` or any `ApplicationListener` configured to receive an `ImapIdleExceptionEvent` or one of its super classes.
|
||||
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 an appropriate (de)serializer needs to be configured to write the resulting `Map` to the TCP stream.
|
||||
A `MapJsonSerializer` is provided as a convenient mechanism to transfer headers and payload over TCP.
|
||||
For more information see <<ip-headers>>.
|
||||
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 "`<<ip-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`, it was necessary to declare a container bean and provide it to the adapter using the `container` attribute.
|
||||
The `task-executor` is now provided, allowing it to be set directly on the 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-rmi-ec]]
|
||||
===== RMI Inbound Gateway
|
||||
|
||||
The RMI Inbound Gateway now supports an `error-channel` attribute.
|
||||
See <<rmi-inbound>>.
|
||||
See "`<<rmi-inbound>>`".
|
||||
|
||||
[[x3.0-xslt-transformer]]
|
||||
===== XsltPayloadTransformer
|
||||
===== `XsltPayloadTransformer`
|
||||
|
||||
You can now specify the transformer factory class name using the `transformer-factory-class` attribute.
|
||||
See <<xml-xslt-payload-transformers>>
|
||||
You can now specify the transformer factory class name by setting the `transformer-factory-class` attribute.
|
||||
See `<<xml-xslt-payload-transformers>>`.
|
||||
|
||||
@@ -1,262 +1,269 @@
|
||||
[[migration-3.0-4.0]]
|
||||
=== Changes between 3.0 and 4.0
|
||||
|
||||
Please be sure to also 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.
|
||||
Migration guides for all versions back to _2.1_ can be found on the https://github.com/spring-projects/spring-integration/wiki[Wiki].
|
||||
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 <<mqtt>>
|
||||
See "`<<mqtt>>`".
|
||||
|
||||
[[x4.0-enable-configuration]]
|
||||
===== @EnableIntegration
|
||||
===== `@EnableIntegration`
|
||||
|
||||
The `@EnableIntegration` annotation has been added, to permit declaration of standard Spring Integration beans when using `@Configuration` classes.
|
||||
See <<annotations>> for more information.
|
||||
We added the `@EnableIntegration` annotation to permit declaration of standard Spring Integration beans when using `@Configuration` classes.
|
||||
See "`<<annotations>>`" for more information.
|
||||
|
||||
[[x4.0-component-scan]]
|
||||
===== @IntegrationComponentScan
|
||||
===== `@IntegrationComponentScan`
|
||||
|
||||
The `@IntegrationComponentScan` annotation has been added, to permit classpath scanning for Spring Integration specific components.
|
||||
See <<annotations>> for more information.
|
||||
We added the `@IntegrationComponentScan` annotation to permit classpath scanning for Spring Integration-specific components.
|
||||
See "`<<annotations>>`" for more information.
|
||||
|
||||
[[x4.0-message-history]]
|
||||
===== @EnableMessageHistory
|
||||
===== "`@EnableMessageHistory`"
|
||||
|
||||
Message history can now be enabled with the `@EnableMessageHistory` annotation in a `@Configuration` class; in addition the message history settings can be modified by a JMX MBean.
|
||||
In addition auto-created `MessageHandler` s for annotated endpoints (e.g.
|
||||
`@ServiceActivator`, `@Splitter` etc.) now are also trackable by `MessageHistory`.
|
||||
For more information, see <<message-history>>.
|
||||
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 "`<<message-history>>`".
|
||||
|
||||
[[x4.0-messaging-gateway]]
|
||||
===== @MessagingGateway
|
||||
===== `@MessagingGateway`
|
||||
|
||||
Messaging gateway interfaces can now be configured with the `@MessagingGateway` annotation.
|
||||
It is an analogue of the `<int:gateway/>` xml element.
|
||||
For more information, see <<messaging-gateway-annotation>>.
|
||||
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 "`<<messaging-gateway-annotation>>`".
|
||||
|
||||
[[x4.0-boot]]
|
||||
===== Spring Boot @EnableAutoConfiguration
|
||||
===== Spring Boot `@EnableAutoConfiguration`
|
||||
|
||||
As well as the `@EnableIntegration` annotation mentioned above, a a hook has been introduced to allow the Spring Integration infrastructure beans to be configured using Spring Boot's `@EnableAutoConfiguration`.
|
||||
For more information seehttp://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-auto-configuration.html[Spring Boot - AutoConfigure].
|
||||
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 http://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
|
||||
===== `@GlobalChannelInterceptor`
|
||||
|
||||
As well as the `@EnableIntegration` annotation mentioned above, the `@GlobalChannelInterceptor` annotation has bean introduced.
|
||||
For more information, see <<annotations>>.
|
||||
As well as the `@EnableIntegration` annotation mentioned above, we introduced the `@GlobalChannelInterceptor` annotation.
|
||||
For more information, see "`<<annotations>>`".
|
||||
|
||||
[[x4.0-integration-converter]]
|
||||
===== @IntegrationConverter
|
||||
===== `@IntegrationConverter`
|
||||
|
||||
The `@IntegrationConverter` annotation has bean introduced, as an analogue of `<int:converter/>` component.
|
||||
For more information, see <<annotations>>.
|
||||
We introduced the `@IntegrationConverter` annotation as an analogue of the `<int:converter/>` component.
|
||||
For more information, see "`<<annotations>>`".
|
||||
|
||||
[[x4.0-enable-publisher]]
|
||||
===== @EnablePublisher
|
||||
===== `@EnablePublisher`
|
||||
|
||||
The `@EnablePublisher` annotation has been added, to allow the specification of a `default-publisher-channel` for `@Publisher` annotations.
|
||||
See <<annotations>> for more information.
|
||||
We added the `@EnablePublisher` annotation to allow the specification of a `default-publisher-channel` for `@Publisher` annotations.
|
||||
See "`<<annotations>>`" for more information.
|
||||
|
||||
[[x4.0-redis-cms]]
|
||||
===== Redis Channel Message Stores
|
||||
|
||||
A new Redis `MessageGroupStore`, that is optimized for use when backing a `QueueChannel` for persistence, is now provided.
|
||||
For more information, see <<redis-cms>>.
|
||||
We added a Redis `MessageGroupStore` that is optimized for use when backing a `QueueChannel` for persistence.
|
||||
For more information, see "`<<redis-cms>>`".
|
||||
|
||||
A new Redis `ChannelPriorityMessageStore` is now provided.
|
||||
This can be used to retrieve messages by priority.
|
||||
For more information, see <<redis-cms>>.
|
||||
We added a Redis `ChannelPriorityMessageStore`.
|
||||
You can use it to retrieve messages by priority.
|
||||
For more information, see "`<<redis-cms>>`".
|
||||
|
||||
[[x4.0-priority-channel-mondodb]]
|
||||
===== MongodDB Channel Message Store
|
||||
|
||||
MongoDB support now provides the `MongoDbChannelMessageStore` - a _channel_ specific `MessageStore` implementation.
|
||||
With `priorityEnabled = true`, it can be used in `<int:priority-queue>` s to achieve _priority_ order polling of persisted messages.
|
||||
For more information see <<mongodb-priority-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 "`<<mongodb-priority-channel-message-store>>`".
|
||||
|
||||
[[x4.0-MBeanExport-annotation]]
|
||||
===== @EnableIntegrationMBeanExport
|
||||
===== `@EnableIntegrationMBeanExport`
|
||||
|
||||
The `IntegrationMBeanExporter` can now be enabled with the `@EnableIntegrationMBeanExport` annotation in a `@Configuration` class.
|
||||
For more information, see <<jmx-mbean-exporter>>.
|
||||
You can now enable the `IntegrationMBeanExporter` with the `@EnableIntegrationMBeanExport` annotation in a `@Configuration` class.
|
||||
For more information, see "`<<jmx-mbean-exporter>>`".
|
||||
|
||||
[[x4.0-channel-security-interceptor]]
|
||||
===== ChannelSecurityInterceptorFactoryBean
|
||||
===== `ChannelSecurityInterceptorFactoryBean`
|
||||
|
||||
Configuration of Spring Security for message channels using `@Configuration` classes is now supported by using a `ChannelSecurityInterceptorFactoryBean`.
|
||||
For more information, see <<security>>.
|
||||
`ChannelSecurityInterceptorFactoryBean` now supports configuration of Spring Security for message channels that use `@Configuration` classes.
|
||||
For more information, see "`<<security>>`".
|
||||
|
||||
[[x4.0-redis-outbound-gateway]]
|
||||
===== Redis Command Gateway
|
||||
|
||||
The Redis support now provides the `<outbound-gateway>` component to perform generic Redis commands using the `RedisConnection#execute` method.
|
||||
For more information, see <<redis-outbound-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 "`<<redis-outbound-gateway>>`".
|
||||
|
||||
[[x4.0-redis-gemfire-lock-registry]]
|
||||
===== RedisLockRegistry and GemfireLockRegistry
|
||||
===== `RedisLockRegistry` and `GemfireLockRegistry`
|
||||
|
||||
The `RedisLockRegistry` and `GemfireLockRegistry` are now available supporting global locks visible to multiple application instances/servers.
|
||||
These can be used with aggregating message handlers across multiple application instances such that group release will occur on only one instance.
|
||||
For more information, see <<redis-lock-registry>>, <<gemfire-lock-registry>> and <<aggregator>>.
|
||||
The `RedisLockRegistry` and `GemfireLockRegistry` are 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 "`<<redis-lock-registry>>`", "`<<gemfire-lock-registry>>`", and "`<<aggregator>>`".
|
||||
|
||||
[[x4.0-poller-annotation]]
|
||||
===== @Poller
|
||||
===== `@Poller`
|
||||
|
||||
Annotation-based messaging configuration can now have a `poller` attribute.
|
||||
This means that methods annotated with (`@ServiceActivator`, `@Aggregator` etc.) can now use an `inputChannel` that is a reference to a `PollableChannel`.
|
||||
For more information, see <<annotations>>.
|
||||
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 "`<<annotations>>`".
|
||||
|
||||
[[x4.0-inbound-channel-adapter-annotation]]
|
||||
===== @InboundChannelAdapter and SmartLifecycle for Annotated Endpoints
|
||||
===== `@InboundChannelAdapter` and `SmartLifecycle` for Annotated Endpoints
|
||||
|
||||
The `@InboundChannelAdapter` method annotation is now available.
|
||||
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 <<annotations>>.
|
||||
In addition, all messaging annotations now provide `SmartLifecycle` options.
|
||||
For more information, see "`<<annotations>>`".
|
||||
|
||||
[[x4.0-twitter-sog]]
|
||||
===== Twitter Search Outbound Gateway
|
||||
|
||||
A new twitter endpoint `<int-twitter-search-outbound-gateway/>` has been added.
|
||||
Unlike the search inbound adapter which polls using the same search query each time, the outbound gateway allows on-demand customized queries.
|
||||
For more information, see <<twitter-sog>>.
|
||||
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 "`<<twitter-sog>>`".
|
||||
|
||||
[[x4.0-gemfire-metadata]]
|
||||
===== Gemfire Metadata Store
|
||||
|
||||
The `GemfireMetadataStore` is provided, allowing it to be used, for example, in a `AbstractPersistentAcceptOnceFileListFilter` implementation in a multiple application instance/server environment.
|
||||
For more information, see <<metadata-store>>, <<file-reading>>, <<ftp-inbound>> and <<sftp-inbound>>.
|
||||
We added the `GemfireMetadataStore`, letting it be used, for example, in an `AbstractPersistentAcceptOnceFileListFilter` implementation in a multiple application instance or server environment.
|
||||
For more information, see "`<<metadata-store>>`", "`<<file-reading>>`", "`<<ftp-inbound>>`", and "`<<sftp-inbound>>`".
|
||||
|
||||
[[x4.0-bridge-annotations]]
|
||||
===== @BridgeFrom and @BridgeTo Annotations
|
||||
===== `@BridgeFrom` and `@BridgeTo` Annotations
|
||||
|
||||
Annotation and Java configuration has introduced `@BridgeFrom` and `@BridgeTo` `@Bean` method annotations to mark `MessageChannel` beans in `@Configuration` classes.
|
||||
For more information, see <<annotations>>.
|
||||
We introduced `@BridgeFrom` and `@BridgeTo` `@Bean` method annotations to mark `MessageChannel` beans in `@Configuration` classes.
|
||||
For more information, see "`<<annotations>>`".
|
||||
|
||||
[[x4.0-meta-messaging-annotations]]
|
||||
===== Meta Messaging Annotations
|
||||
===== Meta-messaging Annotations
|
||||
|
||||
Messaging Annotations (`@ServiceActivator`, `@Router`, `@MessagingGateway` etc.) 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` etc.).
|
||||
For more information, see <<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 "`<<annotations>>`".
|
||||
|
||||
[[x4.0-general]]
|
||||
==== General Changes
|
||||
|
||||
This section describes general changes from version 3.0 to version 4.0.
|
||||
|
||||
===== Requires Spring Framework 4.0
|
||||
|
||||
Core messaging abstractions (`Message`, `MessageChannel` etc) have moved to the Spring Framework `spring-messaging` module.
|
||||
Users who reference these classes directly in their code will 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[Migration Guide].
|
||||
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
|
||||
|
||||
The `header-type` attribute has been introduced for the `header` sub-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 will be converted.
|
||||
For more information see <<xml-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 "`<<xml-xpath-header-enricher>>`".
|
||||
|
||||
[[x4.0-object-to-json-transformer-result-type]]
|
||||
===== Object To Json Transformer: Node Result
|
||||
===== Object To JSON Transformer: Node Result
|
||||
|
||||
The `result-type` attribute has been introduced for the `<int:object-to-json-transformer>`.
|
||||
This attribute provides the target type for the result of object mapping to JSON.
|
||||
It supports `STRING` (default) and `NODE`.
|
||||
For more information see <<transformer-xpath-spel-function>>.
|
||||
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 "`<<transformer-xpath-spel-function>>`".
|
||||
|
||||
[[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 <<jms-header-mapping>>.
|
||||
Previously, `priority` was only considered for outbound messages.
|
||||
For more information, see "`<<jms-header-mapping>>`".
|
||||
|
||||
[[x4.0-jms-ob]]
|
||||
===== JMS Outbound Channel Adapter
|
||||
|
||||
The JMS outbound channel adapter now supports the `session-transacted` attribute (default false).
|
||||
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 <<jms-outbound-channel-adapter>>.
|
||||
See "`<<jms-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 <<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 "`<<jms-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` s in a Datatype channel.
|
||||
For more information see <<channel-datatype-channel>>.
|
||||
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 "`<<channel-datatype-channel>>`".
|
||||
|
||||
[[x4.0-retry-config]]
|
||||
===== Simpler Retry Advice Configuration
|
||||
|
||||
Simplified namespace support has been added to configure a `RequestHandlerRetryAdvice`.
|
||||
For more information see <<retry-config>>.
|
||||
We added simplified namespace support to configure a `RequestHandlerRetryAdvice`.
|
||||
For more information, see "`<<retry-config>>`".
|
||||
|
||||
[[x4.0-release-strategy-group-timeout]]
|
||||
===== Correlation Endpoint: Time-based Release Strategy
|
||||
|
||||
The mutually exclusive `group-timeout` and `group-timeout-expression` attributes have been added to the `<int:aggregator>` and `<int:resequencer>`.
|
||||
These attributes allow forced completion of a partial `MessageGroup`, if the `ReleaseStrategy` does not release a group and no further messages arrive within the time specified.
|
||||
For more information see <<aggregator-config>>.
|
||||
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 "`<<aggregator-config>>`".
|
||||
|
||||
[[x4.0-redis-metadata]]
|
||||
===== Redis Metadata Store
|
||||
|
||||
The `RedisMetadataStore` now implements `ConcurrentMetadataStore`, allowing it to be used, for example, in a `AbstractPersistentAcceptOnceFileListFilter` implementation in a multiple application instance/server environment.
|
||||
For more information, see <<redis-metadata-store>>, <<file-reading>>, <<ftp-inbound>> and <<sftp-inbound>>.
|
||||
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 "`<<redis-metadata-store>>`", "`<<file-reading>>`", "`<<ftp-inbound>>`", and "`<<sftp-inbound>>`".
|
||||
|
||||
[[x4.0-jdbc-cs]]
|
||||
===== JdbcChannelMessageStore and PriorityChannel
|
||||
===== `JdbcChannelMessageStore` and `PriorityChannel`
|
||||
|
||||
The `JdbcChannelMessageStore` now implements `PriorityCapableChannelMessageStore`, allowing it to be used as a `message-store` reference for `priority-queue` s.
|
||||
For more information, see <<jdbc-message-store-channels>>.
|
||||
T`JdbcChannelMessageStore` now implements `PriorityCapableChannelMessageStore`, letting it be used as a `message-store` reference for `priority-queue` instances.
|
||||
For more information, see "`<<jdbc-message-store-channels>>`".
|
||||
|
||||
[[x4.0-amqp]]
|
||||
===== AMQP Endpoints Delivery Mode
|
||||
|
||||
Spring AMQP, by default, creates persistent messages on the broker.
|
||||
This behavior can be overridden by setting the `amqp_deliveryMode` header and/or customizing the mappers.
|
||||
A convenient `default-delivery-mode` attribute has now been added to the adapters to provide easier configuration of this important setting.
|
||||
For more information, see <<amqp-outbound-channel-adapter>> and <<amqp-outbound-gateway>>.
|
||||
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 "`<<amqp-outbound-channel-adapter>>`" and "`<<amqp-outbound-gateway>>`".
|
||||
|
||||
[[x4.0-ftp]]
|
||||
===== FTP Timeouts
|
||||
|
||||
The `DefaultFtpSessionFactory` now exposes the `connectTimeout`, `defaultTimeout` and `dataTimeout` properties, avoiding the need to subclass the factory just to set these common properties.
|
||||
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 <<ftp-session-factory>> for more information.
|
||||
See "`<<ftp-session-factory>>`" for more information.
|
||||
|
||||
[[x4.0-twitter-status-updating]]
|
||||
===== Twitter: StatusUpdatingMessageHandler
|
||||
===== 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 allowing, for example, attaching an image.
|
||||
See <<outbound-twitter-update>> for more information.
|
||||
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 "`<<outbound-twitter-update>>`" for more information.
|
||||
|
||||
[[x4.0-jpa-id-expression]]
|
||||
===== JPA Retrieving Gateway: id-expression
|
||||
===== JPA Retrieving Gateway: `id-expression`
|
||||
|
||||
The `id-expression` attribute has been introduced for `<int-jpa:retrieving-outbound-gateway>` to perform `EntityManager.find(Class entityClass, Object primaryKey)`.
|
||||
See <<jpa-retrieving-outbound-gateway>> for more information.
|
||||
We introduced the `id-expression` attribute for `<int-jpa:retrieving-outbound-gateway>` to perform `EntityManager.find(Class entityClass, Object primaryKey)`.
|
||||
See "`<<jpa-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 will now emit a `TcpDeserializationExceptionEvent`, allowing applications to examine the data at the point the exception occurred.
|
||||
See <<tcp-events>> for more information.
|
||||
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 "`<<tcp-events>>`" for more information.
|
||||
|
||||
[[x4.0-bean-messaging-annotations]]
|
||||
===== Messaging Annotations on @Bean Definitions
|
||||
===== Messaging Annotations on `@Bean` Definitions
|
||||
|
||||
Messaging Annotations (`@ServiceActivator`, `@Router`, `@InboundChannelAdapter` etc.) can now be configured on `@Bean` definitions in `@Configuration` classes.
|
||||
For more information, see <<annotations>>.
|
||||
You can now configure messaging annotations (`@ServiceActivator`, `@Router`, `@InboundChannelAdapter`, and others) on `@Bean` definitions in `@Configuration` classes.
|
||||
For more information, see "`<<annotations>>`".
|
||||
|
||||
@@ -1,225 +1,229 @@
|
||||
[[migration-4.0-4.1]]
|
||||
=== Changes between 4.0 and 4.1
|
||||
|
||||
Please be sure to also 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.
|
||||
Migration guides for all versions back to _2.1_ can be found on the https://github.com/spring-projects/spring-integration/wiki[Wiki].
|
||||
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
|
||||
|
||||
Version 4.1 added a number of new components.
|
||||
|
||||
[[x4.1-promise-gateway]]
|
||||
===== Promise<?> Gateway
|
||||
|
||||
A Reactor `Promise` return type is now supported for Messaging Gateway methods.
|
||||
See <<async-gateway>>.
|
||||
The messaging gateway methods now support a Reactor `Promise` return type.
|
||||
See "`<<async-gateway>>`".
|
||||
|
||||
[[x4.1-web-socket-adapters]]
|
||||
===== WebSocket support
|
||||
|
||||
The _WebSocket_ module is now available.
|
||||
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 <<web-sockets>> for more information.
|
||||
See "`<<web-sockets>>`" for more information.
|
||||
|
||||
[[x4.1-scatter-gather]]
|
||||
===== Scatter-Gather EIP pattern
|
||||
===== Scatter-Gather Enterprise Integration Pattern
|
||||
|
||||
The _Scatter-Gather_ EIP pattern is now implemented.
|
||||
See <<scatter-gather>> for more information.
|
||||
We implemented the scatter-gather enterprise integration pattern.
|
||||
See "`<<scatter-gather>>`" for more information.
|
||||
|
||||
[[x4.1-Routing-Slip]]
|
||||
===== Routing Slip Pattern
|
||||
|
||||
The _Routing Slip_ EIP pattern implementation is now provided.
|
||||
See <<routing-slip>> for more information.
|
||||
We added the routing slip EIP pattern implementation.
|
||||
See "`<<routing-slip>>`" for more information.
|
||||
|
||||
[[x4.1-idempotent-receiver]]
|
||||
===== Idempotent Receiver Pattern
|
||||
|
||||
The _Idempotent Receiver_ EIP implementation is now provided via the `<idempotent-receiver>` component in XML, or the `IdempotentReceiverInterceptor` and `IdempotentReceiver` annotation when using Java Configuration.
|
||||
See <<idempotent-receiver>> and their JavaDocs for more information.
|
||||
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 "`<<idempotent-receiver>>`" and the https://docs.spring.io/spring-integration/api/index.html[Javadoc] for more information.
|
||||
|
||||
[[x4.1-BoonJsonObjectMapper]]
|
||||
===== BoonJsonObjectMapper
|
||||
===== Boon `JsonObjectMapper`
|
||||
|
||||
The _Boon_ `JsonObjectMapper` is now provided for the JSON transformers.
|
||||
See <<transformer>> for more information.
|
||||
We added T\the Boon `JsonObjectMapper` for the JSON transformers.
|
||||
See "`<<transformer>>`" for more information.
|
||||
|
||||
[[x4.1-redis-queue-gateways]]
|
||||
===== Redis Queue Gateways
|
||||
|
||||
The `<redis-queue-inbound-gateway>` and `<redis-queue-outbound-gateway>` components are now provided.
|
||||
See <<redis-queue-inbound-gateway>> and <<redis-queue-outbound-gateway>>.
|
||||
We added the `<redis-queue-inbound-gateway>` and `<redis-queue-outbound-gateway>` components.
|
||||
See "`<<redis-queue-inbound-gateway>>`" and "`<<redis-queue-outbound-gateway>>`".
|
||||
|
||||
[[x4.1-PollSkipAdvice]]
|
||||
===== PollSkipAdvice
|
||||
===== `PollSkipAdvice`
|
||||
|
||||
The `PollSkipAdvice` is now provided to be used within `<advice-chain>` of the `<poller>` to determine if the current _poll_ should be suppressed (skipped) by some condition implemented with `PollSkipStrategy`.
|
||||
See <<polling-consumer>> for more information.
|
||||
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 "`<<polling-consumer>>`" 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 utilize a message listener container (inbound endpoints, channel) now support the `missing-queues-fatal` attribute.
|
||||
See <<amqp>> for more information.
|
||||
Elements that use a message listener container (inbound endpoints and channel) now support the `missing-queues-fatal` attribute.
|
||||
See "`<<amqp>>`" for more information.
|
||||
|
||||
[[x4.1-amqp-outbound-lazy-connect]]
|
||||
===== AMQP Outbound Endpoints
|
||||
|
||||
The AMQP outbound endpoints support a new property `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 attempt to establish the connection during startup).
|
||||
When set the 'false' an attempt to establish the connection is made during application startup.
|
||||
See <<amqp>> for more information.
|
||||
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 "`<<amqp>>`" for more information.
|
||||
|
||||
[[x4.1-sms-copy-on-get]]
|
||||
===== SimpleMessageStore
|
||||
|
||||
The `SimpleMessageStore` no longer makes a copy of the group when calling `getMessageGroup()`.
|
||||
See <<sms-caution>> for more information.
|
||||
See "`<<sms-caution>>`" for more information.
|
||||
|
||||
[[x4.1-ws-encode-uri]]
|
||||
===== Web Service Outbound Gateway: 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 StatusCode
|
||||
===== 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 <<http-namespace>> for more information.
|
||||
See "`<<http-namespace>>`" for more information.
|
||||
|
||||
[[x4.1-mqtt]]
|
||||
===== MQTT Adapter Changes
|
||||
|
||||
The MQTT channel adapters can now be configured to connect to multiple servers, for example, to support High Availability (HA).
|
||||
See <<mqtt>> for more information.
|
||||
You can now configure the MQTT channel adapters to connect to multiple servers -- for example, to support High Availability (HA).
|
||||
See "`<<mqtt>>`" for more information.
|
||||
|
||||
The MQTT message-driven channel adapter now supports specifying the QoS setting for each subscription.
|
||||
See <<mqtt-inbound>> for more information.
|
||||
See "`<<mqtt-inbound>>`" for more information.
|
||||
|
||||
The MQTT outbound channel adapter now supports asynchronous sends, avoiding blocking until delivery is confirmed.
|
||||
See <<mqtt-outbound>> for more information.
|
||||
See "`<<mqtt-outbound>>`" for more information.
|
||||
|
||||
It is now possible to programmatically subscribe to and unsubscribe from topics at runtime.
|
||||
See <<mqtt-inbound>> for more information.
|
||||
See "`<<mqtt-inbound>>`" for more information.
|
||||
|
||||
[[x4.1-sftp]]
|
||||
===== FTP/SFTP Adapter Changes
|
||||
===== FTP and SFTP Adapter Changes
|
||||
|
||||
The FTP and SFTP outbound channel adapters now support appending to remote files, as well as taking specific actions when a remote file already exists.
|
||||
The remote file templates now also support 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.
|
||||
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 <<ftp>> and <<sftp>> for more information.
|
||||
See "`<<ftp>>`" and "`<<sftp>>`" 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 <<splitter>> for more information.
|
||||
See "`<<splitter>>`" for more information.
|
||||
|
||||
[[x4.1-aggregator]]
|
||||
===== Aggregator
|
||||
|
||||
`Aggregator` s now support a new attribute `expire-groups-upon-timeout`.
|
||||
See <<aggregator-config>> for more information.
|
||||
`Aggregator` instancess now support a new attribute `expire-groups-upon-timeout`.
|
||||
See "`<<aggregator>>`" for more information.
|
||||
|
||||
[[x4.1-content-enricher-improvement]]
|
||||
===== Content Enricher Improvements
|
||||
|
||||
An `null-result-expression` attribute has been added, which is evaluated and returned if `<enricher>` returns `null`.
|
||||
It can be added in `<header>` and `<property>`.
|
||||
See <<content-enricher>> for more information.
|
||||
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 "`<<content-enricher>>`" for more information.
|
||||
|
||||
An `error-channel` attribute has been added, which is used to handle an error flow if `Exception` occurs downstream of the `request-channel`.
|
||||
This enable you to return an alternative object to use for enrichment.
|
||||
See <<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 "`<<content-enricher>>`" for more information.
|
||||
|
||||
[[x4.1-header-channel-registry]]
|
||||
===== Header Channel Registry
|
||||
|
||||
The `<header-enricher/>`'s `<header-channels-to-string/>` element can now override the header channel registry's default time for retaining channel mappings.
|
||||
See <<header-channel-registry>> for more information.
|
||||
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 "`<<header-channel-registry>>`" for more information.
|
||||
|
||||
[[x4.1-orderly-shutdown]]
|
||||
===== Orderly Shutdown
|
||||
|
||||
Improvements have been made to the orderly shutdown algorithm.
|
||||
See <<jmx-shutdown>> for more information.
|
||||
We made improvements to the orderly shutdown algorithm.
|
||||
See "`<<jmx-shutdown>>`" for more information.
|
||||
|
||||
[[x4.1-recipientListRouter]]
|
||||
===== Management for RecipientListRouter
|
||||
===== Management for `RecipientListRouter`
|
||||
|
||||
The `RecipientListRouter` provides now several _management_ operations to configure _recipients_ at runtime.
|
||||
With that the `<recipient-list-router>` can now be configured without any `<recipient>` from the start.
|
||||
See <<recipient-list-router-management>> for more information.
|
||||
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 "`<<recipient-list-router-management>>`" for more information.
|
||||
|
||||
[[x4.1-AbstractHeaderMapper-changes]]
|
||||
===== AbstractHeaderMapper: NON_STANDARD_HEADERS token
|
||||
|
||||
The `AbstractHeaderMapper` implementations now provides the additional `NON_STANDARD_HEADERS` token to map any user-defined headers, which aren't mapped by default.
|
||||
See <<amqp-message-headers>> for more information.
|
||||
The `AbstractHeaderMapper` implementation now provides the additional `NON_STANDARD_HEADERS` token to map any user-defined headers, which are not mapped by default.
|
||||
See "`<<amqp-message-headers>>`" for more information.
|
||||
|
||||
[[x4.1-amqp-channels]]
|
||||
===== AMQP Channels: template-channel-transacted
|
||||
===== AMQP Channels: `template-channel-transacted`
|
||||
|
||||
The new `template-channel-transacted` attribute has been introduced for AMQP `MessageChannel` s.
|
||||
See <<amqp-channels>> for more information.
|
||||
We introduced the `template-channel-transacted` attribute for AMQP `MessageChannel` instances.
|
||||
See "`<<amqp-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 <<syslog-inbound-adapter>> for more information.
|
||||
The default syslog message converter now has an option to retain the original message in the payload while still setting the headers.
|
||||
See "`<<syslog-inbound-adapter>>`" for more information.
|
||||
|
||||
[[x4.1-async-gateway]]
|
||||
===== Async Gateway
|
||||
===== Asynchronous Gateway
|
||||
|
||||
In addition to the `Promise` return type mentioned above, gateway methods may now return a `ListenableFuture`, introduced in Spring Framework 4.0.
|
||||
You can also disable the async processing in the gateway, allowing a downstream flow to directly return a `Future`.
|
||||
See <<async-gateway>>.
|
||||
In addition to the `Promise` return type <<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 "`<<async-gateway>>`".
|
||||
|
||||
[[x4.1-aggregator-advice-chain]]
|
||||
===== Aggregator Advice Chain
|
||||
|
||||
`Aggregator` s and `Resequencer` s now support an `<expire-advice-chain/>` and `<expire-transactional/>` sub-elements to _advise_ the `forceComplete` operation.
|
||||
See <<aggregator-config>> for more information.
|
||||
`Aggregator` and `Resequencer` now support `<expire-advice-chain/>` and `<expire-transactional/>` child elements to advise the `forceComplete` operation.
|
||||
See "`<<aggregator-config>>`" for more information.
|
||||
|
||||
[[x4.1-script-outbound-channel-adapter]]
|
||||
===== Outbound Channel Adapter and Scripts
|
||||
|
||||
The `<int:outbound-channel-adapter/>` now supports the `<script/>` sub-element.
|
||||
The `<int:outbound-channel-adapter/>` now supports the `<script/>` child element.
|
||||
The underlying script must have a `void` return type or return `null`.
|
||||
See <<groovy>> and <<scripting>>.
|
||||
See "`<<groovy>>`" and "`<<scripting>>`".
|
||||
|
||||
[[x4.1-reseq]]
|
||||
===== Resequencer Changes
|
||||
|
||||
When a message group in a resequencer is timed out (using `group-timeout` or a `MessageGroupStoreReaper`), late arriving messages will now be discarded immediately by default.
|
||||
See <<resequencer>>.
|
||||
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 "`<<resequencer>>`".
|
||||
|
||||
[[x4.1-Optional-Parameter]]
|
||||
===== Optional POJO method parameter
|
||||
|
||||
Now Spring Integration consistently handles the Java 8's `Optional` type.
|
||||
See <<service-activator-namespace>>.
|
||||
Spring Integration now consistently handles the Java 8's `Optional` type.
|
||||
See "`<<service-activator-namespace>>`".
|
||||
|
||||
[[x4.1-queue-channel-queue.typ]]
|
||||
===== QueueChannel: backed Queue type
|
||||
===== `QueueChannel` backed Queue type
|
||||
|
||||
The `QueueChannel` backed `Queue type` has been changed from `BlockingQueue` to the more generic `Queue`.
|
||||
It allows the use of any external `Queue` implementation, for example Reactor's `PersistentQueue`.
|
||||
See <<channel-configuration-queuechannel>>.
|
||||
This change allows the use of any external `Queue` implementation (for example, Reactor's `PersistentQueue`).
|
||||
See "`<<channel-configuration-queuechannel>>`".
|
||||
|
||||
[[x4.1-channel-interceptor]]
|
||||
===== ChannelInterceptor Changes
|
||||
===== `ChannelInterceptor` Changes
|
||||
|
||||
The `ChannelInterceptor` now supports additional `afterSendCompletion()` and `afterReceiveCompletion()` methods.
|
||||
See <<channel-interceptors>>.
|
||||
See "`<<channel-interceptors>>`".
|
||||
|
||||
[[x4.1-mail-peek]]
|
||||
===== IMAP PEEK
|
||||
|
||||
Since _version 4.1.1_ there is a change of behavior if you explicitly set the javamail property `mail.[protocol].peek` to `false` (where `[protocol]` is `imap` or `imaps`).
|
||||
See <<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 "`<<imap-peek>>`".
|
||||
|
||||
@@ -1,103 +1,101 @@
|
||||
[[migration-4.1-4.2]]
|
||||
=== Changes between 4.1 and 4.2
|
||||
|
||||
Please be sure to also 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.
|
||||
Migration guides for all versions back to _2.1_ can be found on the https://github.com/spring-projects/spring-integration/wiki[Wiki].
|
||||
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
|
||||
|
||||
A new `MetricsFactory` strategy interface has been introduced.
|
||||
This, together with other changes in the JMX and management infrastructure provides much more control over management
|
||||
configuration and runtime performance.
|
||||
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 <<metrics-management>> and <<jmx-42-improvements>>.
|
||||
For complete details, see "`<<metrics-management>>`" and "`<<jmx-42-improvements>>`".
|
||||
|
||||
[[x4.2-mongodb-metadata-store]]
|
||||
==== MongoDB Metadata Store
|
||||
|
||||
The `MongoDbMetadataStore` is now available. For more information, see <<mongodb-metadata-store>>.
|
||||
The `MongoDbMetadataStore` is now available.
|
||||
For more information, see "`<<mongodb-metadata-store>>`".
|
||||
|
||||
[[x4.2-secured-channel-annotation]]
|
||||
==== SecuredChannel Annotation
|
||||
|
||||
The `@SecuredChannel` annotation has been introduced, replacing the deprecated `ChannelSecurityInterceptorFactoryBean`.
|
||||
For more information, see <<security>>.
|
||||
We introduced the `@SecuredChannel` annotation, replacing the deprecated `ChannelSecurityInterceptorFactoryBean`.
|
||||
For more information, see "`<<security>>`".
|
||||
|
||||
[[x4.2-security-context-propagation]]
|
||||
==== SecurityContext Propagation
|
||||
==== `SecurityContext` Propagation
|
||||
|
||||
The `SecurityContextPropagationChannelInterceptor` has been
|
||||
introduced for the `SecurityContext` propagation from one message flow's Thread to another.
|
||||
For more information, see <<security>>.
|
||||
We introduced the `SecurityContextPropagationChannelInterceptor` for the `SecurityContext` propagation from one message flow's thread to another.
|
||||
For more information, see "`<<security>>`".
|
||||
|
||||
|
||||
[[x4.2-file-splitter]]
|
||||
==== FileSplitter
|
||||
|
||||
The `FileSplitter`, which splits text files into lines, was added in 4.1.2.
|
||||
It now has full support in the `int-file:` namespace; see <<file-splitter>> for more information.
|
||||
In 4.1.2, we added `FileSplitter`, which splits text files into lines.
|
||||
It now has full support in the `int-file:` namespace.
|
||||
See "`<<file-splitter>>`" for more information.
|
||||
|
||||
[[x4.2-zk]]
|
||||
==== Zookeeper Support
|
||||
|
||||
Zookeeper support has been added to the framework to assist when running on a clustered/multi-host environment.
|
||||
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
|
||||
* `ZookeeperMetadataStore`
|
||||
* `ZookeeperLockRegistry`
|
||||
* Zookeeper Leadership
|
||||
|
||||
See <<zookeeper>> for more information.
|
||||
See "`<<zookeeper>>`" for more information.
|
||||
|
||||
[[x4.2-barrier]]
|
||||
==== Thread Barrier
|
||||
|
||||
A new thread `<int:barrier/>` component is available allowing a thread to be suspended until some asynchronous event
|
||||
occurs.
|
||||
|
||||
See <<barrier>> for more information.
|
||||
A new thread `<int:barrier/>` component is available, letting a thread be suspended until some asynchronous event occurs.
|
||||
See "`<<barrier>>`" for more information.
|
||||
|
||||
[[x4.2-stomp]]
|
||||
==== STOMP Support
|
||||
|
||||
STOMP support has been added to the framework as _inbound_ and _outbound_ channel adapters pair.
|
||||
See <<stomp>> for more information.
|
||||
We added STOMP support to the framework as an inbound and outbound channel adapters pair.
|
||||
See "`<<stomp>>`" for more information.
|
||||
|
||||
[[x4.2-codec]]
|
||||
==== Codec
|
||||
A new `Codec` abstraction has been introduced, to encode/decode objects to/from `byte[]`.
|
||||
An implementation that uses Kryo is provided.
|
||||
Codec-based transformers and message converters are also provided.
|
||||
|
||||
See <<codec>> for more information.
|
||||
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 "`<<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 the
|
||||
`SqlParameterSourceFactory` to populate parameters on the `PreparedStatement` with the `requestMessage` context.
|
||||
|
||||
See <<jdbc-outbound-channel-adapter>> for more information.
|
||||
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 "`<<jdbc-outbound-channel-adapter>>`" for more information.
|
||||
|
||||
[[x4.2-general]]
|
||||
=== General Changes
|
||||
|
||||
[[x4.2-wire-tap]]
|
||||
==== Wire Tap
|
||||
This section describes general changes from version 4.1 to version 4.2.
|
||||
|
||||
As an alternative to the existing `selector` attribute, the `<wire-tap/>` now supports the `selector-expression` attribute.
|
||||
[[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 <<files>> for more information about these changes.
|
||||
See "`<<files>>`" for more information about these changes.
|
||||
|
||||
===== Appending New Lines
|
||||
|
||||
@@ -107,58 +105,58 @@ The default attribute value is `false`.
|
||||
|
||||
===== Ignoring Hidden Files
|
||||
|
||||
The `ignore-hidden` attribute has been introduced for the `<int-file:inbound-channel-adapter>` to pick up or not
|
||||
the _hidden_ files from the source directory.
|
||||
It is `true` by default.
|
||||
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`
|
||||
|
||||
The `HeadDirectoryScanner` can now be used with other `FileListFilter` s.
|
||||
You can now use the `HeadDirectoryScanner` with other `FileListFilter` implementations.
|
||||
|
||||
===== Last Modified Filter
|
||||
|
||||
The `LastModifiedFileListFilter` has been added.
|
||||
We added the `LastModifiedFileListFilter`.
|
||||
|
||||
===== WatchService Directory Scanner
|
||||
===== Watch Service Directory Scanner
|
||||
|
||||
The `WatchServiceDirectoryScanner` is now available.
|
||||
We added the `WatchServiceDirectoryScanner`.
|
||||
|
||||
===== Persistent File List Filter Changes
|
||||
|
||||
The `AbstractPersistentFileListFilter` has a new property `flushOnUpdate` which, when set to true, will `flush()` the
|
||||
metadata store if it implements `Flushable` (e.g. the `PropertiesPersistingMetadataStore`).
|
||||
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
|
||||
|
||||
The `ScatterGatherHandler` class has been moved from the `org.springframework.integration.handler` to the `org.springframework.integration.scattergather`.
|
||||
We moved the `ScatterGatherHandler` class from the `org.springframework.integration.handler` to the `org.springframework.integration.scattergather`.
|
||||
|
||||
==== 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 are using the serializers directly within user code, you may have to `flush()` the `OutputStream`.
|
||||
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` s are now published whenever an unexpected exception occurs on a TCP server socket (also added to 4.1.3, 4.0.7).
|
||||
See <<tcp-events>> for more information.
|
||||
`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 "`<<tcp-events>>`" for more information.
|
||||
|
||||
[[x4.2-tcp-server-port]]
|
||||
===== TCP Server Port
|
||||
|
||||
If a TCP server socket factory is configured to listen on a random port, the actual port chosen by the OS can now
|
||||
be obtained using `getPort()`.
|
||||
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 <<tcp-connection-factories>> for more information.
|
||||
See "<<tcp-connection-factories>>" for more information.
|
||||
|
||||
[[x4.2-tcp-gw-rto]]
|
||||
===== TCP Gateway Remote Timeout
|
||||
@@ -166,220 +164,214 @@ See <<tcp-connection-factories>> for more information.
|
||||
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.
|
||||
Also, the `remote-timeout` no longer defaults to the same value as `reply-timeout`, which has a completely different meaning.
|
||||
|
||||
See <<tcp-ob-gateway-attributes>> for more information.
|
||||
See "`<<tcp-ob-gateway-attributes>>`" for more information.
|
||||
|
||||
[[x4.2-tcp-ssl]]
|
||||
===== TCP SSLSession Available for Header Mapping
|
||||
|
||||
`TcpConnection` s now support `getSslSession()` to enable users to extract information from the session to add to
|
||||
message headers.
|
||||
|
||||
See <<ip-msg-headers>> for more information.
|
||||
|
||||
`TcpConnection` implementations now support `getSslSession()` to let you extract information from the session to add to message headers.
|
||||
See "`<<ip-msg-headers>>`" for more information.
|
||||
|
||||
[[x4.2-tcp-events]]
|
||||
===== TCP Events
|
||||
|
||||
New events are now published whenever a correlation exception occurs - for example sending a message to a
|
||||
non-existent socket.
|
||||
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.
|
||||
The `TcpConnectionEventListeningMessageProducer` is deprecated.
|
||||
Use the generic event adapter instead.
|
||||
|
||||
See <<tcp-events>> for more information.
|
||||
See "`<<tcp-events>>`" for more information.
|
||||
|
||||
[[x4.2-inbound-channel-adapter-annotation]]
|
||||
==== @InboundChannelAdapter
|
||||
==== `@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/>` s, which defaults to 1.
|
||||
The annotation now defaults this attribute to 1.
|
||||
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`.
|
||||
`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 will need to change the import.
|
||||
Any uses of this iterator need to change the import.
|
||||
|
||||
Of course, Reactor is still supported for functionality such as the `Promise` gateway; the dependency was removed for those users who don't need it.
|
||||
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
|
||||
|
||||
It is now possible to 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 <<jms-outbound-gateway>> for more information.
|
||||
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 "`<<jms-outbound-gateway>>`" for more information.
|
||||
|
||||
===== Conversion Errors in Message-Driven Endpoints
|
||||
|
||||
The `error-channel` now is used for the conversion errors, which have caused a transaction rollback and message redelivery previously.
|
||||
The `error-channel` is now used for the conversion errors. In previous versions, they caused transaction rollback and message redelivery.
|
||||
|
||||
See <<jms-message-driven-channel-adapter>> and <<jms-inbound-gateway>> for more information.
|
||||
See "`<<jms-message-driven-channel-adapter>>`" and "`<<jms-inbound-gateway>>`" for more information.
|
||||
|
||||
===== Default Acknowledge Mode
|
||||
|
||||
When using an implicitly defined `DefaultMessageListenerContainer`, the default `acknowledge` is now `transacted`.
|
||||
`transacted` is recommended 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.
|
||||
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 <<jms-message-driven-channel-adapter>> and <<jms-inbound-gateway>> for more information.
|
||||
See "`<<jms-message-driven-channel-adapter>>`" and "`<<jms-inbound-gateway>>`" for more information.
|
||||
|
||||
===== Shared Subscriptions
|
||||
|
||||
Namespace support for shared subscriptions (JMS 2.0) has been added to message-driven endpoints and the
|
||||
`<int-jms:publish-subscribe-channel>`.
|
||||
Previously, you had to wire up listener containers as `<bean/>` s to use shared connections.
|
||||
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 <<jms>> for more information.
|
||||
See "`<<jms>>`" for more information.
|
||||
|
||||
[[x4.2-conditional-pollers]]
|
||||
==== Conditional Pollers
|
||||
Much more flexibility is now provided for dynamic polling.
|
||||
|
||||
See <<conditional-pollers>> for more information.
|
||||
We now provide much more flexibility for dynamic polling.
|
||||
|
||||
See "`<<conditional-pollers>>`" for more information.
|
||||
|
||||
[[x4.2-amqp-changes]]
|
||||
==== AMQP Changes
|
||||
|
||||
===== Publisher Confirms
|
||||
This section describes general changes to the Spring Integration AMQP functionality.
|
||||
|
||||
The `<int-amqp:outbound-gateway>` now supports `confirm-correlation-expression` and `confirm-(n)ack-channel`
|
||||
attributes with similar purpose as for `<int-amqp:outbound-channel-adapter>`.
|
||||
===== 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
|
||||
|
||||
For both the outbound channel adapter and gateway, if the correlation data is a `Message<?>`, it will be the basis
|
||||
of the message on the ack/nack channel, with the additional header(s) added.
|
||||
Previously, any correlation data (including `Message<?>`) was returned as the payload of the ack/nack message.
|
||||
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.
|
||||
|
||||
===== The 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` or even provide your own `AmqpTemplate` implementation.
|
||||
In addition the `default-reply-to` is exposed to be used if request message doesn't have `replyTo` property.
|
||||
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 <<amqp>> for more information.
|
||||
See "`<<amqp>>`" 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.
|
||||
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 <<xml-xpath-splitting>> for more information.
|
||||
See "`<<xml-xpath-splitting>>`" for more information.
|
||||
|
||||
[[x4.2-http-changes]]
|
||||
==== HTTP Changes
|
||||
|
||||
This section describes general changes to the Spring Integration HTTP functionality.
|
||||
|
||||
===== 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)_.
|
||||
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 <<http-cors>> for more information.
|
||||
See "`<<http-cors>>`" for more information.
|
||||
|
||||
===== Inbound Gateway Timeout
|
||||
|
||||
The HTTP inbound gateway can be configured as to what status code to return when a request times out.
|
||||
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 <<http-response-statuscode>> for more information.
|
||||
See "`<<http-response-statuscode>>`" for more information.
|
||||
|
||||
===== Form Data
|
||||
|
||||
Documentation is provided for when proxying `multipart/form-data` requests.
|
||||
See <<http>> for more information.
|
||||
We added documentation for proxying `multipart/form-data` requests.
|
||||
See "`<<http>>`" for more information.
|
||||
|
||||
[[x4.2-gw]]
|
||||
==== Gateway Changes
|
||||
|
||||
===== Gateway Methods can Return CompletableFuture<?>
|
||||
This section describes general changes to the Spring Integration Gateway functionality.
|
||||
|
||||
===== Gateway Methods can Return `CompletableFuture<?>`
|
||||
|
||||
When using Java 8, gateway methods can now return `CompletableFuture<?>`.
|
||||
See <<gw-completable-future>> for more information.
|
||||
See "`<<gw-completable-future>>`" for more information.
|
||||
|
||||
===== MessagingGateway Annotation
|
||||
|
||||
The request and reply timeout properties are now `String` instead of `Long` to allow configuration with property
|
||||
placeholders or SpEL. See <<messaging-gateway-annotation>>.
|
||||
The request and reply timeout properties are now `String` instead of `Long` to allow configuration with property placeholders or SpEL.
|
||||
See "`<<messaging-gateway-annotation>>`".
|
||||
|
||||
[[x4.2-aggregator-changes]]
|
||||
==== Aggregator Changes
|
||||
|
||||
This section describes general changes to the Spring Integration aggregator functionality.
|
||||
|
||||
===== Aggregator Performance
|
||||
|
||||
This release includes some performance improvements for aggregating components (aggregator, resequencer, etc),
|
||||
by more efficiently removing messages from groups when they are released.
|
||||
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, JDBC, Redis and MongoDB message stores support this property.
|
||||
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
|
||||
|
||||
When using a `ref` or inner bean for the aggregator, it is now possible to bind a `MessageGroupProcessor` directly.
|
||||
In addition, a `SimpleMessageGroupProcessor` is provided that simply returns the collection of messages in the group.
|
||||
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, were messages are held up
|
||||
until they all arrive, and are then released individually. See <<aggregator>> for more information.
|
||||
Configuring the `SimpleMessageGroupProcessor` makes the aggregator a message barrier, where messages are held up until they all arrive and are then released individually.
|
||||
See "`<<aggregator>>`" for more information.
|
||||
|
||||
==== (S)FTP Changes
|
||||
==== FTP and SFTP Changes
|
||||
|
||||
===== Inbound channel adapters
|
||||
This section describes general changes to the Spring Integration FTP and SFTP functionality.
|
||||
|
||||
You can now specify a `remote-directory-expression` on the inbound channel adapters, to determine the directory
|
||||
at runtime.
|
||||
See <<ftp>> and <<sftp>> for more information.
|
||||
===== Inbound Channel Adapters
|
||||
|
||||
You can now specify a `remote-directory-expression` on the inbound channel adapters, to determine the directory at runtime.
|
||||
See "`<<ftp>>`" and "`<<sftp>>`" for more information.
|
||||
|
||||
===== Gateway Partial Results
|
||||
|
||||
When use FTP/SFTP outbound gateways to operate on multiple files (`mget`, `mput`), it is possible for an exception to
|
||||
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` is thrown containing the partial results.
|
||||
See <<ftp-outbound-gateway>> and <<sftp-outbound-gateway>> for more information.
|
||||
If such a condition occurs, a `PartialSuccessException` that contains the partial results is thrown.
|
||||
See "`<<ftp-outbound-gateway>>`" and "`<<sftp-outbound-gateway>>`" for more information.
|
||||
|
||||
===== Delegating Session Factory
|
||||
|
||||
A delegating session factory is now available, enabling the selection of a particular session factory based on some
|
||||
thread context value.
|
||||
We added a delegating session factory, enabling the selection of a particular session factory based on some thread context value.
|
||||
|
||||
See <<ftp-dsf>> and <<sftp-dsf>> for more information.
|
||||
See "`<<ftp-dsf>>`" and "`<<sftp-dsf>>`" for more information.
|
||||
|
||||
===== Default Sftp Session Factory
|
||||
|
||||
Previously, the `DefaultSftpSessionFactory` unconditionally allowed connections to unknown hosts.
|
||||
This is now configurable (default false).
|
||||
This is now configurable (default: `false`).
|
||||
|
||||
The factory now requires a configured `knownHosts` file unless the `allowUnknownKeys` property is `true` (default
|
||||
false).
|
||||
The factory now requires a configured `knownHosts`, file unless the `allowUnknownKeys` property is `true` (default: `false`).
|
||||
|
||||
See <<sftp-unk-hosts>> for more information.
|
||||
See "`<<sftp-unk-keys>>`" for more information.
|
||||
|
||||
===== Message Session Callback
|
||||
|
||||
The `MessageSessionCallback<F, T>` has been introduced to perform any custom `Session` operation(s) with the
|
||||
`requestMessage` context in the `<int-(s)ftp:outbound-gateway/>`.
|
||||
|
||||
See <<ftp-session-callback>> and <<sftp-session-callback>> for more information.
|
||||
We introduced the `MessageSessionCallback<F, T>` to perform any custom `Session` operations with the `requestMessage` context in the `<int-(s)ftp:outbound-gateway/>`.
|
||||
|
||||
See "`<<ftp-session-callback>>`" and "`<<sftp-session-callback>>`" for more information.
|
||||
|
||||
==== Websocket Changes
|
||||
|
||||
`WebSocketHandlerDecoratorFactory` support has been added to the `ServerWebSocketContainer`
|
||||
to allow chained customization for the internal `WebSocketHandler`.
|
||||
See <<web-sockets-namespace>> for more information.
|
||||
We added `WebSocketHandlerDecoratorFactory` support to the `ServerWebSocketContainer` to allow chained customization for the internal `WebSocketHandler`.
|
||||
See "`<<web-sockets-namespace>>`" for more information.
|
||||
|
||||
==== Application Event Adapters changes
|
||||
|
||||
The `ApplicationEvent` adapters can now operate with `payload` as `event` directly allow omitting custom
|
||||
`ApplicationEvent` extensions.
|
||||
The `publish-payload` boolean attribute has been introduced on the `<int-event:outbound-channel-adapter>` for this
|
||||
purpose.
|
||||
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 <<applicationevent>> for more information.
|
||||
|
||||
@@ -1,301 +1,307 @@
|
||||
[[migration-4.2-4.3]]
|
||||
=== Changes between 4.2 and 4.3
|
||||
|
||||
Please be sure to also see the https://github.com/spring-projects/spring-integration/wiki/Spring-Integration-4.2-to-4.3-Migration-Guide[Migration Guide]
|
||||
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.
|
||||
Migration guides for all versions back to _2.1_ can be found on the https://github.com/spring-projects/spring-integration/wiki[Wiki].
|
||||
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
|
||||
|
||||
See <<amqp-async-outbound-gateway>>.
|
||||
See "`<<amqp-async-outbound-gateway>>`".
|
||||
|
||||
==== MessageGroupFactory
|
||||
==== `MessageGroupFactory`
|
||||
|
||||
The new `MessageGroupFactory` strategy has been introduced to allow a control over `MessageGroup` instances
|
||||
in `MessageGroupStore` logic.
|
||||
The `SimpleMessageGroupFactory` is provided for the `SimpleMessageGroup` with the `GroupType.HASH_SET` as the default
|
||||
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 <<message-store>> for more information.
|
||||
See "`<<message-store>>`" for more information.
|
||||
|
||||
==== PersistentMessageGroup
|
||||
==== `PersistentMessageGroup`
|
||||
|
||||
The `PersistentMessageGroup`, - lazy-load proxy, - implementation is provided for persistent `MessageGroupStore` s,
|
||||
which return this instance for the `getMessageGroup()` when their `lazyLoadMessageGroups` is `true` (defaults).
|
||||
See <<message-store>> for more information.
|
||||
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 "`<<message-store>>`" for more information.
|
||||
|
||||
==== FTP/SFTP Streaming Inbound Channel Adapters
|
||||
==== FTP and SFTP Streaming Inbound Channel Adapters
|
||||
|
||||
New inbound channel adapters are provided that return an `InputStream` for each file allowing you to retrieve remote
|
||||
files without writing them to the local file system
|
||||
See <<ftp-streaming>> and <<sftp-streaming>> for more information.
|
||||
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 "`<<ftp-streaming>>`" and "`<<sftp-streaming>>`" for more information.
|
||||
|
||||
==== Stream Transformer
|
||||
==== `StreamTransformer`
|
||||
|
||||
A new `StreamTransformer` is provided to transform an `InputStream` payload to either a `byte[]` or `String`.
|
||||
See <<stream-transformer>> for more information.
|
||||
We added `StreamTransformer` to transform an `InputStream` payload to either a `byte[]` or a `String`.
|
||||
See "`<<stream-transformer>>`" for more information.
|
||||
|
||||
==== Integration Graph
|
||||
|
||||
A new `IntegrationGraphServer` together with the `IntegrationGraphController` REST service are provided to expose the runtime model of a Spring Integration application as a graph.
|
||||
See <<integration-graph>> for more information.
|
||||
We added `IntegrationGraphServer`, together with the `IntegrationGraphController` REST service, to expose the runtime model of a Spring Integration application as a graph.
|
||||
See "`<<integration-graph>>`" for more information.
|
||||
|
||||
==== JDBC Lock Registry
|
||||
|
||||
A new `JdbcLockRegistry` is provided for distributed locks shared through the data base table.
|
||||
See <<jdbc-lock-registry>> for more information.
|
||||
We added `JdbcLockRegistry` for distributed locks shared through a database table.
|
||||
See "`<<jdbc-lock-registry>>`" for more information.
|
||||
|
||||
==== Leader Initiator for Lock Registry
|
||||
==== `LeaderInitiator` for `LockRegistry`
|
||||
|
||||
A new `LeaderInitiator` implementation is provided based on the `LockRegistry` strategy.
|
||||
See <<leadership-event-handling>> for more information.
|
||||
We added `LeaderInitiator` implementation based on the `LockRegistry` strategy.
|
||||
See "`<<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
|
||||
|
||||
===== Outbound Gateway within Chain
|
||||
This section describes general changes to the core of Spring Integration.
|
||||
|
||||
Previously, it was possible to 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 to the chain's output channel
|
||||
if the gateway is the last element.
|
||||
===== 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 configuration, simply remove the `reply-channel`.
|
||||
If you have such a configuration, remove the `reply-channel`.
|
||||
|
||||
===== Async Service Activator
|
||||
===== Asynchronous Service Activator
|
||||
|
||||
An option to make the Service Asynchronous has been added.
|
||||
See <<async-service-activator>> for more information.
|
||||
We added an option to make the service activator be synchronous.
|
||||
See "`<<async-service-activator>>`" for more information.
|
||||
|
||||
===== Messaging Annotation Support changes
|
||||
|
||||
The Messaging Annotation Support doesn't require any more `@MessageEndpoint` (or any other `@Component`) annotation
|
||||
declaration on the class level.
|
||||
To restore the previous behaviour specify the `spring.integration.messagingAnnotations.require.componentAnnotation` of
|
||||
`spring.integration.properties` as `true`.
|
||||
See <<global-properties>> and <<annotations>> for more information.
|
||||
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 "`<<global-properties>>`" and "`<<annotations>>`" for more information.
|
||||
|
||||
==== Mail Changes
|
||||
|
||||
This section describes general changes to the Spring Integration Mail functionality.
|
||||
|
||||
===== 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 using the XML namespace.
|
||||
See <<imap-seen>> for more information.
|
||||
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 "`<<imap-seen>>`" for more information.
|
||||
|
||||
===== Mail Message Mapping
|
||||
|
||||
There is now an option to map inbound mail messages with the `MessageHeaders` containing the mail headers and the
|
||||
payload containing the email content.
|
||||
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 <<mail-mapping>> for more information.
|
||||
See "`<<mail-mapping>>`" for more information.
|
||||
|
||||
==== JMS Changes
|
||||
|
||||
This section describes general changes to the Spring Integration JMS functionality.
|
||||
|
||||
===== Header Mapper
|
||||
|
||||
The `DefaultJmsHeaderMapper` now maps the standard `correlationId` header as a message property by invoking its
|
||||
`toString()` method.
|
||||
See <<jms-header-mapping>> for more information.
|
||||
The `DefaultJmsHeaderMapper` now maps the standard `correlationId` header as a message property by invoking its `toString()` method.
|
||||
See "`<<jms-header-mapping>>`" for more information.
|
||||
|
||||
===== Async Gateway
|
||||
===== Asynchronous Gateway
|
||||
|
||||
The JMS Outbound gateway now has an `async` property.
|
||||
See <<jms-async-gateway>> for more information.
|
||||
The JMS outbound gateway now has an `async` property.
|
||||
See "`<<jms-async-gateway>>`" for more information.
|
||||
|
||||
==== 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 will need to make a small change to your POJO. See this <<agg-message-collection>> note
|
||||
for more information.
|
||||
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 "`<<agg-message-collection>>`" note for more information.
|
||||
|
||||
==== TCP/UDP Changes
|
||||
|
||||
This section describes general changes to the Spring Integration TCP/UDP functionality.
|
||||
|
||||
===== Events
|
||||
|
||||
A new `TcpConnectionServerListeningEvent` is emitted when a server connection factory is started.
|
||||
See <<tcp-events>> for more information.
|
||||
See "`<<tcp-events>>`" for more information.
|
||||
|
||||
The `destination-expression` and `socket-expression` are now available for the `<int-ip:udp-outbound-channel-adapter>`.
|
||||
See <<udp-adapters>> for more information.
|
||||
You can now use the `destination-expression` and `socket-expression` attributes on `<int-ip:udp-outbound-channel-adapter>`.
|
||||
See "`<<udp-adapters>>`" for more information.
|
||||
|
||||
===== Stream Deserializers
|
||||
|
||||
The various deserializers that can't allocate the final buffer until the whole message has been assembled now support
|
||||
pooling of the raw buffer into which the data is received, rather than creating and discarding a buffer for each
|
||||
message.
|
||||
See <<tcp-connection-factories>> for more information.
|
||||
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 "`<<tcp-connection-factories>>`" for more information.
|
||||
|
||||
===== TCP Message Mapper
|
||||
|
||||
The message mapper now, optionally, sets a configured content type header.
|
||||
See <<ip-msg-headers>> for more information.
|
||||
See "`<<ip-msg-headers>>`" for more information.
|
||||
|
||||
==== File Changes
|
||||
|
||||
This section describes general changes to the Spring Integration File functionality.
|
||||
|
||||
===== Destination Directory Creation
|
||||
|
||||
The generated file name for the `FileWritingMessageHandler` can represent _sub-path_ to save the desired directory
|
||||
structure for file in the target directory.
|
||||
See <<file-writing-file-names>> for more information.
|
||||
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 "`<<file-writing-file-names>>`" for more information.
|
||||
|
||||
The `FileReadingMessageSource` now hides the `WatchService` directory scanning logic in the inner class.
|
||||
The `use-watch-service` and `watch-events` options are provided to enable such a behaviour.
|
||||
The top level `WatchServiceDirectoryScanner` has been deprecated because of inconsistency around API.
|
||||
See <<watch-service-directory-scanner>> for more information.
|
||||
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 "`<<watch-service-directory-scanner>>`" for more information.
|
||||
|
||||
===== Buffer Size
|
||||
|
||||
When writing files, you can now specify the buffer size to use.
|
||||
When writing files, you can now specify the buffer size.
|
||||
|
||||
===== 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 <<file-flushing>> for more information.
|
||||
See "`<<file-flushing>>`" for more information.
|
||||
|
||||
===== Preserving Timestamps
|
||||
|
||||
The outbound channel adapter can now be configured to set the destination file's `lastmodified` timestamp.
|
||||
See <<file-timestamps>> for more information.
|
||||
You can now configure the outbound channel adapter to set the destination file's `lastmodified` timestamp.
|
||||
See "`<<file-timestamps>>`" for more information.
|
||||
|
||||
===== Splitter Changes
|
||||
|
||||
The `FileSplitter` will now automatically close an (S)FTP session when the file is completely read.
|
||||
This applies when the outbound gateway returns an `InputStream` or the new (S)FTP streaming channel adapters are being used.
|
||||
Also a new `markers-json` options has been introduced to convert `FileSplitter.FileMarker` to JSON `String` for relaxed downstream network interaction.
|
||||
See <<file-splitter>> for more information.
|
||||
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 "`<<file-splitter>>`" for more information.
|
||||
|
||||
===== File Filters
|
||||
|
||||
A new `ChainFileListFilter` is provided as an alternative to `CompositeFileListFilter`.
|
||||
See <<file-reading>> for more information.
|
||||
|
||||
We added `ChainFileListFilter` as an alternative to `CompositeFileListFilter`.
|
||||
See "`<<file-reading>>`" for more information.
|
||||
|
||||
==== AMQP Changes
|
||||
|
||||
This section describes general changes to the Spring Integration AMQP functionality.
|
||||
|
||||
===== Content Type Message Converter
|
||||
|
||||
The outbound endpoints now support a `RabbitTemplate` configured with a `ContentTypeDelegatingMessageConverter` such
|
||||
that the converter can be chosen based on the message content type.
|
||||
See <<content-type-conversion-outbound>> for more information.
|
||||
that you can choose the converter based on the message content type.
|
||||
See "`<<content-type-conversion-outbound>>`" for more information.
|
||||
|
||||
===== 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].
|
||||
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 now support message mapping.
|
||||
See <<amqp-channels>> for more information.
|
||||
See "`<<amqp-channels>>`" for more information.
|
||||
|
||||
==== Redis Changes
|
||||
|
||||
This section describes general changes to the Spring Integration Redis functionality.
|
||||
|
||||
===== 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.
|
||||
It is now possible to configure the reading and writing direction using `rightPop` and `leftPush` options for the
|
||||
`RedisQueueMessageDrivenEndpoint` and `RedisQueueOutboundChannelAdapter` respectively.
|
||||
See <<redis-queue-inbound-channel-adapter>> and <<redis-queue-outbound-channel-adapter>> for more information.
|
||||
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 "`<<redis-queue-inbound-channel-adapter>>`" and "`<<redis-queue-outbound-channel-adapter>>`" for more information.
|
||||
|
||||
===== 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 <<redis-queue-inbound-gateway>> for more information.
|
||||
The default serializer in the inbound gateway has been changed to a `JdkSerializationRedisSerializer` for compatibility with the outbound gateway.
|
||||
See "`<<redis-queue-inbound-gateway>>`" for more information.
|
||||
|
||||
==== 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 <<http-inbound>> for more information.
|
||||
See "`<<http-inbound>>`" for more information.
|
||||
|
||||
`uriVariablesExpression` now uses a `SimpleEvaluationContext` by default (since 4.3.15).
|
||||
|
||||
See <<mapping-uri-variables>> for more information.
|
||||
See "`<<mapping-uri-variables>>`" for more information.
|
||||
|
||||
==== SFTP Changes
|
||||
|
||||
This section describes general changes to the Spring Integration SFTP functionality.
|
||||
|
||||
===== Factory Bean
|
||||
A new factory bean is provided to simplify the configuration of Jsch proxies for SFTP.
|
||||
See <<sftp-proxy-factory-bean>> for more information.
|
||||
|
||||
===== chmod
|
||||
We added a new factory bean to simplify the configuration of Jsch proxies for SFTP.
|
||||
See "`<<sftp-proxy-factory-bean>>`" for more information.
|
||||
|
||||
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 <<sftp-outbound>> and <<sftp-outbound-gateway>> for more information.
|
||||
===== `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 `<<sftp-outbound>>` and `<<sftp-outbound-gateway>>` for more information.
|
||||
|
||||
==== FTP Changes
|
||||
|
||||
This section describes general changes to the Spring Integration FTP functionality.
|
||||
|
||||
===== Session Changes
|
||||
|
||||
The `FtpSession` now supports `null` for the `list()` and `listNames()` method, since it is possible by the
|
||||
underlying FTP Client.
|
||||
With that the `FtpOutboundGateway` can now be configured without `remoteDirectory` expression.
|
||||
And the `<int-ftp:inbound-channel-adapter>` can be configured without `remote-directory`/`remote-directory-expression`.
|
||||
See <<ftp>> for more information.
|
||||
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 "`<<ftp>>`" for more information.
|
||||
|
||||
==== Router Changes
|
||||
|
||||
The `ErrorMessageExceptionTypeRouter` supports now the `Exception` superclass mappings to avoid duplication
|
||||
for the same channel in case of several inheritors.
|
||||
For this purpose the `ErrorMessageExceptionTypeRouter` loads mapping classes during initialization to fail-fast
|
||||
for a `ClassNotFoundException`.
|
||||
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 <<router>> for more information.
|
||||
See "`<<router>>`" for more information.
|
||||
|
||||
==== Header Mapping
|
||||
|
||||
This section describes the changes to header mapping between version 4.2 and 4.3.
|
||||
|
||||
===== General
|
||||
|
||||
AMQP, WS and XMPP header mappings (e.g. `request-header-mapping`, `reply-header-mapping`) now support negated
|
||||
patterns.
|
||||
See <<amqp-message-headers>>, <<ws-message-headers>>, and <<xmpp-message-headers>> for more information.
|
||||
AMQP, WS, and XMPP header mappings (such as `request-header-mapping` and `reply-header-mapping`) now support negated patterns.
|
||||
See "`<<amqp-message-headers>>`", "`<<ws-message-headers>>`", and "`<<xmpp-message-headers>>`" for more information.
|
||||
|
||||
===== AMQP Header Mapping
|
||||
|
||||
Previously, only standard AMQP headers were mapped by default; users had to explicitly enable mapping of user-defined
|
||||
headers.
|
||||
With this release all headers are mapped by default.
|
||||
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 <<amqp-message-headers>> for more information.
|
||||
See "`<<amqp-message-headers>>`" for more information.
|
||||
|
||||
==== Groovy Scripts
|
||||
|
||||
Groovy scripts can now be configured with the `compile-static` hint or any other `CompilerConfiguration` options.
|
||||
See <<groovy-config>> for more information.
|
||||
You can now configure groovy scripts with the `compile-static` hint or any other `CompilerConfiguration` options.
|
||||
See "`<<groovy-config>>`" for more information.
|
||||
|
||||
==== @InboundChannelAdapter
|
||||
==== `@InboundChannelAdapter` Changes
|
||||
|
||||
The `@InboundChannelAdapter` has now an alias `channel` attribute for regular `value`.
|
||||
In addition the target `SourcePollingChannelAdapter` components can now resolve the target `outputChannel` bean
|
||||
from its provided name (`outputChannelName` options) in late-binding manner.
|
||||
See <<annotations>> for more information.
|
||||
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 "`<<annotations>>`" for more information.
|
||||
|
||||
==== XMPP changes
|
||||
==== XMPP Changes
|
||||
|
||||
The XMPP Extensions (XEP) are now supported by the XMPP channel adapters.
|
||||
See <<xmpp-extensions>> for more information.
|
||||
The XMPP channel adapters now support the XMPP Extensions (XEP).
|
||||
See "`<<xmpp-extensions>>`" for more information.
|
||||
|
||||
==== WireTap Late Binding
|
||||
|
||||
The `WireTap` `ChannelInterceptor` now can accept a `channelName` which is resolved to the target `MessageChannel`
|
||||
The `WireTap` `ChannelInterceptor` now can accept a `channelName` that is resolved to the target `MessageChannel`
|
||||
later, during the first active interceptor operation.
|
||||
See <<channel-wiretap>> for more information.
|
||||
See "`<<channel-wiretap>>`" for more information.
|
||||
|
||||
==== ChannelMessageStoreQueryProvider
|
||||
==== `ChannelMessageStoreQueryProvider` Changes
|
||||
|
||||
The `ChannelMessageStoreQueryProvider` now supports H2 database.
|
||||
See <<jdbc-message-store-channels>> for more information.
|
||||
The `ChannelMessageStoreQueryProvider` now supports H2 databases.
|
||||
See "`<<jdbc-message-store-channels>>`" for more information.
|
||||
|
||||
==== WebSocket Changes
|
||||
|
||||
The `ServerWebSocketContainer` now exposes `allowedOrigins` option and `SockJsServiceOptions` a `suppressCors` option.
|
||||
See <<web-sockets>> for more information.
|
||||
The `ServerWebSocketContainer` now exposes an `allowedOrigins` option, and `SockJsServiceOptions` exposes a `suppressCors` option.
|
||||
See "`<<web-sockets>>`" for more information.
|
||||
|
||||
@@ -1,53 +1,58 @@
|
||||
[[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
|
||||
|
||||
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 <<java-dsl>> for more information about Java DSL support.
|
||||
Also see 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.
|
||||
See "`<<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
|
||||
|
||||
A new Spring Integration Test Framework has been created to assist with testing Spring Integration applications.
|
||||
Now, with the `@SpringIntegrationTest` annotation on test class and `MockIntegration` factory you can make your JUnit tests for integration flows somewhat easier.
|
||||
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 <<testing>> for more information.
|
||||
See "`<<testing>>`" for more information.
|
||||
|
||||
==== MongoDB Outbound Gateway
|
||||
|
||||
The new `MongoDbOutboundGateway` allows you to make queries to the database on demand by sending a message to its request channel.
|
||||
The new `MongoDbOutboundGateway` lets you make queries to the database on demand by sending a message to its request channel.
|
||||
|
||||
See <<mongodb-outbound-gateway>> for more information.
|
||||
See "`<<mongodb-outbound-gateway>>`" for more information.
|
||||
|
||||
==== WebFlux Gateways and Channel Adapters
|
||||
|
||||
The new WebFlux support module has been introduced for Spring WebFlux Framework gateways and channel adapters.
|
||||
We introduced the new WebFlux support module for Spring WebFlux Framework gateways and channel adapters.
|
||||
|
||||
See <<webflux>> for more information.
|
||||
See "`<<webflux>>`" for more information.
|
||||
|
||||
==== Content Type Conversion
|
||||
|
||||
Now that we use the new `InvocableHandlerMethod` -based infrastructure for service method invocations, we can perform `contentType` conversion from payload to target method argument.
|
||||
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 <<content-type-conversion>> for more information.
|
||||
See "`<<content-type-conversion>>`" for more information.
|
||||
|
||||
==== ErrorMessagePublisher and ErrorMessageStrategy
|
||||
==== `ErrorMessagePublisher` and `ErrorMessageStrategy`
|
||||
|
||||
The `ErrorMessagePublisher` and the `ErrorMessageStrategy` are provided for creating `ErrorMessage` instances.
|
||||
We added `ErrorMessagePublisher` and the `ErrorMessageStrategy` for creating `ErrorMessage` instances.
|
||||
|
||||
See <<namespace-errorhandler>> for more information.
|
||||
See "`<<namespace-errorhandler>>`" for more information.
|
||||
|
||||
==== JDBC Metadata Store
|
||||
|
||||
A JDBC implementation of `MetadataStore` implementation is now provided.
|
||||
This is useful when it is necessary to ensure transactional boundaries for metadata.
|
||||
We added a JDBC implementation of the `MetadataStore` implementation.
|
||||
This is useful when you need to ensure transactional boundaries for metadata.
|
||||
|
||||
See <<jdbc-metadata-store>> for more information.
|
||||
See "`<<jdbc-metadata-store>>`" for more information.
|
||||
|
||||
[[x5.0-general]]
|
||||
=== General Changes
|
||||
@@ -58,98 +63,82 @@ Previous Project Reactor versions are no longer supported.
|
||||
==== Core Changes
|
||||
|
||||
The `@Poller` annotation now has the `errorChannel` attribute for easier configuration of the underlying `MessagePublishingErrorHandler`.
|
||||
See "`<<annotations>>`" for more information.
|
||||
|
||||
See <<annotations>> for more information.
|
||||
|
||||
All the request-reply endpoints (based on `AbstractReplyProducingMessageHandler`) can now start transaction and, therefore, make the whole downstream flow transactional.
|
||||
|
||||
See <<tx-handle-message-advice>> for more information.
|
||||
All the request-reply endpoints (based on `AbstractReplyProducingMessageHandler`) can now start transactions and, therefore, make the whole downstream flow transactional.
|
||||
See "`<<tx-handle-message-advice>>`" for more information.
|
||||
|
||||
The `SmartLifecycleRoleController` now provides methods to obtain status of endpoints in roles.
|
||||
See "`<<endpoint-roles>>`" for more information.
|
||||
|
||||
See <<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 "`<<pojo-invocation>>`" for more information.
|
||||
|
||||
POJO methods are now invoked using an `InvocableHandlerMethod` by default, but can be configured to use SpEL as before.
|
||||
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 "`<<service-activator-namespace>>`" for more information.
|
||||
|
||||
See <<pojo-invocation>> for more information.
|
||||
|
||||
When targeting POJO methods as message handlers, one of the service methods can now be marked with the `@Default` annotation to provide a fallback mechanism for non-matched conditions.
|
||||
|
||||
See <<service-activator-namespace>> for more information.
|
||||
|
||||
A simple `PassThroughTransactionSynchronizationFactory` is provided to always store a polled message in the current transaction context.
|
||||
That message is used as a `failedMessage` property of the `MessagingException` which wraps a raw exception thrown during transaction completion.
|
||||
|
||||
See <<transaction-synchronization>> 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 "`<<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 "`<<aggregator-spel>>`" for more information.
|
||||
|
||||
See <<aggregator-spel>> for more information.
|
||||
You can now supply the `ObjectToMapTransformer` with a customized `JsonObjectMapper`.
|
||||
|
||||
The `ObjectToMapTransformer` can now be supplied with a customised `JsonObjectMapper`.
|
||||
|
||||
See <<aggregator-spel>> for more information.
|
||||
See "`<<aggregator-spel>>`" for more information.
|
||||
|
||||
The `@GlobalChannelInterceptor` annotation and `<int:channel-interceptor>` now support negative patterns (via `!` prepending) for component names matching.
|
||||
See "`<<global-channel-configuration-interceptors>>`" for more information.
|
||||
|
||||
See <<global-channel-configuration-interceptors>> for more information.
|
||||
|
||||
A new `OnFailedToAcquireMutexEvent` is emitted now via `DefaultLeaderEventPublisher` by the `LockRegistryLeaderInitiator`, when candidate is failed to acquire the lock.
|
||||
|
||||
See <<leadership-event-handling>> for more information.
|
||||
When a candidate failed to acquire the lock, the `LockRegistryLeaderInitiator` now emits a new `OnFailedToAcquireMutexEvent` through `DefaultLeaderEventPublisher`.
|
||||
See `<<leadership-event-handling>>` for more information.
|
||||
|
||||
==== Gateway Changes
|
||||
|
||||
The gateway now correctly sets the `errorChannel` header when the gateway method has a `void` return type and an error channel is provided.
|
||||
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 had the effect that synchronous downstream flows (running on the calling thread) would send the exception to the configured channel but an exception on an async downstream flow would be sent to the default `errorChannel` instead.
|
||||
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 all the proposed messages exchange contract.
|
||||
The `RequestReplyExchanger` interface now has a `throws MessagingException` clause to meet the proposed messages exchange contract.
|
||||
|
||||
The request and reply timeouts can now be specified as SpEL expressions.
|
||||
|
||||
See <<gateway>> for more information.
|
||||
You can now specify the request and reply timeouts with SpEL expressions.
|
||||
See "`<<gateway>>`" for more information.
|
||||
|
||||
==== Aggregator Performance Changes
|
||||
|
||||
Aggregators now use a `SimpleSequenceSizeReleaseStrategy` by default, which is more efficient, especially with large groups.
|
||||
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 <<aggregator>> for more information.
|
||||
See "`<<aggregator>>`" for more information.
|
||||
|
||||
==== Splitter Changes
|
||||
|
||||
The Splitter component now can 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 object being split.
|
||||
In addition, `AbstractMessageSplitter` provides `protected obtainSizeIfPossible()` methods to allow the determination of the size of the `Iterable` and `Iterator` objects if that is possible.
|
||||
|
||||
See <<splitter>> for more information.
|
||||
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 "`<<splitter>>`" for more information.
|
||||
|
||||
==== JMS Changes
|
||||
|
||||
Previously, Spring Integration JMS XML configuration used a default bean name `connectionFactory` for the JMS Connection Factory, allowing the property to be omitted from component definitions.
|
||||
It has now been renamed to `jmsConnectionFactory`, which is the bean name used by Spring Boot to auto-configure the JMS Connection Factory bean.
|
||||
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 is relying on the previous behavior, rename your `connectionFactory` bean to `jmsConnectionFactory`, or specifically configure your components to use your bean using its current name.
|
||||
|
||||
See <<jms>> for more information.
|
||||
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 "`<<jms>>`" for more information.
|
||||
|
||||
==== Mail Changes
|
||||
|
||||
Some inconsistencies with rendering IMAP mail content have been resolved.
|
||||
|
||||
See <<imap-format-important, the note in the Mail-Receiving Channel Adapter Section>> for more information.
|
||||
See <<imap-format-important,the note in the "`Mail-receiving Channel Adapter`" section>> for more information.
|
||||
|
||||
==== Feed Changes
|
||||
|
||||
Instead of the `com.rometools.fetcher.FeedFetcher`, which is deprecated in ROME, a new `Resource` property has been introduced to the `FeedEntryMessageSource`.
|
||||
|
||||
See <<feed>> for more information.
|
||||
|
||||
Instead of the `com.rometools.fetcher.FeedFetcher`, which is deprecated in ROME, we introduced a new `Resource` property for the `FeedEntryMessageSource`.
|
||||
See "`<<feed>>`" for more information.
|
||||
|
||||
==== File Changes
|
||||
|
||||
The new `FileHeaders.RELATIVE_PATH` Message header has been introduced to represent relative path in the `FileReadingMessageSource`.
|
||||
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.
|
||||
|
||||
@@ -159,158 +148,154 @@ The file outbound channel adapter and gateway (`FileWritingMessageHandler`) now
|
||||
|
||||
They also now support setting file permissions on the newly written file.
|
||||
|
||||
A new `FileSystemMarkerFilePresentFileListFilter` is now available; see <<file-incomplete>> for more information.
|
||||
A new `FileSystemMarkerFilePresentFileListFilter` is now available.
|
||||
See "`<<file-incomplete>>`" 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 <<files>> for more information.
|
||||
See "`<<files>>`" for more information.
|
||||
|
||||
==== (S)FTP Changes
|
||||
==== FTP and SFTP Changes
|
||||
|
||||
The Inbound Channel Adapters now have a property `max-fetch-size` which is used to limit the number of files fetched during a poll when there are no files currently in the local directory.
|
||||
They also are configured with a `FileSystemPersistentAcceptOnceFileListFilter` in the `local-filter` by default.
|
||||
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 via the newly introduced `scanner` attribute.
|
||||
You can also provide a custom `DirectoryScanner` implementation to inbound channel adapters by setting the newly introduced `scanner` attribute.
|
||||
|
||||
The regex and pattern filters can now be configured to always pass directories.
|
||||
This can be useful when using recursion in the outbound gateways.
|
||||
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.
|
||||
|
||||
All the Inbound Channel Adapters (streaming and synchronization-based) now use an appropriate `AbstractPersistentAcceptOnceFileListFilter` implementation by default to prevent remote files duplicate downloads.
|
||||
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 (S)FTP streaming inbound channel adapters now add remote file information in a message header.
|
||||
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 `PUT` command of the outbound gateways, now support `InputStream` as `payload`, too.
|
||||
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 now can build file tree locally using a newly introduced `RecursiveDirectoryScanner`.
|
||||
See `scanner` option for injection.
|
||||
Also these adapters can now be switched to the `WatchService` instead.
|
||||
The inbound channel adapters can now build file trees locally by using a newly introduced `RecursiveDirectoryScanner`.
|
||||
See the `scanner` option in the "`<<ftp-inbound>>`" section for injection.
|
||||
Also, you can now switch these adapters to the `WatchService` instead.
|
||||
|
||||
The `NLST` command has been added to the `AbstractRemoteFileOutboundGateway` to perform only list files names remote command.
|
||||
We added The `NLST` command to the `AbstractRemoteFileOutboundGateway` to perform the list files names remote command.
|
||||
|
||||
The `FtpOutboundGateway` can now be supplied with `workingDirExpression` to change the FTP client working directory for the current request message.
|
||||
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`.
|
||||
|
||||
New filters for detecting incomplete remote files are now provided.
|
||||
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 using the `GET` or `MGET` commands.
|
||||
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 <<ftp>> and <<sftp>> for more information.
|
||||
See "`<<ftp>>`" and "`<<sftp>>`" for more information.
|
||||
|
||||
==== Integration Properties
|
||||
|
||||
Since _version 4.3.2_ a new `spring.integration.readOnly.headers` global property has been added to customize the list of headers which should not be copied to a newly created `Message` by the `MessageBuilder`.
|
||||
|
||||
See <<global-properties>> for more information.
|
||||
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 "`<<global-properties>>`" for more information.
|
||||
|
||||
==== Stream Changes
|
||||
|
||||
There is a new option on the `CharacterStreamReadingMessageSource` to allow it to be used to "pipe" stdin and publish an application event when the pipe is closed.
|
||||
|
||||
See <<stream-reading>> for more information.
|
||||
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 "`<<stream-reading>>`" for more information.
|
||||
|
||||
==== Barrier Changes
|
||||
|
||||
The `BarrierMessageHandler` now supports a discard channel to which late-arriving trigger messages are sent.
|
||||
|
||||
See <<barrier>> for more information.
|
||||
See "`<<barrier>>`" for more information.
|
||||
|
||||
==== AMQP Changes
|
||||
|
||||
The AMQP outbound endpoints now support setting a delay expression for when using the RabbitMQ Delayed Message Exchange plugin.
|
||||
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 1 second).
|
||||
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/message properties appeared in the final message.
|
||||
To override headers set by the converter, set the `headersMappedLast` property to `true`.
|
||||
|
||||
See <<amqp>> for more information.
|
||||
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 "`<<amqp>>`" for more information.
|
||||
|
||||
==== HTTP Changes
|
||||
|
||||
The `DefaultHttpHeaderMapper.userDefinedHeaderPrefix` property is now an empty string by default instead of `X-`.
|
||||
By default, the `DefaultHttpHeaderMapper.userDefinedHeaderPrefix` property is now an empty string instead of `X-`.
|
||||
See "`<<http-header-mapping>>`" for more information.
|
||||
|
||||
See <<http-header-mapping>> for more information.
|
||||
By default, `uriVariablesExpression` now uses a `SimpleEvaluationContext` (since 5.0.4).
|
||||
|
||||
`uriVariablesExpression` now uses a `SimpleEvaluationContext` by default (since 5.0.4).
|
||||
|
||||
See <<mapping-uri-variables>> for more information.
|
||||
See "`<<mapping-uri-variables>>`" for more information.
|
||||
|
||||
==== MQTT Changes
|
||||
|
||||
Inbound messages are now mapped with headers `RECEIVED_TOPIC`, `RECEIVED_QOS` and `RECEIVED_RETAINED` to avoid inadvertent propagation to outbound messages when an application is relaying messages.
|
||||
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 <<mqtt>> for more information.
|
||||
The outbound channel adapter now supports expressions for the topic, qos, and retained properties.
|
||||
The defaults remain the same.
|
||||
See "`<<mqtt>>`" for more information.
|
||||
|
||||
==== STOMP Changes
|
||||
|
||||
The STOMP module has been changed to use `ReactorNettyTcpStompClient`, based on the Project Reactor `3.1` and `reactor-netty` extension.
|
||||
The `Reactor2TcpStompSessionManager` has been renamed to the `ReactorNettyTcpStompSessionManager` according to the `ReactorNettyTcpStompClient` foundation.
|
||||
|
||||
See <<stomp>> for more information.
|
||||
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 "`<<stomp>>`" for more information.
|
||||
|
||||
==== Web Services Changes
|
||||
|
||||
- The `WebServiceOutboundGateway` s can now be supplied with an externally configured `WebServiceTemplate` instances.
|
||||
You can now supply `WebServiceOutboundGateway` instances with an externally configured `WebServiceTemplate` instances.
|
||||
|
||||
- The `DefaultSoapHeaderMapper` can now map a `javax.xml.transform.Source` user-defined header to a SOAP header element.
|
||||
`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.
|
||||
Simple WebService inbound and outbound gateways can now deal with the complete `WebServiceMessage` as a `payload`, allowing the manipulation of MTOM attachments.
|
||||
|
||||
See <<ws>> for more information.
|
||||
See "`<<ws>>`" for more information.
|
||||
|
||||
==== Redis Changes
|
||||
|
||||
The `RedisStoreWritingMessageHandler` is supplied now with additional String-based setters for SpEL expressions - for convenience with Java configuration.
|
||||
The `zsetIncrementExpression` can now be configured on the `RedisStoreWritingMessageHandler`, as well.
|
||||
In addition this property has been changed from `true` to `false` since `INCR` option on `ZADD` Redis command is optional.
|
||||
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.
|
||||
|
||||
The `RedisInboundChannelAdapter` can now be supplied with an `Executor` for executing Redis listener invokers.
|
||||
In addition the received messages now contains a `RedisHeaders.MESSAGE_SOURCE` header to indicate the source of the message - topic or pattern.
|
||||
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 <<redis>> for more information.
|
||||
See "`<<redis>>`" for more information.
|
||||
|
||||
==== TCP Changes
|
||||
|
||||
A new `ThreadAffinityClientConnectionFactory` is provided that binds TCP connections to threads.
|
||||
We added a new `ThreadAffinityClientConnectionFactory` to bind TCP connections to threads.
|
||||
|
||||
You can now configure the TCP connection factories to support `PushbackInputStream` s, allowing deserializers to "unread" (push back) bytes after "reading ahead".
|
||||
You can now configure the TCP connection factories to support `PushbackInputStream` instances, letting deserializers "`unread`" (push back) bytes after "`reading ahead`".
|
||||
|
||||
A `ByteArrayElasticRawDeserializer` has been added without `maxMessageSize` control and buffer incoming data as needed.
|
||||
We added a `ByteArrayElasticRawDeserializer` without `maxMessageSize` to control and buffer incoming data as needed.
|
||||
|
||||
See <<ip>> for more information.
|
||||
See "`<<ip>>`" for more information.
|
||||
|
||||
==== Gemfire Changes
|
||||
|
||||
The `GemfireMetadataStore` now implements `ListenableMetadataStore`, allowing users to listen to cache events by providing `MetadataStoreListener` instances to the store.
|
||||
The `GemfireMetadataStore` now implements `ListenableMetadataStore`, letting you listen to cache events by providing `MetadataStoreListener` instances to the store.
|
||||
See "`<<gemfire>>`" for more information.
|
||||
|
||||
See <<gemfire>> for more information.
|
||||
==== JDBC Changes
|
||||
|
||||
==== Jdbc Changes
|
||||
The `JdbcMessageChannelStore` now provides a setter for `ChannelMessageStorePreparedStatementSetter`, letting you customize message insertion in the store.
|
||||
|
||||
The `JdbcMessageChannelStore` now provides setter for the `ChannelMessageStorePreparedStatementSetter` allowing users to customize a message insertion in the store.
|
||||
The `ExpressionEvaluatingSqlParameterSourceFactory` now provides a setter for `sqlParameterTypes`, letting you customize the SQL types of the parameters.
|
||||
|
||||
The `ExpressionEvaluatingSqlParameterSourceFactory` now provides setter for the sqlParameterTypes allowing users to customize sql types of the parameters.
|
||||
|
||||
See <<jdbc>> for more information.
|
||||
See "`<<jdbc>>`" for more information.
|
||||
|
||||
|
||||
==== Metrics Changes
|
||||
|
||||
http://micrometer.io/[Micrometer] application monitoring is now supported (since _version 5.0.2_).
|
||||
See <<micrometer-integration>> for more information.
|
||||
http://micrometer.io/[Micrometer] application monitoring is now supported (since version 5.0.2).
|
||||
See "`<<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 using Micrometer, a minimum of version 5.0.4 is recommended.
|
||||
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 usie 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 using Java configuration.
|
||||
See <<endpoint-bean-names>> for more information.
|
||||
Introduced in version 5.0.4, this annotation provides control over bean naming when you use Java configuration.
|
||||
See "`<<endpoint-bean-names>>`" for more information.
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
[[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 out of the box to support various transports, such as JMS, File, HTTP, Web Services, Mail, and more.
|
||||
Those will be discussed in upcoming chapters of this reference guide.
|
||||
However, this chapter focuses on the simple but flexible Method-invoking Channel Adapter support.
|
||||
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 destination.
|
||||
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 can invoke any method on a Spring-managed Object and send a non-null return value to a `MessageChannel` after converting it to a `Message`.
|
||||
When the adapter's subscription is activated, a poller will attempt to receive messages from the source.
|
||||
The poller will be scheduled with the `TaskScheduler` according to the provided configuration.
|
||||
To configure the polling interval or cron expression for an individual channel-adapter, provide a 'poller' element with one of the scheduling attributes, such as 'fixed-rate' or 'cron'.
|
||||
An `inbound-channel-adapter` element 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:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:inbound-channel-adapter ref="source1" method="method1" channel="channel1">
|
||||
@@ -26,106 +28,115 @@ To configure the polling interval or cron expression for an individual channel-a
|
||||
<int:poller cron="30 * 9-17 * * MON-FRI"/>
|
||||
</int:channel-adapter>
|
||||
----
|
||||
====
|
||||
|
||||
Also see <<channel-adapter-expressions-and-scripts>>.
|
||||
See also "`<<channel-adapter-expressions-and-scripts>>`".
|
||||
|
||||
NOTE: If no poller is provided, then a single default poller must be registered within the context.
|
||||
See <<endpoint-namespace>> for more detail.
|
||||
See "`<<endpoint-namespace>>`" for more detail.
|
||||
|
||||
[IMPORTANT]
|
||||
.Important: Poller Configuration
|
||||
=====
|
||||
Some `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:
|
||||
|
||||
|
||||
Some `inbound-channel-adapter` types are backed by a `SourcePollingChannelAdapter` which means they contain Poller configuration which will poll the `MessageSource` (invoke a custom method which produces the value that becomes a `Message` payload) based on the configuration specified in the Poller.
|
||||
|
||||
For example:
|
||||
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:poller max-messages-per-poll="1" fixed-rate="1000"/>
|
||||
|
||||
<int:poller max-messages-per-poll="10" fixed-rate="1000"/>
|
||||
----
|
||||
====
|
||||
|
||||
In the the first configuration the polling task will be invoked once per poll and during such task (poll) the method (which results in the production of the Message) will be invoked once based on the `max-messages-per-poll` attribute value.
|
||||
In the second configuration the polling task will be invoked 10 times per poll or until it returns 'null' thus possibly producing 10 Messages per poll while each poll happens at 1 second intervals.
|
||||
However what if the configuration looks like this:
|
||||
|
||||
In the 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 there is no `max-messages-per-poll` specified.
|
||||
As you'll learn later the identical poller configuration in the `PollingConsumer` (e.g., service-activator, filter, router etc.) would have a default value of -1 for `max-messages-per-poll` which means "execute poling task non-stop unless polling method returns null (e.g., no more Messages in the QueueChannel)" and then sleep for 1 second.
|
||||
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` will be set to 1 by default unless you explicitly set it to a negative value (e.g., -1).
|
||||
It is done so to make sure that poller can react to a LifeCycle events (e.g., start/stop) and prevent it from potentially spinning in the infinite loop if the implementation of the custom method of the `MessageSource` has a potential to never return null and happened to be non-interruptible.
|
||||
|
||||
However if you are sure that your method can return null and you need the behavior where you want to poll for as many sources as available per each poll, then you should explicitly set `max-messages-per-poll` to a negative value.
|
||||
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"/>
|
||||
----
|
||||
====
|
||||
=====
|
||||
|
||||
[[channel-adapter-namespace-outbound]]
|
||||
==== Configuring An Outbound Channel Adapter
|
||||
|
||||
An "outbound-channel-adapter" element can also connect a `MessageChannel` to any POJO consumer method that should be invoked with the payload of Messages sent to that channel.
|
||||
An `outbound-channel-adapter` element 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:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:outbound-channel-adapter channel="channel1" ref="target" method="handle"/>
|
||||
|
||||
<beans:bean id="target" class="org.Foo"/>
|
||||
|
||||
<beans:bean id="target" class="org.MyPojo"/>
|
||||
----
|
||||
====
|
||||
|
||||
If the channel being adapted is a `PollableChannel`, provide a poller sub-element:
|
||||
If the channel being adapted is a `PollableChannel`, you must provide a poller sub-element, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<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.Foo"/>
|
||||
|
||||
<beans:bean id="target" class="org.MyPojo"/>
|
||||
----
|
||||
====
|
||||
|
||||
Using a "ref" attribute is generally recommended if the POJO consumer implementation can be reused in other `<outbound-channel-adapter>` definitions.
|
||||
However if the consumer implementation is only referenced by a single definition of the `<outbound-channel-adapter>`, you can define it as inner bean:
|
||||
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 will result in an Exception being thrown.
|
||||
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 will implicitly create an instance of `DirectChannel`.
|
||||
The created channel's name will match the "id" attribute of the `<inbound-channel-adapter>` or `<outbound-channel-adapter>` element.
|
||||
Therefore, if the "channel" is not provided, the "id" is required.
|
||||
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 via 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>` will generate a message anytime 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.
|
||||
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 `<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` using the `location` attribute, you can also set the _refresh-check-delay_ allowing the resource to be refreshed periodically.
|
||||
If you want the script to be checked on each poll, you would need to coordinate this setting with the poller's trigger:
|
||||
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">
|
||||
@@ -133,10 +144,11 @@ If you want the script to be checked on each poll, you would need to coordinate
|
||||
<script:script lang="ruby" location="Foo.rb" refresh-check-delay="5000"/>
|
||||
</int:inbound-channel-adapter>
|
||||
----
|
||||
====
|
||||
|
||||
Also see the `cacheSeconds` property on the `ReloadableResourceBundleExpressionSource` when using the `<expression/>` sub-element.
|
||||
For more information regarding expressions see <<spel>>, and for scripts - <<groovy>> and <<scripting>>.
|
||||
See also the `cacheSeconds` property on the `ReloadableResourceBundleExpressionSource` when using the `<expression/>` sub-element.
|
||||
For more information regarding expressions, see "`<<spel>>`". For scripts, see "`<<groovy>>`" and "`<<scripting>>`".
|
||||
|
||||
IMPORTANT: The `<int:inbound-channel-adapter/>` is an endpoint that starts a message flow via periodic triggering to poll some underlying `MessageSource`.
|
||||
Since, at the time of polling, there is not yet a message object, expressions and scripts don't have access to a root `Message`, so there are no _payload_ or _headers_ properties that are available in most other messaging SpEL expressions.
|
||||
Of course, the script *can* generate and return a complete `Message` object with headers and payload, or just a payload, which will be added to a message with basic headers.
|
||||
IMPORTANT: The `<int:inbound-channel-adapter/>` is endpoint 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.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,34 +1,31 @@
|
||||
[[claim-check]]
|
||||
=== Claim Check
|
||||
|
||||
[[claim-check-introduction]]
|
||||
==== Introduction
|
||||
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.
|
||||
|
||||
In the earlier sections we've covered several Content Enricher type components that 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 http://www.eaipatterns.com/StoreInLibrary.html[Claim Check] pattern describes a mechanism that allows you to store data in a well known place while only maintaining a pointer (Claim Check) to where that data is located.
|
||||
You can pass that pointer around as a payload of a new Message thereby allowing any component within the message flow to get the actual data as soon as it needs it.
|
||||
This approach is very similar to the Certified Mail process where you'll get a Claim Check in your mailbox and would have to go to the Post Office to claim your actual package.
|
||||
Of course it's also the same idea as baggage-claim on a flight or in a hotel.
|
||||
|
||||
Spring Integration provides two types of Claim Check transformers:
|
||||
|
||||
* _Incoming Claim Check Transformer_
|
||||
* _Outgoing Claim Check Transformer_
|
||||
The http://www.eaipatterns.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_ will transform an incoming Message by storing it in the Message Store identified by its `message-store` attribute.
|
||||
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"
|
||||
@@ -36,16 +33,18 @@ An _Incoming Claim Check Transformer_ will transform an incoming Message by stor
|
||||
message-store="testMessageStore"
|
||||
output-channel="output"/>
|
||||
----
|
||||
====
|
||||
|
||||
In the above configuration the Message that is received on the `input-channel` will be persisted to the Message Store identified with the `message-store` attribute and indexed with generated ID.
|
||||
That ID is the Claim Check for that Message.
|
||||
The Claim Check will also become the payload of the new (transformed) Message that will be sent to the `output-channel`.
|
||||
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, lets assume that at some point you do need access to the actual Message.
|
||||
You can of course access the Message Store manually and get the contents of the Message, or you can use the same approach as before except now you will be transforming the Claim Check to the actual Message by using an _Outgoing Claim Check Transformer_.
|
||||
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.
|
||||
|
||||
Here is an overview of all available parameters of an Incoming 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>
|
||||
@@ -59,54 +58,42 @@ Here is an overview of all available parameters of an Incoming Claim Check Trans
|
||||
</int:claim-check-in>
|
||||
----
|
||||
|
||||
<1> Lifecycle attribute signaling if this component should be started during Application Context startup.
|
||||
Defaults to true.
|
||||
Attribute is not available inside a `Chain` element.
|
||||
_Optional_.
|
||||
|
||||
|
||||
<2> Id identifying the underlying bean definition (`MessageTransformingHandler`).
|
||||
Attribute is not available inside a `Chain` element.
|
||||
_Optional_.
|
||||
|
||||
|
||||
<3> The receiving Message channel of this endpoint.
|
||||
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 will be to a bean named _messageStore_.
|
||||
_Optional_.
|
||||
|
||||
|
||||
<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 itself is a Polling Consumer for a channel with a queue.
|
||||
Attribute is not available inside a `Chain` element.
|
||||
_Optional_.
|
||||
|
||||
|
||||
<6> Identifies the Message channel where Message will be sent after its being processed by this endpoint.
|
||||
Attribute is not available inside a `Chain` element.
|
||||
_Optional_.
|
||||
|
||||
|
||||
<7> Specify the maximum amount of time in milliseconds to wait when sending a reply Message to the output channel.
|
||||
Defaults to `-1` - blocking indefinitely.
|
||||
Attribute is not available inside a `Chain` element.
|
||||
_Optional_.
|
||||
|
||||
|
||||
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 `-1` -- blocking indefinitely.
|
||||
This attribute is not available inside a `Chain` element.
|
||||
Optional.
|
||||
<8> Defines a poller.
|
||||
Element is not available inside a `Chain` element.
|
||||
_Optional_.
|
||||
This element is not available inside a `Chain` element.
|
||||
Optional.
|
||||
====
|
||||
|
||||
[[claim-check-out]]
|
||||
==== Outgoing Claim Check Transformer
|
||||
|
||||
An _Outgoing Claim Check Transformer_ allows you to transform a Message with a Claim Check payload into a Message with the original content as its payload.
|
||||
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"
|
||||
@@ -114,12 +101,15 @@ An _Outgoing Claim Check Transformer_ allows you to transform a Message with a C
|
||||
message-store="testMessageStore"
|
||||
output-channel="output"/>
|
||||
----
|
||||
====
|
||||
|
||||
In the above configuration, the Message that is received on the `input-channel` should have a Claim Check as its payload and the _Outgoing Claim Check Transformer_ will transform it into a Message with the original payload by simply 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`.
|
||||
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`.
|
||||
|
||||
Here is an overview of all available parameters of an Outgoing Claim Check Transformer:
|
||||
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>
|
||||
@@ -134,67 +124,53 @@ Here is an overview of all available parameters of an Outgoing Claim Check Trans
|
||||
</int:claim-check-out>
|
||||
----
|
||||
|
||||
<1> Lifecycle attribute signaling if this component should be started during Application Context startup.
|
||||
Defaults to true.
|
||||
Attribute is not available inside a `Chain` element.
|
||||
_Optional_.
|
||||
|
||||
|
||||
<2> Id identifying the underlying bean definition (`MessageTransformingHandler`).
|
||||
Attribute is not available inside a `Chain` element.
|
||||
_Optional_.
|
||||
|
||||
|
||||
<3> The receiving Message channel of this endpoint.
|
||||
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 will be to a bean named _messageStore_.
|
||||
_Optional_.
|
||||
|
||||
|
||||
<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 itself is a Polling Consumer for a channel with a queue.
|
||||
Attribute is not available inside a `Chain` element.
|
||||
_Optional_.
|
||||
|
||||
|
||||
<6> Identifies the Message channel where Message will be sent after its being processed by this endpoint.
|
||||
Attribute is not available inside a `Chain` element.
|
||||
_Optional_.
|
||||
|
||||
|
||||
<7> If set to `true` the Message will be removed from the MessageStore by this transformer.
|
||||
Useful when Message can be "claimed" only once.
|
||||
Defaults to `false`.
|
||||
_Optional_.
|
||||
|
||||
|
||||
<8> Specify the maximum amount of time in milliseconds to wait when sending a reply Message to the output channel.
|
||||
Defaults to `-1` - blocking indefinitely.
|
||||
Attribute is not available inside a `Chain` element.
|
||||
_Optional_.
|
||||
|
||||
|
||||
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 `-1` -- blocking indefinitely.
|
||||
This attribute is not available inside a `Chain` element.
|
||||
Optional.
|
||||
<9> Defines a poller.
|
||||
Element is not available inside a `Chain` element.
|
||||
_Optional_.
|
||||
This element is not available inside a `Chain` element.
|
||||
Optional.
|
||||
====
|
||||
|
||||
_Claim Once_
|
||||
==== Claim Once
|
||||
|
||||
There are scenarios when a particular message must be claimed only once.
|
||||
As an analogy, consider the airplane luggage check-in/out process.
|
||||
Checking-in your luggage on departure and and then claiming it on arrival is a classic example of such a scenario.
|
||||
Sometimes, a particular message must be claimed only once.
|
||||
As an analogy, consider process of handling airplane luggage.
|
||||
You 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 will be removed from the MessageStore, so that it can no longer be claimed again.
|
||||
However, if set to `true`, the claimed message is removed from the `MessageStore` so that it cannot be claimed again.
|
||||
|
||||
This is also something to consider in terms of storage space, especially in the case of the in-memory Map-based `SimpleMessageStore`, where failing to remove the Messages could ultimately lead to an `OutOfMemoryException`.
|
||||
Therefore, if you don't expect multiple claims to be made, it's recommended that you set the `remove-message` attribute's value to `true`.
|
||||
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]
|
||||
----
|
||||
@@ -207,8 +183,10 @@ Therefore, if you don't expect multiple claims to be made, it's recommended that
|
||||
|
||||
==== A word on Message Store
|
||||
|
||||
Although we rarely care about the details of the claim checks as long as they work, it is still worth knowing that the current implementation of the actual Claim Check (the pointer) in Spring Integration is a UUID to ensure uniqueness.
|
||||
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) and `JdbcMessageStore`: an implementation that uses a relational database via JDBC.
|
||||
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
|
||||
|
||||
@@ -1,60 +1,64 @@
|
||||
[[codec]]
|
||||
=== Codec
|
||||
|
||||
==== Introduction
|
||||
|
||||
Spring Integration _version 4.2_ introduces the `Codec` abstraction.
|
||||
Codecs are used to encode/decode objects to/from `byte[]`.
|
||||
They are an alternative to Java Serialization.
|
||||
One advantage is, typically, objects do not have to implement `Serializable`.
|
||||
One implementation, using https://github.com/EsotericSoftware/kryo[Kryo] for serialization, is provided but you
|
||||
can provide your own implementation for use in any of these components:
|
||||
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`
|
||||
|
||||
See their JavaDocs for more information.
|
||||
==== `EncodingPayloadTransformer`
|
||||
|
||||
==== EncodingPayloadTransformer
|
||||
|
||||
This transformer encodes the payload to a `byte[]` using the codec.
|
||||
This transformer encodes the payload to a `byte[]` by using the codec.
|
||||
It does not affect message headers.
|
||||
|
||||
==== DecodingTransformer
|
||||
See the https://docs.spring.io/spring-integration/api/org/springframework/integration/transformer/EncodingPayloadTransformer.html[Javadoc] for more information.
|
||||
|
||||
This transformer decodes a `byte[]` 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 will not be retained.
|
||||
==== `DecodingTransformer`
|
||||
|
||||
==== CodecMessageConverter
|
||||
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.
|
||||
|
||||
Certain endpoints (e.g. TCP, 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/from a `byte[]` for
|
||||
See the https://docs.spring.io/spring-integration/api/org/springframework/integration/transformer/DecodingTransformer.html[Javadoc] for more information.
|
||||
|
||||
==== `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
|
||||
|
||||
Currently, this is the only implementation of `Codec`.
|
||||
There are two `Codec` s - `PojoCodec` which can be used in the transformers and `MessageCodec` which can be used
|
||||
in the `CodecMessageConverter`.
|
||||
Currently, this is the only implementation of `Codec`, and it provides two kinds of `Codec`:
|
||||
|
||||
Several custom serializers are provided by the framework:
|
||||
* `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 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
|
||||
|
||||
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` serializers.
|
||||
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:
|
||||
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> {
|
||||
@@ -72,35 +76,31 @@ public class AddressSerializer extends Serializer<Address> {
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
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[documentation].
|
||||
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 but in our case 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
|
||||
above); we recommend you start at, say 60, to allow for expansion in the framework.
|
||||
These framework defaults can be overridden by configuring the registrars mentioned above.
|
||||
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
|
||||
|
||||
If custom serialization is indicated, please consult the https://github.com/EsotericSoftware/kryo[Kryo] documentation
|
||||
since you will be using the native API.
|
||||
For an example, see the `MessageCodec`.
|
||||
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/master/spring-integration-core/src/main/java/org/springframework/integration/codec/kryo/MessageCodec.java[`MessageCodec`] implementation.
|
||||
|
||||
====== Implementing KryoSerializable
|
||||
|
||||
If you have write access to the domain object source code it may 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. This has the advantage of being much simpler to use
|
||||
with XD, however benchmarks have shown this is not quite as efficient as
|
||||
registering a custom serializer explicitly:
|
||||
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 {
|
||||
@@ -121,12 +121,13 @@ public class Address implements KryoSerializable {
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Note that this technique can also be used to wrap a serialization library other than Kryo.
|
||||
You can also use this technique to wrap a serialization library other than Kryo.
|
||||
|
||||
====== Using DefaultSerializer Annotation
|
||||
====== Using the `@DefaultSerializer` Annotation
|
||||
|
||||
Kryo also provides an annotation as described https://github.com/EsotericSoftware/kryo#default-serializers[here].
|
||||
Kryo also provides a `@DefaultSerializer` annotation, as described https://github.com/EsotericSoftware/kryo#default-serializers[here].
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -136,5 +137,5 @@ public class SomeClass {
|
||||
}
|
||||
----
|
||||
|
||||
If you have write access to the domain object this may be a simpler alternative to specify a custom serializer.
|
||||
Note this does not register the class with an ID, so your mileage may vary.
|
||||
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.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,38 +1,33 @@
|
||||
[[content-enricher]]
|
||||
=== Content Enricher
|
||||
|
||||
[[content-enricher-introduction]]
|
||||
==== Introduction
|
||||
At times, you may have a requirement to enhance a request with more information than was provided by the target system.
|
||||
The http://www.eaipatterns.com/DataEnricher.html[data enricher] pattern describes various scenarios as well as the component (Enricher) that lets you address such requirements.
|
||||
|
||||
At times you may have a requirement to enhance a request with more information than was provided by the target system.
|
||||
The http://www.eaipatterns.com/DataEnricher.html[Content Enricher] pattern describes various scenarios as well as the component (Enricher), which allows you to address such requirements.
|
||||
|
||||
The Spring Integration `Core` module includes 2 enrichers:
|
||||
The Spring Integration `Core` module includes two enrichers:
|
||||
|
||||
* <<header-enricher,Header Enricher>>
|
||||
* <<payload-enricher,Payload Enricher>>
|
||||
|
||||
|
||||
|
||||
Furthermore, several _Adapter specific Header Enrichers_ are included as well:
|
||||
It also includes three adapter-specific header enrichers:
|
||||
|
||||
* <<xml-xpath-header-enricher,XPath Header Enricher (XML Module)>>
|
||||
* <<mail-namespace,Mail Header Enricher (Mail Module)>>
|
||||
* <<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.
|
||||
|
||||
|
||||
Please go to the adapter specific sections of this reference manual to learn more about those adapters.
|
||||
|
||||
For more information regarding expressions support, please see <<spel>>.
|
||||
For more information regarding expressions support, see "`<<spel>>`".
|
||||
|
||||
[[header-enricher]]
|
||||
==== Header Enricher
|
||||
|
||||
If you only need to add headers to a Message, and they are not dynamically determined by the Message content, then 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 via the `<header-enricher>` element.
|
||||
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">
|
||||
@@ -40,8 +35,9 @@ It is exposed via the `<header-enricher>` element.
|
||||
<int:header name="bar" ref="someBean"/>
|
||||
</int:header-enricher>
|
||||
----
|
||||
====
|
||||
|
||||
The _Header Enricher_ also provides helpful sub-elements to set well-known header names.
|
||||
The header enricher also provides helpful sub-elements to set well known header names, as the following example shows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
@@ -55,25 +51,26 @@ The _Header Enricher_ also provides helpful sub-elements to set well-known heade
|
||||
</int:header-enricher>
|
||||
----
|
||||
|
||||
In the above configuration you can clearly see that for well-known headers such as `errorChannel`, `correlationId`, `priority`, `replyChannel`, `routing-slip` etc., 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.
|
||||
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 `routing-slip` sub-element.
|
||||
See <<routing-slip>> for more information.
|
||||
Starting with version 4.1, the header enricher provides a `routing-slip` sub-element.
|
||||
See "`<<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 _Header Enricher_ allows you to also specify a bean reference using the `ref` and `method` attribute.
|
||||
The specified method will calculate the header value.
|
||||
Let's look at the following configuration:
|
||||
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="foo" method="computeValue" ref="myBean"/>
|
||||
<int:header name="something" method="computeValue" ref="myBean"/>
|
||||
</int:header-enricher>
|
||||
|
||||
<bean id="myBean" class="foo.bar.MyBean"/>
|
||||
<bean id="myBean" class="thing1.thing2.MyBean"/>
|
||||
----
|
||||
|
||||
[source,java]
|
||||
@@ -85,9 +82,11 @@ public class MyBean {
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
You can also configure your POJO as inner bean:
|
||||
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">
|
||||
@@ -96,9 +95,11 @@ You can also configure your POJO as inner bean:
|
||||
</int:header>
|
||||
</int:header-enricher>
|
||||
----
|
||||
====
|
||||
|
||||
as well as point to a Groovy script:
|
||||
You can similarly point to a Groovy script, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:header-enricher input-channel="inputChannel" output-channel="outputChannel">
|
||||
@@ -107,14 +108,17 @@ as well as point to a Groovy script:
|
||||
</int:header>
|
||||
</int:header-enricher>
|
||||
----
|
||||
====
|
||||
|
||||
*SpEL Support*
|
||||
===== SpEL Support
|
||||
|
||||
In Spring Integration 2.0 we have introduced the convenience of the http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html[Spring Expression Language (SpEL)] to help configure many different components.
|
||||
The _Header Enricher_ is one of them.
|
||||
Looking again at the POJO example above, you can see that the computation logic to determine the header value is actually pretty simple.
|
||||
A natural question would be: "is there a simpler way to accomplish this?".
|
||||
In Spring Integration 2.0, we introduced the convenience of the http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html[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]
|
||||
----
|
||||
@@ -123,14 +127,15 @@ That is where SpEL shows its true power.
|
||||
</int:header-enricher>
|
||||
----
|
||||
|
||||
As you can see, by using SpEL for such simple cases, we no longer have to provide a separate class and configure it in the application context.
|
||||
All we need is the _expression_ attribute configured 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.
|
||||
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
|
||||
|
||||
The following are some examples of Java Configuration for header enrichers:
|
||||
The following two examples show how to use Java Configuration for header enrichers:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
@@ -155,14 +160,16 @@ public HeaderEnricher enrichHeaders() {
|
||||
return enricher;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The first adds a single literal header.
|
||||
The second adds two headers - a literal header and one based on a SpEL expression.
|
||||
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
|
||||
|
||||
The following is an example of Java DSL Configuration for a header enricher:
|
||||
The following example shows Java DSL Configuration for a header enricher:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
@@ -174,34 +181,40 @@ public IntegrationFlow enrichHeadersInFlow() {
|
||||
.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 converts existing `replyChannel` and `errorChannel` headers (when they are a `MessageChannel`) to a String and stores the channel(s) 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.
|
||||
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.
|
||||
|
||||
Use of this functionality requires the presence of a `HeaderChannelRegistry` bean.
|
||||
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, simply define a bean with id `integrationHeaderChannelRegistry` and configure the required default delay using a constructor argument (milliseconds).
|
||||
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 `removeOnGet` to `true` on the `<bean/>` definition, and the mapping entry will be removed immediately on first use.
|
||||
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.
|
||||
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 only, and is the equivalent of specifying:
|
||||
This sub-element is a convenience, and is the equivalent of specifying the following configuration:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:reply-channel
|
||||
@@ -211,9 +224,12 @@ This sub-element is a convenience only, and is the equivalent of specifying:
|
||||
expression="@integrationHeaderChannelRegistry.channelToChannelName(headers.errorChannel)"
|
||||
overwrite="true" />
|
||||
----
|
||||
====
|
||||
|
||||
Starting with _version 4.1_, you can now override the registry's configured reaper delay, so the the channel mapping is retained for at least the specified time, regardless of the reaper delay:
|
||||
Starting with version 4.1, you can now override the registry's configured reaper delay so that the 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">
|
||||
@@ -225,38 +241,42 @@ Starting with _version 4.1_, you can now override the registry's configured reap
|
||||
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 2 minutes; in the second case, the time to live is specified in the message header and uses an elvis operator to use 2 minutes if there is no header.
|
||||
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 above, may not be sufficient and payloads themselves may have to be enriched with additional information.
|
||||
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.
|
||||
|
||||
Since Spring Integration 2.1, the Payload Enricher is provided.
|
||||
A Payload Enricher defines an endpoint that passes a `Message` to the exposed request channel and then expects a reply message.
|
||||
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 via the `enricher` element.
|
||||
In order to send request messages, the payload enricher has a `request-channel` attribute that allows you to dispatch messages to a request channel.
|
||||
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 that were sent to the request channel to return, and the Enricher then augments the message's payload with the data provided by the reply message.
|
||||
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 only send a subset of the original payload using the `request-payload-expression` attribute.
|
||||
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 users with a maximum degree of flexibility.
|
||||
Therefore, users are not only able to enrich payloads with direct values from the reply channel's `Message`, but they can use SpEL expressions to extract a subset from that Message, only, or to apply addtional inline transformations, allowing them to further manipulate the data.
|
||||
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 only need to enrich payloads with static values, you don't have to provide the `request-channel` attribute.
|
||||
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 and in many cases you could use a Payload Enricher or a generic Transformer implementation to add additional data to your messages payloads.
|
||||
Thus, 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.
|
||||
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
|
||||
|
||||
Below, please find an overview of all available configuration options that are available for the payload enricher:
|
||||
The following example shows all available configuration options for the payload enricher:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
@@ -278,98 +298,77 @@ Below, please find an overview of all available configuration options that are a
|
||||
</int:enricher>
|
||||
----
|
||||
|
||||
<1> Channel to which a Message will be sent to get the data to use for enrichment.
|
||||
_Optional_.
|
||||
|
||||
|
||||
<2> Lifecycle attribute signaling if this component should be started during Application Context startup.
|
||||
Defaults to true._Optional_.
|
||||
|
||||
|
||||
<3> Id of the underlying bean definition, which is either an `EventDrivenConsumer` or a `PollingConsumer`.
|
||||
_Optional_.
|
||||
|
||||
|
||||
<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 itself is a Polling Consumer for a channel with a queue.
|
||||
_Optional_.
|
||||
|
||||
|
||||
<5> Identifies the Message channel where a Message will be sent after it is being processed by this endpoint._Optional_.
|
||||
|
||||
|
||||
<6> By default the original message's payload will be used as payload that will be send to the `request-channel`.
|
||||
By specifying a SpEL expression as value for the `request-payload-expression` attribute, a subset of the original payload, a header value or any other resolvable SpEL expression can be used as the basis for the payload, that will be 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.foo`,
|
||||
`headers.foobar`,
|
||||
`new java.util.Date()`,
|
||||
`'foo' + 'bar'`.
|
||||
|
||||
|
||||
<7> Channel where a reply Message is expected.
|
||||
This is optional; typically the auto-generated temporary reply channel is sufficient.
|
||||
_Optional_.
|
||||
|
||||
|
||||
<8> Channel to which an `ErrorMessage` will be sent if an `Exception` occurs downstream of the `request-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.
|
||||
This is optional; if it is not set then `Exception` is thrown to the caller.
|
||||
_Optional_.
|
||||
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 such channel may 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 '-1', which may cause the send operation on the `MessageChannel`, depending on the implementation, to block indefinitely.
|
||||
_Optional_.
|
||||
|
||||
|
||||
<10> Boolean value indicating whether any payload that implements `Cloneable` should be cloned prior to sending the Message to the request chanenl for acquiring the enriching data.
|
||||
<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 '-1', which can cause the send operation on the `MessageChannel`, depending on the implementation, to block indefinitely.
|
||||
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.
|
||||
Default is `false`.
|
||||
_Optional_.
|
||||
|
||||
|
||||
<11> Allows you to configure a Message Poller if this endpoint is a Polling Consumer.
|
||||
_Optional_.
|
||||
|
||||
|
||||
<12> Each `property` sub-element provides the name of a property (via the mandatory `name` attribute).
|
||||
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 _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 will coerce the value appropriately (as long as a `PropertyEditor`) exists to handle the conversion.
|
||||
If however, the target payload is a `Map` the entry will be populated with the value without conversion.
|
||||
The `type` attribute allows you to, say, convert a String containing a number to an `Integer` value in the target payload.
|
||||
Starting with _4.1_, you can also specify an optional `null-result-expression` attribute.
|
||||
When the `enricher` returns null, it will be evaluated and the output of the evaluation will be returned instead.
|
||||
|
||||
|
||||
<13> Each `header` sub-element provides the name of a Message header (via the mandatory `name` attribute).
|
||||
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).
|
||||
Note, similar to the `<header-enricher>`, the `<enricher>`'s `header` element has `type` and `overwrite` attributes.
|
||||
However, a difference is that, with the `<enricher>`, the `overwrite` attribute is `true` by default, to be consistent with `<enricher>`'s `<property>` sub-element.
|
||||
Starting with _4.1_, you can also specify an optional `null-result-expression` attribute.
|
||||
When the `enricher` returns null, it will be evaluated and the output of the evaluation will be returned instead.
|
||||
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
|
||||
|
||||
Below, please find several examples of using a Payload Enricher in various situations.
|
||||
This section contains several examples of using a payload enricher in various situations.
|
||||
|
||||
In the following example, a `User` object is passed as the payload of the `Message`.
|
||||
The `User` has several properties but only the `username` is set initially.
|
||||
The Enricher's `request-channel` attribute below is configured to pass the `User` on to the `findUserServiceChannel`.
|
||||
TIP: The code samples shown here are part of the Spring Integration Samples project.
|
||||
See "`<<samples>>`".
|
||||
|
||||
Through the implicitly set `reply-channel` a `User` object is returned and using the `property` sub-element, properties from the reply are extracted and used to enrich the original payload.
|
||||
In the following example, a `User` object is passed as the payload of the `Message`:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:enricher id="findUserEnricher"
|
||||
@@ -379,16 +378,19 @@ Through the implicitly set `reply-channel` a `User` object is returned and using
|
||||
<int:property name="password" expression="payload.password"/>
|
||||
</int:enricher>
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: The code samples shown here, are part of the _Spring Integration Samples_ project.
|
||||
Please feel free to check it out in the <<samples>>.
|
||||
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`.
|
||||
|
||||
_How do I pass only a subset of data to the request channel?_
|
||||
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.
|
||||
|
||||
Using a `request-payload-expression` attribute a single property of the payload can be passed on to the request channel instead of the full message.
|
||||
In the example below on the username property is passed on to the request channel.
|
||||
Keep in mind, that although only the username is passed on, the resulting message send to the request channel will contain the full set of `MessageHeaders`.
|
||||
===== 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"
|
||||
@@ -399,14 +401,15 @@ Keep in mind, that although only the username is passed on, the resulting messag
|
||||
<int:property name="password" expression="payload.password"/>
|
||||
</int:enricher>
|
||||
----
|
||||
====
|
||||
|
||||
_How can I enrich payloads that consist of Collection data?_
|
||||
Keep in mind that, although only the username is passed, the resulting message to the request channel contains the full set of `MessageHeaders`.
|
||||
|
||||
In the following example, instead of a `User` object, a `Map` is passed in.
|
||||
The `Map` contains the username under the map key `username`.
|
||||
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 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"
|
||||
@@ -416,20 +419,28 @@ The reply contains a full `User` object, which is ultimately added to the `Map`
|
||||
<int:property name="user" expression="payload"/>
|
||||
</int:enricher>
|
||||
----
|
||||
====
|
||||
|
||||
_How can I enrich payloads with static information without using a request channel?_
|
||||
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.
|
||||
|
||||
Here is an example that does not use a request channel at all, but solely enriches the message's payload with static values.
|
||||
But please be aware that the word 'static' is used loosely here.
|
||||
You can still use SpEL expressions for setting those values.
|
||||
===== 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="foo"/>
|
||||
<int:property name="user.lastName" value="bar"/>
|
||||
<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.
|
||||
|
||||
@@ -1,39 +1,46 @@
|
||||
[[control-bus]]
|
||||
=== Control Bus
|
||||
|
||||
As described in (EIP), 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 it's possible to send Messages as a means of invoking exposed operations.
|
||||
As described in the http://www.eaipatterns.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, e.g.
|
||||
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 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 executes messages on the input channel as Spring Expression Language expressions.
|
||||
It takes a message, compiles the body to an expression, adds some context, and then executes it.
|
||||
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 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` and/or `@ManagedOperation` annotations.
|
||||
Since those are also used for exposing methods to a JMX MBean registry, it's a convenient by-product (often the same types of operations you want to expose to the Control Bus would be reasonable for exposing via JMX).
|
||||
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.
|
||||
Simply 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:
|
||||
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 and Annotations the Control Bus can be configured as follows:
|
||||
With Java annotations, you can configured the control bus as follows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
@@ -42,9 +49,11 @@ public ExpressionControlBusFactoryBean controlBus() {
|
||||
return new ExpressionControlBusFactoryBean();
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Or, when using Java DSL flow definitions:
|
||||
Similarly, you can configure Java DSL flow definitions as follows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
@@ -54,9 +63,11 @@ public IntegrationFlow controlBusFlow() {
|
||||
.get();
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Or, if you prefer Lambda style with automatic `DirectChannel` creation:
|
||||
If you prefer to use lambdas with automatic `DirectChannel` creation, you can create a control bus as follows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
@@ -64,5 +75,6 @@ public IntegrationFlow controlBus() {
|
||||
return IntegrationFlowDefinition::controlBus;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
In this case, the channel is named `controlBus.input`.
|
||||
|
||||
@@ -1,41 +1,45 @@
|
||||
[[delayer]]
|
||||
=== Delayer
|
||||
|
||||
[[delayer-introduction]]
|
||||
==== Introduction
|
||||
|
||||
A Delayer is a simple endpoint that allows a Message flow to be delayed by a certain interval.
|
||||
When a Message is delayed, the original sender will not block.
|
||||
Instead, the delayed Messages will be 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 will be used for the actual execution of releasing the Messages.
|
||||
Below you will find several examples of configuring a 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 'expression' sub-element) that are used to determine the number of milliseconds that each Message should be delayed.
|
||||
The following delays all messages by 3 seconds:
|
||||
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 per-Message determination of the delay, then you can also provide the SpEL expression using the 'expression' attribute:
|
||||
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:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:delayer id="delayer" input-channel="input" output-channel="output"
|
||||
default-delay="3000" expression="headers['delay']"/>
|
||||
----
|
||||
====
|
||||
|
||||
In the example above, the 3 second delay would only apply when the expression evaluates to _null_ for a given inbound Message.
|
||||
If you only want to apply a delay to Messages that have a valid result of the expression evaluation, then you can use a 'default-delay' of 0 (the default).
|
||||
For any Message that has a delay of 0 (or less), the Message will be sent immediately, on the calling Thread.
|
||||
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.
|
||||
|
||||
The java configuration equivalent of the second example is:
|
||||
The following example shows the Java configuration equivalent of the preceding example:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@ServiceActivator(inputChannel = "input")
|
||||
@@ -48,9 +52,11 @@ public DelayHandler delayer() {
|
||||
return handler;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
and with the Java DSL:
|
||||
The following example shows the Java DSL equivalent of the preceding example:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
@@ -63,42 +69,51 @@ public IntegrationFlow flow() {
|
||||
.get();
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: The XML parser uses a message group id `<beanName>.messageGroupId`.
|
||||
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 will be counted from the current time (e.g.
|
||||
a value of 5000 would delay the Message for at least 5 seconds from the time it is received by the Delayer).
|
||||
With a Date instance, the Message will not be released until the time represented by that Date object.
|
||||
In either case, a value that equates to a non-positive delay, or a Date in the past, will not result in any delay.
|
||||
Instead, it will be 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) will be applied.
|
||||
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 (logged at DEBUG level) and the delayer falls back to the default delay (if any).
|
||||
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 above.
|
||||
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`.
|
||||
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]
|
||||
=====
|
||||
Notice in the example above that 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 above header expression can be specified as `headers.delay`.
|
||||
But, different results are achieved if the header is missing.
|
||||
In the first case, the expression will evaluate to `null`; the second will result in something like:
|
||||
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'
|
||||
----
|
||||
====
|
||||
|
||||
So, 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) to use the _Indexer_ syntax instead of _dot property accessor_ syntax, because detecting the null is faster than catching an exception.
|
||||
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) to use 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: <<namespace-taskscheduler>>.
|
||||
If you want to delegate to a different scheduler, you can provide a reference through the delayer element's 'scheduler' attribute:
|
||||
The default scheduler used by the delayer is the `ThreadPoolTaskScheduler` instance provided by Spring Integration on startup.
|
||||
See "`<<namespace-taskscheduler>>`".
|
||||
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"
|
||||
@@ -107,34 +122,39 @@ If you want to delegate to a different scheduler, you can provide a reference th
|
||||
|
||||
<task:scheduler id="exampleTaskScheduler" pool-size="3"/>
|
||||
----
|
||||
====
|
||||
|
||||
TIP: If you configure an external `ThreadPoolTaskScheduler` you can set on this scheduler property `waitForTasksToCompleteOnShutdown = true`.
|
||||
It allows successful completion of 'delay' tasks, which 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 delayer requires an external scheduler instance and `waitForTasksToCompleteOnShutdown` was deleted; you should use the scheduler's own configuration.
|
||||
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: Also keep in mind `ThreadPoolTaskScheduler` has a property `errorHandler` which can be injected with some implementation of `org.springframework.util.ErrorHandler`.
|
||||
This handler allows to process 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 will 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`.
|
||||
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`.
|
||||
|
||||
[[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 required 'id' attribute of `<delayer>` element.) A delayed message is removed from the `MessageStore` by the scheduled task just before the `DelayHandler` sends the Message to the `output-channel`.
|
||||
If the provided `MessageStore` is persistent (e.g.
|
||||
`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 is used when rescheduling.
|
||||
If a delayed Message remained in the `MessageStore` more than its 'delay', it will be sent immediately after startup.
|
||||
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.)
|
||||
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 `<delayer>` can be enriched with mutually exclusive sub-elements `<transactional>` or `<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 `ReleaseMessageHandler`'s transaction will be rolled back.
|
||||
In this case the delayed Message will remain in the persistent `MessageStore`.
|
||||
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>`.
|
||||
A sample configuration of the `<delayer>` may look like this:
|
||||
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"
|
||||
@@ -150,14 +170,20 @@ A sample configuration of the `<delayer>` may look like this:
|
||||
</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 via a `Control Bus` command:
|
||||
// TODO: It would be good to have an example of the <transactional> element here.
|
||||
|
||||
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, please read <<system-management-chapter>>.
|
||||
NOTE: For more information regarding the message store, JMX, and the control bus, see "`<<system-management-chapter>>`".
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,611 +1,193 @@
|
||||
[[endpoint-summary]]
|
||||
== Endpoint Quick Reference Table
|
||||
|
||||
As discussed in the sections above, Spring Integration provides a number of endpoints used to interface with external systems, file systems etc.
|
||||
The following is a summary of the various endpoints with quick links to the appropriate chapter.
|
||||
As discussed in the earlier sections, Spring Integration provides a number of endpoints used to interface with external systems, file systems, and others.
|
||||
|
||||
To recap, *Inbound Channel Adapters* are used for one-way integration bringing data into the messaging application.
|
||||
*Outbound Channel Adapters* are used for one-way integration to send data out of the messaging application.
|
||||
*Inbound Gateways* are used for a bidirectional integration flow where some other system invokes the messaging application and receives a reply.*Outbound Gateways* are used for a bidirectional integration flow where the messaging application invokes some external service or entity, expecting a result.
|
||||
To recap:
|
||||
|
||||
* Inbound channel adapters are used for one-way integration to bring data into the messaging application.
|
||||
* Outbound channel adapters are used for one-way integration to send data out of the messaging application.
|
||||
* Inbound gateways are used for a bidirectional integration flow, where some other system invokes the messaging application and receives a reply.
|
||||
* Outbound Gateways are used for a bidirectional integration flow, where the messaging application invokes some external service or entity and expects a result.
|
||||
|
||||
The following table summarizes the various endpoints with quick links to the appropriate chapter.
|
||||
|
||||
.Endpoint Quick Reference
|
||||
|
||||
[cols="1,1,1,1,1", options="header"]
|
||||
|===
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| Module
|
||||
|
||||
|
||||
| Inbound Adapter
|
||||
|
||||
|
||||
| Outbound Adapter
|
||||
|
||||
|
||||
| Inbound Gateway
|
||||
|
||||
|
||||
| Outbound Gateway
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *AMQP*
|
||||
|
||||
|
||||
| <<amqp-inbound-channel-adapter>>
|
||||
|
||||
|
||||
| <<amqp-outbound-channel-adapter>>
|
||||
|
||||
|
||||
| <<amqp-inbound-gateway>>
|
||||
|
||||
|
||||
| <<amqp-outbound-gateway>>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *Events*
|
||||
|
||||
|
||||
| <<appevent-inbound>>
|
||||
|
||||
|
||||
| <<appevent-outbound>>
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *Feed*
|
||||
|
||||
|
||||
| <<feed-inbound-channel-adapter>>
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *File*
|
||||
|
||||
|
||||
| <<file-reading>> and <<file-tailing>>
|
||||
|
||||
|
||||
| <<file-writing>>
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| <<file-writing>>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *FTP(S)*
|
||||
|
||||
|
||||
| <<ftp-inbound>>
|
||||
|
||||
|
||||
| <<ftp-outbound>>
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| <<ftp-outbound-gateway>>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *Gemfire*
|
||||
|
||||
|
||||
| <<gemfire-inbound>> and <<gemfire-cq>>
|
||||
|
||||
|
||||
| <<gemfire-outbound>>
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *HTTP*
|
||||
|
||||
|
||||
| <<http-namespace>>
|
||||
|
||||
|
||||
| <<http-namespace>>
|
||||
|
||||
|
||||
| <<http-inbound>>
|
||||
|
||||
|
||||
| <<http-outbound>>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *JDBC*
|
||||
|
||||
|
||||
| <<jdbc-inbound-channel-adapter>> and <<stored-procedure-inbound-channel-adapter>>
|
||||
|
||||
|
||||
| <<jdbc-outbound-channel-adapter>> and <<stored-procedure-outbound-channel-adapter>>
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| <<jdbc-outbound-gateway>> and <<stored-procedure-outbound-gateway>>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *JMS*
|
||||
|
||||
|
||||
| <<jms-inbound-channel-adapter>> and <<jms-message-driven-channel-adapter>>
|
||||
|
||||
|
||||
| <<jms-outbound-channel-adapter>>
|
||||
|
||||
|
||||
| <<jms-inbound-gateway>>
|
||||
|
||||
|
||||
| <<jms-outbound-gateway>>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *JMX*
|
||||
|
||||
|
||||
| <<jmx-notification-listening-channel-adapter>> and <<jmx-attribute-polling-channel-adapter>> and <<tree-polling-channel-adapter>>
|
||||
|
||||
|
||||
| <<jmx-notification-publishing-channel-adapter>> and <<jmx-operation-invoking-channel-adapter>>
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| <<jmx-operation-invoking-outbound-gateway>>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *JPA*
|
||||
|
||||
|
||||
| <<jpa-inbound-channel-adapter>>
|
||||
|
||||
|
||||
| <<jpa-outbound-channel-adapter>>
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| <<jpa-updating-outbound-gateway>> and <<jpa-retrieving-outbound-gateway>>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *Mail*
|
||||
|
||||
|
||||
| <<mail-inbound>>
|
||||
|
||||
|
||||
| <<mail-outbound>>
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *MongoDB*
|
||||
|
||||
|
||||
| <<mongodb-inbound-channel-adapter>>
|
||||
|
||||
|
||||
| <<mongodb-outbound-channel-adapter>>
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *MQTT*
|
||||
|
||||
|
||||
| <<mqtt-inbound>>
|
||||
|
||||
|
||||
| <<mqtt-outbound>>
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *Redis*
|
||||
|
||||
|
||||
| <<redis-inbound-channel-adapter>> and <<redis-queue-inbound-channel-adapter>> and <<redis-store-inbound-channel-adapter>>
|
||||
|
||||
|
||||
| <<redis-outbound-channel-adapter>> and <<redis-queue-outbound-channel-adapter>> and <<redis-store-outbound-channel-adapter>>
|
||||
|
||||
|
||||
| <<redis-queue-inbound-gateway>>
|
||||
|
||||
|
||||
| <<redis-outbound-gateway>> and <<redis-queue-outbound-gateway>>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *Resource*
|
||||
|
||||
|
||||
| <<resource-inbound-channel-adapter>>
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *RMI*
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| <<rmi-inbound>>
|
||||
|
||||
|
||||
| <<rmi-outbound>>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *SFTP*
|
||||
|
||||
|
||||
| <<sftp-inbound>>
|
||||
|
||||
|
||||
| <<sftp-outbound>>
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| <<sftp-outbound-gateway>>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *STOMP*
|
||||
|
||||
|
||||
| <<stomp-inbound-adapter>>
|
||||
|
||||
|
||||
| <<stomp-outbound-adapter>>
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *Stream*
|
||||
|
||||
|
||||
| <<stream-reading>>
|
||||
|
||||
|
||||
| <<stream-writing>>
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
|
||||
|
||||
| *Syslog*
|
||||
|
||||
|
||||
| <<syslog-inbound-adapter>>
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *TCP*
|
||||
|
||||
|
||||
| <<tcp-adapters>>
|
||||
|
||||
|
||||
| <<tcp-adapters>>
|
||||
|
||||
|
||||
| <<tcp-gateways>>
|
||||
|
||||
|
||||
| <<tcp-gateways>>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *Twitter*
|
||||
|
||||
|
||||
| <<twitter-inbound>>
|
||||
|
||||
|
||||
| <<twitter-outbound>>
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| <<twitter-sog>>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *UDP*
|
||||
|
||||
|
||||
| <<udp-adapters>>
|
||||
|
||||
|
||||
| <<udp-adapters>>
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *Web Services*
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| <<webservices-inbound>>
|
||||
|
||||
|
||||
| <<webservices-outbound>>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *Web Sockets*
|
||||
|
||||
|
||||
| <<web-socket-inbound-adapter>>
|
||||
|
||||
|
||||
| <<web-socket-outbound-adapter>>
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| *XMPP*
|
||||
|
||||
|
||||
| <<xmpp-messages>> and <<xmpp-presence>>
|
||||
|
||||
|
||||
| <<xmpp-messages>> and <<xmpp-presence>>
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
| N
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|===
|
||||
|
||||
In addition, as discussed in <<spring-integration-core-messaging>>, endpoints are provided for interfacing with Plain Old Java Objects (POJOs).
|
||||
As discussed in <<channel-adapter>>, the `<int:inbound-channel-adapter>` allows polling a java method for data; the `<int:outbound-channel-adapter>` allows sending data to a `void` method, and as discussed in <<gateway>>, the `<int:gateway>` allows any Java program to invoke a messaging flow.
|
||||
Each of these without requiring any source level dependencies on Spring Integration.
|
||||
The equivalent of an outbound gateway in this context would be to use a <<service-activator>> to invoke a method that returns an Object of some kind.
|
||||
In addition, as discussed in "`<<spring-integration-core-messaging>>`", Spring Integration provides endpoints for interfacing with Plain Old Java Objects (POJOs).
|
||||
As discussed in "`<<channel-adapter>>`", the `<int:inbound-channel-adapter>` element lets you poll a Java method for data.
|
||||
The `<int:outbound-channel-adapter>` element lets you send data to a `void` method.
|
||||
As discussed in "`<<gateway>>`", the `<int:gateway>` element lets any Java program invoke a messaging flow.
|
||||
Each of these works without requiring any source-level dependencies on Spring Integration.
|
||||
The equivalent of an outbound gateway in this context is using a service activator (see <<service-activator>>) to invoke a method that returns an `Object` of some kind.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,27 +1,26 @@
|
||||
[[applicationevent]]
|
||||
== Spring ApplicationEvent Support
|
||||
== Spring `ApplicationEvent` Support
|
||||
|
||||
Spring Integration provides support for inbound and outbound `ApplicationEvents` as defined by the underlying Spring Framework.
|
||||
For more information about Spring's support for events and listeners, refer to the http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#context-functionality-events[Spring Reference Manual].
|
||||
Spring Integration provides support for inbound and outbound `ApplicationEvents`, as defined by the underlying Spring Framework.
|
||||
For more information about Spring's support for events and listeners, see the http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#context-functionality-events[Spring Reference Manual].
|
||||
|
||||
[[appevent-inbound]]
|
||||
=== Receiving Spring Application Events
|
||||
|
||||
To receive events and send them to a channel, simply define an instance of Spring Integration's `ApplicationEventListeningMessageProducer`.
|
||||
To receive events and send them to a channel, you can define an instance of Spring Integration's `ApplicationEventListeningMessageProducer`.
|
||||
This class is an implementation of Spring's `ApplicationListener` interface.
|
||||
By default it will pass all received events as Spring Integration Messages.
|
||||
To limit based on the type of event, configure the list of event types that you want to receive with the 'eventTypes' property.
|
||||
If a received event has a Message instance as its 'source', then that will be passed as-is.
|
||||
Otherwise, if a SpEL-based "payloadExpression" has been provided, that will be evaluated against the ApplicationEvent instance.
|
||||
If the event's source is not a Message instance and no "payloadExpression" has been provided, then the ApplicationEvent itself will be passed as the payload.
|
||||
By default, it passes all received events as Spring Integration messages.
|
||||
To limit based on the type of event, you can use the 'eventTypes' property to configure the list of event types that you want to receive.
|
||||
If a received event has a `Message` instance as its 'source', that `Message` is passed as-is.
|
||||
Otherwise, if a SpEL-based `payloadExpression` has been provided, that is evaluated against the `ApplicationEvent` instance.
|
||||
If the event's source is not a `Message` instance and no `payloadExpression` has been provided, the `ApplicationEvent` itself is passed as the payload.
|
||||
|
||||
Starting with _version 4.2_ the `ApplicationEventListeningMessageProducer` implements `GenericApplicationListener`
|
||||
and can be configured to accept not only `ApplicationEvent` types, but any type for treating _payload events_
|
||||
which are supported since Spring Framework 4.2, too.
|
||||
Starting with version 4.2, the `ApplicationEventListeningMessageProducer` implements `GenericApplicationListener` and can be configured to accept not only `ApplicationEvent` types but any type for treating payload events (which are also supported since Spring Framework 4.2).
|
||||
When the accepted event is an instance of `PayloadApplicationEvent`, its `payload` is used for the message to send.
|
||||
|
||||
For convenience namespace support is provided to configure an `ApplicationEventListeningMessageProducer` via the _inbound-channel-adapter_ element.
|
||||
For convenience, namespace support is provided to configure an `ApplicationEventListeningMessageProducer` with the `inbound-channel-adapter` element, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-event:inbound-channel-adapter channel="eventChannel"
|
||||
@@ -30,30 +29,34 @@ For convenience namespace support is provided to configure an `ApplicationEvent
|
||||
|
||||
<int:publish-subscribe-channel id="eventChannel"/>
|
||||
----
|
||||
====
|
||||
|
||||
In the above example, all Application Context events that match one of the types specified by the 'event-types' (optional) attribute will be delivered as Spring Integration Messages to the Message Channel named 'eventChannel'.
|
||||
If a downstream component throws an exception, a MessagingException containing the failed message and exception will be sent to the channel named 'eventErrorChannel'.
|
||||
If no "error-channel" is specified and the downstream channels are synchronous, the Exception will be propagated to the caller.
|
||||
In the preceding example, all application context events that match one of the types specified by the 'event-types' (optional) attribute are delivered as Spring Integration messages to the message channel named 'eventChannel'.
|
||||
If a downstream component throws an exception, a `MessagingException` that contains the failed message and exception is sent to the channel named 'eventErrorChannel'.
|
||||
If no `error-channel` is specified and the downstream channels are synchronous, the exception is propagated to the caller.
|
||||
|
||||
[[appevent-outbound]]
|
||||
=== Sending Spring Application Events
|
||||
|
||||
To send Spring `ApplicationEvents`, create an instance of the `ApplicationEventPublishingMessageHandler` and register it within an endpoint.
|
||||
This implementation of the `MessageHandler` interface also implements Spring's `ApplicationEventPublisherAware` interface and thus acts as a bridge between Spring Integration Messages and `ApplicationEvents`.
|
||||
This implementation of the `MessageHandler` interface also implements Spring's `ApplicationEventPublisherAware` interface and consequently acts as a bridge between Spring Integration messages and `ApplicationEvents`.
|
||||
|
||||
For convenience namespace support is provided to configure an `ApplicationEventPublishingMessageHandler` via the _outbound-channel-adapter_ element.
|
||||
For convenience, namespace support is provided to configure an `ApplicationEventPublishingMessageHandler` with the `outbound-channel-adapter` element, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:channel id="eventChannel"/>
|
||||
|
||||
<int-event:outbound-channel-adapter channel="eventChannel"/>
|
||||
----
|
||||
====
|
||||
|
||||
If you are using a PollableChannel (e.g., Queue), you can also provide _poller_ as a sub-element of the _outbound-channel-adapter_ element.
|
||||
You can also optionally provide a _task-executor_ reference for that poller.
|
||||
The following example demonstrates both.
|
||||
If you use a `PollableChannel` (such as a `Queue`), you can also provide `poller` as a child element of the `outbound-channel-adapter` element.
|
||||
You can also optionally provide a `task-executor` reference for that poller.
|
||||
The following example demonstrates both:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:channel id="eventChannel">
|
||||
@@ -66,11 +69,11 @@ The following example demonstrates both.
|
||||
|
||||
<task:executor id="executor" pool-size="5"/>
|
||||
----
|
||||
====
|
||||
|
||||
In the above example, all messages sent to the 'eventChannel' channel will be published as ApplicationEvents to any relevant ApplicationListener instances that are registered within the same Spring ApplicationContext.
|
||||
If the payload of the Message is an ApplicationEvent, it will be passed as-is.
|
||||
Otherwise the Message itself will be wrapped in a `MessagingEvent` instance.
|
||||
In the preceding example, all messages sent to the 'eventChannel' channel are published as `ApplicationEvent` instances to any relevant `ApplicationListener` instances that are registered within the same Spring `ApplicationContext`.
|
||||
If the payload of the message is an `ApplicationEvent`, it is passed as-is.
|
||||
Otherwise, the message itself is wrapped in a `MessagingEvent` instance.
|
||||
|
||||
Starting with _version 4.2_ the `ApplicationEventPublishingMessageHandler` (`<int-event:outbound-channel-adapter>`)
|
||||
can be configured with the `publish-payload` boolean attribute to publish to the application context `payload` as is,
|
||||
Starting with version 4.2, you can configure the `ApplicationEventPublishingMessageHandler` (`<int-event:outbound-channel-adapter>`) with the `publish-payload` boolean attribute to publish to the application context `payload` as is,
|
||||
instead of wrapping it to a `MessagingEvent` instance.
|
||||
|
||||
@@ -1,29 +1,31 @@
|
||||
[[feed]]
|
||||
== Feed Adapter
|
||||
|
||||
Spring Integration provides support for Syndication via Feed Adapters.
|
||||
Spring Integration provides support for syndication through feed adapters.
|
||||
The implementation is based on the https://rometools.github.io/rome/[ROME Framework].
|
||||
|
||||
[[feed-intro]]
|
||||
=== Introduction
|
||||
Web syndication is a way to publish material such as news stories, press releases, blog posts, and other items typically available on a website but also made available in a feed format such as RSS or ATOM.
|
||||
|
||||
Web syndication is a form of publishing material such as news stories, press releases, blog posts, and other items typically available on a website but also made available in a feed format such as RSS or ATOM.
|
||||
|
||||
Spring integration provides support for Web Syndication via its 'feed' adapter and provides convenient namespace-based configuration for it.
|
||||
Spring integration provides support for web syndication through its 'feed' adapter and provides convenient namespace-based configuration for it.
|
||||
To configure the 'feed' namespace, include the following elements within the headers of your XML configuration file:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
xmlns:int-feed="http://www.springframework.org/schema/integration/feed"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration/feed
|
||||
http://www.springframework.org/schema/integration/feed/spring-integration-feed.xsd"
|
||||
----
|
||||
====
|
||||
|
||||
[[feed-inbound-channel-adapter]]
|
||||
=== Feed Inbound Channel Adapter
|
||||
|
||||
The only adapter that is really needed to provide support for retrieving feeds is an _inbound channel adapter_.
|
||||
This allows you to subscribe to a particular URL.
|
||||
Below is an example configuration:
|
||||
The only adapter you really need to provide support for retrieving feeds is an inbound channel adapter.
|
||||
It lets you subscribe to a particular URL.
|
||||
The following example shows a possible configuration:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-feed:inbound-channel-adapter id="feedAdapter"
|
||||
@@ -32,43 +34,45 @@ Below is an example configuration:
|
||||
<int:poller fixed-rate="10000" max-messages-per-poll="100" />
|
||||
</int-feed:inbound-channel-adapter>
|
||||
----
|
||||
====
|
||||
|
||||
In the above configuration, we are subscribing to a URL identified by the `url` attribute.
|
||||
In the preceding configuration, we are subscribing to a URL identified by the `url` attribute.
|
||||
|
||||
As news items are retrieved they will be converted to Messages and sent to a channel identified by the `channel` attribute.
|
||||
The payload of each message will be a `com.sun.syndication.feed.synd.SyndEntry` instance.
|
||||
That encapsulates various data about a news item (content, dates, authors, etc.).
|
||||
As news items are retrieved, they are converted to messages and sent to a channel identified by the `channel` attribute.
|
||||
The payload of each message is a `com.sun.syndication.feed.synd.SyndEntry` instance.
|
||||
Each one encapsulates various data about a news item (content, dates, authors, and other details).
|
||||
|
||||
You can also see that the _Inbound Feed Channel Adapter_ is a Polling Consumer.
|
||||
That means you have to provide a poller configuration.
|
||||
However, one important thing you must understand with regard to Feeds is that its inner-workings are slightly different then most other poling consumers.
|
||||
When an Inbound Feed adapter is started, it does the first poll and receives a `com.sun.syndication.feed.synd.SyndEntryFeed` instance.
|
||||
That is an object that contains multiple `SyndEntry` objects.
|
||||
Each entry is stored in the local entry queue and is released based on the value in the `max-messages-per-poll` attribute such that each Message will contain a single entry.
|
||||
If during retrieval of the entries from the entry queue the queue had become empty, the adapter will attempt to update the Feed thereby populating the queue with more entries (`SyndEntry` instances) if available.
|
||||
Otherwise the next attempt to poll for a feed will be determined by the trigger of the poller (e.g., every 10 seconds in the above configuration).
|
||||
The inbound feed channel adapter is a polling consumer.
|
||||
That means that you must provide a poller configuration.
|
||||
However, one important thing you must understand with regard to a feed is that its inner workings are slightly different then most other polling consumers.
|
||||
When an inbound feed adapter is started, it does the first poll and receives a `com.sun.syndication.feed.synd.SyndEntryFeed` instance.
|
||||
That object contains multiple `SyndEntry` objects.
|
||||
Each entry is stored in the local entry queue and is released based on the value in the `max-messages-per-poll` attribute, such that each message contains a single entry.
|
||||
If, during retrieval of the entries from the entry queue, the queue has become empty, the adapter attempts to update the feed, thereby populating the queue with more entries (`SyndEntry` instances), if any are available.
|
||||
Otherwise the next attempt to poll for a feed is determined by the trigger of the poller (every ten seconds in the preceding configuration).
|
||||
|
||||
_Duplicate Entries_
|
||||
=== Duplicate Entries
|
||||
|
||||
Polling for a Feed might result in entries that have already been processed ("I already read that news item, why are you showing it to me again?").
|
||||
Polling for a feed can result in entries that have already been processed ("`I already read that news item, why are you showing it to me again?`").
|
||||
Spring Integration provides a convenient mechanism to eliminate the need to worry about duplicate entries.
|
||||
Each feed entry will have a _published date_ field.
|
||||
Every time a new `Message` is generated and sent, Spring Integration will store the value of the latest _published date_ in an instance of the `MetadataStore` strategy (<<metadata-store>>).
|
||||
Each feed entry has a "`published date`" field.
|
||||
Every time a new `Message` is generated and sent, Spring Integration stores the value of the latest published date in an instance of the `MetadataStore` strategy (see "`<<metadata-store>>`").
|
||||
|
||||
NOTE: The key used to persist the latest _published date_ is the value of the (required) `id` attribute of the Feed Inbound Channel Adapter component plus the `feedUrl` (if any) from the adapter's configuration.
|
||||
NOTE: The key used to persist the latest published date is the value of the (required) `id` attribute of the feed inbound channel adapter component plus the `feedUrl` (if any) from the adapter's configuration.
|
||||
|
||||
_Other Options_
|
||||
=== Other Options
|
||||
|
||||
Starting with _version 5.0_, the deprecated `com.rometools.fetcher.FeedFetcher` option has been removed and an overloaded `FeedEntryMessageSource` constructor for an `org.springframework.core.io.Resource` is provided.
|
||||
This is useful when Feed source isn't an HTTP endpoint, but any other resource, local or remote on FTP, for example.
|
||||
In the `FeedEntryMessageSource` logic such a resource (or provided `URL`) is parsed by the `SyndFeedInput` to the `SyndFeed` object for processing mentioned above.
|
||||
A customized `SyndFeedInput` (for example with the `allowDoctypes` option) instance also can be injected to the `FeedEntryMessageSource`.
|
||||
Starting with version 5.0, the deprecated `com.rometools.fetcher.FeedFetcher` option has been removed and an overloaded `FeedEntryMessageSource` constructor for an `org.springframework.core.io.Resource` is provided.
|
||||
This is useful when the feed source is not an HTTP endpoint but is any other resource (such as local or remote on FTP).
|
||||
In the `FeedEntryMessageSource` logic, such a resource (or provided `URL`) is parsed by the `SyndFeedInput` to the `SyndFeed` object for the processing mentioned earlier.
|
||||
You can also inject a customized `SyndFeedInput` (for example, with the `allowDoctypes` option) instance into the `FeedEntryMessageSource`.
|
||||
|
||||
[[feed-java-configuration]]
|
||||
=== Java DSL and Annotation configuration
|
||||
=== Java DSL Configuration
|
||||
|
||||
The following Spring Boot application provides an example of configuring the Inbound Adapter using the Java DSL:
|
||||
The following Spring Boot application shows an example of how to configure the inbound adapter with the Java DSL:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@@ -102,3 +106,4 @@ public class FeedJavaApplication {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,17 +1,17 @@
|
||||
[[filter]]
|
||||
=== Filter
|
||||
|
||||
[[filter-introduction]]
|
||||
==== Introduction
|
||||
Message filters are used to decide whether a `Message` should be passed along or dropped based on some criteria, such as a message header value or message content itself.
|
||||
Therefore, a message filter is similar to a router, except that, for each message received from the filter's input channel, that same message may or may not be sent to the filter's output channel.
|
||||
Unlike the router, it makes no decision regarding which message channel to send the message to but decides only whether to send the message at all.
|
||||
|
||||
Message Filters are used to decide whether a Message should be passed along or dropped based on some criteria such as a Message Header value or Message content itself.
|
||||
Therefore, a Message Filter is similar to a router, except that for each Message received from the filter's input channel, that same Message may or may not be sent to the filter's output channel.
|
||||
Unlike the router, it makes no decision regarding _which_ Message Channel to send the Message to but only decides _whether_ to send.
|
||||
NOTE: As we describe later in this section, the filter also supports a discard channel.
|
||||
In certain cases, it can play the role of a very simple router (or "`switch`"), based on a boolean condition.
|
||||
|
||||
NOTE: As you will see momentarily, the Filter also supports a discard channel, so in certain cases it _can_ play the role of a very simple router (or "switch") based on a boolean condition.
|
||||
In Spring Integration, you can configure a message filter as a message endpoint that delegates to an implementation of the `MessageSelector` interface.
|
||||
That interface is itself quite simple, as the following listing shows:
|
||||
|
||||
In Spring Integration, a Message Filter may be configured as a Message Endpoint that delegates to an implementation of the `MessageSelector` interface.
|
||||
That interface is itself quite simple:
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public interface MessageSelector {
|
||||
@@ -20,36 +20,43 @@ public interface MessageSelector {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The `MessageFilter` constructor accepts a selector instance:
|
||||
The `MessageFilter` constructor accepts a selector instance, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
MessageFilter filter = new MessageFilter(someSelector);
|
||||
----
|
||||
====
|
||||
|
||||
In combination with the namespace and SpEL, very powerful filters can be configured with very little java code.
|
||||
[[filter-config]]
|
||||
==== Configuring Filter
|
||||
In combination with the namespace and SpEL, you can configure powerful filters with very little Java code.
|
||||
|
||||
[[filter-xml]]
|
||||
===== Configuring a Filter with XML
|
||||
==== Configuring a Filter with XML
|
||||
|
||||
The <filter> element is used to create a Message-selecting endpoint.
|
||||
In addition to `input-channel` and `output-channel` attributes, it requires a `ref`.
|
||||
The `ref` may point to a `MessageSelector` implementation:
|
||||
You can use the `<filter>` element is used to create a message-selecting endpoint.
|
||||
In addition to `input-channel` and `output-channel` attributes, it requires a `ref` attribute.
|
||||
The `ref` can point to a `MessageSelector` implementation, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:filter input-channel="input" ref="selector" output-channel="output"/>
|
||||
|
||||
<bean id="selector" class="example.MessageSelectorImpl"/>
|
||||
----
|
||||
====
|
||||
|
||||
Alternatively, the `method` attribute can be added at which point the `ref` may refer to any object.
|
||||
The referenced method may expect either the `Message` type or the payload type of inbound Messages.
|
||||
Alternatively, you can add the `method` attribute.
|
||||
In that case, the `ref` attribute may refer to any object.
|
||||
The referenced method may expect either the `Message` type or the payload type of inbound messages.
|
||||
The method must return a boolean value.
|
||||
If the method returns 'true', the Message _will_ be sent to the output-channel.
|
||||
If the method returns 'true', the message is sent to the output channel.
|
||||
The following example shows how to configure a filter that uses the `method` attribute:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:filter input-channel="input" output-channel="output"
|
||||
@@ -58,67 +65,81 @@ If the method returns 'true', the Message _will_ be sent to the output-channel.
|
||||
<bean id="exampleObject" class="example.SomeObject"/>
|
||||
----
|
||||
|
||||
If the selector or adapted POJO method returns `false`, there are a few settings that control the handling of the rejected Message.
|
||||
By default (if configured like the example above), rejected Messages will be silently dropped.
|
||||
If rejection should instead result in an error condition, then set the `throw-exception-on-rejection` attribute to `true`:
|
||||
====
|
||||
If the selector or adapted POJO method returns `false`, a few settings control the handling of the rejected message.
|
||||
By default (if configured as in the preceding example), rejected messages are silently dropped.
|
||||
If rejection should instead result in an error condition, set the `throw-exception-on-rejection` attribute to `true`, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:filter input-channel="input" ref="selector"
|
||||
output-channel="output" throw-exception-on-rejection="true"/>
|
||||
----
|
||||
====
|
||||
|
||||
If you want rejected messages to be routed to a specific channel, provide that reference as the `discard-channel`:
|
||||
If you want rejected messages to be routed to a specific channel, provide that reference as the `discard-channel`, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:filter input-channel="input" ref="selector"
|
||||
output-channel="output" discard-channel="rejectedMessages"/>
|
||||
----
|
||||
====
|
||||
|
||||
Also see <<advising-filters>>.
|
||||
See also "`<<advising-filters>>`".
|
||||
|
||||
NOTE: Message Filters are commonly used in conjunction with a Publish Subscribe Channel.
|
||||
Many filter endpoints may be subscribed to the same channel, and they decide whether or not to pass the Message to the next endpoint which could be any of the supported types (e.g.
|
||||
Service Activator).
|
||||
This provides a _reactive_ alternative to the more _proactive_ approach of using a Message Router with a single Point-to-Point input channel and multiple output channels.
|
||||
NOTE: Message filters are commonly used in conjunction with a publish-subscribe channel.
|
||||
Many filter endpoints may be subscribed to the same channel, and they decide whether or not to pass the message to the next endpoint, which could be any of the supported types (such as a service activator).
|
||||
This provides a reactive alternative to the more proactive approach of using a message router with a single point-to-point input channel and multiple output channels.
|
||||
|
||||
Using a `ref` attribute is generally recommended if the custom filter implementation is referenced in other `<filter>` definitions.
|
||||
However if the custom filter implementation is scoped to a single `<filter>` element, provide an inner bean definition:
|
||||
We recommend using a `ref` attribute if the custom filter implementation is referenced in other `<filter>` definitions.
|
||||
However, if the custom filter implementation is scoped to a single `<filter>` element, you should provide an inner bean definition, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:filter method="someMethod" input-channel="inChannel" output-channel="outChannel">
|
||||
<beans:bean class="org.foo.MyCustomFilter"/>
|
||||
</filter>
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: Using both the `ref` attribute and an inner handler definition in the same `<filter>` configuration is not allowed, as it creates an ambiguous condition, and an Exception will be thrown.
|
||||
NOTE: Using both the `ref` attribute and an inner handler definition in the same `<filter>` configuration is not allowed, as it creates an ambiguous condition and throws an exception.
|
||||
|
||||
IMPORTANT: If the "ref" attribute references a bean that extends `MessageFilter` (such as filters provided by the framework itself), the configuration is optimized by injecting the output channel into the filter bean directly.
|
||||
In this case, each "ref" must be to a separate bean instance (or a `prototype`-scoped bean), or use the inner `<bean/>` configuration type.
|
||||
However, this optimization only applies if you don't provide any filter-specific attributes in the filter XML definition.
|
||||
If you inadvertently reference the same message handler from multiple beans, you will get a configuration exception.
|
||||
IMPORTANT: If the `ref` attribute references a bean that extends `MessageFilter` (such as filters provided by the framework itself), the configuration is optimized by injecting the output channel into the filter bean directly.
|
||||
In this case, each `ref` must be to a separate bean instance (or a `prototype`-scoped bean) or use the inner `<bean/>` configuration type.
|
||||
However, this optimization applies only if you do not provide any filter-specific attributes in the filter XML definition.
|
||||
If you inadvertently reference the same message handler from multiple beans, you get a configuration exception.
|
||||
|
||||
With the introduction of SpEL support, Spring Integration added the `expression` attribute to the filter element.
|
||||
It can be used to avoid Java entirely for simple filters.
|
||||
It can be used to avoid Java entirely for simple filters, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:filter input-channel="input" expression="payload.equals('nonsense')"/>
|
||||
----
|
||||
====
|
||||
|
||||
The string passed as the expression attribute will be evaluated as a SpEL expression with the Message available in the evaluation context.
|
||||
If it is necessary to include the result of an expression in the scope of the application context you can use the #{} notation as defined in the http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html#expressions-beandef[SpEL reference documentation].
|
||||
The string passed as the value of the expression attribute is evaluated as a SpEL expression with the message available in the evaluation context.
|
||||
If you must include the result of an expression in the scope of the application context, you can use the `#{}` notation, as defined in the http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html#expressions-beandef[SpEL reference documentation], as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:filter input-channel="input"
|
||||
expression="payload.matches(#{filterPatterns.nonsensePattern})"/>
|
||||
----
|
||||
====
|
||||
|
||||
If the Expression itself needs to be dynamic, then an 'expression' sub-element may be used.
|
||||
That provides a level of indirection for resolving the Expression by its key from an ExpressionSource.
|
||||
That is a strategy interface that you can implement directly, or you can rely upon a version available in Spring Integration that loads Expressions from a "resource bundle" and can check for modifications after a given number of seconds.
|
||||
All of this is demonstrated in the following configuration sample where the Expression could be reloaded within one minute if the underlying file had been modified.
|
||||
If the ExpressionSource bean is named "expressionSource", then it is not necessary to provide the` source` attribute on the <expression> element, but in this case it's shown for completeness.
|
||||
If the expression itself needs to be dynamic, you can use an 'expression' sub-element.
|
||||
That provides a level of indirection for resolving the expression by its key from an `ExpressionSource`.
|
||||
That is a strategy interface that you can implement directly, or you can rely upon a version available in Spring Integration that loads expressions from a "`resource bundle`" and can check for modifications after a given number of seconds.
|
||||
All of this is demonstrated in the following configuration example, where the expression could be reloaded within one minute if the underlying file had been modified:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:filter input-channel="input" output-channel="output">
|
||||
@@ -130,24 +151,29 @@ If the ExpressionSource bean is named "expressionSource", then it is not necessa
|
||||
<beans:property name="basename" value="config/integration/expressions"/>
|
||||
<beans:property name="cacheSeconds" value="60"/>
|
||||
</beans:bean>
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
Then, the 'config/integration/expressions.properties' file (or any more specific version with a locale extension to be resolved in the typical way that resource-bundles are loaded) would contain a key/value pair:
|
||||
[source,xml]
|
||||
If the `ExpressionSource` bean is named `expressionSource`, you need not provide the` source` attribute on the `<expression>` element. However, in the preceding example, we show it for completeness.
|
||||
|
||||
The 'config/integration/expressions.properties' file (or any more-specific version with a locale extension to be resolved in the typical way that resource-bundles are loaded) can contain a key/value pair, as the following example shows:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
filterPatterns.example=payload > 100
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: All of these examples that use `expression` as an attribute or sub-element can also be applied within transformer, router, splitter, service-activator, and header-enricher elements.
|
||||
Of course, the semantics/role of the given component type would affect the interpretation of the evaluation result in the same way that the return value of a method-invocation would be interpreted.
|
||||
For example, an expression can return Strings that are to be treated as Message Channel names by a router component.
|
||||
However, the underlying functionality of evaluating the expression against the Message as the root object, and resolving bean names if prefixed with '@' is consistent across all of the core EIP components within Spring Integration.
|
||||
The semantics and role of the given component type would affect the interpretation of the evaluation result, in the same way that the return value of a method-invocation would be interpreted.
|
||||
For example, an expression can return strings that are to be treated as message channel names by a router component.
|
||||
However, the underlying functionality of evaluating the expression against the message as the root object and resolving bean names if prefixed with '@' is consistent across all of the core EIP components within Spring Integration.
|
||||
|
||||
[[filter-annotations]]
|
||||
===== Configuring a Filter with Annotations
|
||||
==== Configuring a Filter with Annotations
|
||||
|
||||
A filter configured using annotations would look like this.
|
||||
The following example shows how to configure a filter by using annotations:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -160,12 +186,12 @@ public class PetFilter {
|
||||
}
|
||||
----
|
||||
|
||||
<1> An annotation indicating that this method shall be used as a filter.
|
||||
Must be specified if this class will be used as a filter.
|
||||
<1> An annotation indicating that this method is to be used as a filter.
|
||||
It must be specified if this class is to be used as a filter.
|
||||
|
||||
|
||||
All of the configuration options provided by the xml element are also available for the `@Filter` annotation.
|
||||
All of the configuration options provided by the XML element are also available for the `@Filter` annotation.
|
||||
|
||||
The filter can be either referenced explicitly from XML or, if the `@MessageEndpoint` annotation is defined on the class, detected automatically through classpath scanning.
|
||||
|
||||
Also see <<advising-with-annotations>>.
|
||||
See also "`<<advising-with-annotations>>`".
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,18 +1,20 @@
|
||||
[[gateway]]
|
||||
=== Messaging Gateways
|
||||
|
||||
The primary purpose of a Gateway is to hide the messaging API provided by Spring Integration.
|
||||
It allows your application's business logic to be completely unaware of the Spring Integration API and using a generic Gateway, your code interacts instead with a simple interface, only.
|
||||
A gateway hides the messaging API provided by Spring Integration.
|
||||
It lets your application's business logic be unaware of the Spring Integration API.
|
||||
By using a generic Gateway, your code interacts with only a simple interface.
|
||||
|
||||
[[gateway-proxy]]
|
||||
==== Enter the GatewayProxyFactoryBean
|
||||
==== Enter the `GatewayProxyFactoryBean`
|
||||
|
||||
As mentioned above, it would be great to have no dependency on the Spring Integration API at all - including the gateway class.
|
||||
For that reason, Spring Integration provides the `GatewayProxyFactoryBean` that generates a proxy for any interface and internally invokes the gateway methods shown below.
|
||||
Using dependency injection you can then expose the interface to your business methods.
|
||||
As mentioned earlier, it would be great to have no dependency on the Spring Integration API -- including the gateway class.
|
||||
For that reason, Spring Integration provides the `GatewayProxyFactoryBean`, which generates a proxy for any interface and internally invokes the gateway methods shown below.
|
||||
By using dependency injection, you can then expose the interface to your business methods.
|
||||
|
||||
Here is an example of an interface that can be used to interact with Spring Integration:
|
||||
The following example shows an interface that can be used to interact with Spring Integration:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
package org.cafeteria;
|
||||
@@ -23,12 +25,15 @@ public interface Cafe {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[[gateway-namespace]]
|
||||
==== Gateway XML Namespace Support
|
||||
|
||||
Namespace support is also provided which allows you to configure such an interface as a service as demonstrated by the following example.
|
||||
Namespace support is also provided.
|
||||
It lets you configure an interface as a service, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:gateway id="cafeService"
|
||||
@@ -37,40 +42,52 @@ Namespace support is also provided which allows you to configure such an interfa
|
||||
default-reply-timeout="10000"
|
||||
default-reply-channel="replyChannel"/>
|
||||
----
|
||||
====
|
||||
|
||||
With this configuration defined, the "cafeService" can now be injected into other beans, and the code that invokes the methods on that proxied instance of the Cafe interface has no awareness of the Spring Integration API.
|
||||
The general approach is similar to that of Spring Remoting (RMI, HttpInvoker, etc.).
|
||||
See the "Samples" Appendix for an example that uses this "gateway" element (in the Cafe demo).
|
||||
With this configuration defined, the `cafeService` can now be injected into other beans, and the code that invokes the methods on that proxied instance of the `Cafe` interface has no awareness of the Spring Integration API.
|
||||
The general approach is similar to that of Spring Remoting (RMI, HttpInvoker, and so on).
|
||||
See the <<samples,"`Samples`">> Appendix for an example that uses the `gateway` element (in the Cafe demo).
|
||||
|
||||
The defaults in the configuration above are applied to all methods on the gateway interface; if a reply timeout is not
|
||||
specified, the calling thread will wait indefinitely for a reply.
|
||||
See <<gateway-no-response>>.
|
||||
The defaults in the preceding configuration are applied to all methods on the gateway interface.
|
||||
If a reply timeout is not specified, the calling thread waits indefinitely for a reply.
|
||||
See "`<<gateway-no-response>>`".
|
||||
|
||||
The defaults can be overridden for individual methods; see <<gateway-configuration-annotations>>.
|
||||
The defaults can be overridden for individual methods.
|
||||
See "`<<gateway-configuration-annotations>>`".
|
||||
|
||||
[[gateway-default-reply-channel]]
|
||||
==== Setting the Default Reply Channel
|
||||
|
||||
Typically you don't have to specify the `default-reply-channel`, since a Gateway will auto-create a temporary, anonymous reply channel, where it will listen for the reply.
|
||||
However, there are some cases which may prompt you to define a `default-reply-channel` (or `reply-channel` with adapter gateways such as HTTP, JMS, etc.).
|
||||
Typically, you need not specify the `default-reply-channel`, since a Gateway auto-creates a temporary, anonymous reply channel, where it listens for the reply.
|
||||
However, some cases may prompt you to define a `default-reply-channel` (or `reply-channel` with adapter gateways, such as HTTP, JMS, and others).
|
||||
|
||||
For some background, we'll quickly discuss some of the inner-workings of the Gateway.
|
||||
A Gateway will create a temporary point-to-point reply channel which is anonymous and is added to the Message Headers with the name `replyChannel`.
|
||||
When providing an explicit `default-reply-channel` (`reply-channel` with remote adapter gateways), you have the option to point to a publish-subscribe channel, which is so named because you can add more than one subscriber to it.
|
||||
Internally Spring Integration will create a Bridge between the temporary `replyChannel` and the explicitly defined `default-reply-channel`.
|
||||
For some background, we briefly discuss some of the inner workings of the gateway.
|
||||
A gateway creates a temporary point-to-point reply channel.
|
||||
It is anonymous and is added to the message headers with the name, `replyChannel`.
|
||||
When providing an explicit `default-reply-channel` (`reply-channel` with remote adapter gateways), you can point to a publish-subscribe channel, which is so named because you can add more than one subscriber to it.
|
||||
Internally, Spring Integration creates a bridge between the temporary `replyChannel` and the explicitly defined `default-reply-channel`.
|
||||
|
||||
So let's say you want your reply to go not only to the gateway, but also to some other consumer.
|
||||
In this case you would want two things: _a) a named channel you can subscribe to and b) that channel is a publish-subscribe-channel._ The default strategy used by the gateway will not satisfy those needs, because the reply channel added to the header is anonymous and point-to-point.
|
||||
This means that no other subscriber can get a handle to it and even if it could, the channel has point-to-point behavior such that only one subscriber would get the Message.
|
||||
So by defining a `default-reply-channel` you can point to a channel of your choosing, which in this case would be a `publish-subscribe-channel`.
|
||||
The Gateway would create a bridge from it to the temporary, anonymous reply channel that is stored in the header.
|
||||
Suppose you want your reply to go not only to the gateway but also to some other consumer.
|
||||
In this case, you want two things:
|
||||
|
||||
Another case where you might want to provide a reply channel explicitly is for monitoring or auditing via an interceptor (e.g., wiretap).
|
||||
You need a named channel in order to configure a Channel Interceptor.
|
||||
* A named channel to which you can subscribe
|
||||
* That channel to be a publish-subscribe-channel
|
||||
|
||||
The default strategy used by the gateway does not satisfy those needs, because the reply channel added to the header is anonymous and point-to-point.
|
||||
This means that no other subscriber can get a handle to it and, even if it could, the channel has point-to-point behavior such that only one subscriber would get the message.
|
||||
By defining a `default-reply-channel` you can point to a channel of your choosing.
|
||||
In this case, that is a `publish-subscribe-channel`.
|
||||
The gateway creates a bridge from it to the temporary, anonymous reply channel that is stored in the header.
|
||||
|
||||
You might also want to explicitly provide a reply channel for monitoring or auditing through an interceptor (for example, <<channel-wiretap, wiretap>>).
|
||||
To configure a channel interceptor, you need a named channel.
|
||||
|
||||
[[gateway-configuration-annotations]]
|
||||
==== Gateway Configuration with Annotations and/or XML
|
||||
==== Gateway Configuration with Annotations and XML
|
||||
|
||||
Consider the following example, which expands on the previous `Cafe` interface example by adding a `@Gateway` annotation:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public interface Cafe {
|
||||
@@ -80,12 +97,11 @@ public interface Cafe {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
You may alternatively provide such content in `method` sub-elements if you prefer XML configuration (see the next paragraph).
|
||||
|
||||
It is also possible to pass values to be interpreted as Message headers on the Message that is created and sent to the
|
||||
request channel by using the `@Header` annotation:
|
||||
The `@Header` annotation lets you add values that are interpreted as message headers, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public interface FileWriter {
|
||||
@@ -95,9 +111,11 @@ public interface FileWriter {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
If you prefer the XML approach of configuring Gateway methods, you can provide _method_ sub-elements to the gateway configuration.
|
||||
If you prefer the XML approach to configuring gateway methods, you can add `method` elements to the gateway configuration, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:gateway id="myGateway" service-interface="org.foo.bar.TestGateway"
|
||||
@@ -108,12 +126,16 @@ If you prefer the XML approach of configuring Gateway methods, you can provide _
|
||||
<int:method name="echoViaDefault"/>
|
||||
</int:gateway>
|
||||
----
|
||||
====
|
||||
|
||||
You can also provide individual headers per method invocation via XML.
|
||||
This could be very useful if the headers you want to set are static in nature and you don't want to embed them in the gateway's method signature via `@Header` annotations.
|
||||
For example, in the Loan Broker example we want to influence how aggregation of the Loan quotes will be done based on what type of request was initiated (single quote or all quotes).
|
||||
Determining the type of the request by evaluating what gateway method was invoked, although possible, would violate the separation of concerns paradigm (the method is a java artifact), but expressing your intention (meta information) via Message headers is natural in a Messaging architecture.
|
||||
You can also use XML to provide individual headers for each method invocation.
|
||||
This could be useful if the headers you want to set are static in nature and you do not want to embed them in the gateway's method signature by using `@Header` annotations.
|
||||
For example, in the loan broker example, we want to influence how aggregation of the loan quotes is done, based on what type of request was initiated (single quote or all quotes).
|
||||
Determining the type of the request by evaluating which gateway method was invoked, although possible, would violate the separation of concerns paradigm (the method is a Java artifact).
|
||||
However, expressing your intention (meta information) in message headers is natural in a messaging architecture.
|
||||
The following example shows how to add a different message header for each of two methods:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:gateway id="loanBrokerGateway"
|
||||
@@ -126,62 +148,67 @@ Determining the type of the request by evaluating what gateway method was invoke
|
||||
</int:method>
|
||||
</int:gateway>
|
||||
----
|
||||
====
|
||||
|
||||
In the above case you can clearly see how a different value will be set for the 'RESPONSE_TYPE' header based on the gateway's method.
|
||||
In the preceding example a different value is set for the 'RESPONSE_TYPE' header, based on the gateway's method.
|
||||
|
||||
*Expressions and "Global" Headers*
|
||||
===== Expressions and "`Global`" Headers
|
||||
|
||||
The `<header/>` element supports `expression` as an alternative to `value`.
|
||||
The SpEL expression is evaluated to determine the value of the header.
|
||||
There is no `#root` object but the following variables are available:
|
||||
There is no `#root` object, but the following variables are available:
|
||||
|
||||
#args - an `Object[]` containing the method arguments
|
||||
* #args: An `Object[]` containing the method arguments
|
||||
* #gatewayMethod: The object (derived from `java.reflect.Method`) that represents the method in the `service-interface` that was invoked.
|
||||
A header containing this variable can be used later in the flow (for example, for routing).
|
||||
For example, if you wish to route on the simple method name, you might add a header with the following expression: `#gatewayMethod.name`.
|
||||
|
||||
NOTE: The `java.reflect.Method` is not serializable.
|
||||
A header with an expression of `#gatewayMethod` is lost if you later serialize the message.
|
||||
Consequently, you may wish to use `#gatewayMethod.name` or `#gatewayMethod.toString()` in those cases.
|
||||
The `toString()` method provides a `String` representation of the method, including parameter and return types.
|
||||
|
||||
#gatewayMethod - the `java.reflect.Method` object representing the method in the `service-interface` that was invoked.
|
||||
A header containing this variable can be used later in the flow, for example, for routing.
|
||||
For example, if you wish to route on the simple method name, you might add a header, with expression `#gatewayMethod.name`.
|
||||
|
||||
NOTE: The `java.reflect.Method` is not serializable; a header with expression `#gatewayMethod` will be lost if you later serialize the message.
|
||||
So, you may wish to use `#gatewayMethod.name` or `#gatewayMethod.toString()` in those cases; the `toString()` method provides a String representation of the method, including parameter and return types.
|
||||
|
||||
Since 3.0, `<default-header/>` s can be defined to add headers to all messages produced by the gateway, regardless of the method invoked.
|
||||
Since version 3.0, `<default-header/>` elements can be defined to add headers to all the messages produced by the gateway, regardless of the method invoked.
|
||||
Specific headers defined for a method take precedence over default headers.
|
||||
Specific headers defined for a method here will override any `@Header` annotations in the service interface.
|
||||
However, default headers will NOT override any `@Header` annotations in the service interface.
|
||||
Specific headers defined for a method here override any `@Header` annotations in the service interface.
|
||||
However, default headers do NOT override any `@Header` annotations in the service interface.
|
||||
|
||||
The gateway now also supports a `default-payload-expression` which will be applied for all methods (unless overridden).
|
||||
The gateway now also supports a `default-payload-expression`, which is applied for all methods (unless overridden).
|
||||
|
||||
[[gateway-mapping]]
|
||||
==== Mapping Method Arguments to a Message
|
||||
|
||||
Using the configuration techniques in the previous section allows control of how method arguments are mapped to message elements (payload and header(s)).
|
||||
Using the configuration techniques in the previous section allows control of how method arguments are mapped to message elements (payload and headers).
|
||||
When no explicit configuration is used, certain conventions are used to perform the mapping.
|
||||
In some cases, these conventions cannot determine which argument is the payload and which should be mapped to headers.
|
||||
Consider the following example:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public String send1(Object thing1, Map thing2);
|
||||
|
||||
public String send1(Object foo, Map bar);
|
||||
|
||||
public String send2(Map foo, Map bar);
|
||||
|
||||
public String send2(Map thing1, Map thing2);=
|
||||
----
|
||||
====
|
||||
|
||||
In the first case, the convention will map the first argument to the payload (as long as it is not a `Map`) and the contents of the second become headers.
|
||||
In the first case, the convention is to map the first argument to the payload (as long as it is not a `Map`) and the contents of the second argument become headers.
|
||||
|
||||
In the second case (or the first when the argument for parameter `foo` is a `Map`), the framework cannot determine which argument should be the payload; mapping will fail.
|
||||
This can generally be resolved using a `payload-expression`, a `@Payload` annotation and/or a `@Headers` annotation.
|
||||
In the second case (or the first when the argument for parameter `thing1` is a `Map`), the framework cannot determine which argument should be the payload.
|
||||
Consequently, mapping fails.
|
||||
This can generally be resolved using a `payload-expression`, a `@Payload` annotation, or a `@Headers` annotation.
|
||||
|
||||
Alternatively, and whenever the conventions break down, you can take the entire responsibility for mapping the method calls to messages.
|
||||
To do this, implement an `MethodArgsMessageMapper` and provide it to the `<gateway/>` using the `mapper` attribute.
|
||||
The mapper maps a `MethodArgsHolder`, which is a simple class wrapping the `java.reflect.Method` instance and an `Object[]` containing the arguments.
|
||||
When providing a custom mapper, the `default-payload-expression` attribute and `<default-header/>` elements are not allowed on the gateway; similarly, the `payload-expression` attribute and `<header/>` elements are not allowed on any `<method/>` elements.
|
||||
Alternatively (and whenever the conventions break down), you can take the entire responsibility for mapping the method calls to messages.
|
||||
To do so, implement an `MethodArgsMessageMapper` and provide it to the `<gateway/>` by using the `mapper` attribute.
|
||||
The mapper maps a `MethodArgsHolder`, which is a simple class that wraps the `java.reflect.Method` instance and an `Object[]` containing the arguments.
|
||||
When providing a custom mapper, the `default-payload-expression` attribute and `<default-header/>` elements are not allowed on the gateway.
|
||||
Similarly, the `payload-expression` attribute and `<header/>` elements are not allowed on any `<method/>` elements.
|
||||
|
||||
*Mapping Method Arguments*
|
||||
===== Mapping Method Arguments
|
||||
|
||||
Here are examples showing how method arguments can be mapped to the message (and some examples of invalid configuration):
|
||||
The following examples show how method arguments can be mapped to the message and shows some examples of invalid configuration:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public interface MyGateway {
|
||||
@@ -219,38 +246,43 @@ public interface MyGateway {
|
||||
void payloadAndHeadersAnnotationsOnSameParameter(@Payload @Headers Map<String, Object> map);
|
||||
|
||||
}
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
<1> Note that in this example, the SpEL variable `#this` refers to the argument - in this case, the value of `'s'`.
|
||||
<1> Note that, in this example, the SpEL variable, `#this`, refers to the argument -- in this case, the value of `s`.
|
||||
|
||||
The XML equivalent looks a little different, since there is no `#this` context for the method argument, but expressions can refer to method arguments using the `#args` variable:
|
||||
The XML equivalent looks a little different, since there is no `#this` context for the method argument.
|
||||
However, expressions can refer to method arguments by using the `#args` variable, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:gateway id="myGateway" service-interface="org.foo.bar.MyGateway">
|
||||
<int:method name="send1" payload-expression="#args[0] + 'bar'"/>
|
||||
<int:gateway id="myGateway" service-interface="org.something.MyGateway">
|
||||
<int:method name="send1" payload-expression="#args[0] + 'thing2'"/>
|
||||
<int:method name="send2" payload-expression="@someBean.sum(#args[0])"/>
|
||||
<int:method name="send3" payload-expression="#method"/>
|
||||
<int:method name="send4">
|
||||
<int:header name="foo" expression="#args[2].toUpperCase()"/>
|
||||
<int:header name="thing1" expression="#args[2].toUpperCase()"/>
|
||||
</int:method>
|
||||
</int:gateway>
|
||||
----
|
||||
====
|
||||
|
||||
[[messaging-gateway-annotation]]
|
||||
==== @MessagingGateway Annotation
|
||||
==== `@MessagingGateway` Annotation
|
||||
|
||||
Starting with _version 4.0_, gateway service interfaces can be marked with a `@MessagingGateway` annotation instead of requiring the definition of a `<gateway />` xml element for configuration.
|
||||
The following compares the two approaches for configuring the same gateway:
|
||||
Starting with version 4.0, gateway service interfaces can be marked with a `@MessagingGateway` annotation instead of requiring the definition of a `<gateway />` xml element for configuration.
|
||||
The following pair of examples compares the two approaches for configuring the same gateway:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:gateway id="myGateway" service-interface="org.foo.bar.TestGateway"
|
||||
<int:gateway id="myGateway" service-interface="org.something.TestGateway"
|
||||
default-request-channel="inputC">
|
||||
<int:default-header name="calledMethod" expression="#gatewayMethod.name"/>
|
||||
<int:method name="echo" request-channel="inputA" reply-timeout="2" request-timeout="200"/>
|
||||
<int:method name="echoUpperCase" request-channel="inputB">
|
||||
<int:header name="foo" value="bar"/>
|
||||
<int:header name="thing1" value="thing2"/>
|
||||
</int:method>
|
||||
<int:method name="echoViaDefault"/>
|
||||
</int:gateway>
|
||||
@@ -266,45 +298,43 @@ public interface TestGateway {
|
||||
@Gateway(requestChannel = "inputA", replyTimeout = 2, requestTimeout = 200)
|
||||
String echo(String payload);
|
||||
|
||||
@Gateway(requestChannel = "inputB", headers = @GatewayHeader(name = "foo", value="bar"))
|
||||
@Gateway(requestChannel = "inputB", headers = @GatewayHeader(name = "thing1", value="thing2"))
|
||||
String echoUpperCase(String payload);
|
||||
|
||||
String echoViaDefault(String payload);
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
IMPORTANT: As with the XML version, Spring Integration creates the `proxy` implementation with its messaging infrastructure, when discovering these annotations during a component scan.
|
||||
IMPORTANT: Similarly to the XML version, when Spring Integration discovers these annotations during a component scan, it creates the `proxy` implementation with its messaging infrastructure.
|
||||
To perform this scan and register the `BeanDefinition` in the application context, add the `@IntegrationComponentScan` annotation to a `@Configuration` class.
|
||||
The standard `@ComponentScan` infrastructure doesn't deal with interfaces, therefore the custom `@IntegrationComponentScan` logic has been introduced
|
||||
to determine `@MessagingGateway` annotation on the interfaces and register `GatewayProxyFactoryBean` s for them.
|
||||
See also <<annotations>>
|
||||
The standard `@ComponentScan` infrastructure does not deal with interfaces.
|
||||
Consequently, we introduced the custom `@IntegrationComponentScan` logic to fine the `@MessagingGateway` annotation on the interfaces and register `GatewayProxyFactoryBean` instances for them.
|
||||
See also "`<<annotations>>`".
|
||||
|
||||
NOTE: If you have no XML configuration, the `@EnableIntegration` annotation is required on at least one `@Configuration`
|
||||
class.
|
||||
See <<configuration-enable-integration>> for more information.
|
||||
See "`<<configuration-enable-integration>>`" for more information.
|
||||
|
||||
[[gateway-calling-no-argument-methods]]
|
||||
==== Invoking No-Argument Methods
|
||||
|
||||
When invoking methods on a Gateway interface that do not have any arguments, the default behavior is to _receive_ a `Message` from a `PollableChannel`.
|
||||
When invoking methods on a Gateway interface that do not have any arguments, the default behavior is to receive a `Message` from a `PollableChannel`.
|
||||
|
||||
At times however, you may want to trigger no-argument methods so that you can in fact interact with other components downstream that do not require user-provided parameters, e.g.
|
||||
triggering no-argument SQL calls or Stored Procedures.
|
||||
Sometimes, however, you may want to trigger no-argument methods so that you can interact with other components downstream that do not require user-provided parameters, such as triggering no-argument SQL calls or stored procedures.
|
||||
|
||||
In order to achieve _send-and-receive_ semantics, you must provide a payload.
|
||||
In order to generate a payload, method parameters on the interface are not necessary.
|
||||
You can either use the `@Payload` annotation or the `payload-expression` attribute in XML on the `method` sub-element.
|
||||
Below please find a few examples of what the payloads could be:
|
||||
To achieve send-and-receive semantics, you must provide a payload.
|
||||
To generate a payload, method parameters on the interface are not necessary.
|
||||
You can either use the `@Payload` annotation or the `payload-expression` attribute in XML on the `method` element.
|
||||
The following list includes a few examples of what the payloads could be:
|
||||
|
||||
* a literal string
|
||||
* #gatewayMethod.name
|
||||
* new java.util.Date()
|
||||
* @someBean.someMethod()'s return value
|
||||
|
||||
|
||||
|
||||
Here is an example using the `@Payload` annotation:
|
||||
The following example shows how to use the `@Payload` annotation:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
@@ -316,26 +346,29 @@ public interface Cafe {
|
||||
}
|
||||
----
|
||||
|
||||
If a method has no argument and no return value, but does contain a payload expression, it will be treated as a _send-only_ operation.
|
||||
If a method has no argument and no return value but does contain a payload expression, it is treated as a send-only operation.
|
||||
|
||||
[[gateway-error-handling]]
|
||||
==== Error Handling
|
||||
|
||||
Of course, the Gateway invocation might result in errors.
|
||||
By default, any error that occurs downstream will be re-thrown as is upon the Gateway's method invocation.
|
||||
The gateway invocation can result in errors.
|
||||
By default, any error that occurs downstream is re-thrown "`as is`" upon the gateway's method invocation.
|
||||
For example, consider the following simple flow:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
gateway -> service-activator
|
||||
----
|
||||
====
|
||||
|
||||
If the service invoked by the service activator throws a `FooException`, the framework wraps it in a `MessagingException`, attaching the message passed to the service activator in the `failedMessage` property.
|
||||
Any logging performed by the framework will therefore have full context of the failure.
|
||||
When the exception is caught by the gateway, by default, the `FooException` will be unwrapped and thrown to the caller.
|
||||
You can configure a `throws` clause on the gateway method declaration for matching the particular exception type in the cause chain.
|
||||
For example if you would like to catch a whole `MessagingException` with all the messaging information of the reason of downstream error, you should have a gateway method like this:
|
||||
If the service invoked by the service activator throws a `MyException` (for example), the framework wraps it in a `MessagingException` and attaches the message passed to the service activator in the `failedMessage` property.
|
||||
Consequently, any logging performed by the framework has full the context of the failure.
|
||||
By default, when the exception is caught by the gateway, the `MyException` is unwrapped and thrown to the caller.
|
||||
You can configure a `throws` clause on the gateway method declaration to match the particular exception type in the cause chain.
|
||||
For example, if you want to catch a whole `MessagingException` with all the messaging information of the reason of downstream error, you should have a gateway method similar to the following:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
public interface MyGateway {
|
||||
@@ -344,15 +377,18 @@ public interface MyGateway {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Since we encourage POJO programming, you may not want to expose the caller to messaging infrastructure.
|
||||
|
||||
If your gateway method does not have a `throws` clause, the gateway will traverse the cause tree looking for a `RuntimeException` (that is not a `MessagingException`).
|
||||
If none is found, the framework will simply throw the `MessagingException`.
|
||||
If the `FooException` in the discussion above has a cause `BarException` and your method `throws BarException` then the gateway will further unwrap that and throw it to the caller.
|
||||
If your gateway method does not have a `throws` clause, the gateway traverses the cause tree, looking for a `RuntimeException` that is not a `MessagingException`.
|
||||
If none is found, the framework throws the `MessagingException`.
|
||||
If the `MyException` in the preceding discussion has a cause of `SomeOtherException` and your method `throws SomeOtherException`, the gateway further unwraps that and throws it to the caller.
|
||||
|
||||
When a gateway is declared with no `service-interface`, an internal framework interface `RequestReplyExchanger` is used.
|
||||
|
||||
Consider the following example:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
public interface RequestReplyExchanger {
|
||||
@@ -362,12 +398,12 @@ public interface RequestReplyExchanger {
|
||||
}
|
||||
----
|
||||
|
||||
Before _version 5.0_ this `exchange` method did not have a `throws` clause and therefore the exception was unwrapped.
|
||||
If you are using this interface, and wish to restore the previous unwrap behavior, use a custom `service-interface` instead, or simply access the `cause` of the `MessagingException` yourself.
|
||||
Before version 5.0, this `exchange` method did not have a `throws` clause and, as a result, the exception was unwrapped.
|
||||
If you use this interface and want to restore the previous unwrap behavior, use a custom `service-interface` instead or access the `cause` of the `MessagingException` yourself.
|
||||
|
||||
However there are times when you may want to simply log the error rather than propagating it, or you may want to treat an Exception as a valid reply, by mapping it to a Message that will conform to some "error message" contract that the caller understands.
|
||||
To accomplish this, the Gateway provides support for a Message Channel dedicated to the errors via the _error-channel_ attribute.
|
||||
In the example below, you can see that a 'transformer' is used to create a reply `Message` from the `Exception`.
|
||||
However, you may want to log the error rather than propagating it or you may want to treat an exception as a valid reply (by mapping it to a message that conforms to some "error message" contract that the caller understands).
|
||||
To accomplish this, the gateway provides support for a message channel dedicated to the errors by including support for the `error-channel` attribute.
|
||||
In the following example, a 'transformer' creates a reply `Message` from the `Exception`:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
@@ -381,78 +417,82 @@ In the example below, you can see that a 'transformer' is used to create a reply
|
||||
|
||||
----
|
||||
|
||||
The _exceptionTransformer_ could be a simple POJO that knows how to create the expected error response objects.
|
||||
That would then be the payload that is sent back to the caller.
|
||||
Obviously, you could do many more elaborate things in such an "error flow" if necessary.
|
||||
The `exceptionTransformer` could be a simple POJO that knows how to create the expected error response objects.
|
||||
That becomes the payload that is sent back to the caller.
|
||||
You could do many more elaborate things in such an "`error flow`", if necessary.
|
||||
It might involve routers (including Spring Integration's `ErrorMessageExceptionTypeRouter`), filters, and so on.
|
||||
Most of the time, a simple 'transformer' should be sufficient, however.
|
||||
|
||||
Alternatively, you might want to only log the Exception (or send it somewhere asynchronously).
|
||||
If you provide a one-way flow, then nothing would be sent back to the caller.
|
||||
In the case that you want to completely suppress Exceptions, you can provide a reference to the global "nullChannel" (essentially a /dev/null approach).
|
||||
Finally, as mentioned above, if no "error-channel" is defined at all, then the Exceptions will propagate as usual.
|
||||
Alternatively, you might want to only log the exception (or send it somewhere asynchronously).
|
||||
If you provide a one-way flow, nothing would be sent back to the caller.
|
||||
If you want to completely suppress exceptions, you can provide a reference to the global `nullChannel` (essentially a `/dev/null` approach).
|
||||
Finally, as mentioned above, if no `error-channel` is defined, then the exceptions propagate as usual.
|
||||
|
||||
When using the `@MessagingGateway` annotation (see <<messaging-gateway-annotation>>), use the `errroChannel` attribute.
|
||||
When you use the `@MessagingGateway` annotation (see `<<messaging-gateway-annotation>>`), you can use use the `errorChannel` attribute.
|
||||
|
||||
Starting with _version 5.0_, when using a gateway method with a `void` return type (one-way flow), the `error-channel` reference (if provided) is populated in the standard `errorChannel` header of each message sent.
|
||||
This allows a downstream async flow, based on the standard `ExecutorChannel` configuration (or a `QueueChannel`), to override a default global `errorChannel` exceptions sending behavior.
|
||||
Previously you had to specify an `errorChannel` header manually via `@GatewayHeader` annotation or `<header>` sub-element.
|
||||
The `error-channel` property was ignored for `void` methods with an asynchronous flow; error messages were sent to the default `errorChannel` instead.
|
||||
Starting with version 5.0, when you use a gateway method with a `void` return type (one-way flow), the `error-channel` reference (if provided) is populated in the standard `errorChannel` header of each sent message.
|
||||
This feature allows a downstream asynchronous flow, based on the standard `ExecutorChannel` configuration (or a `QueueChannel`), to override a default global `errorChannel` exceptions sending behavior.
|
||||
Previously you had to manually specify an `errorChannel` header with the `@GatewayHeader` annotation or the `<header>` element.
|
||||
The `error-channel` property was ignored for `void` methods with an asynchronous flow.
|
||||
Instead, error messages were sent to the default `errorChannel`.
|
||||
|
||||
|
||||
IMPORTANT: Exposing the messaging system via simple POJI Gateways obviously provides benefits, but "hiding" the reality of the underlying messaging system does come at a price so there are certain things you should consider.
|
||||
We want our Java method to return as quickly as possible and not hang for an indefinite amount of time while the caller is waiting on it to return (void, return value, or a thrown Exception).
|
||||
When regular methods are used as a proxies in front of the Messaging system, we have to take into account the potentially asynchronous nature of the underlying messaging.
|
||||
This means that there might be a chance that a Message that was initiated by a Gateway could be dropped by a Filter, thus never reaching a component that is responsible for producing a reply.
|
||||
Some Service Activator method might result in an Exception, thus providing no reply (as we don't generate Null messages).
|
||||
So as you can see there are multiple scenarios where a reply message might not be coming.
|
||||
IMPORTANT: Exposing the messaging system through simple POJI Gateways provides benefits, but "`hiding`" the reality of the underlying messaging system does come at a price, so there are certain things you should consider.
|
||||
We want our Java method to return as quickly as possible and not hang for an indefinite amount of time while the caller is waiting on it to return (whether void, a return value, or a thrown Exception).
|
||||
When regular methods are used as a proxies in front of the messaging system, we have to take into account the potentially asynchronous nature of the underlying messaging.
|
||||
This means that there might be a chance that a message that was initiated by a gateway could be dropped by a filter and never reach a component that is responsible for producing a reply.
|
||||
Some service activator method might result in an exception, thus providing no reply (as we do not generate null messages).
|
||||
In other words, multiple scenarios can cause a reply message to never come.
|
||||
That is perfectly natural in messaging systems.
|
||||
However think about the implication on the gateway method. The Gateway's method input arguments were incorporated into a Message and sent downstream.
|
||||
The reply Message would be converted to a return value of the Gateway's method.
|
||||
So you might want to ensure that for each Gateway call there will always be a reply Message.
|
||||
Otherwise, your Gateway method might never return and will hang indefinitely.
|
||||
One of the ways of handling this situation is via an Asynchronous Gateway (explained later in this section).
|
||||
Another way of handling it is to explicitly set the reply-timeout attribute.
|
||||
That way, the gateway will not hang any longer than the time specified by the reply-timeout and will return 'null' if that timeout does elapse.
|
||||
Finally, you might want to consider setting downstream flags such as 'requires-reply' on a service-activator or 'throw-exceptions-on-rejection' on a filter. These options will be discussed in more detail in the final section of this chapter.
|
||||
However, think about the implication on the gateway method. The gateway's method input arguments were incorporated into a message and sent downstream.
|
||||
The reply message would be converted to a return value of the gateway's method.
|
||||
So you might want to ensure that, for each gateway call, there is always a reply message.
|
||||
Otherwise, your gateway method might never return and hang indefinitely.
|
||||
One way to handle this situation is by using an asynchronous gateway (explained later in this section).
|
||||
Another way of handling it is to explicitly set the `reply-timeout` attribute.
|
||||
That way, the gateway does not hang any longer than the time specified by the `reply-timeout` and returns 'null' if that timeout does elapse.
|
||||
Finally, you might want to consider setting downstream flags, such as 'requires-reply', on a service-activator or 'throw-exceptions-on-rejection' on a filter. These options are discussed in more detail in the final section of this chapter.
|
||||
|
||||
NOTE: If the downstream flow returns an `ErrorMessage`, its `payload` (a `Throwable`) is treated as a regular downstream
|
||||
error: if there is an `error-channel` configured, it will be sent there, to the error flow; otherwise the payload is
|
||||
thrown to the caller of gateway.
|
||||
Similarly, if the error flow on the `error-channel` returns an `ErrorMessage` its payload is thrown to the caller.
|
||||
NOTE: If the downstream flow returns an `ErrorMessage`, its `payload` (a `Throwable`) is treated as a regular downstream error.
|
||||
If there is an `error-channel` configured, it is sent to the error flow.
|
||||
Otherwise the payload is thrown to the caller of the gateway.
|
||||
Similarly, if the error flow on the `error-channel` returns an `ErrorMessage`, its payload is thrown to the caller.
|
||||
The same applies to any message with a `Throwable` payload.
|
||||
This can be useful in async situations when when there is a need propagate an `Exception` directly to the caller.
|
||||
To achieve this you can either return an `Exception` as the `reply` from some service, or simply throw it.
|
||||
Generally, even with an async flow, the framework will take care of propagating an exception thrown by the
|
||||
This can be useful in asynchronous situations when when you need to propagate an `Exception` directly to the caller.
|
||||
To do so, you can either return an `Exception` (as the `reply` from some service) or throw it.
|
||||
Generally, even with an asynchronous flow, the framework takes care of propagating an exception thrown by the
|
||||
downstream flow back to the gateway.
|
||||
The https://github.com/spring-projects/spring-integration-samples/tree/master/intermediate/tcp-client-server-multiplex[TCP Client-Server Multiplex]
|
||||
sample demonstrates both techniques to return the exception to the caller.
|
||||
It emulates a Socket IO error to the waiting thread using an `aggregator` with `group-timeout` (see <<agg-and-group-to>>)
|
||||
and `MessagingTimeoutException` reply on the discard flow.
|
||||
The https://github.com/spring-projects/spring-integration-samples/tree/master/intermediate/tcp-client-server-multiplex[TCP Client-Server Multiplex] sample demonstrates both techniques to return the exception to the caller.
|
||||
It emulates a socket IO error to the waiting thread by using an `aggregator` with `group-timeout` (see "`<<agg-and-group-to>>`")
|
||||
and a `MessagingTimeoutException` reply on the discard flow.
|
||||
|
||||
[[gateway-timeouts]]
|
||||
==== Gateway Timeouts
|
||||
|
||||
There are two properties `requestTimeout` and `replyTimeout`.
|
||||
The request timeout only applies if the channel can block (e.g. a bounded `QueueChannel` that is full).
|
||||
The reply timeout is how long the gateway will wait for a reply, or return `null`; it defaults to infinity.
|
||||
Gateways have two timeout properties: `requestTimeout` and `replyTimeout`.
|
||||
The request timeout applies only if the channel can block (for example, a bounded `QueueChannel` that is full).
|
||||
The `replyTimeout` value is how long the gateway waits for a reply or returns `null`.
|
||||
It defaults to infinity.
|
||||
|
||||
The timeouts can be set as defaults for all methods on the gateway (`defaultRequestTimeout`, `defaultReplyTimeout`) (or on the `MessagingGateway` interface annotation).
|
||||
The timeouts can be set as defaults for all methods on the gateway (`defaultRequestTimeout` and `defaultReplyTimeout`) or on the `MessagingGateway` interface annotation.
|
||||
Individual methods can override these defaults (in `<method/>` child elements) or on the `@Gateway` annotation.
|
||||
|
||||
Starting with _version 5.0_, the timeouts can be defined as expressions:
|
||||
Starting with version 5.0, the timeouts can be defined as expressions, as the following example shows:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Gateway(payloadExpression = "#args[0]", requestChannel = "someChannel",
|
||||
requestTimeoutExpression = "#args[1]", replyTimeoutExpression = "#args[2]")
|
||||
String lateReply(String payload, long requestTimeout, long replyTimeout);
|
||||
----
|
||||
====
|
||||
|
||||
The evaluation context has a `BeanResolver` (use `@someBean` to reference other beans) and the `#args` array variable is available.
|
||||
The evaluation context has a `BeanResolver` (use `@someBean` to reference other beans), and the `#args` array variable is available.
|
||||
|
||||
When configuring with XML, the timeout attributes can be a simple long value or a SpEL expression.
|
||||
When configuring with XML, the timeout attributes can be a long value or a SpEL expression, as the following example shows:
|
||||
|
||||
====
|
||||
[source, xml]
|
||||
----
|
||||
<method name="someMethod" request-channel="someRequestChannel"
|
||||
@@ -461,20 +501,19 @@ When configuring with XML, the timeout attributes can be a simple long value or
|
||||
reply-timeout="#args[1]">
|
||||
</method>
|
||||
----
|
||||
====
|
||||
|
||||
[[async-gateway]]
|
||||
==== Asynchronous Gateway
|
||||
|
||||
===== Introduction
|
||||
As a pattern, the messaging gateway offers a nice way to hide messaging-specific code while still exposing the full capabilities of the messaging system.
|
||||
As <<gateway-proxy,described earlier>>, the `GatewayProxyFactoryBean` provides a convenient way to expose a proxy over a service-interface giving you POJO-based access to a messaging system (based on objects in your own domain, primitives/Strings, or other objects).
|
||||
However, when a gateway is exposed through simple POJO methods that return values, it implies that, for each request message (generated when the method is invoked), there must be a reply message (generated when the method has returned).
|
||||
Since messaging systems are naturally asynchronous, you may not always be able to guarantee the contract where "`for each request, there will always be be a reply`". Spring Integration 2.0 introduced support for an asynchronous gateway, which offers a convenient way to initiate flows when you may not know if a reply is expected or how long it takes for replies to arrive.
|
||||
|
||||
As a pattern, the Messaging Gateway is a very nice way to hide messaging-specific code while still exposing the full capabilities of the messaging system.
|
||||
As you've seen, the `GatewayProxyFactoryBean` provides a convenient way to expose a Proxy over a service-interface thus giving you POJO-based access to a messaging system (based on objects in your own domain, or primitives/Strings, etc).
|
||||
But when a gateway is exposed via simple POJO methods which return values it does imply that for each Request message (generated when the method is invoked) there must be a Reply message (generated when the method has returned).
|
||||
Since Messaging systems naturally are asynchronous you may not always be able to guarantee the contract where _"for each request there will always be be a reply"_. With Spring Integration 2.0 we introduced support for an _Asynchronous Gateway_ which is a convenient way to initiate flows where you may not know if a reply is expected or how long will it take for replies to arrive.
|
||||
To handle these types of scenarios, Spring Integration uses `java.util.concurrent.Future` instances to support an asynchronous gateway.
|
||||
|
||||
A natural way to handle these types of scenarios in Java would be relying upon _java.util.concurrent.Future_ instances, and that is exactly what Spring Integration uses to support an _Asynchronous Gateway_.
|
||||
|
||||
From the XML configuration, there is nothing different and you still define _Asynchronous Gateway_ the same way as a regular Gateway.
|
||||
From the XML configuration, nothing changes, and you still define asynchronous gateway the same way as you define a regular gateway, as the following example shows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
@@ -483,7 +522,7 @@ From the XML configuration, there is nothing different and you still define _Asy
|
||||
default-request-channel="requestChannel"/>
|
||||
----
|
||||
|
||||
However the Gateway Interface (service-interface) is a little different:
|
||||
However, the gateway interface (a service interface) is a little different, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -494,12 +533,15 @@ public interface MathServiceGateway {
|
||||
}
|
||||
----
|
||||
|
||||
As you can see from the example above, the return type for the gateway method is a `Future`.
|
||||
When `GatewayProxyFactoryBean` sees that the return type of the gateway method is a `Future`, it immediately switches to the async mode by utilizing an `AsyncTaskExecutor`.
|
||||
That is all.
|
||||
As the preceding example shows, the return type for the gateway method is a `Future`.
|
||||
When `GatewayProxyFactoryBean` sees that the return type of the gateway method is a `Future`, it immediately switches to the asynchronous mode by using an `AsyncTaskExecutor`.
|
||||
That is the extent of the differences.
|
||||
The call to such a method always returns immediately with a `Future` instance.
|
||||
Then, you can interact with the `Future` at your own pace to get the result, cancel, etc.
|
||||
And, as with any other use of Future instances, calling get() may reveal a timeout, an execution exception, and so on.
|
||||
Then you can interact with the `Future` at your own pace to get the result, cancel, and so on.
|
||||
Also, as with any other use of `Future` instances, calling `get()` may reveal a timeout, an execution exception, and so on.
|
||||
The following example shows how to use a `Future` that returns from an asynchronous gateway:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
MathServiceGateway mathService = ac.getBean("mathService", MathServiceGateway.class);
|
||||
@@ -507,18 +549,21 @@ Future<Integer> result = mathService.multiplyByTwo(number);
|
||||
// do something else here since the reply might take a moment
|
||||
int finalResult = result.get(1000, TimeUnit.SECONDS);
|
||||
----
|
||||
====
|
||||
|
||||
For a more detailed example, please refer to the https://github.com/spring-projects/spring-integration-samples/tree/master/intermediate/async-gateway[_async-gateway_] sample distributed within the Spring Integration samples.
|
||||
For a more detailed example, see the https://github.com/spring-projects/spring-integration-samples/tree/master/intermediate/async-gateway[async-gateway] sample in the Spring Integration samples.
|
||||
|
||||
===== ListenableFuture
|
||||
===== `ListenableFuture`
|
||||
|
||||
Starting with _version 4.1_, async gateway methods can also return `ListenableFuture` (introduced in Spring Framework 4.0).
|
||||
These return types allow you to provide a callback which is invoked when the result is available (or an exception occurs).
|
||||
When the gateway detects this return type, and the task executor (see below) is an `AsyncListenableTaskExecutor`, the executor's `submitListenable()` method is invoked.
|
||||
Starting with version 4.1, asynchronous gateway methods can also return `ListenableFuture` (introduced in Spring Framework 4.0).
|
||||
These return types let you provide a callback, which is invoked when the result is available (or an exception occurs).
|
||||
When the gateway detects this return type and the <<gateway-asynctaskexecutor,task executor>> is an `AsyncListenableTaskExecutor`, the executor's `submitListenable()` method is invoked.
|
||||
The following example shows how to use a `ListenableFuture`:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
ListenableFuture<String> result = this.asyncGateway.async("foo");
|
||||
ListenableFuture<String> result = this.asyncGateway.async("something");
|
||||
result.addCallback(new ListenableFutureCallback<String>() {
|
||||
|
||||
@Override
|
||||
@@ -532,15 +577,18 @@ result.addCallback(new ListenableFutureCallback<String>() {
|
||||
}
|
||||
});
|
||||
----
|
||||
====
|
||||
|
||||
===== AsyncTaskExecutor
|
||||
[[gateway-asynctaskexecutor]]
|
||||
===== `AsyncTaskExecutor`
|
||||
|
||||
By default, the `GatewayProxyFactoryBean` uses `org.springframework.core.task.SimpleAsyncTaskExecutor` when submitting internal `AsyncInvocationTask` instances for any gateway method whose return type is `Future`.
|
||||
However the `async-executor` attribute in the `<gateway/>` element's configuration allows you to provide a reference to any implementation of `java.util.concurrent.Executor` available within the Spring application context.
|
||||
By default, the `GatewayProxyFactoryBean` uses `org.springframework.core.task.SimpleAsyncTaskExecutor` when submitting internal `AsyncInvocationTask` instances for any gateway method whose return type is a `Future`.
|
||||
However, the `async-executor` attribute in the `<gateway/>` element's configuration lets you provide a reference to any implementation of `java.util.concurrent.Executor` available within the Spring application context.
|
||||
|
||||
The (default) `SimpleAsyncTaskExecutor` supports both `Future` and `ListenableFuture` return types, returning `FutureTask` or `ListenableFutureTask` respectively. Also see <<gw-completable-future>> below.
|
||||
Even though there is a default executor, it is often useful to provide an external one so that you can identify its threads in logs (when using XML, the thread name is based on the executor's bean name):
|
||||
The (default) `SimpleAsyncTaskExecutor` supports both `Future` and `ListenableFuture` return types, returning `FutureTask` or `ListenableFutureTask` respectively. See "`<<gw-completable-future>>`".
|
||||
Even though there is a default executor, it is often useful to provide an external one so that you can identify its threads in logs (when using XML, the thread name is based on the executor's bean name), as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
@@ -558,11 +606,14 @@ public interface ExecGateway {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
If you wish to return a different `Future` implementation, you can provide a custom executor, or disable the executor altogether and return the `Future` in the reply message payload from the downstream flow.
|
||||
To disable the executor, simply set it to `null` in the `GatewayProxyFactoryBean` (`setAsyncTaskExecutor(null)`).
|
||||
When configuring the gateway with XML, use `async-executor=""`; when configuring using the `@MessagingGateway` annotation, use:
|
||||
If you wish to return a different `Future` implementation, you can provide a custom executor or disable the executor altogether and return the `Future` in the reply message payload from the downstream flow.
|
||||
To disable the executor, set it to `null` in the `GatewayProxyFactoryBean` (by using `setAsyncTaskExecutor(null)`).
|
||||
When configuring the gateway with XML, use `async-executor=""`.
|
||||
When configuring by using the `@MessagingGateway` annotation, use code similar to the following:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@MessagingGateway(asyncExecutor = AnnotationConstants.NULL)
|
||||
@@ -573,80 +624,78 @@ public interface NoExecGateway {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
IMPORTANT: If the return type is a specific concrete `Future` implementation or some other sub-interface that is not supported by the configured executor, the flow will run on the caller's thread and the flow must return the required type in the reply message payload.
|
||||
IMPORTANT: If the return type is a specific concrete `Future` implementation or some other sub-interface that is not supported by the configured executor, the flow runs on the caller's thread and the flow must return the required type in the reply message payload.
|
||||
|
||||
[[gw-completable-future]]
|
||||
===== CompletableFuture
|
||||
===== `CompletableFuture`
|
||||
|
||||
Starting with _version 4.2_, gateway methods can now return `CompletableFuture<?>`.
|
||||
There are several modes of operation when returning this type:
|
||||
Starting with version 4.2, gateway methods can now return `CompletableFuture<?>`.
|
||||
There are two modes of operation when returning this type:
|
||||
|
||||
When an async executor is provided *and* the return type is exactly `CompletableFuture` (not a subclass), the framework
|
||||
will run the task on the executor and immediately return a `CompletableFuture` to the caller.
|
||||
* When an async executor is provided and the return type is exactly `CompletableFuture` (not a subclass), the framework runs the task on the executor and immediately returns a `CompletableFuture` to the caller.
|
||||
`CompletableFuture.supplyAsync(Supplier<U> supplier, Executor executor)` is used to create the future.
|
||||
|
||||
When the async executor is explicitly set to `null` and the return type is `CompletableFuture` *or* the return type
|
||||
is a subclass of `CompletableFuture`, the flow is invoked on the caller's thread.
|
||||
In this scenario, it is expected that the downstream flow will return a `CompletableFuture` of the appropriate type.
|
||||
* When the async executor is explicitly set to `null` and the return type is `CompletableFuture` or the return type is a subclass of `CompletableFuture`, the flow is invoked on the caller's thread.
|
||||
In this scenario, the downstream flow is expected to return a `CompletableFuture` of the appropriate type.
|
||||
|
||||
*Usage Scenarios*
|
||||
====== Usage Scenarios
|
||||
|
||||
[source, java]
|
||||
----
|
||||
|
||||
CompletableFuture<Invoice> order(Order order);
|
||||
----
|
||||
|
||||
[source, xml]
|
||||
----
|
||||
|
||||
<int:gateway service-interface="foo.Service" default-request-channel="orders" />
|
||||
----
|
||||
|
||||
In this scenario, the caller thread returns immediately with a `CompletableFuture<Invoice>` which will be completed
|
||||
In the following scenario, the caller thread returns immediately with a `CompletableFuture<Invoice>`, which is completed
|
||||
when the downstream flow replies to the gateway (with an `Invoice` object).
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
|
||||
CompletableFuture<Invoice> order(Order order);
|
||||
----
|
||||
|
||||
[source, xml]
|
||||
----
|
||||
|
||||
<int:gateway service-interface="foo.Service" default-request-channel="orders"
|
||||
async-executor="" />
|
||||
<int:gateway service-interface="something.Service" default-request-channel="orders" />
|
||||
----
|
||||
====
|
||||
|
||||
In this scenario, the caller thread will return with a CompletableFuture<Invoice> when the downstream flow provides
|
||||
In the following scenario, the caller thread returns with a `CompletableFuture<Invoice>` when the downstream flow provides
|
||||
it as the payload of the reply to the gateway.
|
||||
Some other process must complete the future when the invoice is ready.
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
CompletableFuture<Invoice> order(Order order);
|
||||
----
|
||||
|
||||
[source, xml]
|
||||
----
|
||||
<int:gateway service-interface="foo.Service" default-request-channel="orders"
|
||||
async-executor="" />
|
||||
----
|
||||
====
|
||||
|
||||
In the following scenario, the caller thread returns with a `CompletableFuture<Invoice>` when the downstream flow provides
|
||||
it as the payload of the reply to the gateway.
|
||||
Some other process must complete the future when the invoice is ready.
|
||||
If `DEBUG` logging is enabled, a log entry is emitted, indicating that the async executor cannot be used for this scenario.
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
MyCompletableFuture<Invoice> order(Order order);
|
||||
----
|
||||
|
||||
[source, xml]
|
||||
----
|
||||
|
||||
<int:gateway service-interface="foo.Service" default-request-channel="orders" />
|
||||
----
|
||||
====
|
||||
|
||||
In this scenario, the caller thread will return with a CompletableFuture<Invoice> when the downstream flow provides
|
||||
it as the payload of the reply to the gateway.
|
||||
Some other process must complete the future when the invoice is ready.
|
||||
If `DEBUG` logging is enabled, a log is emitted indicating that the async executor cannot be used for this scenario.
|
||||
|
||||
|
||||
`CompletableFuture` s can be used to perform additional manipulation on the reply, such as:
|
||||
`CompletableFuture` instances can be used to perform additional manipulation on the reply, as the following example shows:
|
||||
|
||||
===
|
||||
[source, java]
|
||||
----
|
||||
|
||||
CompletableFuture<String> process(String data);
|
||||
|
||||
...
|
||||
@@ -658,20 +707,22 @@ CompletableFuture result = process("foo")
|
||||
|
||||
String out = result.get(10, TimeUnit.SECONDS);
|
||||
----
|
||||
====
|
||||
|
||||
===== Reactor Mono
|
||||
===== Reactor `Mono`
|
||||
|
||||
Starting with _version 5.0_, the `GatewayProxyFactoryBean` allows the use of the Project Reactor with gateway interface methods, utilizing a https://github.com/reactor/reactor-core[`Mono<T>`] return type.
|
||||
Starting with version 5.0, the `GatewayProxyFactoryBean` allows the use of https://projectreactor.io/[Project Reactor] with gateway interface methods, using a https://github.com/reactor/reactor-core[`Mono<T>`] return type.
|
||||
The internal `AsyncInvocationTask` is wrapped in a `Mono.fromCallable()`.
|
||||
|
||||
A `Mono` can be used to retrieve the result later (similar to a `Future<?>`) or you can consume from it with the dispatcher invoking your `Consumer` when the result is returned to the gateway.
|
||||
A `Mono` can be used to retrieve the result later (similar to a `Future<?>`), or you can consume from it with the dispatcher by invoking your `Consumer` when the result is returned to the gateway.
|
||||
|
||||
IMPORTANT: The `Mono` isn't _flushed_ immediately by the framework.
|
||||
Hence the underlying message flow won't be started before the gateway method returns (as it is with `Future<?>` `Executor` task).
|
||||
The flow will be started when the `Mono` is _subscribed_.
|
||||
Alternatively, the `Mono` (being a *Composable*) might be a part of Reactor stream, when the `subscribe()` is related to the entire `Flux`.
|
||||
For example:
|
||||
IMPORTANT: The `Mono` is not immediately flushed by the framework.
|
||||
Consequently, the underlying message flow is not started before the gateway method returns (as it is with a `Future<?>` `Executor` task).
|
||||
The flow starts when the `Mono` is subscribed to.
|
||||
Alternatively, the `Mono` (being a `Composable`) might be a part of Reactor stream, when the `subscribe()` is related to the entire `Flux`.
|
||||
The following example shows how to create a gateway with Project Reactor:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@MessagingGateway
|
||||
@@ -698,73 +749,86 @@ public static interface TestGateway {
|
||||
.subscribe(integers -> ...);
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
Another example is a simple callback scenario:
|
||||
Another example that uses Project Reactor is a simple callback scenario, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Mono<Invoice> mono = service.process(myOrder);
|
||||
|
||||
mono.subscribe(invoice -> handleInvoice(invoice));
|
||||
----
|
||||
====
|
||||
|
||||
The calling thread continues, with `handleInvoice()` being called when the flow completes.
|
||||
|
||||
[[gateway-no-response]]
|
||||
==== Gateway behavior when no response arrives
|
||||
==== Gateway Behavior When No response Arrives
|
||||
|
||||
As it was explained earlier, the Gateway provides a convenient way of interacting with a Messaging system via POJO method invocations, but realizing that a typical method invocation, which is generally expected to always return (even with an Exception), might not always map one-to-one to message exchanges (e.g., a reply message might not arrive - which is equivalent to a method not returning).
|
||||
It is important to go over several scenarios especially in the Sync Gateway case and understand the default behavior of the Gateway and how to deal with these scenarios to make the Sync Gateway behavior more predictable regardless of the outcome of the message flow that was initialed from such Gateway.
|
||||
As <<gateway-proxy,explained earlier>>, the gateway provides a convenient way of interacting with a messaging system through POJO method invocations.
|
||||
However, a typical method invocation, which is generally expected to always return (even with an Exception), might not always map one-to-one to message exchanges (for example, a reply message might not arrive -- the equivalent to a method not returning).
|
||||
|
||||
There are certain attributes that could be configured to make Sync Gateway behavior more predictable, but some of them might not always work as you might have expected.
|
||||
One of them is _reply-timeout_ (at the method level or _default-reply-timeout_ at the gateway level).
|
||||
So, lets look at the _reply-timeout_ attribute and see how it can/can't influence the behavior of the Sync Gateway in various scenarios.
|
||||
We will look at single-threaded scenario (all components downstream are connected via Direct Channel) and multi-threaded scenarios (e.g., somewhere downstream you may have Pollable or Executor Channel which breaks single-thread boundary)
|
||||
The rest of this section covers various scenarios and how to make the gateway behave more predictably.
|
||||
Certain attributes can be configured to make synchronous gateway behavior more predictable, but some of them might not always work as you might expect.
|
||||
One of them is `reply-timeout` (at the method level or `default-reply-timeout` at the gateway level).
|
||||
We examine the `reply-timeout` attribute to see how it can and cannot influence the behavior of the synchronous gateway in various scenarios.
|
||||
We examine a single-threaded scenario (all components downstream are connected through a direct channel) and multi-threaded scenarios (for example, somewhere downstream you may have a pollable or executor channel that breaks the single-thread boundary).
|
||||
|
||||
_Long running process downstream_
|
||||
[[long-running-process-downstream]]
|
||||
===== Long-running Process Downstream
|
||||
|
||||
_Sync Gateway - single-threaded_.
|
||||
If a component downstream is still running (e.g., infinite loop or a very slow service), then setting a _reply-timeout_ has no effect and the Gateway method call will not return until such downstream service exits (via return or exception).
|
||||
_Sync Gateway - multi-threaded_.
|
||||
If a component downstream is still running (e.g., infinite loop or a very slow service), in a multi-threaded message flow setting the _reply-timeout_ will have an effect by allowing gateway method invocation to return once the timeout has been reached, since the `GatewayProxyFactoryBean` will simply poll on the reply channel waiting for a message until the timeout expires.
|
||||
However it could result in a 'null' return from the Gateway method if the timeout has been reached before the actual reply was produced. It is also important to understand that the reply message (if produced) will be sent to a reply channel after the Gateway method invocation might have returned, so you must be aware of that and design your flow with this in mind.
|
||||
Sync Gateway, single-threaded::
|
||||
If a component downstream is still running (perhaps because of an infinite loop or a slow service), setting a `reply-timeout` has no effect, and the gateway method call does not return until the downstream service exits (by returning or throwing an exception).
|
||||
Sync Gateway, multi-threaded::
|
||||
If a component downstream is still running (perhaps because of an infinite loop or a slow service) in a multi-threaded message flow, setting the `reply-timeout` has an effect by allowing gateway method invocation to return once the timeout has been reached, because the `GatewayProxyFactoryBean` polls on the reply channel, waiting for a message until the timeout expires.
|
||||
However, if the timeout has been reached before the actual reply was produced, it could result in a 'null' return from the gateway method.
|
||||
You should understand that the reply message (if produced) is sent to a reply channel after the gateway method invocation might have returned, so you must be aware of that and design your flow with it in mind.
|
||||
|
||||
_Downstream component returns 'null'_
|
||||
===== Downstream Component Returns 'null'
|
||||
|
||||
_Sync Gateway - single-threaded_.
|
||||
If a component downstream returns 'null' and no _reply-timeout_ has been configured, the Gateway method call will hang indefinitely unless: a) a _reply-timeout_ has been configured or b) the _requires-reply_ attribute has been set on the downstream component (e.g., service-activator) that might return 'null'.
|
||||
In this case, an Exception would be thrown and propagated to the Gateway._Sync Gateway - multi-threaded_.
|
||||
Behavior is the same as above.
|
||||
Sync Gateway -- single-threaded::
|
||||
If a component downstream returns 'null' and no `reply-timeout` has been configured, the gateway method call hangs indefinitely, unless a `reply-timeout` has been configured or the `requires-reply` attribute has been set on the downstream component (for example, a service activator) that might return 'null'.
|
||||
In this case, an exception would be thrown and propagated to the gateway.
|
||||
Sync Gateway -- multi-threaded::
|
||||
The behavior is the same as the previous case.
|
||||
|
||||
_Downstream component return signature is 'void' while Gateway method signature is non-void_
|
||||
===== Downstream Component Return Signature is 'void' While Gateway Method Signature Is Non-void
|
||||
|
||||
_Sync Gateway - single-threaded_.
|
||||
If a component downstream returns 'void' and no _reply-timeout_ has been configured, the Gateway method call will hang indefinitely unless a _reply-timeout_ has been configured _Sync Gateway - multi-threaded_ Behavior is the same as above.
|
||||
Sync Gateway -- single-threaded::
|
||||
If a component downstream returns 'void' and no `reply-timeout` has been configured, the gateway method call hangs indefinitely unless a `reply-timeout` has been configured.
|
||||
Sync Gateway -- multi-threaded::
|
||||
The behavior is the same as the previous case.
|
||||
|
||||
_Downstream component results in Runtime Exception (regardless of the method signature)_
|
||||
===== Downstream Component Results in Runtime Exception
|
||||
|
||||
_Sync Gateway - single-threaded_.
|
||||
If a component downstream throws a Runtime Exception, such exception will be propagated via an Error Message back to the gateway and re-thrown.
|
||||
_Sync Gateway - multi-threaded_ Behavior is the same as above.
|
||||
Sync Gateway -- single-threaded::
|
||||
If a component downstream throws a runtime exception, the exception is propagated through an error message back to the gateway and re-thrown.
|
||||
Sync Gateway -- multi-threaded::
|
||||
The behavior is the same as the previous case.
|
||||
|
||||
IMPORTANT: It is also important to understand that by default _reply-timeout_ is unbounded* which means that if not explicitly set there are several scenarios (described above) where your Gateway method invocation might hang indefinitely.
|
||||
So, make sure you analyze your flow and if there is even a remote possibility of one of these scenarios to occur, set the _reply-timeout_ attribute to a 'safe' value or, even better, set the _requires-reply_ attribute of the downstream component to 'true' to ensure a timely response as produced by the throwing of an Exception as soon as that downstream component does return null internally.
|
||||
But also, realize that there are some scenarios (see the very first one) where _reply-timeout_ will not help.
|
||||
That means it is also important to analyze your message flow and decide when to use a Sync Gateway vs an Async Gateway.
|
||||
As you've seen the latter case is simply a matter of defining Gateway methods that return Future instances.
|
||||
Then, you are guaranteed to receive that return value, and you will have more granular control over the results of the invocation.Also, when dealing with a Router you should remember that setting the _resolution-required_ attribute to 'true' will result in an Exception thrown by the router if it can not resolve a particular channel.
|
||||
Likewise, when dealing with a Filter, you can set the _throw-exception-on-rejection_ attribute.
|
||||
In both of these cases, the resulting flow will behave like that containing a service-activator with the 'requires-reply' attribute.
|
||||
In other words, it will help to ensure a timely response from the Gateway method invocation.
|
||||
IMPORTANT: You should understand that, by default, `reply-timeout` is unbounded.
|
||||
Consequently, if you do not explicitly set the `reply-timeout`, your gateway method invocation might hang indefinitely.
|
||||
So, to make sure you analyze your flow and if there is even a remote possibility of one of these scenarios to occur, you should set the `reply-timeout` attribute to a "'safe'" value.
|
||||
Even better, you can set the `requires-reply` attribute of the downstream component to 'true' to ensure a timely response, as produced by the throwing of an exception as soon as that downstream component returns null internally.
|
||||
However you should also realize that there are some scenarios (see <<long-running-process-downstream,the first one>>) where `reply-timeout` does not help.
|
||||
That means it is also important to analyze your message flow and decide when to use a synchronous gateway rather than an asynchrnous gateway.
|
||||
As <<async-gateway,described earlier>>, the latter case is a matter of defining gateway methods that return `Future` instances.
|
||||
Then you are guaranteed to receive that return value, and you have more granular control over the results of the invocation.
|
||||
Also, when dealing with a router, you should remember that setting the `resolution-required` attribute to 'true' results in an exception thrown by the router if it can not resolve a particular channel.
|
||||
Likewise, when dealing with a Filter, you can set the `throw-exception-on-rejection` attribute.
|
||||
In both of these cases, the resulting flow behaves like it contain a service activator with the 'requires-reply' attribute.
|
||||
In other words, it helps to ensure a timely response from the gateway method invocation.
|
||||
|
||||
NOTE: * _reply-timeout_ is unbounded for _<gateway/>_ elements (created by the GatewayProxyFactoryBean).
|
||||
Inbound gateways for external integration (ws, http, etc.) share many characteristics and attributes with these gateways.
|
||||
However, for those inbound gateways, the default _reply-timeout_ is 1000 milliseconds (1 second).
|
||||
If a downstream async hand-off is made to another thread, you may need to increase this attribute to allow enough time for the flow to complete before the gateway times out.
|
||||
NOTE: `reply-timeout` is unbounded for `<gateway/>` elements (created by the `GatewayProxyFactoryBean`).
|
||||
Inbound gateways for external integration (WS, HTTP, and so on) share many characteristics and attributes with these gateways.
|
||||
However, for those inbound gateways, the default `reply-timeout` is 1000 milliseconds (one second).
|
||||
If a downstream asynchronous hand-off is made to another thread, you may need to increase this attribute to allow enough time for the flow to complete before the gateway times out.
|
||||
|
||||
IMPORTANT: It is important to understand that the timer starts when the thread returns to the gateway, i.e. when the
|
||||
flow completes or a message is handed off to another thread.
|
||||
IMPORTANT: You should understand that the timer starts when the thread returns to the gateway -- that is, when the flow completes or a message is handed off to another thread.
|
||||
At that time, the calling thread starts waiting for the reply.
|
||||
If the flow was completely synchronous, the reply will be immediately available; for asynchronous flows, the thread
|
||||
will wait for up to this time.
|
||||
If the flow was completely synchronous, the reply is immediately available.
|
||||
For asynchronous flows, the thread waits for up to this time.
|
||||
|
||||
Also see <<java-dsl-gateway>> in the Java DSL chapter for options to define gateways via `IntegrationFlows`.
|
||||
See "`<<java-dsl-gateway>>`" in the Java DSL chapter for options to define gateways through `IntegrationFlows`.
|
||||
|
||||
@@ -3,18 +3,16 @@
|
||||
|
||||
Spring Integration provides support for Pivotal GemFire and Apache Geode
|
||||
|
||||
[[gemfire-intro]]
|
||||
=== Introduction
|
||||
|
||||
GemFire is a distributed data management platform providing a key-value data grid along with advanced distributed system features such as event processing, continuous querying, and remote function execution.
|
||||
GemFire is a distributed data management platform that provides a key-value data grid along with advanced distributed system features, such as event processing, continuous querying, and remote function execution.
|
||||
This guide assumes some familiarity with the commercial https://pivotal.io/pivotal-gemfire[Pivotal GemFire] or Open Source http://geode.apache.org[Apache Geode].
|
||||
|
||||
Spring integration provides support for GemFire implementing inbound adapters for entry and continuous query events, an outbound adapter to write entries to the cache, and Message & Metadata stores and `GemfireLockRegistry` implementations.
|
||||
Spring integration provides support for GemFire by implementing inbound adapters for entry and continuous query events, an outbound adapter to write entries to the cache, and message and metadata stores and `GemfireLockRegistry` implementations.
|
||||
Spring integration leverages the http://projects.spring.io/spring-data-gemfire[Spring Data for Pivotal GemFire] project, providing a thin wrapper over its components.
|
||||
|
||||
Starting with _version 5.1_, the Spring Integration GemFire module uses the https://github.com/spring-projects/spring-data-geode[Spring Data for Apache Geode] transitive dependency by default.
|
||||
To switch to the commercial Pivotal GemFire based Spring Data for Pivotal GemFire, exclude `spring-data-geode` from dependencies and add `spring-data-gemfire`:
|
||||
Starting with version 5.1, the Spring Integration GemFire module uses the https://github.com/spring-projects/spring-data-geode[Spring Data for Apache Geode] transitive dependency by default.
|
||||
To switch to the commercial Pivotal GemFire-based Spring Data for Pivotal GemFire, exclude `spring-data-geode` from dependencies and add `spring-data-gemfire`, as the following Maven snippet shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<dependency>
|
||||
@@ -33,23 +31,30 @@ To switch to the commercial Pivotal GemFire based Spring Data for Pivotal GemFir
|
||||
<artifactId>spring-data-gemfire</artifactId>
|
||||
</dependency>
|
||||
----
|
||||
====
|
||||
|
||||
To configure the 'int-gfe' namespace, include the following elements within the headers of your XML configuration file:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
xmlns:int-gfe="http://www.springframework.org/schema/integration/gemfire"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration/gemfire
|
||||
http://www.springframework.org/schema/integration/gemfire/spring-integration-gemfire.xsd"
|
||||
----
|
||||
====
|
||||
|
||||
[[gemfire-inbound]]
|
||||
=== Inbound Channel Adapter
|
||||
|
||||
The _inbound-channel-adapter_ produces messages on a channel triggered by a GemFire `EntryEvent`.
|
||||
GemFire generates events whenever an entry is `CREATED`, `UPDATED`, `DESTROYED` or `INVALIDATED` in the associated region.
|
||||
The inbound channel adapter allows you to filter on a subset of these events.
|
||||
For example, you may want to only produce messages in response to an entry being CREATED.
|
||||
The inbound channel adapter produces messages on a channel when triggered by a GemFire `EntryEvent`.
|
||||
GemFire generates events whenever an entry is `CREATED`, `UPDATED`, `DESTROYED`, or `INVALIDATED` in the associated region.
|
||||
The inbound channel adapter lets you filter on a subset of these events.
|
||||
For example, you may want to produce messages only in response to an entry being created.
|
||||
In addition, the inbound channel adapter can evaluate a SpEL expression if, for example, you want your message payload to contain an event property such as the new entry value.
|
||||
The following example shows how to configure an inbound channel adapter with a SpEL language (in the `expression` attribute):
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:cache/>
|
||||
@@ -57,36 +62,43 @@ In addition, the inbound channel adapter can evaluate a SpEL expression if, for
|
||||
<int-gfe:inbound-channel-adapter id="inputChannel" region="region"
|
||||
cache-events="CREATED" expression="newValue"/>
|
||||
----
|
||||
====
|
||||
|
||||
In the above configuration, we are creating a GemFire `Cache` and `Region` using Spring GemFire's 'gfe' namespace.
|
||||
The inbound-channel-adapter requires a reference to the GemFire region for which the adapter will be listening for events.
|
||||
Optional attributes include `cache-events` which can contain a comma separated list of event types for which a message will be produced on the input channel.
|
||||
By default CREATED and UPDATED are enabled.
|
||||
Note that this adapter conforms to Spring integration conventions.
|
||||
If no `channel` attribute is provided, the channel will be created from the `id` attribute.
|
||||
The preceding configuration creates a GemFire `Cache` and `Region` by using Spring GemFire's 'gfe' namespace.
|
||||
The `inbound-channel-adapter` element requires a reference to the GemFire region on which the adapter listens for events.
|
||||
Optional attributes include `cache-events`, which can contain a comma-separated list of event types for which a message is produced on the input channel.
|
||||
By default, `CREATED` and `UPDATED` are enabled.
|
||||
If no `channel` attribute is provided, the channel is created from the `id` attribute.
|
||||
This adapter also supports an `error-channel`.
|
||||
The GemFire hhttps://geode.apache.org/releases/latest/javadoc/org/apache/geode/cache/EntryEvent.html[EntryEvent] is the `#root` object of the `expression` evaluation.
|
||||
Example:
|
||||
The GemFire https://geode.apache.org/releases/latest/javadoc/org/apache/geode/cache/EntryEvent.html[`EntryEvent`] is the `#root` object of the `expression` evaluation.
|
||||
The following example shows an expression that replaces a value for a key:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
expression="new foo.MyEvent(key, oldValue, newValue)"
|
||||
expression="new something.MyEvent(key, oldValue, newValue)"
|
||||
----
|
||||
====
|
||||
|
||||
If the `expression` attribute is not provided, the message payload will be the GemFire `EntryEvent` itself.
|
||||
If the `expression` attribute is not provided, the message payload is the GemFire `EntryEvent` itself.
|
||||
|
||||
NOTE: This adapter conforms to Spring Integration conventions.
|
||||
|
||||
[[gemfire-cq]]
|
||||
=== Continuous Query Inbound Channel Adapter
|
||||
|
||||
The _cq-inbound-channel-adapter_ produces messages a channel triggered by a GemFire continuous query or `CqEvent` event.
|
||||
Spring Data introduced continuous query support in release `1.1`, including a `ContinuousQueryListenerContainer` which provides a nice abstraction over the GemFire native API.
|
||||
This adapter requires a reference to a ContinuousQueryListenerContainer, and creates a listener for a given `query` and executes the query.
|
||||
The continuous query acts as an event source that will fire whenever its result set changes state.
|
||||
The continuous query inbound channel adapter produces messages on a channel when triggered by a GemFire continuous query or `CqEvent` event.
|
||||
In release `1.1`, Spring Data introduced continuous query support, including `ContinuousQueryListenerContainer`, which provides a nice abstraction over the GemFire native API.
|
||||
This adapter requires a reference to a `ContinuousQueryListenerContainer` instance, creates a listener for a given `query`, and executes the query.
|
||||
The continuous query acts as an event source that fires whenever its result set changes state.
|
||||
|
||||
NOTE: GemFire queries are written in OQL and are scoped to the entire cache (not just one region).
|
||||
Additionally, continuous queries require a remote (i.e., running in a separate process or remote host) cache server.
|
||||
Please consult the http://gemfire82.docs.pivotal.io/docs-gemfire/gemfire_nativeclient/continuous-querying/continuous-querying.html[GemFire documentation] for more information on implementing continuous queries.
|
||||
Additionally, continuous queries require a remote (that is, running in a separate process or remote host) cache server.
|
||||
See the http://gemfire82.docs.pivotal.io/docs-gemfire/gemfire_nativeclient/continuous-querying/continuous-querying.html[GemFire documentation] for more information on implementing continuous queries.
|
||||
|
||||
The following configuration creates a GemFire client cache (recall that a remote cache server is required for this implementation and its address is configured as a child element of the pool), a client region, and a `ContinuousQueryListenerContainer` that uses Spring Data:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:client-cache id="client-cache" pool-name="client-pool"/>
|
||||
@@ -104,56 +116,67 @@ Please consult the http://gemfire82.docs.pivotal.io/docs-gemfire/gemfire_nativec
|
||||
cq-listener-container="queryListenerContainer"
|
||||
query="select * from /test"/>
|
||||
----
|
||||
====
|
||||
|
||||
In the above configuration, we are creating a GemFire client cache (recall a remote cache server is required for this implementation and its address is configured as a sub-element of the pool), a client region and a `ContinuousQueryListenerContainer` using Spring Data.
|
||||
The continuous query inbound channel adapter requires a `cq-listener-container` attribute which contains a reference to the `ContinuousQueryListenerContainer`.
|
||||
Optionally, it accepts an `expression` attribute which uses SpEL to transform the `CqEvent` or extract an individual property as needed.
|
||||
The cq-inbound-channel-adapter provides a `query-events` attribute, containing a comma separated list of event types for which a message will be produced on the input channel.
|
||||
Available event types are `CREATED`, `UPDATED`, `DESTROYED`, `REGION_DESTROYED` and `REGION_INVALIDATED`.
|
||||
`CREATED` and `UPDATED` are enabled by default.
|
||||
Additional optional attributes include, `query-name` which provides an optional query name, and `expression` which works as described in the above section, and `durable` - a boolean value indicating if the query is durable (false by default).
|
||||
Note that this adapter conforms to Spring integration conventions.
|
||||
If no `channel` attribute is provided, the channel will be created from the `id` attribute.
|
||||
The continuous query inbound channel adapter requires a `cq-listener-container` attribute, which must contain a reference to the `ContinuousQueryListenerContainer`.
|
||||
Optionally, it accepts an `expression` attribute that uses SpEL to transform the `CqEvent` or extract an individual property as needed.
|
||||
The `cq-inbound-channel-adapter` provides a `query-events` attribute that contains a comma-separated list of event types for which a message is produced on the input channel.
|
||||
The available event types are `CREATED`, `UPDATED`, `DESTROYED`, `REGION_DESTROYED`, and `REGION_INVALIDATED`.
|
||||
By default, `CREATED` and `UPDATED` are enabled.
|
||||
Additional optional attributes include `query-name` (which provides an optional query name), `expression` (which works as described in the preceding section), and `durable` (a boolean value indicating if the query is durable -- it is false by default).
|
||||
If you do not provide a `channel`, the channel is created from the `id` attribute.
|
||||
This adapter also supports an `error-channel`.
|
||||
|
||||
NOTE: This adapter conforms to Spring Integration conventions.
|
||||
|
||||
[[gemfire-outbound]]
|
||||
=== Outbound Channel Adapter
|
||||
|
||||
The _outbound-channel-adapter_ writes cache entries mapped from the message payload.
|
||||
The outbound channel adapter writes cache entries that are mapped from the message payload.
|
||||
In its simplest form, it expects a payload of type `java.util.Map` and puts the map entries into its configured region.
|
||||
The following example shows how to configure an outbound channel adapter:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-gfe:outbound-channel-adapter id="cacheChannel" region="region"/>
|
||||
----
|
||||
====
|
||||
|
||||
Given the above configuration, an exception will be thrown if the payload is not a Map.
|
||||
Additionally, the outbound channel adapter can be configured to create a map of cache entries using SpEL of course.
|
||||
Given the preceding configuration, an exception is thrown if the payload is not a `Map`.
|
||||
Additionally, you can configure the outbound channel adapter to create a map of cache entries by using SpEL.
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-gfe:outbound-channel-adapter id="cacheChannel" region="region">
|
||||
<int-gfe:cache-entries>
|
||||
<entry key="payload.toUpperCase()" value="payload.toLowerCase()"/>
|
||||
<entry key="'foo'" value="'bar'"/>
|
||||
<entry key="'thing1'" value="'thing2'"/>
|
||||
</int-gfe:cache-entries>
|
||||
</int-gfe:outbound-channel-adapter>
|
||||
----
|
||||
====
|
||||
|
||||
In the above configuration, the inner element `cache-entries` is semantically equivalent to Spring 'map' element.
|
||||
In the preceding configuration, the inner element (`cache-entries`) is semantically equivalent to a Spring 'map' element.
|
||||
The adapter interprets the `key` and `value` attributes as SpEL expressions with the message as the evaluation context.
|
||||
Note that this contain arbitrary cache entries (not only those derived from the message) and that literal values must be enclosed in single quotes.
|
||||
In the above example, if the message sent to `cacheChannel` has a String payload with a value "Hello", two entries `[HELLO:hello, foo:bar]` will be written (created or updated) in the cache region.
|
||||
This adapter also supports the `order` attribute which may be useful if it is bound to a PublishSubscribeChannel.
|
||||
Note that this can contain arbitrary cache entries (not only those derived from the message) and that literal values must be enclosed in single quotes.
|
||||
In the preceding example, if the message sent to `cacheChannel` has a `String` payload with a value `Hello`, two entries (`[HELLO:hello, thing1:thing2]`) are written (either created or updated) in the cache region.
|
||||
This adapter also supports the `order` attribute, which may be useful if it is bound to a `PublishSubscribeChannel`.
|
||||
|
||||
[[gemfire-message-store]]
|
||||
=== Gemfire Message Store
|
||||
|
||||
As described in EIP, a http://www.eaipatterns.com/MessageStore.html[Message Store] allows you to persist Messages.
|
||||
This can be very useful when dealing with components that have a capability to buffer messages (_QueueChannel, Aggregator, Resequencer_, etc.) if reliability is a concern.
|
||||
In Spring Integration, the MessageStore strategy also provides the foundation for the http://www.eaipatterns.com/StoreInLibrary.html[ClaimCheck] pattern, which is described in EIP as well.
|
||||
As described in EIP, a http://www.eaipatterns.com/MessageStore.html[message store] lets you persist messages.
|
||||
This can be useful when dealing with components that have a capability to buffer messages (`QueueChannel`, `Aggregator`, `Resequencer`, and others) if reliability is a concern.
|
||||
In Spring Integration, the `MessageStore` strategy interface also provides the foundation for the http://www.eaipatterns.com/StoreInLibrary.html[claim check] pattern, which is described in EIP as well.
|
||||
|
||||
Spring Integration's Gemfire module provides the `GemfireMessageStore` which is an implementation of both the the `MessageStore` strategy (mainly used by the _QueueChannel_ and _ClaimCheck_ patterns) and the `MessageGroupStore` strategy (mainly used by the _Aggregator_ and _Resequencer_ patterns).
|
||||
Spring Integration's Gemfire module provides `GemfireMessageStore`, which is an implementation of both the the `MessageStore` strategy (mainly used by the `QueueChannel` and `ClaimCheck` patterns) and the `MessageGroupStore` strategy (mainly used by the `Aggregator` and `Resequencer` patterns).
|
||||
|
||||
The following example configures the cache and region by using the `spring-gemfire` namespace (not to be confused with the `spring-integration-gemfire` namespace):
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="gemfireMessageStore" class="o.s.i.gemfire.store.GemfireMessageStore">
|
||||
@@ -172,11 +195,13 @@ Spring Integration's Gemfire module provides the `GemfireMessageStore` which is
|
||||
<int:aggregator input-channel="inputChannel" output-channel="outputChannel"
|
||||
message-store="gemfireMessageStore"/>
|
||||
----
|
||||
====
|
||||
|
||||
In the above example, the cache and region are configured using the spring-gemfire namespace (not to be confused with the spring-integration-gemfire namespace).
|
||||
Often it is desirable for the message store to be maintained in one or more remote cache servers in a client-server configuration.
|
||||
In this case, you configure a client cache, client region, and client pool and inject the region into the MessageStore.
|
||||
Here is an example:
|
||||
Often, it is desirable for the message store to be maintained in one or more remote cache servers in a client-server configuration.
|
||||
In this case, you should configure a client cache, a client region, and a client pool and inject the region into the `MessageStore`.
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="gemfireMessageStore"
|
||||
@@ -192,30 +217,33 @@ Here is an example:
|
||||
<gfe:server host="localhost" port="40404" />
|
||||
</gfe:pool>
|
||||
----
|
||||
====
|
||||
|
||||
Note the _pool_ element is configured with the address of a cache server (a locator may be substituted here).
|
||||
The region is configured as a 'PROXY' so that no data will be stored locally.
|
||||
The region's id corresponds to a region with the same name configured in the cache server.
|
||||
Note that the `pool` element is configured with the address of a cache server (you can substitute a locator here).
|
||||
The region is configured as a 'PROXY' so that no data is stored locally.
|
||||
The region's `id` corresponds to a region with the same name in the cache server.
|
||||
|
||||
Starting with version _4.3.12_, the `GemfireMessageStore` supports the key `prefix` option to allow distinguishing between instances of the store on the same GemFire region.
|
||||
Starting with version 4.3.12, the `GemfireMessageStore` supports the key `prefix` option to allow distinguishing between instances of the store on the same GemFire region.
|
||||
|
||||
[[gemfire-lock-registry]]
|
||||
=== Gemfire Lock Registry
|
||||
|
||||
Starting with _version 4.0_, the `GemfireLockRegistry` is available.
|
||||
Certain components (for example aggregator and resequencer) use a lock obtained from a `LockRegistry` instance to ensure that only one thread is manipulating a group at a time.
|
||||
The `DefaultLockRegistry` performs this function within a single component; you can now configure an external lock registry on these components.
|
||||
When used with a shared `MessageGroupStore`, the `GemfireLockRegistry` can be use to provide this functionality across multiple application instances, such that only one instance can manipulate the group at a time.
|
||||
Starting with version 4.0, the `GemfireLockRegistry` is available.
|
||||
Certain components (for example, the aggregator and the resequencer) use a lock obtained from a `LockRegistry` instance to ensure that only one thread is manipulating a group at any given time.
|
||||
The `DefaultLockRegistry` performs this function within a single component.
|
||||
You can now configure an external lock registry on these components.
|
||||
When you use a shared `MessageGroupStore` with the `GemfireLockRegistry`, it can provide this functionality across multiple application instances, such that only one instance can manipulate the group at a time.
|
||||
|
||||
NOTE: One of the `GemfireLockRegistry` constructors requires a `Region` as an argument; it is used to obtain a `Lock` via the `getDistributedLock()` method.
|
||||
NOTE: One of the `GemfireLockRegistry` constructors requires a `Region` as an argument.
|
||||
It is used to obtain a `Lock` from the `getDistributedLock()` method.
|
||||
This operation requires `GLOBAL` scope for the `Region`.
|
||||
Another constructor requires `Cache` and the `Region` will be created with `GLOBAL` scope and with the name `LockRegistry`.
|
||||
Another constructor requires a `Cache`, and the `Region` is created with `GLOBAL` scope and with the name, `LockRegistry`.
|
||||
|
||||
[[gemfire-metadata-store]]
|
||||
=== Gemfire Metadata Store
|
||||
|
||||
As of _version 4.0_, a new Gemfire-based `MetadataStore` (<<metadata-store>>) implementation is available.
|
||||
The `GemfireMetadataStore` can be used to maintain metadata state across application restarts.
|
||||
Version 4.0 introduced a new Gemfire-based `MetadataStore` (<<metadata-store>>) implementation.
|
||||
You can use the `GemfireMetadataStore` to maintain metadata state across application restarts.
|
||||
This new `MetadataStore` implementation can be used with adapters such as:
|
||||
|
||||
* <<twitter-inbound>>
|
||||
@@ -224,15 +252,16 @@ This new `MetadataStore` implementation can be used with adapters such as:
|
||||
* <<ftp-inbound>>
|
||||
* <<sftp-inbound>>
|
||||
|
||||
In order to instruct these adapters to use the new `GemfireMetadataStore`, simply declare a Spring bean using the bean name *metadataStore*.
|
||||
The _Twitter Inbound Channel Adapter_ and the _Feed Inbound Channel Adapter_ will both automatically pick up and use the declared `GemfireMetadataStore`.
|
||||
To get these adapters to use the new `GemfireMetadataStore`, declare a Spring bean with a bean name of `metadataStore`.
|
||||
The Twitter inbound channel adapter and the feed inbound channel adapter both automatically pick up and use the declared `GemfireMetadataStore`.
|
||||
|
||||
NOTE: The `GemfireMetadataStore` also implements `ConcurrentMetadataStore`, allowing it to be reliably shared across multiple application instances where only one instance will be allowed to store or modify a key's value.
|
||||
NOTE: The `GemfireMetadataStore` also implements `ConcurrentMetadataStore`, letting it be reliably shared across multiple application instances, where only one instance can store or modify a key's value.
|
||||
These methods give various levels of concurrency guarantees based on the scope and data policy of the region.
|
||||
They are implemented in the peer cache and client/server cache but are disallowed in peer Regions having NORMAL or EMPTY data policies.
|
||||
They are implemented in the peer cache and client-server cache but are disallowed in peer regions that have `NORMAL` or `EMPTY` data policies.
|
||||
|
||||
NOTE: Since _version 5.0_, the `GemfireMetadataStore` also implements `ListenableMetadataStore`, allowing users to listen to cache events by providing `MetadataStoreListener` instances to the store:
|
||||
NOTE: Since version 5.0, the `GemfireMetadataStore` also implements `ListenableMetadataStore`, which lets you listen to cache events by providing `MetadataStoreListener` instances to the store, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
GemfireMetadataStore metadataStore = new GemfireMetadataStore(cache);
|
||||
@@ -245,3 +274,4 @@ metadataStore.addListener(new MetadataStoreListenerAdapter() {
|
||||
|
||||
});
|
||||
----
|
||||
====
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
[[integration-graph]]
|
||||
=== Integration Graph
|
||||
|
||||
Starting with _version 4.3_, Spring Integration provides access to an application's runtime object model which can, optionally, include component metrics.
|
||||
Starting with version 4.3, Spring Integration provides access to an application's runtime object model, which can, optionally, include component metrics.
|
||||
It is exposed as a graph, which may be used to visualize the current state of the integration application.
|
||||
The `o.s.i.support.management.graph` package contains all the required classes to collect, build and render the runtime state of Spring Integration components as a single tree-like `Graph` object.
|
||||
The `IntegrationGraphServer` should be declared as a bean to build, retrieve and refresh the `Graph` object.
|
||||
The `o.s.i.support.management.graph` package contains all the required classes to collect, build, and render the runtime state of Spring Integration components as a single tree-like `Graph` object.
|
||||
The `IntegrationGraphServer` should be declared as a bean to build, retrieve, and refresh the `Graph` object.
|
||||
The resulting `Graph` object can be serialized to any format, although JSON is flexible and convenient to parse and represent on the client side.
|
||||
A simple Spring Integration application with only the default components would expose a graph as follows:
|
||||
A Spring Integration application with only the default components would expose a graph as follows:
|
||||
|
||||
====
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
@@ -67,31 +68,35 @@ A simple Spring Integration application with only the default components would e
|
||||
]
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
As you can see, the graph consists of three top-level elements.
|
||||
In the preceding example, the graph consists of three top-level elements.
|
||||
|
||||
The `contentDescriptor` graph element is pretty straightforward and contains general information about the application providing the data.
|
||||
The `name` can be customized on the `IntegrationGraphServer` bean or via `spring.application.name` application context environment property.
|
||||
Other properties are provided by the framework and allows you to distinguish a similar model from other sources.
|
||||
The `contentDescriptor` graph element contains general information about the application providing the data.
|
||||
The `name` can be customized on the `IntegrationGraphServer` bean or in the `spring.application.name` application context environment property.
|
||||
Other properties are provided by the framework and let you distinguish a similar model from other sources.
|
||||
|
||||
The `links` graph element represents connections between nodes from the `nodes` graph element and, therefore, between integration components in the source Spring Integration application.
|
||||
For example _from_ a `MessageChannel` _to_ an `EventDrivenConsumer` with some `MessageHandler`;
|
||||
or _from_ an `AbstractReplyProducingMessageHandler` _to_ a `MessageChannel`.
|
||||
For the convenience and to allow to determine a link purpose, the model is supplied with the `type` attribute.
|
||||
For example, from a `MessageChannel` to an `EventDrivenConsumer` with some `MessageHandler`
|
||||
or from an `AbstractReplyProducingMessageHandler` to a `MessageChannel`.
|
||||
For convenience and to let you determine a link's purpose, the model includes the `type` attribute.
|
||||
The possible types are:
|
||||
|
||||
- _input_ - identify the direction from `MessageChannel` to the endpoint; `inputChannel` or `requestChannel` property;
|
||||
- _output_ - the direction from `MessageHandler`, `MessageProducer` or `SourcePollingChannelAdapter` to the `MessageChannel` via an `outputChannel` or `replyChannel` property;
|
||||
- _error_ - from `MessageHandler` on `PollingConsumer` or `MessageProducer` or `SourcePollingChannelAdapter` to the `MessageChannel` via an `errorChannel` property;
|
||||
- _discard_ - from `DiscardingMessageHandler` (e.g. `MessageFilter`) to the `MessageChannel` via `errorChannel` property.
|
||||
- _route_ - from `AbstractMappingMessageRouter` (e.g. `HeaderValueRouter`) to the `MessageChannel`.
|
||||
Similar to _output_ but determined at run-time.
|
||||
May be a configured channel mapping, or a dynamically resolved channel.
|
||||
Routers will typically only retain up to 100 dynamic routes for this purpose, but this can be modified using the `dynamicChannelLimit` property.
|
||||
* `input`: Identifes the direction from `MessageChannel` to the endpoint, `inputChannel`, or `requestChannel` property
|
||||
* `output`: The direction from the `MessageHandler`, `MessageProducer`, or `SourcePollingChannelAdapter` to the `MessageChannel` through an `outputChannel` or `replyChannel` property
|
||||
* `error`: From `MessageHandler` on `PollingConsumer` or `MessageProducer` or `SourcePollingChannelAdapter` to the `MessageChannel` through an `errorChannel` property;
|
||||
* `discard`: From `DiscardingMessageHandler` (such as `MessageFilter`) to the `MessageChannel` through an `errorChannel` property.
|
||||
* `route`: From `AbstractMappingMessageRouter` (such as `HeaderValueRouter`) to the `MessageChannel`.
|
||||
Similar to `output` but determined at run-time.
|
||||
May be a configured channel mapping or a dynamically resolved channel.
|
||||
Routers typically retain only up to 100 dynamic routes for this purpose, but you can modify this value by setting the `dynamicChannelLimit` property.
|
||||
|
||||
The information from this element can be used by a visualizing tool to render connections between nodes from the `nodes` graph element, where the `from` and `to` numbers represent the value from the `nodeId` property of the linked nodes.
|
||||
For example the link `type` can be used to determine the proper _port_ on the target node:
|
||||
The information from this element can be used by a visualization tool to render connections between nodes from the `nodes` graph element, where the `from` and `to` numbers represent the value from the `nodeId` property of the linked nodes.
|
||||
For example, the `link` element can be used to determine the proper `port` on the target node.
|
||||
|
||||
The following "`text image`" shows the relationships between the types:
|
||||
|
||||
====
|
||||
----
|
||||
+---(discard)
|
||||
|
|
||||
@@ -107,16 +112,18 @@ For example the link `type` can be used to determine the proper _port_ on the ta
|
||||
|
|
||||
+---(error)
|
||||
----
|
||||
====
|
||||
|
||||
The `nodes` graph element is perhaps the most interesting because its elements contain not only the runtime components with their `componentType` s and `name` s, but can also optionally contain metrics exposed by the component.
|
||||
Node elements contain various properties which are generally self-explanatory.
|
||||
For example, expression-based components include the `expression` property containing the primary expression string for the component.
|
||||
To enable the metrics, add an `@EnableIntegrationManagement` to some `@Configuration` class or add an `<int:management/>` element to your XML configuration.
|
||||
The `nodes` graph element is perhaps the most interesting, because its elements contain not only the runtime components with their `componentType` instances and `name` values but can also optionally contain metrics exposed by the component.
|
||||
Node elements contain various properties that are generally self-explanatory.
|
||||
For example, expression-based components include the `expression` property that contains the primary expression string for the component.
|
||||
To enable the metrics, add an `@EnableIntegrationManagement` to a `@Configuration` class or add an `<int:management/>` element to your XML configuration.
|
||||
You can control exactly which components in the framework collect statistics.
|
||||
See <<metrics-management>> for complete information.
|
||||
See the `stats` attribute from the `_org.springframework.integration.errorLogger` component in the JSON example above.
|
||||
The `nullChannel` and `errorChannel` don't provide statistics information in this case, because the configuration for this example was:
|
||||
See "`<<metrics-management>>`" for complete information.
|
||||
See the `stats` attribute from the `o.s.i.errorLogger` component in the JSON example shown earlier.
|
||||
In this case, The `nullChannel` and `errorChannel` do not provide statistics information, because the configuration for this example was as follows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
@@ -133,26 +140,28 @@ public class ManagementConfiguration {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The `nodeId` represents a unique incremental identifier to distinguish one component from another.
|
||||
The `nodeId` represents a unique incremental identifier to let you distinguish one component from another.
|
||||
It is also used in the `links` element to represent a relationship (connection) of this component to others, if any.
|
||||
The `input` and `output` attributes are for the `inputChannel` and `outputChannel` properties of the `AbstractEndpoint`, `MessageHandler`, `SourcePollingChannelAdapter` or `MessageProducerSupport`.
|
||||
See the next paragraph for more information.
|
||||
The `input` and `output` attributes are for the `inputChannel` and `outputChannel` properties of the `AbstractEndpoint`, `MessageHandler`, `SourcePollingChannelAdapter`, or `MessageProducerSupport`.
|
||||
See the next section for more information.
|
||||
|
||||
==== Graph Runtime Model
|
||||
|
||||
Spring Integration components have various levels of complexity.
|
||||
For example, any polled `MessageSource` also has a `SourcePollingChannelAdapter` and a `MessageChannel` to which to send messages from the source data periodically.
|
||||
Other components might be middleware request-reply components, e.g. `JmsOutboundGateway`, with a consuming `AbstractEndpoint` to subscribe to (or poll) the `requestChannel` (`input`) for messages, and a `replyChannel` (`output`) to produce a reply message to send downstream.
|
||||
Meanwhile, any `MessageProducerSupport` implementation (e.g. `ApplicationEventListeningMessageProducer`) simply wraps some source protocol listening logic and sends messages to the `outputChannel`.
|
||||
For example, any polled `MessageSource` also has a `SourcePollingChannelAdapter` and a `MessageChannel` to which to periodically send messages from the source data.
|
||||
Other components might be middleware request-reply components (such as `JmsOutboundGateway`) with a consuming `AbstractEndpoint` to subscribe to (or poll) the `requestChannel` (`input`) for messages, and a `replyChannel` (`output`) to produce a reply message to send downstream.
|
||||
Meanwhile, any `MessageProducerSupport` implementation (such as `ApplicationEventListeningMessageProducer`) wraps some source protocol listening logic and sends messages to the `outputChannel`.
|
||||
|
||||
Within the graph, Spring Integration components are represented using the `IntegrationNode` class hierarchy, which you can find in the `o.s.i.support.management.graph` package.
|
||||
For example the `ErrorCapableDiscardingMessageHandlerNode` could be used for the `AggregatingMessageHandler` (because it has a `discardChannel` option) and can produce errors when consuming from a `PollableChannel` using a `PollingConsumer`.
|
||||
Another sample is `CompositeMessageHandlerNode` - for a `MessageHandlerChain` when subscribed to a `SubscribableChannel`, using an `EventDrivenConsumer`.
|
||||
Within the graph, Spring Integration components are represented by using the `IntegrationNode` class hierarchy, which you can find in the `o.s.i.support.management.graph` package.
|
||||
For example, you can use the `ErrorCapableDiscardingMessageHandlerNode` for the `AggregatingMessageHandler` (because it has a `discardChannel` option) and can produce errors when consuming from a `PollableChannel` by using a `PollingConsumer`.
|
||||
Another example is `CompositeMessageHandlerNode` -- for a `MessageHandlerChain` when subscribed to a `SubscribableChannel` by using an `EventDrivenConsumer`.
|
||||
|
||||
NOTE: The `@MessagingGateway` (see <<gateway>>) provides nodes for each its method, where the `name` attribute is based on the gateway's bean name and the short method signature.
|
||||
For example the gateway:
|
||||
NOTE: The `@MessagingGateway` (see "`<<gateway>>`") provides nodes for each of its method, where the `name` attribute is based on the gateway's bean name and the short method signature.
|
||||
Consider the following example of a gateway:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@MessagingGateway(defaultRequestChannel = "four")
|
||||
@@ -166,8 +175,11 @@ public interface Gate {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
produces nodes like:
|
||||
The preceding gateway produces nodes similar to the following:
|
||||
|
||||
====
|
||||
[source,json]
|
||||
|
||||
----
|
||||
@@ -196,28 +208,32 @@ produces nodes like:
|
||||
"errors" : null
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
This `IntegrationNode` hierarchy can be used for parsing the graph model on the client side, as well as for the understanding the general Spring Integration runtime behavior.
|
||||
See also <<programming-tips>> for more information.
|
||||
You can use this `IntegrationNode` hierarchy for parsing the graph model on the client side as well as to understand the general Spring Integration runtime behavior.
|
||||
See also "`<<programming-tips>>`" for more information.
|
||||
|
||||
=== Integration Graph Controller
|
||||
|
||||
If your application is WEB-based (or built on top of Spring Boot using an embedded web container) and the Spring Integration HTTP or WebFlux module (see <<http>> and <<webflux>>) is present on the classpath, you can use a `IntegrationGraphController` to expose the `IntegrationGraphServer` functionality as a REST service.
|
||||
For this purpose, the `@EnableIntegrationGraphController` `@Configuration` class annotation and the `<int-http:graph-controller/>` XML element, are available in the HTTP module.
|
||||
Together with the `@EnableWebMvc` annotation (or `<mvc:annotation-driven/>` for xml definitions), this configuration registers an `IntegrationGraphController` `@RestController` where its `@RequestMapping.path` can be configured on the `@EnableIntegrationGraphController` annotation or `<int-http:graph-controller/>` element.
|
||||
If your application is web-based (or built on top of Spring Boot with an embedded web container) and the Spring Integration HTTP or WebFlux module (see "`<<http>>`" and "`<<webflux>>`", respectively) is present on the classpath, you can use a `IntegrationGraphController` to expose the `IntegrationGraphServer` functionality as a REST service.
|
||||
For this purpose, the `@EnableIntegrationGraphController` and `@Configuration` class annotations and the `<int-http:graph-controller/>` XML element are available in the HTTP module.
|
||||
Together with the `@EnableWebMvc` annotation (or `<mvc:annotation-driven/>` for XML definitions), this configuration registers an `IntegrationGraphController` `@RestController` where its `@RequestMapping.path` can be configured on the `@EnableIntegrationGraphController` annotation or `<int-http:graph-controller/>` element.
|
||||
The default path is `/integration`.
|
||||
|
||||
The `IntegrationGraphController` `@RestController` provides these services:
|
||||
The `IntegrationGraphController` `@RestController` provides the following services:
|
||||
|
||||
- `@GetMapping(name = "getGraph")` - to retrieve the state of the Spring Integration components since the last `IntegrationGraphServer` refresh.
|
||||
The `o.s.i.support.management.graph.Graph` is returned as a `@ResponseBody` of the REST service;
|
||||
- `@GetMapping(path = "/refresh", name = "refreshGraph")` - to refresh the current `Graph` for the actual runtime state and return it as a REST response.
|
||||
It is not necessary to refresh the graph for metrics, they are provided in real-time when the graph is retrieved.
|
||||
Refresh can be called if the application context has been modified since the graph was last retrieved and the graph is completely rebuilt.
|
||||
* `@GetMapping(name = "getGraph")`: To retrieve the state of the Spring Integration components since the last `IntegrationGraphServer` refresh.
|
||||
The `o.s.i.support.management.graph.Graph` is returned as a `@ResponseBody` of the REST service.
|
||||
* `@GetMapping(path = "/refresh", name = "refreshGraph")`: To refresh the current `Graph` for the actual runtime state and return it as a REST response.
|
||||
It is not necessary to refresh the graph for metrics.
|
||||
They are provided in real-time when the graph is retrieved.
|
||||
Refresh can be called if the application context has been modified since the graph was last retrieved.
|
||||
In that case, the graph is completely rebuilt.
|
||||
|
||||
Any Security and Cross Origin restrictions for the `IntegrationGraphController` can be achieved with the standard configuration options and components provided by Spring Security and Spring MVC projects.
|
||||
A simple example of that follows:
|
||||
You can set security and cross-origin restrictions for the `IntegrationGraphController` with the standard configuration options and components provided by the Spring Security and Spring MVC projects.
|
||||
The following example achieves those goals:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<mvc:annotation-driven />
|
||||
@@ -235,10 +251,11 @@ A simple example of that follows:
|
||||
|
||||
<int-http:graph-controller path="/myIntegration" />
|
||||
----
|
||||
====
|
||||
|
||||
The Java & Annotation Configuration variant follows; note that, for convenience, the annotation provides an `allowedOrigins` attribute; this just provides `GET` access to the `path`.
|
||||
For more sophistication, you can configure the CORS mappings using standard Spring MVC mechanisms.
|
||||
The following example shows how to do the same thing with Java configuration:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
@@ -261,3 +278,8 @@ public class IntegrationConfiguration extends WebSecurityConfigurerAdapter {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Note that, for convenience, the `@EnableIntegrationGraphController` annotation provides an `allowedOrigins` attribute.
|
||||
This provides `GET` access to the `path`.
|
||||
For more sophistication, you can configure the CORS mappings by using standard Spring MVC mechanisms.
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
[[groovy]]
|
||||
=== Groovy support
|
||||
|
||||
In Spring Integration 2.0 we added Groovy support allowing you to use the Groovy scripting language to provide the logic for various integration components similar to the way the Spring Expression Language (SpEL) is supported for routing, transformation and other integration concerns.
|
||||
For more information about Groovy please refer to the Groovy documentation which you can find on the http://www.groovy-lang.org/[project website].
|
||||
In Spring Integration 2.0, we added Groovy support, letting you use the Groovy scripting language to provide the logic for various integration components -- similar to the way the Spring Expression Language (SpEL) is supported for routing, transformation, and other integration concerns.
|
||||
For more information about Groovy, see the Groovy documentation, which you can find on the http://www.groovy-lang.org/[project website].
|
||||
|
||||
[[groovy-config]]
|
||||
==== Groovy configuration
|
||||
==== Groovy Configuration
|
||||
|
||||
With Spring Integration 2.1, Groovy Support's configuration namespace is an extension of Spring Integration's Scripting Support and shares the core configuration and behavior described in detail in the <<scripting,Scripting Support>> section.
|
||||
Even though Groovy scripts are well supported by generic Scripting Support, Groovy Support provides the _Groovy_ configuration namespace which is backed by the Spring Framework's `org.springframework.scripting.groovy.GroovyScriptFactory` and related components, offering extended capabilities for using Groovy.
|
||||
Below are a couple of sample configurations:
|
||||
With Spring Integration 2.1, the configuration namespace for the Groovy support is an extension of Spring Integration's scripting support and shares the core configuration and behavior described in detail in the "`<<scripting>>`" section.
|
||||
Even though Groovy scripts are well supported by generic scripting support, the Groovy support provides the `Groovy` configuration namespace, which is backed by the Spring Framework's `org.springframework.scripting.groovy.GroovyScriptFactory` and related components, offering extended capabilities for using Groovy.
|
||||
The following listing shows two sample configurations:
|
||||
|
||||
_Filter_
|
||||
.Filter
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:filter input-channel="referencedScriptInput">
|
||||
@@ -24,29 +25,37 @@ _Filter_
|
||||
]]></int-groovy:script>
|
||||
</int:filter>
|
||||
----
|
||||
====
|
||||
|
||||
As the above examples show, the configuration looks identical to the general Scripting Support configuration.
|
||||
The only difference is the use of the Groovy namespace as indicated in the examples by the _int-groovy_ namespace prefix.
|
||||
As the preceding examples show, the configuration looks identical to the general scripting support configuration.
|
||||
The only difference is the use of the Groovy namespace, as indicated by the `int-groovy` namespace prefix.
|
||||
Also note that the `lang` attribute on the `<script>` tag is not valid in this namespace.
|
||||
|
||||
_Groovy object customization_
|
||||
==== Groovy Object Customization
|
||||
|
||||
If you need to customize the Groovy object itself, beyond setting variables, you can reference a bean that implements `GroovyObjectCustomizer` via the `customizer` attribute.
|
||||
For example, this might be useful if you want to implement a domain-specific language (DSL) by modifying the `MetaClass` and registering functions to be available within the script:
|
||||
If you need to customize the Groovy object itself (beyond setting variables) you can reference a bean that implements `GroovyObjectCustomizer` by using the `customizer` attribute.
|
||||
For example, this might be useful if you want to implement a domain-specific language (DSL) by modifying the `MetaClass` and registering functions to be available within the script.
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:service-activator input-channel="groovyChannel">
|
||||
<int-groovy:script location="foo/SomeScript.groovy" customizer="groovyCustomizer"/>
|
||||
<int-groovy:script location="somewhere/SomeScript.groovy" customizer="groovyCustomizer"/>
|
||||
</int:service-activator>
|
||||
|
||||
<beans:bean id="groovyCustomizer" class="org.foo.MyGroovyObjectCustomizer"/>
|
||||
<beans:bean id="groovyCustomizer" class="org.something.MyGroovyObjectCustomizer"/>
|
||||
----
|
||||
====
|
||||
|
||||
Setting a custom `GroovyObjectCustomizer` is not mutually exclusive with `<variable>` sub-elements or the `script-variable-generator` attribute.
|
||||
Setting a custom `GroovyObjectCustomizer` is not mutually exclusive with `<variable>` elements or the `script-variable-generator` attribute.
|
||||
It can also be provided when defining an inline script.
|
||||
|
||||
With _Spring Integration 3.0_, in addition to the `variable` sub-element, the `variables` attribute has been introduced.
|
||||
Also, groovy scripts have the ability to resolve a variable to a bean in the `BeanFactory`, if a binding variable was not provided with the name:
|
||||
Spring Integration 3.0 introduced the `variables` attribute, which works in conjunction with the `variable` element.
|
||||
Also, groovy scripts have the ability to resolve a variable to a bean in the `BeanFactory`, if a binding variable was not provided with the name.
|
||||
The following example shows how to use a variable (`entityManager`):
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-groovy:script>
|
||||
@@ -56,28 +65,30 @@ Also, groovy scripts have the ability to resolve a variable to a bean in the `Be
|
||||
]]>
|
||||
</int-groovy:script>
|
||||
----
|
||||
====
|
||||
|
||||
where variable `entityManager` is an appropriate bean in the application context.
|
||||
`entityManager` must be an appropriate bean in the application context.
|
||||
|
||||
For more information regarding `<variable>`, `variables`, and `script-variable-generator`, see the paragraph '_Script variable bindings_' of <<scripting-config>>.
|
||||
For more information regarding the `<variable>` element, the `variables` attribute, and the `script-variable-generator` attribute, see "`<<scripting-script-variable-bindings>>`".
|
||||
|
||||
_Groovy Script Compiler Customization_
|
||||
==== Groovy Script Compiler Customization
|
||||
|
||||
The `@CompileStatic` hint is the most popular Groovy compiler customization option,
|
||||
which can be used on the class or method level.
|
||||
See more information in the Groovy
|
||||
http://docs.groovy-lang.org/latest/html/documentation/index.html#_static_compilation[Reference Manual] and,
|
||||
specifically, http://docs.groovy-lang.org/latest/html/documentation/index.html#compilestatic-annotation[@CompileStatic].
|
||||
To utilize this feature for short scripts (in integration scenarios), we are forced to change a simple script like this
|
||||
(a `<filter>` script):
|
||||
The `@CompileStatic` hint is the most popular Groovy compiler customization option.
|
||||
It can be used on the class or method level.
|
||||
For more information, see the Groovy http://docs.groovy-lang.org/latest/html/documentation/index.html#_static_compilation[Reference Manual] and, specifically, http://docs.groovy-lang.org/latest/html/documentation/index.html#compilestatic-annotation[@CompileStatic].
|
||||
To utilize this feature for short scripts (in integration scenarios), we are forced to change simple scripts to more Java-like code.
|
||||
Consider the following `<filter>` script:
|
||||
|
||||
====
|
||||
[source,groovy]
|
||||
----
|
||||
headers.type == 'good'
|
||||
----
|
||||
====
|
||||
|
||||
to more Java-like code:
|
||||
The preceding script becomes the following method in Spring Integration:
|
||||
|
||||
====
|
||||
[source,groovy]
|
||||
----
|
||||
@groovy.transform.CompileStatic
|
||||
@@ -87,61 +98,60 @@ String filter(Map headers) {
|
||||
|
||||
filter(headers)
|
||||
----
|
||||
====
|
||||
|
||||
With that, the `filter()` method will be transformed and compiled to static Java code, bypassing the Groovy
|
||||
dynamic phases of invocation, like `getProperty()` factories and `CallSite` proxies.
|
||||
With that, the `filter()` method is transformed and compiled to static Java code, bypassing the Groovy
|
||||
dynamic phases of invocation, such as `getProperty()` factories and `CallSite` proxies.
|
||||
|
||||
Starting with _version 4.3_, Spring Integration Groovy components can be configured with the `compile-static` `boolean`
|
||||
option, specifying that `ASTTransformationCustomizer` for `@CompileStatic` should be added to the internal
|
||||
`CompilerConfiguration`.
|
||||
With that in place, we can omit the method declaration with `@CompileStatic` in our script code and still get compiled
|
||||
Starting with version 4.3, you can configure the Spring Integration Groovy components with the `compile-static` `boolean` option, specifying that `ASTTransformationCustomizer` for `@CompileStatic` should be added to the internal `CompilerConfiguration`.
|
||||
With that in place, you can omit the method declaration with `@CompileStatic` in our script code and still get compiled
|
||||
plain Java code.
|
||||
In this case our script can still be short but still needs to be a little more verbose than interpreted script:
|
||||
In this case, the preceding script can be short but still needs to be a little more verbose than interpreted script, as the following example shows:
|
||||
|
||||
====
|
||||
[source,groovy]
|
||||
----
|
||||
binding.variables.headers.type == 'good'
|
||||
----
|
||||
Where we can access the `headers` and `payload` (or any other) variables only through the `groovy.lang.Script`
|
||||
`binding` property since, with `@CompileStatic`, we don't have the dynamic `GroovyObject.getProperty()` capability.
|
||||
====
|
||||
|
||||
In addition, the `compiler-configuration` bean reference has been introduced.
|
||||
With this attribute, you can provide any other required Groovy compiler customizations, e.g. `ImportCustomizer`.
|
||||
For more information about this feature, please, refer to the Groovy Documentation:
|
||||
http://groovy.jmiguel.eu/groovy.codehaus.org/Advanced+compiler+configuration.html[Advanced compiler configuration].
|
||||
You must access the `headers` and `payload` (or any other) variables through the `groovy.lang.Script` `binding` property because, with `@CompileStatic`, we do not have the dynamic `GroovyObject.getProperty()` capability.
|
||||
|
||||
NOTE: Using `compilerConfiguration` does not automatically add a `ASTTransformationCustomizer` for `@CompileStatic`
|
||||
and overrides the `compileStatic` option.
|
||||
If `CompileStatic` is still requirement, a `new ASTTransformationCustomizer(CompileStatic.class)` should be manually
|
||||
added into the `CompilationCustomizers` of that custom `compilerConfiguration`.
|
||||
In addition, we introduced the `compiler-configuration` bean reference.
|
||||
With this attribute, you can provide any other required Groovy compiler customizations, such as `ImportCustomizer`.
|
||||
For more information about this feature, see the Groovy Documentation for http://groovy.jmiguel.eu/groovy.codehaus.org/Advanced+compiler+configuration.html[advanced compiler configuration].
|
||||
|
||||
NOTE: The Groovy compiler customization does not have any effect to the `refresh-check-delay` option
|
||||
and reloadable scripts can be statically compiled, too.
|
||||
NOTE: Using `compilerConfiguration` does not automatically add an `ASTTransformationCustomizer` for the `@CompileStatic` annotation, and it overrides the `compileStatic` option.
|
||||
If you still need `CompileStatic`, you should manually add a `new ASTTransformationCustomizer(CompileStatic.class)` into the `CompilationCustomizers` of that custom `compilerConfiguration`.
|
||||
|
||||
NOTE: The Groovy compiler customization does not have any effect on the `refresh-check-delay` option, and reloadable scripts can be statically compiled, too.
|
||||
|
||||
[[groovy-control-bus]]
|
||||
==== Control Bus
|
||||
|
||||
As described in (http://www.eaipatterns.com/ControlBus.html[EIP]), 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 it's possible to send Messages as a means of invoking exposed operations.
|
||||
As described in (http://www.eaipatterns.com/ControlBus.html[Enterprise Integration Patterns]), the idea behind the control bus is that you can use the same messaging system 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 earlier so that you can send Messages as a means of invoking exposed operations.
|
||||
One option for those operations is Groovy scripts.
|
||||
The following example configures a Groovy script for the control bus:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-groovy: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.
|
||||
The control bus has an input channel that can be accessed to invoke operations on the beans in the application context.
|
||||
|
||||
The Groovy Control Bus executes messages on the input channel as Groovy scripts.
|
||||
It takes a message, compiles the body to a Script, customizes it with a `GroovyObjectCustomizer`, and then executes it.
|
||||
The Control Bus' `MessageProcessor` exposes all beans in the application context that are annotated with `@ManagedResource`, implement Spring's `Lifecycle` interface or extend Spring's `CustomizableThreadCreator` base class (e.g.
|
||||
several of the `TaskExecutor` and `TaskScheduler` implementations).
|
||||
The Groovy control bus runs messages on the input channel as Groovy scripts.
|
||||
It takes a message, compiles the body to a script, customizes it with a `GroovyObjectCustomizer`, and runs it.
|
||||
The control bus' `MessageProcessor` exposes all beans in the application context that are annotated with `@ManagedResource` and implement Spring's `Lifecycle` interface or extend Spring's `CustomizableThreadCreator` base class (for example, several of the `TaskExecutor` and `TaskScheduler` implementations).
|
||||
|
||||
IMPORTANT: Be careful about using managed beans with custom scopes (e.g.
|
||||
'request') in the Control Bus' command scripts, especially inside an _async_ message flow.
|
||||
If The Control Bus' `MessageProcessor` can't expose a bean from the application context, you may end up with some `BeansException` during _command script's_ executing.
|
||||
For example, if a custom scope's context is not established, the attempt to get a bean within that scope will trigger a `BeanCreationException`.
|
||||
IMPORTANT: Be careful about using managed beans with custom scopes (such as 'request') in the Control Bus' command scripts, especially inside an asynchronous message flow.
|
||||
If `MessageProcessor` of the control bus cannot expose a bean from the application context, you may end up with some `BeansException` during the command script's run.
|
||||
For example, if a custom scope's context is not established, the attempt to get a bean within that scope triggers a `BeanCreationException`.
|
||||
|
||||
If you need to further customize the Groovy objects, you can also provide a reference to a bean that implements `GroovyObjectCustomizer` via the `customizer` attribute.
|
||||
If you need to further customize the Groovy objects, you can also provide a reference to a bean that implements `GroovyObjectCustomizer` through the `customizer` attribute, as the following example shows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
[[message-handler-advice-chain]]
|
||||
=== Adding Behavior to Endpoints
|
||||
|
||||
[[mhac-intro]]
|
||||
==== Introduction
|
||||
|
||||
Prior to Spring Integration _2.2_, you could add behavior to an entire Integration flow by adding an AOP Advice to a poller's `<advice-chain/>` element.
|
||||
However, let's say you want to retry, say, just a REST Web Service call, and not any downstream endpoints.
|
||||
Prior to Spring Integration 2.2, you could add behavior to an entire Integration flow by adding an AOP Advice to a poller's `<advice-chain/>` element.
|
||||
However, suppose you want to retry, say, just a REST Web Service call, and not any downstream endpoints.
|
||||
|
||||
For example, consider the following flow:
|
||||
|
||||
_inbound-adapter->poller->http-gateway1->http-gateway2->jdbc-outbound-adapter_
|
||||
|
||||
If you configure some retry-logic into an advice chain on the poller, and, the call to _http-gateway2_ failed because of a network glitch, the retry would cause both _http-gateway1_ and _http-gateway2_ to be called a second time.
|
||||
Similarly, after a transient failure in the _jdbc-outbound-adapter_, both http-gateways would be called a second time before again calling the _jdbc-outbound-adapter_.
|
||||
====
|
||||
[source]
|
||||
inbound-adapter->poller->http-gateway1->http-gateway2->jdbc-outbound-adapter
|
||||
====
|
||||
|
||||
If you configure some retry-logic into an advice chain on the poller and the call to `http-gateway2` failed because of a network glitch, the retry causes both `http-gateway1` and `http-gateway2` to be called a second time.
|
||||
Similarly, after a transient failure in the jdbc-outbound-adapter, both HTTP gateways aree called a second time before again calling the `jdbc-outbound-adapter`.
|
||||
|
||||
Spring Integration 2.2 adds the ability to add behavior to individual endpoints.
|
||||
This is achieved by the addition of the `<request-handler-advice-chain/>` element to many endpoints.
|
||||
For example:
|
||||
The following example shows how to the `<request-handler-advice-chain/>` element within an `outbound-gateway`:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
@@ -30,78 +31,75 @@ For example:
|
||||
</int-http:outbound-gateway>
|
||||
----
|
||||
|
||||
In this case, _myRetryAdvice_ will only be applied locally to this gateway and will not apply to further actions taken downstream after the reply is sent to the _nextChannel_.
|
||||
In this case, `myRetryAdvice` is applied only locally to this gateway and does not apply to further actions taken downstream after the reply is sent to `nextChannel`.
|
||||
The scope of the advice is limited to the endpoint itself.
|
||||
|
||||
[IMPORTANT]
|
||||
=====
|
||||
At this time, you cannot advise an entire `<chain/>` of endpoints.
|
||||
The schema does not allow a `<request-handler-advice-chain/>` as a child element of the chain itself.
|
||||
The schema does not allow a `<request-handler-advice-chain>` as a child element of the chain itself.
|
||||
|
||||
However, a `<request-handler-advice-chain/>` can be added to individual reply-producing endpoints _within_ a `<chain/>` element.
|
||||
An exception is that, in a chain that produces no reply, because the last element in the chain is an _outbound-channel-adapter_, that _last_ element cannot be advised.
|
||||
If you need to advise such an element, it must be moved outside of the chain (with the _output-channel_ of the chain being the _input-channel_ of the adapter.
|
||||
The adapter can then be advised as normal.
|
||||
However, a `<request-handler-advice-chain>` can be added to individual reply-producing endpoints within a `<chain>` element.
|
||||
An exception is that, in a chain that produces no reply, because the last element in the chain is an `outbound-channel-adapter`, that last element cannot be advised.
|
||||
If you need to advise such an element, it must be moved outside of the chain (with the `output-channel` of the chain being the `input-channel` of the adapter).
|
||||
The adapter can then be advised as usual.
|
||||
For chains that produce a reply, every child element can be advised.
|
||||
=====
|
||||
|
||||
[[advice-classes]]
|
||||
==== Provided Advice Classes
|
||||
|
||||
In addition to providing the general mechanism to apply AOP Advice classes in this way, three standard Advices are provided:
|
||||
In addition to providing the general mechanism to apply AOP advice classes, Spring Integration provides three standard advice classes:
|
||||
|
||||
* `RequestHandlerRetryAdvice`
|
||||
* `RequestHandlerCircuitBreakerAdvice`
|
||||
* `ExpressionEvaluatingRequestHandlerAdvice`
|
||||
|
||||
These are each described in detail in the following sections.
|
||||
* `RequestHandlerRetryAdvice` (described in <<retry-advice>>)
|
||||
* `RequestHandlerCircuitBreakerAdvice` (described in <<circuit-breaker-advice>>)
|
||||
* `ExpressionEvaluatingRequestHandlerAdvice` (described in <<expression-advice>>)
|
||||
|
||||
[[retry-advice]]
|
||||
===== Retry Advice
|
||||
|
||||
The retry advice (`o.s.i.handler.advice.RequestHandlerRetryAdvice`) leverages the rich retry mechanisms provided by the https://github.com/spring-projects/spring-retry[Spring Retry] project.
|
||||
The core component of `spring-retry` is the `RetryTemplate`, which allows configuration of sophisticated retry scenarios, including `RetryPolicy` and `BackoffPolicy` strategies, with a number of implementations, as well as a `RecoveryCallback` strategy to determine the action to take when retries are exhausted.
|
||||
The core component of `spring-retry` is the `RetryTemplate`, which allows configuration of sophisticated retry scenarios, including `RetryPolicy` and `BackoffPolicy` strategies (with a number of implementations) as well as a `RecoveryCallback` strategy to determine the action to take when retries are exhausted.
|
||||
|
||||
*Stateless Retry*
|
||||
Stateless Retry::
|
||||
Stateless retry is the case where the retry activity is handled entirely within the advice. The thread pauses (if configured to do so) and retries the action.
|
||||
|
||||
Stateless retry is the case where the retry activity is handled entirely within the advice, where the thread pauses (if so configured) and retries the action.
|
||||
|
||||
*Stateful Retry*
|
||||
|
||||
Stateful retry is the case where the retry state is managed within the advice, but where an exception is thrown and the caller resubmits the request.
|
||||
An example for stateful retry is when we want the message originator (e.g.
|
||||
JMS) to be responsible for resubmitting, rather than performing it on the current thread.
|
||||
Stateful Retry::
|
||||
Stateful retry is the case where the retry state is managed within the advice but where an exception is thrown and the caller resubmits the request.
|
||||
An example for stateful retry is when we want the message originator (for example,JMS) to be responsible for resubmitting, rather than performing it on the current thread.
|
||||
Stateful retry needs some mechanism to detect a retried submission.
|
||||
|
||||
*Further Information*
|
||||
For more information on `spring-retry`, see https://docs.spring.io/spring-integration/api/[the project's Javadoc] and the reference documentation for http://docs.spring.io/spring-batch/reference/html/retry.html[Spring Batch], where `spring-retry` originated.
|
||||
|
||||
For more information on `spring-retry`, refer to the project's javadocs, as well as the reference documentation for http://docs.spring.io/spring-batch/reference/html/retry.html[Spring Batch], where `spring-retry` originated.
|
||||
|
||||
WARNING: The default back off behavior is no back off - retries are attempted immediately.
|
||||
WARNING: The default back off behavior is to not back off. Retries are attempted immediately.
|
||||
Using a back off policy that causes threads to pause between attempts may cause performance issues, including excessive memory use and thread starvation.
|
||||
In high volume environments, back off policies should be used with caution.
|
||||
In high-volume environments, back off policies should be used with caution.
|
||||
|
||||
[[retry-config]]
|
||||
====== Configuring the Retry Advice
|
||||
|
||||
The following examples use a simple `<service-activator/>` that always throws an exception:
|
||||
The examples in this section use the following `<service-activator>` that always throws an exception:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class FailingService {
|
||||
|
||||
public void service(String message) {
|
||||
throw new RuntimeException("foo");
|
||||
throw new RuntimeException("error");
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
*Simple Stateless Retry*
|
||||
|
||||
This example uses the default `RetryTemplate` which has a `SimpleRetryPolicy` which tries 3 times.
|
||||
There is no `BackOffPolicy` so the 3 attempts are made back-to-back-to-back with no delay between attempts.
|
||||
There is no `RecoveryCallback` so, the result is to throw the exception to the caller after the final failed retry occurs.
|
||||
In a _Spring Integration_ environment, this final exception might be handled using an `error-channel` on the inbound endpoint.
|
||||
|
||||
Simple Stateless Retry::
|
||||
The default `RetryTemplate` has a `SimpleRetryPolicy` which tries three times.
|
||||
There is no `BackOffPolicy`, so the three attempts are made back-to-back-to-back with no delay between attempts.
|
||||
There is no `RecoveryCallback`, so the result is to throw the exception to the caller after the final failed retry occurs.
|
||||
In a Spring Integration environment, this final exception might be handled by using an `error-channel` on the inbound endpoint.
|
||||
The following example uses `RetryTemplate` and shows its `DEBUG` output:
|
||||
+
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:service-activator input-channel="input" ref="failer" method="service">
|
||||
@@ -119,11 +117,12 @@ DEBUG [task-scheduler-2]Retry: count=2
|
||||
DEBUG [task-scheduler-2]Checking for rethrow: count=3
|
||||
DEBUG [task-scheduler-2]Retry failed last attempt: count=3
|
||||
----
|
||||
====
|
||||
|
||||
*Simple Stateless Retry with Recovery*
|
||||
|
||||
This example adds a `RecoveryCallback` to the above example; it uses a `ErrorMessageSendingRecoverer` to send an `ErrorMessage` to a channel.
|
||||
|
||||
Simple Stateless Retry with Recovery::
|
||||
The following example adds a `RecoveryCallback` to the preceding example and uses an `ErrorMessageSendingRecoverer` to send an `ErrorMessage` to a channel:
|
||||
+
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:service-activator input-channel="input" ref="failer" method="service">
|
||||
@@ -148,13 +147,15 @@ DEBUG [task-scheduler-2]Checking for rethrow: count=3
|
||||
DEBUG [task-scheduler-2]Retry failed last attempt: count=3
|
||||
DEBUG [task-scheduler-2]Sending ErrorMessage :failedMessage:[Payload=...]
|
||||
----
|
||||
====
|
||||
|
||||
*Stateless Retry with Customized Policies, and Recovery*
|
||||
|
||||
Stateless Retry with Customized Policies, and Recovery::
|
||||
For more sophistication, we can provide the advice with a customized `RetryTemplate`.
|
||||
This example continues to use the `SimpleRetryPolicy` but it increases the attempts to 4.
|
||||
It also adds an `ExponentialBackoffPolicy` where the first retry waits 1 second, the second waits 5 seconds and the third waits 25 (for 4 attempts in all).
|
||||
|
||||
This example continues to use the `SimpleRetryPolicy` but increases the attempts to four.
|
||||
It also adds an `ExponentialBackoffPolicy` where the first retry waits one second, the second waits five seconds and the third waits 25 (for four attempts in all).
|
||||
The following listing shows the example and its `DEBUG` output:
|
||||
+
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:service-activator input-channel="input" ref="failer" method="service">
|
||||
@@ -200,11 +201,12 @@ It also adds an `ExponentialBackoffPolicy` where the first retry waits 1 second,
|
||||
58.084 DEBUG [task-scheduler-1]Retry failed last attempt: count=4
|
||||
58.086 DEBUG [task-scheduler-1]Sending ErrorMessage :failedMessage:[Payload=...]
|
||||
----
|
||||
====
|
||||
|
||||
*Namespace Support for Stateless Retry*
|
||||
|
||||
Starting with _version 4.0_, the above configuration can be greatly simplified with the namespace support for the retry advice:
|
||||
|
||||
Namespace Support for Stateless Retry::
|
||||
Starting with version 4.0, the preceding configuration can be greatly simplified, thanks to the namespace support for the retry advice, as the following example shows:
|
||||
+
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:service-activator input-channel="input" ref="failer" method="service">
|
||||
@@ -217,10 +219,12 @@ Starting with _version 4.0_, the above configuration can be greatly simplified w
|
||||
<int:exponential-back-off initial="1000" multiplier="5.0" maximum="60000" />
|
||||
</int:handler-retry-advice>
|
||||
----
|
||||
|
||||
In this example, the advice is defined as a top level bean so it can be used in multiple `request-handler-advice-chain` s.
|
||||
You can also define the advice directly within the chain:
|
||||
|
||||
====
|
||||
+
|
||||
In the preceding example, the advice is defined as a top-level bean so that it can be used in multiple `request-handler-advice-chain` instances.
|
||||
You can also define the advice directly within the chain, as the following example shows:
|
||||
+
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:service-activator input-channel="input" ref="failer" method="service">
|
||||
@@ -231,20 +235,24 @@ You can also define the advice directly within the chain:
|
||||
</request-handler-advice-chain>
|
||||
</int:service-activator>
|
||||
----
|
||||
|
||||
A `<handler-retry-advice/>` with no child element uses no back off; it can have a `fixed-back-off` or `exponential-back-off` child element.
|
||||
====
|
||||
+
|
||||
A `<handler-retry-advice>` can have a `<fixed-back-off>` or `<exponential-back-off>` child element or have no child element.
|
||||
A `<handler-retry-advice>` with no child element uses no back off.
|
||||
If there is no `recovery-channel`, the exception is thrown when retries are exhausted.
|
||||
The namespace can only be used with stateless retry.
|
||||
+
|
||||
For more complex environments (custom policies etc), use normal `<bean>` definitions.
|
||||
|
||||
For more complex environments (custom policies etc), use normal `<bean/>` definitions.
|
||||
|
||||
*Simple Stateful Retry with Recovery*
|
||||
|
||||
To make retry stateful, we need to provide the Advice with a RetryStateGenerator implementation.
|
||||
Simple Stateful Retry with Recovery::
|
||||
To make retry stateful, we need to provide the advice with a `RetryStateGenerator` implementation.
|
||||
This class is used to identify a message as being a resubmission so that the `RetryTemplate` can determine the current state of retry for this message.
|
||||
The framework provides a `SpelExpressionRetryStateGenerator` which determines the message identifier using a SpEL expression.
|
||||
This is shown below; this example again uses the default policies (3 attempts with no back off); of course, as with stateless retry, these policies can be customized.
|
||||
|
||||
The framework provides a `SpelExpressionRetryStateGenerator`, which determines the message identifier by using a SpEL expression.
|
||||
This example again uses the default policies (three attempts with no back off).
|
||||
As with stateless retry, these policies can be customized.
|
||||
The following listing shows the example and its `DEBUG` output:
|
||||
+
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:service-activator input-channel="input" ref="failer" method="service">
|
||||
@@ -301,42 +309,45 @@ Caused by: java.lang.RuntimeException: foo
|
||||
27.426 DEBUG [Container#0-1]Retry failed last attempt: count=3
|
||||
27.426 DEBUG [Container#0-1]Sending ErrorMessage :failedMessage:[Payload=...]
|
||||
----
|
||||
====
|
||||
+
|
||||
If you compare the preceding example with the stateless examples, you can see that, with stateful retry, the exception is thrown to the caller on each failure.
|
||||
|
||||
Comparing with the stateless examples, you can see that with stateful retry, the exception is thrown to the caller on each failure.
|
||||
|
||||
*Exception Classification for Retry*
|
||||
|
||||
Exception Classification for Retry::
|
||||
Spring Retry has a great deal of flexibility for determining which exceptions can invoke retry.
|
||||
The default configuration will retry for all exceptions and the exception classifier just looks at the top level exception.
|
||||
If you configure it to, say, only retry on `BarException` and your application throws a `FooException` where the cause is a `BarException`, retry will not occur.
|
||||
|
||||
Since _Spring Retry 1.0.3_, the `BinaryExceptionClassifier` has a property `traverseCauses` (default `false`).
|
||||
When `true` it will traverse exception causes until it finds a match or there is no cause.
|
||||
|
||||
To use this classifier for retry, use a `SimpleRetryPolicy` created with the constructor that takes the max attempts, the `Map` of `Exception` s and the boolean (traverseCauses), and inject this policy into the `RetryTemplate`.
|
||||
The default configuration retries for all exceptions and the exception classifier looks at the top-level exception.
|
||||
If you configure it to, say, retry only on `MyException` and your application throws a `SomeOtherException` where the cause is a `MyException`, retry does not occur.
|
||||
+
|
||||
Since Spring Retry 1.0.3, the `BinaryExceptionClassifier` has a property called `traverseCauses` (the default is `false`).
|
||||
When `true`, it traverses exception causes until it finds a match or runs out of causes to traverse.
|
||||
+
|
||||
To use this classifier for retry, use a `SimpleRetryPolicy` created with the constructor that takes the max attempts, the `Map` of `Exception` objects, and the `traverseCauses` boolean. Then you can inject this policy into the `RetryTemplate`.
|
||||
|
||||
[[circuit-breaker-advice]]
|
||||
===== Circuit Breaker Advice
|
||||
|
||||
The general idea of the Circuit Breaker Pattern is that, if a service is not currently available, then don't waste time (and resources) trying to use it.
|
||||
The general idea of the circuit breaker pattern is that, if a service is not currently available, do not waste time (and resources) trying to use it.
|
||||
The `o.s.i.handler.advice.RequestHandlerCircuitBreakerAdvice` implements this pattern.
|
||||
When the circuit breaker is in the _closed_ state, the endpoint will attempt to invoke the service.
|
||||
The circuit breaker goes to the _open_ state if a certain number of consecutive attempts fail; when it is in the _open_ state, new requests will "fail fast" and no attempt will be made to invoke the service until some time has expired.
|
||||
When the circuit breaker is in the closed state, the endpoint attempts to invoke the service.
|
||||
The circuit breaker goes to the open state if a certain number of consecutive attempts fail.
|
||||
When it is in the open state, new requests "`fail fast`" and no attempt is made to invoke the service until some time has expired.
|
||||
|
||||
When that time has expired, the circuit breaker is set to the _half-open_ state.
|
||||
When in this state, if even a single attempt fails, the breaker will immediately go to the _open_ state; if the attempt succeeds, the breaker will go to the _closed_ state, in which case, it won't go to the _open_ state again until the configured number of consecutive failures again occur.
|
||||
Any successful attempt resets the state to zero failures for the purpose of determining when the breaker might go to the _open_ state again.
|
||||
When that time has expired, the circuit breaker is set to the half-open state.
|
||||
When in this state, if even a single attempt fails, the breaker immediately goes to the open state.
|
||||
If the attempt succeeds, the breaker goes to the closed state, in which case it does not go to the open state again until the configured number of consecutive failures again occur.
|
||||
Any successful attempt resets the state to zero failures for the purpose of determining when the breaker might go to the open state again.
|
||||
|
||||
Typically, this Advice might be used for external services, where it might take some time to fail (such as a timeout attempting to make a network connection).
|
||||
Typically, this advice might be used for external services, where it might take some time to fail (such as a timeout attempting to make a network connection).
|
||||
|
||||
The `RequestHandlerCircuitBreakerAdvice` has two properties: `threshold` and `halfOpenAfter`.
|
||||
The _threshold_ property represents the number of consecutive failures that need to occur before the breaker goes _open_.
|
||||
It defaults to 5.
|
||||
The _halfOpenAfter_ property represents the time after the last failure that the breaker will wait before attempting another request.
|
||||
Default is 1000 milliseconds.
|
||||
The `threshold` property represents the number of consecutive failures that need to occur before the breaker goes open.
|
||||
It defaults to `5`.
|
||||
The `halfOpenAfter` property represents the time after the last failure that the breaker waits before attempting another request.
|
||||
The default is 1000 milliseconds.
|
||||
|
||||
Example:
|
||||
The following example configures a circuit breaker and shows its `DEBUG` and `ERROR` output:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:service-activator input-channel="input" ref="failer" method="service">
|
||||
@@ -366,10 +377,14 @@ Example:
|
||||
30.598 DEBUG [task-scheduler-1]preSend on channel 'input', message: [Payload=foo...]
|
||||
30.599 ERROR [task-scheduler-1]org.springframework.messaging.MessagingException: Circuit Breaker is Open for ServiceActivator
|
||||
----
|
||||
====
|
||||
|
||||
In the above example, the threshold is set to 2 and halfOpenAfter is set to 12 seconds; a new request arrives every 5 seconds.
|
||||
You can see that the first two attempts invoked the service; the third and fourth failed with an exception indicating the circuit breaker is open.
|
||||
The fifth request was attempted because the request was 15 seconds after the last failure; the sixth attempt fails immediately because the breaker immediately went to _open_.
|
||||
In the preceding example, the threshold is set to `2` and `halfOpenAfter` is set to `12` seconds.
|
||||
A new request arrives every 5 seconds.
|
||||
The first two attempts invoked the service.
|
||||
The third and fourth failed with an exception indicating that the circuit breaker is open.
|
||||
The fifth request was attempted because the request was 15 seconds after the last failure.
|
||||
The sixth attempt fails immediately because the breaker immediately went to open.
|
||||
|
||||
[[expression-advice]]
|
||||
===== Expression Evaluating Advice
|
||||
@@ -377,21 +392,22 @@ The fifth request was attempted because the request was 15 seconds after the las
|
||||
The final supplied advice class is the `o.s.i.handler.advice.ExpressionEvaluatingRequestHandlerAdvice`.
|
||||
This advice is more general than the other two advices.
|
||||
It provides a mechanism to evaluate an expression on the original inbound message sent to the endpoint.
|
||||
Separate expressions are available to be evaluated, either after success, or failure.
|
||||
Separate expressions are available to be evaluated, after either success or failure.
|
||||
Optionally, a message containing the evaluation result, together with the input message, can be sent to a message channel.
|
||||
|
||||
A typical use case for this advice might be with an `<ftp:outbound-channel-adapter/>`, perhaps to move the file to one directory if the transfer was successful, or to another directory if it fails:
|
||||
A typical use case for this advice might be with an `<ftp:outbound-channel-adapter/>`, perhaps to move the file to one directory if the transfer was successful or to another directory if it fails:
|
||||
|
||||
The Advice has properties to set an expression when successful, an expression for failures, and corresponding channels for each.
|
||||
For the successful case, the message sent to the _successChannel_ is an `AdviceMessage`, with the payload being the result of the expression evaluation, and an additional property `inputMessage` which contains the original message sent to the handler.
|
||||
A message sent to the _failureChannel_ (when the handler throws an exception) is an `ErrorMessage` with a payload of `MessageHandlingExpressionEvaluatingAdviceException`.
|
||||
Like all `MessagingException` s, this payload has `failedMessage` and `cause` properties, as well as an additional property `evaluationResult`, containing the result of the expression evaluation.
|
||||
The advice has properties to set an expression when successful, an expression for failures, and corresponding channels for each.
|
||||
For the successful case, the message sent to the `successChannel` is an `AdviceMessage`, with the payload being the result of the expression evaluation.
|
||||
An additional property, called `inputMessage`, contains the original message sent to the handler.
|
||||
A message sent to the `failureChannel` (when the handler throws an exception) is an `ErrorMessage` with a payload of `MessageHandlingExpressionEvaluatingAdviceException`.
|
||||
Like all `MessagingException` instances, this payload has `failedMessage` and `cause` properties, as well as an additional property called `evaluationResult`, which contains the result of the expression evaluation.
|
||||
|
||||
When an exception is thrown in the scope of the advice, by default, that exception is thrown to caller after any
|
||||
`failureExpression` is evaluated.
|
||||
When an exception is thrown in the scope of the advice, by default, that exception is thrown to the caller after any `failureExpression` is evaluated.
|
||||
If you wish to suppress throwing the exception, set the `trapException` property to `true`.
|
||||
The following advice shows how to configure an advice with Java DSL:
|
||||
|
||||
.Example - Configuring the Advice with Java DSL
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@@ -441,16 +457,18 @@ public class EerhaApplication {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[[custom-advice]]
|
||||
==== Custom Advice Classes
|
||||
|
||||
In addition to the provided Advice classes above, you can implement your own Advice classes.
|
||||
While you can provide any implementation of `org.aopalliance.aop.Advice` (usually `org.aopalliance.intercept.MethodInterceptor`), it is generally recommended that you subclass `o.s.i.handler.advice.AbstractRequestHandlerAdvice`.
|
||||
This has the benefit of avoiding writing low-level _Aspect Oriented Programming_ code as well as providing a starting point that is specifically tailored for use in this environment.
|
||||
In addition to the provided advice classes <<advice-classes,described earlier>>, you can implement your own advice classes.
|
||||
While you can provide any implementation of `org.aopalliance.aop.Advice` (usually `org.aopalliance.intercept.MethodInterceptor`), we generally recommend that you subclass `o.s.i.handler.advice.AbstractRequestHandlerAdvice`.
|
||||
This has the benefit of avoiding the writing of low-level aspect-oriented programming code as well as providing a starting point that is specifically tailored for use in this environment.
|
||||
|
||||
Subclasses need to implement the `doInvoke()`` method:
|
||||
Subclasses need to implement the `doInvoke()`` method, the definition of which follows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
/**
|
||||
@@ -464,20 +482,25 @@ Subclasses need to implement the `doInvoke()`` method:
|
||||
*/
|
||||
protected abstract Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception;
|
||||
----
|
||||
====
|
||||
|
||||
The _callback_ parameter is simply a convenience to avoid subclasses dealing with AOP directly; invoking the `callback.execute()` method invokes the message handler.
|
||||
The callback parameter is a convenience to avoid subclasses that deal with AOP directly.
|
||||
Invoking the `callback.execute()` method invokes the message handler.
|
||||
|
||||
The _target_ parameter is provided for those subclasses that need to maintain state for a specific handler, perhaps by maintaining that state in a `Map`, keyed by the target.
|
||||
This allows the same advice to be applied to multiple handlers.
|
||||
The `RequestHandlerCircuitBreakerAdvice` uses this to keep circuit breaker state for each handler.
|
||||
The `target` parameter is provided for those subclasses that need to maintain state for a specific handler, perhaps by maintaining that state in a `Map` keyed by the target.
|
||||
This feature allows the same advice to be applied to multiple handlers.
|
||||
The `RequestHandlerCircuitBreakerAdvice` uses advice this to keep circuit breaker state for each handler.
|
||||
|
||||
The _message_ parameter is the message that will be sent to the handler.
|
||||
The `message` parameter is the message sent to the handler.
|
||||
While the advice cannot modify the message before invoking the handler, it can modify the payload (if it has mutable properties).
|
||||
Typically, an advice would use the message for logging and/or to send a copy of the message somewhere before or after invoking the handler.
|
||||
Typically, an advice would use the message for logging or to send a copy of the message somewhere before or after invoking the handler.
|
||||
|
||||
The return value would normally be the value returned by `callback.execute()`; but the advice does have the ability to modify the return value.
|
||||
Note that only `AbstractReplyProducingMessageHandler` s return a value.
|
||||
The return value would normally be the value returned by `callback.execute()`.
|
||||
However, the advice does have the ability to modify the return value.
|
||||
Note that only `AbstractReplyProducingMessageHandler` instances return values.
|
||||
The following example shows a custom advice class that extends `AbstractRequestHandlerAdvice`:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class MyAdvice extends AbstractRequestHandlerAdvice {
|
||||
@@ -491,38 +514,44 @@ public class MyAdvice extends AbstractRequestHandlerAdvice {
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
=====
|
||||
In addition to the `execute()` method, the `ExecutionCallback` provides an additional method `cloneAndExecute()`.
|
||||
In addition to the `execute()` method, `ExecutionCallback` provides an additional method: `cloneAndExecute()`.
|
||||
This method must be used in cases where the invocation might be called multiple times within a single execution of `doInvoke()`, such as in the `RequestHandlerRetryAdvice`.
|
||||
This is required because the Spring AOP `org.springframework.aop.framework.ReflectiveMethodInvocation` object maintains state of which advice in a chain was last invoked; this state must be reset for each call.
|
||||
This is required because the Spring AOP `org.springframework.aop.framework.ReflectiveMethodInvocation` object maintains state by keeping track of which advice in a chain was last invoked.
|
||||
This state must be reset for each call.
|
||||
|
||||
For more information, see the http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/aop/framework/ReflectiveMethodInvocation.html[ReflectiveMethodInvocation] JavaDocs.
|
||||
For more information, see the http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/aop/framework/ReflectiveMethodInvocation.html[ReflectiveMethodInvocation] Javadoc.
|
||||
=====
|
||||
|
||||
[[other-advice]]
|
||||
==== Other Advice Chain Elements
|
||||
|
||||
While the abstract class mentioned above is provided as a convenience, you can add any `Advice` to the chain, including a transaction advice.
|
||||
While the abstract class mentioned above is a convenience, you can add any `Advice`, including a transaction advice, to the chain.
|
||||
|
||||
[[handle-message-advice]]
|
||||
==== Handle Message Advice
|
||||
==== Handling Message Advice
|
||||
|
||||
As discussed in <<mhac-intro, the introduction to this section>>, advice objects in a request handler advice chain are applied to just the current endpoint, not the downstream flow (if any).
|
||||
For `MessageHandler` s that produce a reply (`AbstractReplyProducingMessageHandler`), the advice is applied to an internal method
|
||||
`handleRequestMessage()` (called from `MessageHandler.handleMessage()`).
|
||||
For `MessageHandler` objects that produce a reply (such as those that extend `AbstractReplyProducingMessageHandler`), the advice is applied to an internal method: `handleRequestMessage()` (called from `MessageHandler.handleMessage()`).
|
||||
For other message handlers, the advice is applied to `MessageHandler.handleMessage()`.
|
||||
|
||||
There are some circumstances where, even if a message handler is an `AbstractReplyProducingMessageHandler`, the advice must be applied to the `handleMessage` method - for example, the <<idempotent-receiver, Idempotent Receiver>> might return `null` and this would cause an exception if the handler's `replyRequired` property is true.
|
||||
Another example is the `BoundRabbitChannelAdvice` - see <<amqp-strict-ordering>>.
|
||||
There are some circumstances where, even if a message handler is an `AbstractReplyProducingMessageHandler`, the advice must be applied to the `handleMessage` method.
|
||||
For example, the <<idempotent-receiver, idempotent receiver>> might return `null`, which would cause an exception if the handler's `replyRequired` property is set to `true`.
|
||||
Another example is the `BoundRabbitChannelAdvice` -- see "`<<amqp-strict-ordering>>`".
|
||||
|
||||
Starting with _version 4.3.1_, a new `HandleMessageAdvice` and the `AbstractHandleMessageAdvice` base implementation have been introduced.
|
||||
`Advice` s that implement `HandleMessageAdvice` will always be applied to the `handleMessage()` method, regardless of the handler type.
|
||||
Starting with version 4.3.1, a new `HandleMessageAdvice` interface and its base implementation (`AbstractHandleMessageAdvice`) have been introduced.
|
||||
`Advice` objects that implement `HandleMessageAdvice` are always applied to the `handleMessage()` method, regardless of the handler type.
|
||||
|
||||
It is important to understand that `HandleMessageAdvice` implementations (such as <<idempotent-receiver, Idempotent Receiver>>), when applied to a handler that returns a response, are dissociated from the `adviceChain` and properly applied to the `MessageHandler.handleMessage()` method.
|
||||
Bear in mind, however, that this means the advice chain order is not complied with; and, with configuration such as:
|
||||
It is important to understand that `HandleMessageAdvice` implementations (such as <<idempotent-receiver, idempotent receiver>>), when applied to a handlers that return responses, are dissociated from the `adviceChain` and properly applied to the `MessageHandler.handleMessage()` method.
|
||||
|
||||
NOTE: Because of this disassociation, the advice chain order is not honored.
|
||||
|
||||
Consider the following configuration:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<some-reply-producing-endpoint ... >
|
||||
@@ -532,20 +561,24 @@ Bear in mind, however, that this means the advice chain order is not complied wi
|
||||
</int:request-handler-advice-chain>
|
||||
</some-reply-producing-endpoint>
|
||||
----
|
||||
====
|
||||
|
||||
The `<tx:advice>` is applied to the `AbstractReplyProducingMessageHandler.handleRequestMessage()`, but `myHandleMessageAdvice` is applied for to `MessageHandler.handleMessage()` and, therefore, invoked **before** the `<tx:advice>`.
|
||||
To retain the order, you should follow with standard http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop-api.html[Spring AOP] configuration approach and use endpoint `id` together with the `.handler` suffix to obtain the target `MessageHandler` bean.
|
||||
Note, however, that in that case, the entire downstream flow would be within the transaction scope.
|
||||
In the preceding example, the `<tx:advice>` is applied to the `AbstractReplyProducingMessageHandler.handleRequestMessage()`.
|
||||
However, `myHandleMessageAdvice` is applied for to `MessageHandler.handleMessage()`.
|
||||
Therefore, it is invoked *before* the `<tx:advice>`.
|
||||
To retain the order, you should follow the standard http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop-api.html[Spring AOP] configuration approach and use an endpoint `id` together with the `.handler` suffix to obtain the target `MessageHandler` bean.
|
||||
Note that, in that case, the entire downstream flow is within the transaction scope.
|
||||
|
||||
In the case of a `MessageHandler` that does **not** return a response, the advice chain order is retained.
|
||||
In the case of a `MessageHandler` that does not return a response, the advice chain order is retained.
|
||||
|
||||
[[tx-handle-message-advice]]
|
||||
==== Transaction Support
|
||||
|
||||
Starting with _version 5.0_ a new `TransactionHandleMessageAdvice` has been introduced to make the whole downstream flow transactional, thanks to the `HandleMessageAdvice` implementation.
|
||||
When regular `TransactionInterceptor` is used in the `<request-handler-advice-chain>`, for example via `<tx:advice>` configuration, a started transaction is only applied only for an internal `AbstractReplyProducingMessageHandler.handleRequestMessage()` and isn't propagated to the downstream flow.
|
||||
Starting with version 5.0, a new `TransactionHandleMessageAdvice` has been introduced to make the whole downstream flow transactional, thanks to the `HandleMessageAdvice` implementation.
|
||||
When a regular `TransactionInterceptor` is used in the `<request-handler-advice-chain>` element (for example, through configuring `<tx:advice>`), a started transaction is only applied only for an internal `AbstractReplyProducingMessageHandler.handleRequestMessage()` and is not propagated to the downstream flow.
|
||||
|
||||
To simplify XML configuration, alongside with the `<request-handler-advice-chain>`, a `<transactional>` sub-element has been added to all `<outbound-gateway>` and `<service-activator>` & family components:
|
||||
To simplify XML configuration, along with the `<request-handler-advice-chain>`, a `<transactional>` element has been added to all `<outbound-gateway>` and `<service-activator>` and related components.
|
||||
The following example shows `<transactional>` in use:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
@@ -559,9 +592,9 @@ To simplify XML configuration, alongside with the `<request-handler-advice-chain
|
||||
</bean>
|
||||
----
|
||||
|
||||
For whom is familiar with <<jpa, JPA Integration components>> such a configuration isn't new, but now we can start transaction from any point in our flow, not only from the `<poller>` or Message Driven Channel Adapter like in <<jms-message-driven-channel-adapter, JMS>>.
|
||||
If you are familiar with the <<jpa, JPA integration components>>, such a configuration is not new, but now we can start a transaction from any point in our flow -- not only from the `<poller>` or a message-driven channel adapter such as <<jms-message-driven-channel-adapter, JMS>>.
|
||||
|
||||
Java & Annotation configuration can be simplified via newly introduced `TransactionInterceptorBuilder` and the result bean name can be used in the <<annotations, Messaging Annotations>> `adviceChain` attribute:
|
||||
Java configuration can be simplified by using the `TransactionInterceptorBuilder`, and the result bean name can be used in the <<annotations, messaging annotations>> `adviceChain` attribute, as the following example shows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -598,9 +631,12 @@ public Transformer transformer() {
|
||||
}
|
||||
----
|
||||
|
||||
Note the `true` for the `TransactionInterceptorBuilder` constructor, which means produce `TransactionHandleMessageAdvice`, not regular `TransactionInterceptor`.
|
||||
Note the `true` parameter on the `TransactionInterceptorBuilder` constructor.
|
||||
It causes the creation of a `TransactionHandleMessageAdvice`, not a regular `TransactionInterceptor`.
|
||||
|
||||
Java DSL supports such an `Advice` via `.transactional()` options on the endpoint configuration:
|
||||
Java DSL supports an `Advice` through the `.transactional()` options on the endpoint configuration, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
@@ -611,24 +647,25 @@ public IntegrationFlow updatingGatewayFlow() {
|
||||
.channel(c -> c.queue("persistResults"));
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[[advising-filters]]
|
||||
==== Advising Filters
|
||||
|
||||
There is an additional consideration when advising `Filter` s.
|
||||
By default, any discard actions (when the filter returns false) are performed _within_ the scope of the advice chain.
|
||||
This could include all the flow downstream of the _discard channel_.
|
||||
So, for example if an element downstream of the _discard-channel_ throws an exception, and there is a retry advice, the process will be retried.
|
||||
This is also the case if _throwExceptionOnRejection_ is set to true (the exception is thrown within the scope of the advice).
|
||||
There is an additional consideration when advising `Filter` advices.
|
||||
By default, any discard actions (when the filter returns `false`) are performed within the scope of the advice chain.
|
||||
This could include all the flow downstream of the discard channel.
|
||||
So, for example, if an element downstream of the discard channel throws an exception and there is a retry advice, the process is retried.
|
||||
Also, if `throwExceptionOnRejection` is set to `true` (the exception is thrown within the scope of the advice).
|
||||
|
||||
Setting _discard-within-advice_ to "false" modifies this behavior and the discard (or exception) occurs after the advice chain is called.
|
||||
Setting `discard-within-advice` to `false` modifies this behavior and the discard (or exception) occurs after the advice chain is called.
|
||||
|
||||
[[advising-with-annotations]]
|
||||
==== Advising Endpoints Using Annotations
|
||||
|
||||
When configuring certain endpoints using annotations (`@Filter`, `@ServiceActivator`, `@Splitter`, and `@Transformer`), you can supply a bean name for the advice chain in the `adviceChain` attribute.
|
||||
In addition, the `@Filter` annotation also has the `discardWithinAdvice` attribute, which can be used to configure the discard behavior as discussed in <<advising-filters>>.
|
||||
An example with the discard being performed after the advice is shown below.
|
||||
When configuring certain endpoints by using annotations (`@Filter`, `@ServiceActivator`, `@Splitter`, and `@Transformer`), you can supply a bean name for the advice chain in the `adviceChain` attribute.
|
||||
In addition, the `@Filter` annotation also has the `discardWithinAdvice` attribute, which can be used to configure the discard behavior, as discussed in "`<<advising-filters>>`".
|
||||
The following example causes the discard to be performed after the advice:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -646,43 +683,46 @@ public class MyAdvisedFilter {
|
||||
[[advice-order]]
|
||||
==== Ordering Advices within an Advice Chain
|
||||
|
||||
Advice classes are "around" advices and are applied in a nested fashion.
|
||||
The first advice is the outermost, the last advice the innermost (closest to the handler being advised).
|
||||
Advice classes are "`around`" advices and are applied in a nested fashion.
|
||||
The first advice is the outermost, while the last advice is the innermost (that is, closest to the handler being advised).
|
||||
It is important to put the advice classes in the correct order to achieve the functionality you desire.
|
||||
|
||||
For example, let's say you want to add a retry advice and a transaction advice.
|
||||
For example, suppose you want to add a retry advice and a transaction advice.
|
||||
You may want to place the retry advice advice first, followed by the transaction advice.
|
||||
Then, each retry will be performed in a new transaction.
|
||||
On the other hand, if you want all the attempts, and any recovery operations (in the retry `RecoveryCallback`), to be scoped within the transaction, you would put the transaction advice first.
|
||||
Consequently, each retry is performed in a new transaction.
|
||||
On the other hand, if you want all the attempts and any recovery operations (in the retry `RecoveryCallback`) to be scoped within the transaction, you could put the transaction advice first.
|
||||
|
||||
[[advised-handler-properties]]
|
||||
==== Advised Handler Properties
|
||||
|
||||
Sometimes, it is useful to access handler properties from within the advice.
|
||||
For example, most handlers implement `NamedComponent` and you can access the component name.
|
||||
For example, most handlers implement `NamedComponent` to let you access the component name.
|
||||
|
||||
The target object can be accessed via the `target` argument when subclassing `AbstractRequestHandlerAdvice` or
|
||||
`invocation.getThis()` when implementing `org.aopalliance.intercept.MethodInterceptor`.
|
||||
The target object can be accessed through the `target` argument (when subclassing `AbstractRequestHandlerAdvice`) or
|
||||
`invocation.getThis()` (when implementing `org.aopalliance.intercept.MethodInterceptor`).
|
||||
|
||||
When the entire handler is advised (such as when the handler does not produce replies, or the advice implements `HandleMessageAdvice`), you can simply cast the target object to the desired implemented interface, such as `NamedComponent`.
|
||||
When the entire handler is advised (such as when the handler does not produce replies or the advice implements `HandleMessageAdvice`), you can cast the target object to an interface, such as `NamedComponent`, as shown in the following example:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
String componentName = ((NamedComponent) target).getComponentName();
|
||||
----
|
||||
|
||||
or
|
||||
When you implement `MethodInterceptor` directly, you could cast the target object as follows:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
String componentName = ((NamedComponent) invocation.getThis()).getComponentName();
|
||||
----
|
||||
|
||||
when implementing `MethodInterceptor` directly.
|
||||
====
|
||||
|
||||
When only the `handleRequestMessage()` method is advised (in a reply-producing handler), you need to access the
|
||||
full handler, which is an `AbstractReplyProducingMessageHandler`...
|
||||
full handler, which is an `AbstractReplyProducingMessageHandler`.
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
AbstractReplyProducingMessageHandler handler =
|
||||
@@ -690,30 +730,36 @@ AbstractReplyProducingMessageHandler handler =
|
||||
|
||||
String componentName = handler.getComponentName();
|
||||
----
|
||||
====
|
||||
|
||||
[[idempotent-receiver]]
|
||||
==== Idempotent Receiver Enterprise Integration Pattern
|
||||
|
||||
Starting with _version 4.1_, Spring Integration provides an implementation of the http://www.eaipatterns.com/IdempotentReceiver.html[Idempotent Receiver] Enterprise Integration Pattern.
|
||||
It is a _functional_ pattern and the whole _idempotency_ logic should be implemented in the application, however to simplify the decision-making, the `IdempotentReceiverInterceptor` component is provided.
|
||||
This is an AOP `Advice`, which is applied to the `MessageHandler.handleMessage()` method and can `filter` a request message or mark it as a `duplicate`, according to its configuration.
|
||||
Starting with version 4.1, Spring Integration provides an implementation of the http://www.eaipatterns.com/IdempotentReceiver.html[Idempotent Receiver] Enterprise Integration Pattern.
|
||||
It is a functional pattern and the whole idempotency logic should be implemented in the application.
|
||||
However, to simplify the decision-making, the `IdempotentReceiverInterceptor` component is provided.
|
||||
This is an AOP `Advice` that is applied to the `MessageHandler.handleMessage()` method and that can `filter` a request message or mark it as a `duplicate`, according to its configuration.
|
||||
|
||||
Previously, users could have implemented this pattern, by using a custom MessageSelector in a `<filter/>` (<<filter>>), for example.
|
||||
However, since this pattern is really behavior of an endpoint rather than being an endpoint itself, the Idempotent Receiver implementation doesn't provide an _endpoint_ component; rather, it is applied to endpoints declared in the application.
|
||||
Previously, you could have implemented this pattern by using a custom `MessageSelector` in a `<filter/>` (see "`<<filter>>`"), for example.
|
||||
However, since this pattern really defines the behavior of an endpoint rather than being an endpoint itself, the idempotent receiver implementation does not provide an endpoint component.
|
||||
Rather, it is applied to endpoints declared in the application.
|
||||
|
||||
The logic of the `IdempotentReceiverInterceptor` is based on the provided `MessageSelector` and, if the message isn't accepted by that selector, it will be enriched with the `duplicateMessage` header set to `true`.
|
||||
The target `MessageHandler` (or downstream flow) can consult this header to implement the correct _idempotency_ logic.
|
||||
If the `IdempotentReceiverInterceptor` is configured with a `discardChannel` and/or `throwExceptionOnRejection = true`, the _duplicate_ Message won't be sent to the target `MessageHandler.handleMessage()`, but discarded.
|
||||
If you simply want to discard (do nothing with) the _duplicate_ Message, the `discardChannel` should be configured with a `NullChannel`, such as the default `nullChannel` bean.
|
||||
The logic of the `IdempotentReceiverInterceptor` is based on the provided `MessageSelector` and, if the message is not accepted by that selector, it is enriched with the `duplicateMessage` header set to `true`.
|
||||
The target `MessageHandler` (or downstream flow) can consult this header to implement the correct idempotency logic.
|
||||
If the `IdempotentReceiverInterceptor` is configured with a `discardChannel` or `throwExceptionOnRejection = true`, the duplicate message is not sent to the target `MessageHandler.handleMessage()`.
|
||||
Rather, it is discarded.
|
||||
If you want to discard (do nothing with) the duplicate message, the `discardChannel` should be configured with a `NullChannel`, such as the default `nullChannel` bean.
|
||||
|
||||
To maintain _state_ between messages and provide the ability to compare messages for the idempotency, the `MetadataStoreSelector` is provided.
|
||||
To maintain state between messages and provide the ability to compare messages for the idempotency, we provide the `MetadataStoreSelector`.
|
||||
It accepts a `MessageProcessor` implementation (which creates a lookup key based on the `Message`) and an optional `ConcurrentMetadataStore` (<<metadata-store>>).
|
||||
See the `MetadataStoreSelector` JavaDocs for more information.
|
||||
The `value` for `ConcurrentMetadataStore` also can be customized using additional `MessageProcessor`.
|
||||
By default `MetadataStoreSelector` uses `timestamp` message header.
|
||||
See the https://docs.spring.io/spring-integration/api/org/springframework/integration/selector/MetadataStoreSelector.html[`MetadataStoreSelector` Javadoc] for more information.
|
||||
You can also customize the `value` for `ConcurrentMetadataStore` by using an additional `MessageProcessor`.
|
||||
By default, `MetadataStoreSelector` uses the `timestamp` message header.
|
||||
|
||||
For convenience, the `MetadataStoreSelector` options are configurable directly on the `<idempotent-receiver>` component:
|
||||
For convenience, the `MetadataStoreSelector` options are configurable directly on the `<idempotent-receiver>` component.
|
||||
The following listing shows all the possible attributes:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<idempotent-receiver
|
||||
@@ -729,78 +775,53 @@ For convenience, the `MetadataStoreSelector` options are configurable directly o
|
||||
throw-exception-on-rejection="" /> <10>
|
||||
----
|
||||
|
||||
<1> The id of the `IdempotentReceiverInterceptor` bean.
|
||||
_Optional_.
|
||||
|
||||
|
||||
|
||||
<2> Consumer Endpoint name(s) or pattern(s) to which this interceptor will be applied.
|
||||
Separate names (patterns) with commas (`,`) e.g.
|
||||
`endpoint="aaa, bbb*, *ccc, *ddd*, eee*fff"`.
|
||||
Endpoint bean names matching these patterns are then used to retrieve the target endpoint's `MessageHandler` bean (using its `.handler` suffix), and the `IdempotentReceiverInterceptor` will be applied to those beans.
|
||||
_Required_.
|
||||
|
||||
|
||||
|
||||
<1> The ID of the `IdempotentReceiverInterceptor` bean.
|
||||
Optional.
|
||||
<2> Consumer endpoint name(s) or pattern(s) to which this interceptor is applied.
|
||||
Separate names (patterns) with commas (`,`), such as `endpoint="aaa, bbb*, *ccc, *ddd*, eee*fff"`.
|
||||
Endpoint bean names matching these patterns are then used to retrieve the target endpoint's `MessageHandler` bean (using its `.handler` suffix), and the `IdempotentReceiverInterceptor` is applied to those beans.
|
||||
Required.
|
||||
<3> A `MessageSelector` bean reference.
|
||||
Mutually exclusive with `metadata-store` and `key-strategy (key-expression)`.
|
||||
When `selector` is not provided, one of `key-strategy` or `key-strategy-expression` is required.
|
||||
|
||||
|
||||
|
||||
<4> Identifies the channel to which to send a message when the `IdempotentReceiverInterceptor` doesn't accept it.
|
||||
<4> Identifies the channel to which to send a message when the `IdempotentReceiverInterceptor` does not accept it.
|
||||
When omitted, duplicate messages are forwarded to the handler with a `duplicateMessage` header.
|
||||
_Optional_.
|
||||
|
||||
|
||||
|
||||
Optional.
|
||||
<5> A `ConcurrentMetadataStore` reference.
|
||||
Used by the underlying `MetadataStoreSelector`.
|
||||
Mutually exclusive with `selector`.
|
||||
_Optional_.
|
||||
The default `MetadataStoreSelector` uses an internal `SimpleMetadataStore` which does not maintain state across application executions.
|
||||
|
||||
|
||||
|
||||
Optional.
|
||||
The default `MetadataStoreSelector` uses an internal `SimpleMetadataStore` that does not maintain state across application executions.
|
||||
<6> A `MessageProcessor` reference.
|
||||
Used by the underlying `MetadataStoreSelector`.
|
||||
Evaluates an `idempotentKey` from the request Message.
|
||||
Evaluates an `idempotentKey` from the request message.
|
||||
Mutually exclusive with `selector` and `key-expression`.
|
||||
When a `selector` is not provided, one of `key-strategy` or `key-strategy-expression` is required.
|
||||
|
||||
|
||||
|
||||
<7> A SpEL expression to populate an `ExpressionEvaluatingMessageProcessor`.
|
||||
Used by the underlying `MetadataStoreSelector`.
|
||||
Evaluates an `idempotentKey` using the request Message as the evaluation context root object.
|
||||
Evaluates an `idempotentKey` by using the request message as the evaluation context root object.
|
||||
Mutually exclusive with `selector` and `key-strategy`.
|
||||
When a `selector` is not provided, one of `key-strategy` or `key-strategy-expression` is required.
|
||||
|
||||
|
||||
|
||||
<8> A `MessageProcessor` reference.
|
||||
Used by the underlying `MetadataStoreSelector`.
|
||||
Evaluates a `value` for the `idempotentKey` from the request Message.
|
||||
Evaluates a `value` for the `idempotentKey` from the request message.
|
||||
Mutually exclusive with `selector` and `value-expression`.
|
||||
By default, the 'MetadataStoreSelector' uses the 'timestamp' message header as the Metadata 'value'.
|
||||
|
||||
|
||||
|
||||
<9> A SpEL expression to populate an `ExpressionEvaluatingMessageProcessor`.
|
||||
Used by the underlying `MetadataStoreSelector`.
|
||||
Evaluates a `value` for the `idempotentKey` using the request Message as the evaluation context root object.
|
||||
Evaluates a `value` for the `idempotentKey` by using the request message as the evaluation context root object.
|
||||
Mutually exclusive with `selector` and `value-strategy`.
|
||||
By default, the 'MetadataStoreSelector' uses the 'timestamp' message header as the Metadata 'value'.
|
||||
|
||||
|
||||
|
||||
<10> Throw an exception if the `IdempotentReceiverInterceptor` rejects the message defaults to `false`.
|
||||
By default, the 'MetadataStoreSelector' uses the 'timestamp' message header as the metadata 'value'.
|
||||
<10> Whether to throw an exception if the `IdempotentReceiverInterceptor` rejects the message.
|
||||
Defaults to `false`.
|
||||
It is applied regardless of whether or not a `discard-channel` is provided.
|
||||
====
|
||||
|
||||
For Java configuration, Spring Integration provides the method-level `@IdempotentReceiver` annotation.
|
||||
It is used to mark a `method` that has a messaging annotation (`@ServiceActivator`, `@Router, and others) to specify which `IdempotentReceiverInterceptor` objects are applied to this endpoint.
|
||||
The following example shows how to use the `@IdempotentReceiver` annotation:
|
||||
|
||||
For Java configuration, the method level `IdempotentReceiver` annotation is provided.
|
||||
It is used to mark a `method` that has a Messaging annotation (`@ServiceActivator`, `@Router` etc.) to specify which `IdempotentReceiverInterceptor` s will be applied to this endpoint:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
@@ -816,9 +837,11 @@ public MessageHandler myService() {
|
||||
....
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
And with the Java DSL, the interceptor is added to the endpoint's advice chain:
|
||||
When you use the Java DSL, you can add the interceptor to the endpoint's advice chain, as the following example shows:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
@@ -829,6 +852,8 @@ public IntegrationFlow flow() {
|
||||
...
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: The `IdempotentReceiverInterceptor` is designed only for the `MessageHandler.handleMessage(Message<?>)` method and starting with _version 4.3.1_ it implements `HandleMessageAdvice`, with the `AbstractHandleMessageAdvice` as a base class, for better dissociation.
|
||||
See <<handle-message-advice>> for more information.
|
||||
NOTE: The `IdempotentReceiverInterceptor` is designed only for the `MessageHandler.handleMessage(Message<?>)` method.
|
||||
Starting with version 4.3.1, it implements `HandleMessageAdvice`, with the `AbstractHandleMessageAdvice` as a base class, for better dissociation.
|
||||
See "`<<handle-message-advice>>`" for more information.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
BIN
src/reference/asciidoc/images/epub-cover.png
Normal file
BIN
src/reference/asciidoc/images/epub-cover.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
14
src/reference/asciidoc/images/epub-cover.svg
Normal file
14
src/reference/asciidoc/images/epub-cover.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 9.1 KiB |
@@ -1,25 +1,39 @@
|
||||
[[spring-integration-reference]]
|
||||
|
||||
= Spring Integration Reference Manual
|
||||
Mark Fisher, Marius Bogoevici, Iwein Fuld, Jonas Partner, Oleg Zhurakousky, Gary Russell, Dave Syer, Josh Long, David Turanski, Gunnar Hillert, Artem Bilan, Amol Nayak, Jay Bryant
|
||||
:revnumber: {version}
|
||||
:revdate: {localdate}
|
||||
:linkcss:
|
||||
:doctype: book
|
||||
:docinfo: shared
|
||||
:toc: left
|
||||
:toclevels: 4
|
||||
:source-highlighter: prettify
|
||||
:icons: font
|
||||
:imagesdir: images
|
||||
ifdef::backend-epub3[:front-cover-image: image:epub-cover.png[Front Cover,1050,1600]]
|
||||
|
||||
:toc:
|
||||
(C) 2009-2018 Pivotal Software, Inc. All rights reserved.
|
||||
|
||||
NOTE: Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.
|
||||
|
||||
include::./preface.adoc[]
|
||||
[[whats-new-part]]
|
||||
= What's new?
|
||||
= What's New?
|
||||
|
||||
[[spring-integration-intro-new]]
|
||||
For those who are already familiar with Spring Integration, this chapter provides a brief overview of the new features of version 5.0.
|
||||
If you are interested in the changes and features, that were introduced in earlier versions, please see chapter: <<history>>
|
||||
If you are interested in the changes and features that were introduced in earlier versions, see the <<history>>.
|
||||
|
||||
include::./whats-new.adoc[]
|
||||
|
||||
[[spring-integration-introduction]]
|
||||
= Overview of Spring Integration Framework
|
||||
|
||||
[[spring-integration-intro]]
|
||||
Spring Integration provides an extension of the Spring programming model to support the well-known http://www.eaipatterns.com/[Enterprise Integration Patterns].
|
||||
It enables lightweight messaging _within_ Spring-based applications and supports integration with external systems via declarative adapters.
|
||||
Those adapters provide a higher-level of abstraction over Spring's support for remoting, messaging, and scheduling.
|
||||
Spring Integration provides an extension of the Spring programming model to support the well known http://www.eaipatterns.com/[Enterprise Integration Patterns].
|
||||
It enables lightweight messaging within Spring-based applications and supports integration with external systems through declarative adapters.
|
||||
Those adapters provide a higher level of abstraction over Spring's support for remoting, messaging, and scheduling.
|
||||
Spring Integration's primary goal is to provide a simple model for building enterprise integration solutions while maintaining the separation of concerns that is essential for producing maintainable, testable code.
|
||||
|
||||
include::./overview.adoc[]
|
||||
@@ -29,14 +43,14 @@ include::./overview.adoc[]
|
||||
|
||||
[[spring-integration-core-msg]]
|
||||
This section covers all aspects of the core messaging API in Spring Integration.
|
||||
Here you will learn about Messages, Message Channels, and Message Endpoints.
|
||||
Many of the Enterprise Integration Patterns are covered here as well, such as Filters, Routers, Transformers, Service-Activators, Splitters, and Aggregators.
|
||||
The section also contains material about System Management, including the Control Bus and Message History support.
|
||||
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.
|
||||
The section also contains material about system management, including the control bus and message history support.
|
||||
|
||||
// BE SURE TO PRECEDE ALL include:: with a blank line - see https://github.com/asciidoctor/asciidoctor/issues/1297
|
||||
include::./messaging-channels.adoc[]
|
||||
|
||||
include::./message-construction.adoc[]
|
||||
include::./message.adoc[leveloffset=-1]
|
||||
|
||||
include::./message-routing.adoc[]
|
||||
|
||||
@@ -52,7 +66,7 @@ include::./system-management.adoc[]
|
||||
= Integration Endpoints
|
||||
|
||||
[[spring-integration-adapters]]
|
||||
This section covers the various Channel Adapters and Messaging Gateways provided by Spring Integration to support Message-based communication with external systems.
|
||||
This section covers the various channel adapters and messaging gateways provided by Spring Integration to support message-based communication with external systems.
|
||||
|
||||
// BE SURE TO PRECEDE ALL include:: with a blank line - see https://github.com/asciidoctor/asciidoctor/issues/1297
|
||||
include::./endpoint-summary.adoc[]
|
||||
@@ -115,7 +129,6 @@ include::./zookeeper.adoc[]
|
||||
// BE SURE TO PRECEDE ALL include:: with a blank line - see https://github.com/asciidoctor/asciidoctor/issues/1297
|
||||
|
||||
[[spring-integration-appendices]]
|
||||
|
||||
= Appendices
|
||||
|
||||
[[spring-integration-adapters-advanced]]
|
||||
@@ -123,19 +136,27 @@ Advanced Topics and Additional Resources
|
||||
|
||||
[appendix]
|
||||
include::./spel.adoc[]
|
||||
|
||||
[appendix]
|
||||
include::./message-publishing.adoc[]
|
||||
|
||||
[appendix]
|
||||
include::./transactions.adoc[]
|
||||
|
||||
[appendix]
|
||||
include::./security.adoc[]
|
||||
|
||||
[appendix]
|
||||
include::./configuration.adoc[]
|
||||
|
||||
[appendix]
|
||||
include::./testing.adoc[]
|
||||
|
||||
[appendix]
|
||||
include::./samples.adoc[]
|
||||
|
||||
[appendix]
|
||||
include::./resources.adoc[]
|
||||
|
||||
[appendix]
|
||||
include::./history.adoc[]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,29 +1,32 @@
|
||||
[[jmx]]
|
||||
=== JMX Support
|
||||
|
||||
Spring Integration provides _Channel Adapters_ for receiving and publishing JMX Notifications.
|
||||
There is also an_Inbound Channel Adapter_ for polling JMX MBean attribute values, and an _Outbound Channel Adapter_ for invoking JMX MBean operations.
|
||||
Spring Integration provides channel Adapters for receiving and publishing JMX Notifications.
|
||||
An inbound channel adapter allows for polling JMX MBean attribute values, and an outbound channel adapter allows for invoking JMX MBean operations.
|
||||
|
||||
[[jmx-notification-listening-channel-adapter]]
|
||||
==== Notification Listening Channel Adapter
|
||||
==== Notification-listening Channel Adapter
|
||||
|
||||
The _Notification-listening Channel Adapter_ requires a JMX ObjectName for the MBean that publishes notifications to which this listener should be registered.
|
||||
A very simple configuration might look like this:
|
||||
The notification-listening channel adapter requires a JMX `ObjectName` for the MBean that publishes notifications to which this listener should be registered.
|
||||
A very simple configuration might resemble the following:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-jmx:notification-listening-channel-adapter id="adapter"
|
||||
channel="channel"
|
||||
object-name="example.domain:name=publisher"/>
|
||||
----
|
||||
====
|
||||
|
||||
TIP: The _notification-listening-channel-adapter_ registers with an `MBeanServer` at startup, and the default bean name is _mbeanServer_ which happens to be the same bean name generated when using Spring's _<context:mbean-server/>_ element.
|
||||
If you need to use a different name, be sure to include the_mbean-server_ attribute.
|
||||
TIP: The `notification-listening-channel-adapter` registers with an `MBeanServer` at startup, and the default bean name is `mbeanServer`, which happens to be the same bean name generated when using Spring's `<context:mbean-server/>` element.
|
||||
If you need to use a different name, be sure to include the `mbean-server` attribute.
|
||||
|
||||
The adapter can also accept a reference to a `NotificationFilter` and a _handback_ Object to provide some context that is passed back with each Notification.
|
||||
The adapter can also accept a reference to a `NotificationFilter` and a "`handback`" object to provide some context that is passed back with each notification.
|
||||
Both of those attributes are optional.
|
||||
Extending the above example to include those attributes as well as an explicit `MBeanServer` bean name would produce the following:
|
||||
Extending the preceding example to include those attributes as well as an explicit `MBeanServer` bean name produces the following example:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-jmx:notification-listening-channel-adapter id="adapter"
|
||||
@@ -33,15 +36,18 @@ Extending the above example to include those attributes as well as an explicit `
|
||||
notification-filter="notificationFilter"
|
||||
handback="myHandback"/>
|
||||
----
|
||||
====
|
||||
|
||||
The _Notification-listening Channel Adapter_ is event-driven and registered with the `MBeanServer` directly.
|
||||
The _Notification-listening channel adapter is event-driven and registered with the `MBeanServer` directly.
|
||||
It does not require any poller configuration.
|
||||
|
||||
[NOTE]
|
||||
=====
|
||||
For this component only, the _object-name_ attribute can contain an ObjectName pattern (e.g.
|
||||
"org.foo:type=Bar,name=*") and the adapter will receive notifications from all MBeans with ObjectNames that match the pattern.
|
||||
In addition, the _object-name_ attribute can contain a SpEL reference to a <util:list/> of ObjectName patterns:
|
||||
====
|
||||
For this component only, the `object-name` attribute can contain an object name pattern (for example,
|
||||
"org.something:type=MyType,name=*").
|
||||
In that case, the adapter receives notifications from all MBeans with object names that match the pattern.
|
||||
In addition, the `object-name` attribute can contain a SpEL reference to a `<util:list>` of object name patterns, as the following example shows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<jmx:notification-listening-channel-adapter id="manyNotificationsAdapter"
|
||||
@@ -54,15 +60,16 @@ In addition, the _object-name_ attribute can contain a SpEL reference to a <util
|
||||
</util:list>
|
||||
----
|
||||
|
||||
The names of the located MBean(s) will be logged when DEBUG level logging is enabled.
|
||||
=====
|
||||
The names of the located MBean(s) are logged when DEBUG level logging is enabled.
|
||||
====
|
||||
|
||||
[[jmx-notification-publishing-channel-adapter]]
|
||||
==== Notification Publishing Channel Adapter
|
||||
==== Notification-publishing Channel Adapter
|
||||
|
||||
The _Notification-publishing Channel Adapter_ is relatively simple.
|
||||
It only requires a JMX ObjectName in its configuration as shown below.
|
||||
The notification-publishing channel adapter is relatively simple.
|
||||
It requires only a JMX object name in its configuration, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<context:mbean-export/>
|
||||
@@ -71,19 +78,21 @@ It only requires a JMX ObjectName in its configuration as shown below.
|
||||
channel="channel"
|
||||
object-name="example.domain:name=publisher"/>
|
||||
----
|
||||
====
|
||||
|
||||
It does also require that an `MBeanExporter` be present in the context.
|
||||
That is why the _<context:mbean-export/>_ element is shown above as well.
|
||||
It also requires that an `MBeanExporter` be present in the context.
|
||||
That is why the `<context:mbean-export/>` element is also shown in the preceding example.
|
||||
|
||||
When Messages are sent to the channel for this adapter, the Notification is created from the Message content.
|
||||
If the payload is a String it will be passed as the _message_ text for the Notification.
|
||||
Any other payload type will be passed as the _userData_ of the Notification.
|
||||
When messages are sent to the channel for this adapter, the notification is created from the message content.
|
||||
If the payload is a `String`, it is passed as the `message` text for the notification.
|
||||
Any other payload type is passed as the `userData` of the notification.
|
||||
|
||||
JMX Notifications also have a _type_, and it should be a dot-delimited String.
|
||||
There are two ways to provide the _type_.
|
||||
Precedence will always be given to a Message header value associated with the `JmxHeaders.NOTIFICATION_TYPE` key.
|
||||
On the other hand, you can rely on a fallback _default-notification-type_ attribute provided in the configuration.
|
||||
JMX notifications also have a `type`, and it should be a dot-delimited `String`.
|
||||
There are two ways to provide the `type`.
|
||||
Precedence is always given to a message header value associated with the `JmxHeaders.NOTIFICATION_TYPE` key.
|
||||
Alternatively, you can provide a fallback `default-notification-type` attribute in the configuration, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<context:mbean-export/>
|
||||
@@ -93,15 +102,19 @@ On the other hand, you can rely on a fallback _default-notification-type_ attrib
|
||||
object-name="example.domain:name=publisher"
|
||||
default-notification-type="some.default.type"/>
|
||||
----
|
||||
====
|
||||
|
||||
[[jmx-attribute-polling-channel-adapter]]
|
||||
==== Attribute Polling Channel Adapter
|
||||
==== Attribute-polling Channel Adapter
|
||||
|
||||
The _Attribute Polling Channel Adapter_ is useful when you have a requirement, to periodically check on some value that is available through an MBean as a managed attribute.
|
||||
The poller can be configured in the same way as any other polling adapter in Spring Integration (or it's possible to rely on the default poller).
|
||||
The _object-name_ and _attribute-name_ are required.
|
||||
An MBeanServer reference is also required, but it will automatically check for a bean named _mbeanServer_ by default, just like the _Notification-listening Channel Adapter_ described above.
|
||||
The attribute-polling channel adapter is useful when you need to periodically check on some value that is available through an MBean as a managed attribute.
|
||||
You can configured the poller in the same way as any other polling adapter in Spring Integration (or you can rely on the default poller).
|
||||
The `object-name` and the `attribute-name` are required.
|
||||
An MBeanServer reference is also required.
|
||||
However, by default, it automatically checks for a bean named `mbeanServer`, same as the notification-listening channel adapter <<jmx-notification-listening-channel-adapter,described earlier>>.
|
||||
The following example shows how to configure an attribute-polling channel adapter with XML:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-jmx:attribute-polling-channel-adapter id="adapter"
|
||||
@@ -111,15 +124,18 @@ An MBeanServer reference is also required, but it will automatically check for a
|
||||
<int:poller max-messages-per-poll="1" fixed-rate="5000"/>
|
||||
</int-jmx:attribute-polling-channel-adapter>
|
||||
----
|
||||
====
|
||||
|
||||
[[tree-polling-channel-adapter]]
|
||||
==== Tree Polling Channel Adapter
|
||||
==== Tree-polling Channel Adapter
|
||||
|
||||
The _Tree Polling Channel Adapter_ queries the JMX MBean tree and sends a message with a payload that is the graph of objects that matches the query.
|
||||
By default the MBeans are mapped to primitives and simple Objects like Map, List and arrays - permitting simple transformation, for example, to JSON.
|
||||
An MBeanServer reference is also required, but it will automatically check for a bean named _mbeanServer_ by default, just like the _Notification-listening Channel Adapter_ described above.
|
||||
A basic configuration would be:
|
||||
The tree-polling channel adapter queries the JMX MBean tree and sends a message with a payload that is the graph of objects that matches the query.
|
||||
By default, the MBeans are mapped to primitives and simple objects, such as `Map`, `List`, and arrays. Doing so permits simple transformation to (for example) JSON.
|
||||
An MBeanServer reference is also required.
|
||||
However, by default, it automatically checks for a bean named `mbeanServer`, same as the notification-listening channel adapter <<jmx-notification-listening-channel-adapter,described earlier>>.
|
||||
The following example shows how to configure an tree-polling channel adapter with XML:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-jmx:tree-polling-channel-adapter id="adapter"
|
||||
@@ -128,44 +144,53 @@ A basic configuration would be:
|
||||
<int:poller max-messages-per-poll="1" fixed-rate="5000"/>
|
||||
</int-jmx:tree-polling-channel-adapter>
|
||||
----
|
||||
====
|
||||
|
||||
This will include all attributes on the MBeans selected.
|
||||
The preceding example includes all of the attributes on the selected MBeans.
|
||||
You can filter the attributes by providing an `MBeanObjectConverter` that has an appropriate filter configured.
|
||||
The converter can be provided as a reference to a bean definition using the `converter` attribute, or as an inner <bean/> definition.
|
||||
A `DefaultMBeanObjectConverter` is provided which can take a `MBeanAttributeFilter` in its constructor argument.
|
||||
You can provide the converter as a reference to a bean definition by using the `converter` attribute, or you can use an inner `<bean/>` definition.
|
||||
Spring Integration provides a `DefaultMBeanObjectConverter` that can take a `MBeanAttributeFilter` in its constructor argument.
|
||||
|
||||
Two standard filters are provided; the `NamedFieldsMBeanAttributeFilter` allows you to specify a list of attributes to include and the `NotNamedFieldsMBeanAttributeFilter` allows you to specify a list of attributes to exclude.
|
||||
You can also implement your own filter
|
||||
Spring Integration provides two standard filters.
|
||||
The `NamedFieldsMBeanAttributeFilter` lets you specify a list of attributes to include.
|
||||
The `NotNamedFieldsMBeanAttributeFilter` lets you specify a list of attributes to exclude.
|
||||
You can also implement your own filter.
|
||||
// TODO How can people implement their own filters? This needs at least a reference to which class to extend or interface to implement. An example would be better still.
|
||||
|
||||
[[jmx-operation-invoking-channel-adapter]]
|
||||
==== Operation Invoking Channel Adapter
|
||||
==== Operation-invoking Channel Adapter
|
||||
|
||||
The _operation-invoking-channel-adapter_ enables Message-driven invocation of any managed operation exposed by an MBean.
|
||||
Each invocation requires the operation name to be invoked and the ObjectName of the target MBean.
|
||||
Both of these must be explicitly provided via adapter configuration:
|
||||
The operation-invoking channel adapter enables message-driven invocation of any managed operation exposed by an MBean.
|
||||
Each invocation requires the operation name to be invoked and the object name of the target MBean.
|
||||
Both of these must be explicitly provided by adapter configuration, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-jmx:operation-invoking-channel-adapter id="adapter"
|
||||
object-name="example.domain:name=TestBean"
|
||||
operation-name="ping"/>
|
||||
----
|
||||
====
|
||||
|
||||
Then the adapter only needs to be able to discover the _mbeanServer_ bean.
|
||||
If a different bean name is required, then provide the _mbean-server_ attribute with a reference.
|
||||
Then the adapter only needs to be able to discover the `mbeanServer` bean.
|
||||
If a different bean name is required, then provide the `mbean-server` attribute with a reference.
|
||||
|
||||
The payload of the Message will be mapped to the parameters of the operation, if any.
|
||||
A Map-typed payload with String keys is treated as name/value pairs, whereas a List or array would be passed as a simple argument list (with no explicit parameter names).
|
||||
If the operation requires a single parameter value, then the payload can represent that single value, and if the operation requires no parameters, then the payload would be ignored.
|
||||
The payload of the message is mapped to the parameters of the operation, if any.
|
||||
A `Map`-typed payload with `String` keys is treated as name/value pairs, whereas a `List` or array is passed as a simple argument list (with no explicit parameter names).
|
||||
If the operation requires a single parameter value, the payload can represent that single value.
|
||||
Also, if the operation requires no parameters, the payload would be ignored.
|
||||
|
||||
If you want to expose a channel for a single common operation to be invoked by Messages that need not contain headers, then that option works well.
|
||||
If you want to expose a channel for a single common operation to be invoked by messages that need not contain headers, that last option works well.
|
||||
|
||||
[[jmx-operation-invoking-outbound-gateway]]
|
||||
==== Operation Invoking Outbound Gateway
|
||||
==== Operation-invoking Outbound Gateway
|
||||
|
||||
Similar to the _operation-invoking-channel-adapter_ Spring Integration also provides a _operation-invoking-outbound-gateway_, which could be used when dealing with non-void operations and a return value is required.
|
||||
Such return value will be sent as message payload to the _reply-channel_ specified by this Gateway.
|
||||
Similarly to the operation-invoking channel adapter, Spring Integration also provides an operation-invoking outbound gateway, which you can use when dealing with non-void operations when a return value is required.
|
||||
The return value is sent as the message payload to the `reply-channel` specified by the gateway.
|
||||
The following example shows how to configure an operation-invoking outbound gateway with XML:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-jmx:operation-invoking-outbound-gateway request-channel="requestChannel"
|
||||
@@ -173,18 +198,21 @@ Such return value will be sent as message payload to the _reply-channel_ specifi
|
||||
object-name="o.s.i.jmx.config:type=TestBean,name=testBeanGateway"
|
||||
operation-name="testWithReturn"/>
|
||||
----
|
||||
====
|
||||
|
||||
If the _reply-channel_ attribute is not provided, the reply message will be sent to the channel that is identified by the `IntegrationMessageHeaderAccessor.REPLY_CHANNEL` header.
|
||||
That header is typically auto-created by the entry point into a message flow, such as any _Gateway_ component.
|
||||
However, if the message flow was started by manually creating a Spring Integration Message and sending it directly to a _Channel_, then you must specify the message header explicitly or use the provided _reply-channel_ attribute.
|
||||
If you do not provide the `reply-channel` attribute, the reply message is sent to the channel identified by the `IntegrationMessageHeaderAccessor.REPLY_CHANNEL` header.
|
||||
That header is typically auto-created by the entry point into a message flow, such as any gateway component.
|
||||
However, if the message flow was started by manually creating a Spring Integration message and sending it directly to a channel, you must specify the message header explicitly or use the `reply-channel` attribute.
|
||||
|
||||
[[jmx-mbean-exporter]]
|
||||
==== MBean Exporter
|
||||
|
||||
Spring Integration components themselves may be exposed as MBeans when the `IntegrationMBeanExporter` is configured.
|
||||
Spring Integration components may themselvesbe exposed as MBeans when the `IntegrationMBeanExporter` is configured.
|
||||
To create an instance of the `IntegrationMBeanExporter`, define a bean and provide a reference to an `MBeanServer` and a domain name (if desired).
|
||||
The domain can be left out, in which case the default domain is _org.springframework.integration_.
|
||||
You can leave out the domain, in which case the default domain is `org.springframework.integration`.
|
||||
The following example shows how to declare an instance of an `IntegrationMBeanExporter` and an associated `MBeanServer` instance:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-jmx:mbean-export id="integrationMBeanExporter"
|
||||
@@ -194,18 +222,22 @@ The domain can be left out, in which case the default domain is _org.springframe
|
||||
<property name="locateExistingServerIfPossible" value="true"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
[IMPORTANT]
|
||||
=====
|
||||
The MBean exporter is orthogonal to the one provided in Spring core - it registers message channels and message handlers, but not itself.
|
||||
You can expose the exporter itself, and certain other components in Spring Integration, using the standard `<context:mbean-export/>` tag.
|
||||
The exporter has a some metrics attached to it, for instance a count of the number of active handlers and the number of queued messages.
|
||||
The MBean exporter is orthogonal to the one provided in Spring core.
|
||||
It registers message channels and message handlers but does not register itself.
|
||||
You can expose the exporter itself (and certain other components in Spring Integration) by using the standard `<context:mbean-export/>` tag.
|
||||
The exporter has some metrics attached to it -- for instance, a count of the number of active handlers and the number of queued messages.
|
||||
|
||||
It also has a useful operation, as discussed in <<jmx-mbean-shutdown>>.
|
||||
It also has a useful operation, as discussed in "`<<jmx-mbean-shutdown>>`".
|
||||
=====
|
||||
|
||||
Starting with _Spring Integration 4.0_ the `@EnableIntegrationMBeanExport` annotation has been introduced for convenient configuration of a default (`integrationMbeanExporter`) bean of type `IntegrationMBeanExporter` with several useful options at the `@Configuration` class level.
|
||||
For example:
|
||||
Spring Integration 4.0 introduced the `@EnableIntegrationMBeanExport` annotation to allow for convenient configuration of a default `integrationMbeanExporter` bean of type `IntegrationMBeanExporter` with several useful options at the `@Configuration` class level.
|
||||
The following example shows how to configure this bean:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
@@ -219,66 +251,63 @@ public class ContextConfiguration {
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
If there is a need to provide more options, or have several `IntegrationMBeanExporter` beans e.g.
|
||||
for different MBean Servers, or to avoid conflicts with the standard Spring `MBeanExporter` (e.g.
|
||||
via `@EnableMBeanExport`), you can simply configure an `IntegrationMBeanExporter` as a generic bean.
|
||||
If you need to provide more options or have several `IntegrationMBeanExporter` beans (such as
|
||||
for different MBean Servers or to avoid conflicts with the standard Spring `MBeanExporter` -- such as through
|
||||
`@EnableMBeanExport`), you can configure an `IntegrationMBeanExporter` as a generic bean.
|
||||
|
||||
[[jmx-mbean-features]]
|
||||
===== MBean ObjectNames
|
||||
===== MBean Object Names
|
||||
|
||||
All the `MessageChannel`, `MessageHandler` and `MessageSource` instances in the application are wrapped by the MBean exporter to provide management and monitoring features.
|
||||
The generated JMX object names for each component type are listed in the table below:
|
||||
|
||||
.MBean ObjectNames
|
||||
All the `MessageChannel`, `MessageHandler`, and `MessageSource` instances in the application are wrapped by the MBean exporter to provide management and monitoring features.
|
||||
The generated JMX object names for each component type are listed in the following table:
|
||||
|
||||
.MBean Object Names
|
||||
[cols="1,3l", options="header"]
|
||||
|===
|
||||
| Component Type
|
||||
| ObjectName
|
||||
| Object Name
|
||||
|
||||
| MessageChannel
|
||||
| o.s.i:type=MessageChannel,name=<channelName>
|
||||
| `o.s.i:type=MessageChannel,name=<channelName>`
|
||||
|
||||
| MessageSource
|
||||
| o.s.i:type=MessageSource,name=<channelName>,bean=<source>
|
||||
| `o.s.i:type=MessageSource,name=<channelName>,bean=<source>`
|
||||
|
||||
| MessageHandler
|
||||
| o.s.i:type=MessageSource,name=<channelName>,bean=<source>
|
||||
| `o.s.i:type=MessageSource,name=<channelName>,bean=<source>`
|
||||
|===
|
||||
|
||||
The _bean_ attribute in the object names for sources and handlers takes one of the values in the table below:
|
||||
The `bean` attribute in the object names for sources and handlers takes one of the values in the following table:
|
||||
|
||||
.bean ObjectName Part
|
||||
|
||||
|
||||
[cols="1,3", options="header"]
|
||||
|===
|
||||
| Bean Value
|
||||
| Description
|
||||
|
||||
| endpoint
|
||||
| The bean name of the enclosing endpoint (e.g.
|
||||
<service-activator>) if there is one
|
||||
| The bean name of the enclosing endpoint (for example `<service-activator>`), if there is one
|
||||
|
||||
| anonymous
|
||||
| An indication that the enclosing endpoint didn't have a user-specified bean name, so the JMX name is the input channel name
|
||||
| An indication that the enclosing endpoint did not have a user-specified bean name, so the JMX name is the input channel name.
|
||||
|
||||
| internal
|
||||
| For well-known Spring Integration default components
|
||||
| For well known Spring Integration default components
|
||||
|
||||
| handler/source
|
||||
| None of the above: fallback to the `toString()` of the object being monitored (handler or source)
|
||||
| None of the above. Fall back to the `toString()` method of the object being monitored (handler or source)
|
||||
|
||||
|===
|
||||
|
||||
Custom elements can be appended to the object name by providing a reference to a `Properties` object in the `object-name-static-properties` attribute.
|
||||
You can append custom elements to the object name by providing a reference to a `Properties` object in the `object-name-static-properties` attribute.
|
||||
|
||||
Also, since _Spring Integration 3.0_, you can use a custom http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jmx/export/naming/ObjectNamingStrategy.html[ObjectNamingStrategy] using the `object-naming-strategy` attribute.
|
||||
This permits greater control over the naming of the MBeans.
|
||||
For example, to group all Integration MBeans under an 'Integration' type.
|
||||
A simple custom naming strategy implementation might be:
|
||||
Also, since Spring Integration 3.0, you can use a custom http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jmx/export/naming/ObjectNamingStrategy.html[`ObjectNamingStrategy`] by setting the `object-naming-strategy` attribute.
|
||||
Doing so permits greater control over the naming of the MBeans, such as grouping all integration MBeans under an 'Integration' type.
|
||||
The following example shows one possible custom naming strategy implementation:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class Namer implements ObjectNamingStrategy {
|
||||
@@ -292,123 +321,117 @@ public class Namer implements ObjectNamingStrategy {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The `beanKey` argument is a String containing the standard object name beginning with the `default-domain` and including any additional static properties.
|
||||
This example simply moves the standard `type` part to `componentType` and sets the `type` to 'Integration', enabling selection of all Integration MBeans in one query:`"my.domain:type=Integration,*`.
|
||||
This also groups the beans under one tree entry under the domain in tools like VisualVM.
|
||||
The `beanKey` argument is a `String` that contain the standard object name, beginning with the `default-domain` and including any additional static properties.
|
||||
The preceding example moves the standard `type` part to `componentType` and sets the `type` to 'Integration', enabling selection of all Integration MBeans in one query:`"my.domain:type=Integration,*`.
|
||||
Doing so also groups the beans under one tree entry under the domain in such tools as VisualVM.
|
||||
|
||||
NOTE: The default naming strategy is a http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jmx/export/naming/MetadataNamingStrategy.html[MetadataNamingStrategy].
|
||||
The exporter propagates the `default-domain` to that object to allow it to generate a fallback object name if parsing of the bean key fails.
|
||||
If your custom naming strategy is a `MetadataNamingStrategy` (or subclass), the exporter will *not* propagate the `default-domain`; you will need to configure it on your strategy bean.
|
||||
NOTE: The default naming strategy is a http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jmx/export/naming/MetadataNamingStrategy.html[`MetadataNamingStrategy`].
|
||||
The exporter propagates the `default-domain` to that object to let it generate a fallback object name if parsing of the bean key fails.
|
||||
If your custom naming strategy is a `MetadataNamingStrategy` (or a subclass of it), the exporter does not propagate the `default-domain`.
|
||||
You must configure it on your strategy bean.
|
||||
|
||||
[[jmx-42-improvements]]
|
||||
===== JMX Improvements
|
||||
|
||||
_Version 4.2_ introduced some important improvements, representing a fairly major overhaul to the JMX support in the framework.
|
||||
These resulted in a significant performance improvement of the JMX statistics collection and much more control thereof, but has some implications for user code in a few specific (uncommon) situations.
|
||||
These changes are detailed below, with a *caution* where necessary.
|
||||
Version 4.2 introduced some important improvements, representing a fairly major overhaul to the JMX support in the framework.
|
||||
These resulted in a significant performance improvement of the JMX statistics collection and much more control thereof.
|
||||
However, it has some implications for user code in a few specific (uncommon) situations.
|
||||
These changes are detailed below, with a caution where necessary.
|
||||
|
||||
* *Metrics Capture*
|
||||
|
||||
Previously, `MessageSource`, `MessageChannel` and `MessageHandler` metrics were captured by wrapping the object in a JDK dynamic proxy to intercept appropriate method calls and capture the statistics.
|
||||
Metrics Capture::
|
||||
Previously, `MessageSource`, `MessageChannel`, and `MessageHandler` metrics were captured by wrapping the object in a JDK dynamic proxy to intercept appropriate method calls and capture the statistics.
|
||||
The proxy was added when an integration MBean exporter was declared in the context.
|
||||
|
||||
Now, the statistics are captured by the beans themselves; see <<metrics-management>> for more information.
|
||||
|
||||
+
|
||||
Now, the statistics are captured by the beans themselves.
|
||||
See "`<<metrics-management>>`" for more information.
|
||||
+
|
||||
WARNING: This change means that you no longer automatically get an MBean or statistics for custom `MessageHandler` implementations, unless those custom handlers extend `AbstractMessageHandler`.
|
||||
The simplest way to resolve this is to extend `AbstractMessageHandler`.
|
||||
If that's not possible, or desired, another work-around is to implement the `MessageHandlerMetrics` interface.
|
||||
If you cannot do so, another work around is to implement the `MessageHandlerMetrics` interface.
|
||||
For convenience, a `DefaultMessageHandlerMetrics` is provided to capture and report statistics.
|
||||
Invoke the `beforeHandle` and `afterHandle` at the appropriate times.
|
||||
You should invoke the `beforeHandle` and `afterHandle` at the appropriate times.
|
||||
Your `MessageHandlerMetrics` methods can then delegate to this object to obtain each statistic.
|
||||
Similarly, `MessageSource` implementations must extend `AbstractMessageSource` or implement `MessageSourceMetrics`.
|
||||
Message sources only capture a count so there is no provided convenience class; simply maintain the count in an `AtomicLong` field.
|
||||
Message sources capture only a count, so there is no provided convenience class.
|
||||
You should maintain the count in an `AtomicLong` field.
|
||||
+
|
||||
The removal of the proxy has two additional benefits:
|
||||
+
|
||||
* Stack traces in exceptions are reduced (when JMX is enabled) because the proxy is not on the stack
|
||||
* Cases where two MBeans were exported for the same bean now only export a single MBean with consolidated attributes and operations (see the MBean consolidation bullet, later).
|
||||
|
||||
The removal of the proxy has two additional benefits; 1) stack traces in exceptions are reduced (when JMX is enabled) because the proxy is not on the stack; 2) cases where 2 MBeans were exported for the same bean now only export a single MBean with consolidated attributes/operations (see the MBean consolidation bullet below).
|
||||
Resolution::
|
||||
`System.nanoTime()` (rather than `System.currentTimeMillis()`) is now used to capture times .
|
||||
This may provide more accuracy on some JVMs, espcially when you expect durations of less than one millisecond.
|
||||
|
||||
Setting Initial Statistics Collection State::
|
||||
Previously, when JMX was enabled, all sources, channels, and handlers captured statistics.
|
||||
You can now control whether the statistics are enabled on an individual component.
|
||||
Further, you can capture simple counts on `MessageChannel` instances and `MessageHandler` instances instead of capturing the complete time-based statistics.
|
||||
This can have significant performance implications, because you can selectively configure where you need detailed statistics and enable and disable collection at runtime.
|
||||
+
|
||||
See "`<<metrics-management>>`".
|
||||
|
||||
* *Resolution*
|
||||
|
||||
`System.nanoTime()` is now used to capture times instead of `System.currentTimeMillis()`.
|
||||
This may provide more accuracy on some JVMs, espcially when durations of less than 1 millisecond are expected
|
||||
|
||||
|
||||
* *Setting Initial Statistics Collection State*
|
||||
|
||||
Previously, when JMX was enabled, all sources, channels, handlers captured statistics.
|
||||
It is now possible to control whether the statisics are enabled on an individual component.
|
||||
Further, it is possible to capture simple counts on `MessageChannel` s and `MessageHandler` s instead of the complete time-based statistics.
|
||||
This can have significant performance implications because you can selectively configure where you need detailed statistics, as well as enable/disable at runtime.
|
||||
|
||||
See <<metrics-management>>.
|
||||
|
||||
* *@IntegrationManagedResource*
|
||||
|
||||
Similar to the `@ManagedResource` annotation, the `@IntegrationManagedResource` marks a class as eligible to be exported as an MBean; however, it will only be exported if there is an `IntegrationMBeanExporter` in the application context.
|
||||
|
||||
@IntegrationManagedResource::
|
||||
Similar to the `@ManagedResource` annotation, the `@IntegrationManagedResource` marks a class as being eligible to be exported as an MBean.
|
||||
However, it is exported only if the application context has an `IntegrationMBeanExporter`.
|
||||
+
|
||||
Certain Spring Integration classes (in the `org.springframework.integration`) package) that were previously annotated with`@ManagedResource` are now annotated with both `@ManagedResource` and `@IntegrationManagedResource`.
|
||||
This is for backwards compatibility (see the next bullet).
|
||||
Such MBeans will be exported by any context `MBeanServer`*or* an `IntegrationMBeanExporter` (but not both - if both exporters are present, the bean is exported by the integration exporter if the bean matches a `managed-components` pattern).
|
||||
|
||||
|
||||
* *Consolidated MBeans*
|
||||
|
||||
Certain classes within the framework (mapping routers for example) have additional attributes/operations over and above those provided by metrics and `Lifecycle`.
|
||||
We will use a `Router` as an example here.
|
||||
This is for backwards compatibility (see the next item).
|
||||
Such MBeans are exported by any context `MBeanServer` or by an `IntegrationMBeanExporter` (but not both -- if both exporters are present, the bean is exported by the integration exporter if the bean matches a `managed-components` pattern).
|
||||
|
||||
Consolidated MBeans::
|
||||
Certain classes within the framework (mapping routers, for example) have additional attributes and operations over and above those provided by metrics and `Lifecycle`.
|
||||
We use a `Router` as an example here.
|
||||
+
|
||||
Previously, beans of these types were exported as two distinct MBeans:
|
||||
|
||||
1) the metrics MBean (with an objectName such as: `intDomain:type=MessageHandler,name=myRouter,bean=endpoint`).
|
||||
+
|
||||
* The metrics MBean (with an object name such as `intDomain:type=MessageHandler,name=myRouter,bean=endpoint`).
|
||||
This MBean had metrics attributes and metrics/Lifecycle operations.
|
||||
|
||||
2) a second MBean (with an objectName such as:
|
||||
`ctxDomain:name=org.springframework.integration.config.RouterFactoryBean#0`
|
||||
`,type=MethodInvokingRouter`)
|
||||
was exported with the channel mappings attribute and operations.
|
||||
|
||||
Now, the attributes and operations are consolidated into a single MBean.
|
||||
The objectName will depend on the exporter.
|
||||
If exported by the integration MBean exporter, the objectName will be, for example: `intDomain:type=MessageHandler,name=myRouter,bean=endpoint`.
|
||||
If exported by another exporter, the objectName will be, for example: `ctxDomain:name=org.springframework.integration.config.RouterFactoryBean#0`
|
||||
`,type=MethodInvokingRouter`.
|
||||
There is no difference between these MBeans (aside from the objectName), except that the statistics will *not* be enabled (the attributes will be 0) by exporters other than the integration exporter; statistics can be enabled at runtime using the JMX operations.
|
||||
When exported by the integration MBean exporter, the initial state can be managed as described above.
|
||||
|
||||
WARNING: If you are currently using the second MBean to change, for example, channel mappings, *and* you are using the integration MBean exporter, note that the objectName has changed because of the MBean consolidation.
|
||||
* A second MBean (with an object name such as `ctxDomain:name=org.springframework.integration.config.RouterFactoryBean#0,type=MethodInvokingRouter`) was exported with the channel mappings attribute and operations.
|
||||
+
|
||||
Now the attributes and operations are consolidated into a single MBean.
|
||||
The object name depends on the exporter.
|
||||
If exported by the integration MBean exporter, the object name is, for example: `intDomain:type=MessageHandler,name=myRouter,bean=endpoint`.
|
||||
If exported by another exporter, the object name is, for example: `ctxDomain:name=org.springframework.integration.config.RouterFactoryBean#0,type=MethodInvokingRouter`.
|
||||
There is no difference between these MBeans (aside from the object name), except that the statistics are not enabled (the attributes are `0`) by exporters other than the integration exporter.
|
||||
You can enable statistics at runtime by using the JMX operations.
|
||||
When exported by the integration MBean exporter, the initial state can be managed as described earlier.
|
||||
+
|
||||
WARNING: If you currently use the second MBean to change, for example, channel mappings and you use the integration MBean exporter, note that the object name has changed because of the MBean consolidation.
|
||||
There is no change if you are not using the integration MBean exporter.
|
||||
|
||||
|
||||
* *MBean Exporter Bean Name Patterns*
|
||||
|
||||
MBean Exporter Bean Name Patterns::
|
||||
Previously, the `managed-components` patterns were inclusive only.
|
||||
If a bean name matched one of the patterns it would be included.
|
||||
If a bean name matched one of the patterns, it would be included.
|
||||
Now, the pattern can be negated by prefixing it with `!`.
|
||||
i.e.
|
||||
`"!foo*, foox"` will match all beans that don't start with `foo`, except `foox`.
|
||||
Patterns are evaluated left to right and the first match (positive or negative) wins and no further patterns are applied.
|
||||
|
||||
For example, `!thing*, things` matches all bean names that do not start with `thing` except `things`.
|
||||
Patterns are evaluated left to right.
|
||||
The first match (positive or negative) wins, and then no further patterns are applied.
|
||||
+
|
||||
WARNING: The addition of this syntax to the pattern causes one possible (although perhaps unlikely) problem.
|
||||
If you have a bean `"!foo"`*and* you included a pattern `"!foo"` in your MBean exporter's `managed-components` patterns; it will no long match; the pattern will now match all beans *not* named `foo`.
|
||||
If you have a bean named `"!thing"` and you included a pattern of `!thing` in your MBean exporter's `managed-components` patterns, it no longer matches; the pattern now matches all beans not named `thing`.
|
||||
In this case, you can escape the `!` in the pattern with `\`.
|
||||
The pattern `"\!foo"` means match a bean named `"!foo"`.
|
||||
The `\!thing` pattern matches a bean named `!thing`.
|
||||
|
||||
* *IntegrationMBeanExporter changes*
|
||||
|
||||
The `IntegrationMBeanExporter` no longer implements `SmartLifecycle`; this means that `start()` and `stop()` operations
|
||||
are no longer available to register/unregister MBeans.
|
||||
IntegrationMBeanExporter changes::
|
||||
The `IntegrationMBeanExporter` no longer implements `SmartLifecycle`.
|
||||
This means that `start()` and `stop()` operations are no longer available to registerand unregister MBeans.
|
||||
The MBeans are now registered during context initialization and unregistered when the context is destroyed.
|
||||
|
||||
|
||||
|
||||
[[jmx-mbean-shutdown]]
|
||||
===== Orderly Shutdown Managed Operation
|
||||
|
||||
The MBean exporter provides a JMX operation to shut down the application in an orderly manner, intended for use before terminating the JVM.
|
||||
The following example shows how to use it:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public void stopActiveComponents(long howLong)
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
Its use and operation are described in <<jmx-shutdown>>.
|
||||
Its use and operation are described in "`<<jmx-shutdown>>`".
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,16 @@
|
||||
[[logging-channel-adapter]]
|
||||
=== Logging Channel Adapter
|
||||
|
||||
The `<logging-channel-adapter/>` is often used in conjunction with a Wire Tap, as discussed in <<channel-wiretap>>.
|
||||
The `<logging-channel-adapter>` is often used in conjunction with a wire tap, as discussed in "`<<channel-wiretap>>`".
|
||||
However, it can also be used as the ultimate consumer of any flow.
|
||||
For example, consider a flow that ends with a `<service-activator/>` that returns a result, but you wish to discard that result.
|
||||
For example, consider a flow that ends with a `<service-activator>` that returns a result, but you wish to discard that result.
|
||||
To do that, you could send the result to `NullChannel`.
|
||||
Alternatively, you can route it to an `INFO` level `<logging-channel-adapter/>`; that way, you can see the discarded message when logging at `INFO` level, but not see it when logging at, say, `WARN` level.
|
||||
With a `NullChannel`, you would only see the discarded message when logging at `DEBUG` level.
|
||||
Alternatively, you can route it to an `INFO` level `<logging-channel-adapter>`.
|
||||
That way, you can see the discarded message when logging at `INFO` level but not see it when logging at (for example) the `WARN` level.
|
||||
With a `NullChannel`, you would see only the discarded message when logging at the `DEBUG` level.
|
||||
The following listing shows all the possible attributes for the `logging-channel-adapter` element:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
|
||||
@@ -19,31 +22,27 @@ With a `NullChannel`, you would only see the discarded message when logging at `
|
||||
logger-name="" /> <5>
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
<1> The channel connecting the logging adapter to an upstream component.
|
||||
|
||||
|
||||
<2> The logging level at which messages sent to this adapter will be logged.
|
||||
Default: `INFO`.
|
||||
|
||||
|
||||
<3> A SpEL expression representing exactly what part(s) of the message will be logged.
|
||||
Default: `payload` - just the payload will be logged.
|
||||
This attribute cannot be specified if `log-full-message` is specified.
|
||||
|
||||
|
||||
<4> When `true`, the entire message will be logged (including headers).
|
||||
Default: `false` - just the payload will be logged.
|
||||
<3> A SpEL expression representing exactly what parts of the message are logged.
|
||||
Default: `payload` -- only the payload is logged.
|
||||
if `log-full-message` is specified, this attribute cannot be specified.
|
||||
<4> When `true`, the entire message (including headers) is logged.
|
||||
Default: `false` -- only the payload is logged.
|
||||
This attribute cannot be specified if `expression` is specified.
|
||||
|
||||
|
||||
<5> Specifies the _name_ of the logger (known as `category` in `log4j`) used for log messages created by this adapter.
|
||||
<5> Specifies the `name` of the logger (known as `category` in `log4j`).
|
||||
Used to identify log messages created by this adapter.
|
||||
This enables setting the log name (in the logging subsystem) for individual adapters.
|
||||
By default, all adapters will log under the name `org.springframework.integration.handler.LoggingHandler`.
|
||||
By default, all adapters log under the following name: `org.springframework.integration.handler.LoggingHandler`.
|
||||
|
||||
==== Configuring with Java Configuration
|
||||
==== Using Java Configuration
|
||||
|
||||
The following Spring Boot application provides an example of configuring the `LoggingHandler` using Java configuration:
|
||||
The following Spring Boot application shows an example of configuring the `LoggingHandler` by using Java configuration:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@@ -76,11 +75,13 @@ public class LoggingJavaApplication {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
==== Configuring with the Java DSL
|
||||
|
||||
The following Spring Boot application provides an example of configuring the logging channel adapter using the Java DSL:
|
||||
The following Spring Boot application shows an example of configuring the logging channel adapter by using the Java DSL:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@@ -110,4 +111,5 @@ public class LoggingJavaApplication {
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
----
|
||||
====
|
||||
|
||||
@@ -1,30 +1,37 @@
|
||||
[[mail]]
|
||||
== Mail Support
|
||||
|
||||
This section describes how to work with mail messages in Spring Integration.
|
||||
|
||||
[[mail-outbound]]
|
||||
=== Mail-Sending Channel Adapter
|
||||
=== Mail-sending Channel Adapter
|
||||
|
||||
Spring Integration provides support for outbound email with the `MailSendingMessageHandler`.
|
||||
It delegates to a configured instance of Spring's `JavaMailSender`:
|
||||
It delegates to a configured instance of Spring's `JavaMailSender`, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
JavaMailSender mailSender = context.getBean("mailSender", JavaMailSender.class);
|
||||
|
||||
MailSendingMessageHandler mailSendingHandler = new MailSendingMessageHandler(mailSender);
|
||||
----
|
||||
====
|
||||
|
||||
`MailSendingMessageHandler` has various mapping strategies that use Spring's `MailMessage` abstraction.
|
||||
If the received Message's payload is already a `MailMessage` instance, it will be sent directly.
|
||||
Therefore, it is generally recommended to precede this consumer with a Transformer for non-trivial `MailMessage` construction requirements.
|
||||
However, a few simple Message mapping strategies are supported out-of-the-box.
|
||||
For example, if the message payload is a byte array, then that will be mapped to an attachment.
|
||||
For simple text-based emails, you can provide a String-based Message payload.
|
||||
In that case, a MailMessage will be created with that String as the text content.
|
||||
If you are working with a Message payload type whose `toString()` method returns appropriate mail text content, then consider adding Spring Integration's _ObjectToStringTransformer_ prior to the outbound Mail adapter (see the example within <<transformer-namespace>> for more detail).
|
||||
If the received message's payload is already a `MailMessage` instance, it is sent directly.
|
||||
Therefore, we generally recommend that you precede this consumer with a transformer for non-trivial `MailMessage` construction requirements.
|
||||
However, Spring Integration supports a few simple message mapping strategies.
|
||||
For example, if the message payload is a byte array, that is mapped to an attachment.
|
||||
For simple text-based emails, you can provide a string-based message payload.
|
||||
In that case, a `MailMessage` is created with that `String` as the text content.
|
||||
If you work with a message payload type whose `toString()` method returns appropriate mail text content, consider adding Spring Integration's `ObjectToStringTransformer` prior to the outbound mail adapter (see the example in "`<<transformer-namespace>>`" for more detail).
|
||||
|
||||
The outbound MailMessage may also be configured with certain values from the `MessageHeaders`.
|
||||
If available, values will be mapped to the outbound mail's properties, such as the recipients (TO, CC, and BCC), the from/reply-to, and the subject.
|
||||
You can also configure the outbound `MailMessage` with certain values from `MessageHeaders`.
|
||||
If available, values are mapped to the outbound mail's properties, such as the recipients (To, Cc, and BCc), the from, the reply-to, and the subject.
|
||||
The header names are defined by the following constants:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
MailHeaders.SUBJECT
|
||||
@@ -34,104 +41,109 @@ The header names are defined by the following constants:
|
||||
MailHeaders.FROM
|
||||
MailHeaders.REPLY_TO
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: `MailHeaders` also allows you to override corresponding `MailMessage` values.
|
||||
For example: If `MailMessage.to` is set to 'foo@bar.com' and `MailHeaders.TO` Message header is provided it will take precedence and override the corresponding value in `MailMessage`.
|
||||
NOTE: `MailHeaders` also lets you override corresponding `MailMessage` values.
|
||||
For example, if `MailMessage.to` is set to 'thing1@things.com' and the `MailHeaders.TO` message header is provided, it takes precedence and overrides the corresponding value in `MailMessage`.
|
||||
|
||||
[[mail-inbound]]
|
||||
=== Mail-Receiving Channel Adapter
|
||||
=== Mail-receiving Channel Adapter
|
||||
|
||||
Spring Integration also provides support for inbound email with the `MailReceivingMessageSource`.
|
||||
It delegates to a configured instance of Spring Integration's own `MailReceiver` interface, and there are two implementations: `Pop3MailReceiver` and `ImapMailReceiver`.
|
||||
The easiest way to instantiate either of these is by passing the 'uri' for a Mail store to the receiver's constructor.
|
||||
For example:
|
||||
It delegates to a configured instance of Spring Integration's own `MailReceiver` interface.
|
||||
There are two implementations: `Pop3MailReceiver` and `ImapMailReceiver`.
|
||||
The easiest way to instantiate either of these is by passing the 'uri' for a mail store to the receiver's constructor, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
MailReceiver receiver = new Pop3MailReceiver("pop3://usr:pwd@localhost/INBOX");
|
||||
----
|
||||
====
|
||||
|
||||
Another option for receiving mail is the IMAP "idle" command (if supported by the mail server you are using).
|
||||
Spring Integration provides the `ImapIdleChannelAdapter` which is itself a Message-producing endpoint.
|
||||
It delegates to an instance of the `ImapMailReceiver` but enables asynchronous reception of Mail Messages.
|
||||
There are examples in the next section of configuring both types of inbound Channel Adapter with Spring Integration's namespace support in the 'mail' schema.
|
||||
Another option for receiving mail is the IMAP `idle` command (if supported by your mail server).
|
||||
Spring Integration provides the `ImapIdleChannelAdapter`, which is itself a message-producing endpoint.
|
||||
It delegates to an instance of the `ImapMailReceiver` but enables asynchronous reception of mail messages.
|
||||
The next section has examples of configuring both types of inbound channel adapter with Spring Integration's namespace support in the 'mail' schema.
|
||||
|
||||
[[imap-format-important]]
|
||||
[IMPORTANT]
|
||||
====
|
||||
Normally, when `IMAPMessage.getContent()` method is called, certain headers as well as the body are rendered (for a simple text email):
|
||||
Normally, when the `IMAPMessage.getContent()` method is called, certain headers as well as the body are rendered (for a simple text email), as the following example shows:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
To: foo@bar
|
||||
From: bar@baz
|
||||
To: thing1@things.com
|
||||
From: thing2@morethings.com
|
||||
Subject: Test Email
|
||||
|
||||
foo
|
||||
something
|
||||
----
|
||||
====
|
||||
|
||||
With a simple `MimeMessage`, `getContent()` just returns the mail body (`foo` in this case).
|
||||
With a simple `MimeMessage`, `getContent()` returns the mail body (`something` in the preceding example).
|
||||
|
||||
Starting with _version 2.2_, the framework eagerly fetches IMAP messages and exposes them as an internal subclass of `MimeMessage`.
|
||||
Starting with version 2.2, the framework eagerly fetches IMAP messages and exposes them as an internal subclass of `MimeMessage`.
|
||||
This had the undesired side effect of changing the `getContent()` behavior.
|
||||
This inconsistency was further exacerbated by the <<mail-mapping, Mail Mapping>> enhancement in _version 4.3_ in that, when a header mapper was provided, the payload was rendered by the `IMAPMessage.getContent()` method.
|
||||
This meant that IMAP content differed depending on whether or not a header mapper was provided.
|
||||
Starting with _version 5.0_, messages originating from an IMAP source will now render the content in accordance with `IMAPMessage.getContent()` behavior, regardless of whether a header mapper is provided.
|
||||
If you are not using a header mapper, and you wish to revert to the previous behavior of just rendering the body, set the `simpleContent` boolean property on the mail receiver to `true`.
|
||||
This property now controls the rendering regardless of whether a header mapper is used; it now allows the simple body-only rendering when a header mapper is provided.
|
||||
This inconsistency was further exacerbated by the <<mail-mapping, Mail Mapping>> enhancement introduced in version 4.3, because, when a header mapper was provided, the payload was rendered by the `IMAPMessage.getContent()` method.
|
||||
This meant that the IMAP content differed, depending on whether or not a header mapper was provided.
|
||||
Starting with version 5.0, messages originating from an IMAP source render the content in accordance with `IMAPMessage.getContent()` behavior, regardless of whether a header mapper is provided.
|
||||
If you do not use a header mapper and you wish to revert to the previous behavior of rendering only the body, set the `simpleContent` boolean property on the mail receiver to `true`.
|
||||
This property now controls the rendering regardless of whether a header mapper is used.
|
||||
It now allows body-only rendering when a header mapper is provided.
|
||||
====
|
||||
|
||||
[[mail-mapping]]
|
||||
=== Inbound Mail Message Mapping
|
||||
|
||||
By default, the payload of messages produced by the inbound adapters is the raw `MimeMessage`; you can interrogate
|
||||
the headers and content using that object.
|
||||
Starting with _version 4.3_, you can provide a `HeaderMapper<MimeMessage>` to map the headers to `MessageHeaders`; for
|
||||
convenience, a `DefaultMailHeaderMapper` is provided for this purpose.
|
||||
This maps the following headers:
|
||||
By default, the payload of messages produced by the inbound adapters is the raw `MimeMessage`.
|
||||
You can use that object to interrogate the headers and content.
|
||||
Starting with version 4.3, you can provide a `HeaderMapper<MimeMessage>` to map the headers to `MessageHeaders`.
|
||||
For convenience, Spring Integration provides a `DefaultMailHeaderMapper` for this purpose.
|
||||
It maps the following headers:
|
||||
|
||||
- `mail_from` - A String representation of the `from` address.
|
||||
- `mail_bcc` - A String array containing the `bcc` addresses.
|
||||
- `mail_cc` - A String array containing the `cc` addresses.
|
||||
- `mail_to` - A String array containing the `to` addresses.
|
||||
- `mail_replyTo` - A String representation of the `replyTo` address.
|
||||
- `mail_subject` - The mail subject.
|
||||
- `mail_lineCount` - A line count (if available).
|
||||
- `mail_receivedDate` - The received date (if available).
|
||||
- `mail_size` - The mail size (if available).
|
||||
- `mail_expunged` - A boolean indicating if the message is expunged.
|
||||
- `mail_raw` - A `MultiValueMap` containing all the mail headers and their values.
|
||||
- `mail_contentType` - The content type of the original mail message.
|
||||
- `contentType` - The payload content type (see below).
|
||||
* `mail_from`: A `String` representation of the `from` address.
|
||||
* `mail_bcc`: A `String` array containing the `bcc` addresses.
|
||||
* `mail_cc`: A `String` array containing the `cc` addresses.
|
||||
* `mail_to`: A `String` array containing the `to` addresses.
|
||||
* `mail_replyTo`: A `String` representation of the `replyTo` address.
|
||||
* `mail_subject`: The mail subject.
|
||||
* `mail_lineCount`: A line count (if available).
|
||||
* `mail_receivedDate`: The received date (if available).
|
||||
* `mail_size`: The mail size (if available).
|
||||
* `mail_expunged`: A boolean indicating if the message is expunged.
|
||||
* `mail_raw`: A `MultiValueMap` containing all the mail headers and their values.
|
||||
* `mail_contentType`: The content type of the original mail message.
|
||||
* `contentType`: The payload content type (see below).
|
||||
|
||||
When message mapping is enabled, the payload depends on the mail message and its implementation.
|
||||
Email contents are usually rendered by a `DataHandler` within the `MimeMessage`.
|
||||
|
||||
- For a simple `text/*` email, the payload will be a String and the `contentType` header will be the same as
|
||||
`mail_contentType`.
|
||||
For a `text/*` email, the payload is a `String` and the `contentType` header is the same as `mail_contentType`.
|
||||
|
||||
- For a messages with embedded `javax.mail.Part` s, the `DataHandler` usually renders a `Part` object - these objects
|
||||
are not `Serializable`, and are not suitable for serialization using alternative technologies such as `Kryo`.
|
||||
For this reason, by default, when mapping is enabled, such payloads are rendered as a raw `byte[]` containing the
|
||||
`Part` data.
|
||||
For a messages with embedded `javax.mail.Part` instances, the `DataHandler` usually renders a `Part` object.
|
||||
These objects are not `Serializable` and are not suitable for serialization with alternative technologies such as `Kryo`.
|
||||
For this reason, by default, when mapping is enabled, such payloads are rendered as a raw `byte[]` containing the `Part` data.
|
||||
Examples of `Part` are `Message` and `Multipart`.
|
||||
The `contentType` header is `application/octet-stream` in this case.
|
||||
To change this behavior, and receive a `Multipart` object payload, set `embeddedPartsAsBytes` to `false` on the
|
||||
`MailReceiver`.
|
||||
For content types that are unknown to the `DataHandler`, the contents are rendered as a `byte[]` with a `contentType`
|
||||
header of `application/octet-stream`.
|
||||
To change this behavior and receive a `Multipart` object payload, set `embeddedPartsAsBytes` to `false` on `MailReceiver`.
|
||||
For content types that are unknown to the `DataHandler`, the contents are rendered as a `byte[]` with a `contentType` header of `application/octet-stream`.
|
||||
|
||||
When you do not provide a header mapper, the message payload is the `MimeMessage` presented by `javax.mail`.
|
||||
The framework provides a `MailToStringTransformer` which can be used to convert the message using a simple strategy
|
||||
to convert the mail contents to a String.
|
||||
This is also available using the XML namespace:
|
||||
The framework provides a `MailToStringTransformer` that you can use to convert the message by using a strategy to convert the mail contents to a `String`.
|
||||
This is also available by using the XML namespace, as the following example shows:
|
||||
|
||||
====
|
||||
[source, xml]
|
||||
----
|
||||
<int-mail:mail-to-string-transformer ... >
|
||||
----
|
||||
====
|
||||
|
||||
and with Java configuration:
|
||||
The following example does the same thing with Java configuration:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
@@ -140,27 +152,30 @@ public Transformer transformer() {
|
||||
return new MailToStringTransformer();
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
and with the Java DSL:
|
||||
The following example does the same thing with the Java DSL:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
...
|
||||
.transform(Mail.toStringTransformer())
|
||||
...
|
||||
----
|
||||
====
|
||||
|
||||
Starting with _version 4.3_, the transformer will handle embedded `Part` as well as `Multipart` which was handled
|
||||
previously.
|
||||
The transformer is a subclass of `AbstractMailTransformer` which maps the address and subject headers from the list
|
||||
above.
|
||||
Starting with version 4.3, the transformer handles embedded `Part` instances (as well as `Multipart` instances, which were handled previously).
|
||||
The transformer is a subclass of `AbstractMailTransformer` that maps the address and subject headers from the preceding list.
|
||||
If you wish to perform some other transformation on the message, consider subclassing `AbstractMailTransformer`.
|
||||
|
||||
[[mail-namespace]]
|
||||
=== Mail Namespace Support
|
||||
|
||||
Spring Integration provides a namespace for mail-related configuration.
|
||||
To use it, configure the following schema locations.
|
||||
To use it, configure the following schema locations:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
@@ -172,27 +187,36 @@ To use it, configure the following schema locations.
|
||||
http://www.springframework.org/schema/integration/mail
|
||||
http://www.springframework.org/schema/integration/mail/spring-integration-mail.xsd">
|
||||
----
|
||||
====
|
||||
|
||||
To configure an outbound Channel Adapter, provide the channel to receive from, and the MailSender:
|
||||
To configure an outbound channel adapter, provide the channel from which to receive and the MailSender, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-mail:outbound-channel-adapter channel="outboundMail"
|
||||
mail-sender="mailSender"/>
|
||||
----
|
||||
====
|
||||
|
||||
Alternatively, provide the host, username, and password:
|
||||
Alternatively, you can provide the host, username, and password, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-mail:outbound-channel-adapter channel="outboundMail"
|
||||
host="somehost" username="someuser" password="somepassword"/>
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: Keep in mind, as with any outbound Channel Adapter, if the referenced channel is a `PollableChannel`,
|
||||
a `<poller>` sub-element should be provided (see <<endpoint-namespace>>).
|
||||
NOTE: As with any outbound Channel Adapter, if the referenced channel is a `PollableChannel`, you should provide a `<poller>` element (see "`<<endpoint-namespace>>`").
|
||||
|
||||
When using the namespace support, a _header-enricher_ Message Transformer is also available.
|
||||
This simplifies the application of the headers mentioned above to any Message prior to sending to the Mail Outbound Channel Adapter.
|
||||
When you use the namespace support, you can also use a `header-enricher` message transformer.
|
||||
Doing so simplifies the application of the headers mentioned earlier to any message prior to sending to the mail outbound channel adapter.
|
||||
|
||||
The following example assumes the payload is a Java bean with appropriate getters for the specified properties, but you can use any SpEL expression:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-mail:header-enricher input-channel="expressionsInput" default-overwrite="false">
|
||||
@@ -204,14 +228,17 @@ This simplifies the application of the headers mentioned above to any Message pr
|
||||
<int-mail:subject expression="payload.subject" overwrite="true"/>
|
||||
</int-mail:header-enricher>
|
||||
----
|
||||
====
|
||||
|
||||
This example assumes the payload is a JavaBean with appropriate getters for the specified properties, but any SpEL expression can be used.
|
||||
Alternatively, use the `value` attribute to specify a literal.
|
||||
Notice also that you can specify `default-overwrite` and individual `overwrite` attributes to control the behavior with existing headers.
|
||||
Alternatively, you can use the `value` attribute to specify a literal.
|
||||
You also can specify `default-overwrite` and individual `overwrite` attributes to control the behavior with existing headers.
|
||||
|
||||
To configure an Inbound Channel Adapter, you have the choice between polling or event-driven (assuming your mail server supports IMAP IDLE - if not, then polling is the only option).
|
||||
A polling Channel Adapter simply requires the store URI and the channel to send inbound Messages to.
|
||||
The URI may begin with "pop3" or "imap":
|
||||
To configure an inbound channel adapter, you have the choice between polling or event-driven (assuming your mail server supports IMAP `idle` -- if not, then polling is the only option).
|
||||
A polling channel adapter requires the store URI and the channel to which to send inbound messages.
|
||||
The URI may begin with `pop3` or `imap`.
|
||||
The following example uses an `imap` URI:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-mail:inbound-channel-adapter id="imapAdapter"
|
||||
@@ -224,10 +251,14 @@ The URI may begin with "pop3" or "imap":
|
||||
<int:poller max-messages-per-poll="1" fixed-rate="5000"/>
|
||||
</int-mail:inbound-channel-adapter>
|
||||
----
|
||||
====
|
||||
|
||||
If you do have IMAP idle support, then you may want to configure the "imap-idle-channel-adapter" element instead.
|
||||
Since the "idle" command enables event-driven notifications, no poller is necessary for this adapter.
|
||||
It will send a Message to the specified channel as soon as it receives the notification that new mail is available:
|
||||
If you do have IMAP `idle` support, you may want to configure the `imap-idle-channel-adapter` element instead.
|
||||
Since the `idle` command enables event-driven notifications, no poller is necessary for this adapter.
|
||||
It sends a message to the specified channel as soon as it receives the notification that new mail is available.
|
||||
The following example configures an IMAP `idle` mail channel:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-mail:imap-idle-channel-adapter id="customAdapter"
|
||||
@@ -238,13 +269,15 @@ It will send a Message to the specified channel as soon as it receives the notif
|
||||
should-mark-messages-as-read="true"
|
||||
java-mail-properties="javaMailProperties"/>
|
||||
----
|
||||
====
|
||||
|
||||
\...where _javaMailProperties_ could be provided by creating and populating a regular `java.utils.Properties` object.
|
||||
For example via _util_ namespace provided by Spring.
|
||||
You can provide `javaMailProperties` by creating and populating a regular `java.utils.Properties` object -- for example, by using the `util` namespace provided by Spring.
|
||||
|
||||
IMPORTANT: If your username contains the '@' character use '%40' instead of '@' to avoid parsing errors from the underlying JavaMail API.
|
||||
IMPORTANT: If your username contains the '@' character, use '%40' instead of '@' to avoid parsing errors from the underlying JavaMail API.
|
||||
|
||||
The following example shows how to configure a `java.util.Properties` object:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<util:properties id="javaMailProperties">
|
||||
@@ -254,20 +287,24 @@ IMPORTANT: If your username contains the '@' character use '%40' instead of '@'
|
||||
<prop key="mail.debug">false</prop>
|
||||
</util:properties>
|
||||
----
|
||||
====
|
||||
|
||||
[[search-term]]
|
||||
By default, the `ImapMailReceiver` will search for Messages based on the default `SearchTerm` which is _All mails that
|
||||
are RECENT (if supported), that are NOT ANSWERED, that are NOT DELETED, that are NOT SEEN and have not
|
||||
been processed by this mail receiver (enabled by the use of the custom USER flag or simply NOT FLAGGED if not
|
||||
supported)_.
|
||||
The custom user flag is `spring-integration-mail-adapter` but can be configured.
|
||||
Since version 2.2, the `SearchTerm` used by the `ImapMailReceiver` is fully configurable via the `SearchTermStrategy`
|
||||
which you can inject via the `search-term-strategy` attribute.
|
||||
`SearchTermStrategy` is a simple strategy interface with a single method that allows you to create an instance of the
|
||||
`SearchTerm` that will be used by the `ImapMailReceiver`.
|
||||
By default, the `ImapMailReceiver` searches for messages based on the default `SearchTerm`, which is all mail messages that:
|
||||
|
||||
See <<imap-seen>> regarding message flagging.
|
||||
* Are RECENT (if supported)
|
||||
* Are NOT ANSWERED
|
||||
* Are NOT DELETED
|
||||
* Are NOT SEEN
|
||||
* hHave not been processed by this mail receiver (enabled by the use of the custom USER flag or simply NOT FLAGGED if not supported)
|
||||
|
||||
The custom user flag is `spring-integration-mail-adapter`, but you can configure it.
|
||||
Since version 2.2, the `SearchTerm` used by the `ImapMailReceiver` is fully configurable with `SearchTermStrategy`,
|
||||
which you can inject by using the `search-term-strategy` attribute.
|
||||
`SearchTermStrategy` is a strategy interface with a single method that lets you create an instance of the `SearchTerm` used by the `ImapMailReceiver`.
|
||||
The following listing shows the `SearchTermStrategy` interface:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public interface SearchTermStrategy {
|
||||
@@ -276,121 +313,129 @@ public interface SearchTermStrategy {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
For example:
|
||||
The following example relies `TestSearchTermStrategy` rather than the default `SearchTermStrategy`:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<mail:imap-idle-channel-adapter id="customAdapter"
|
||||
store-uri="imap:foo"
|
||||
store-uri="imap:something"
|
||||
…
|
||||
search-term-strategy="searchTermStrategy"/>
|
||||
|
||||
<bean id="searchTermStrategy"
|
||||
class="o.s.i.mail.config.ImapIdleChannelAdapterParserTests.TestSearchTermStrategy"/>
|
||||
----
|
||||
====
|
||||
|
||||
In the above example instead of relying on the default `SearchTermStrategy` the `TestSearchTermStrategy` will be used instead
|
||||
See "`<<imap-seen>>`" for information about message flagging.
|
||||
|
||||
[[imap-peek]]
|
||||
[IMPORTANT]
|
||||
.Important: IMAP PEEK
|
||||
=====
|
||||
Starting with _version 4.1.1_, the IMAP mail receiver will use the `mail.imap.peek` or `mail.imaps.peek` javamail property, if specified.
|
||||
Previously, the receiver ignored the property and always set the PEEK flag.
|
||||
Now, if you explicitly set this property to `false`, the message will be marked as `\Seen` regardless of the setting of `shouldMarkMessagesRead`.
|
||||
Starting with version 4.1.1, the IMAP mail receiver uses the `mail.imap.peek` or `mail.imaps.peek` JavaMail property, if specified.
|
||||
Previously, the receiver ignored the property and always set the `PEEK` flag.
|
||||
Now, if you explicitly set this property to `false`, the message ise marked as `\Seen` regardless of the setting of `shouldMarkMessagesRead`.
|
||||
If not specified, the previous behavior is retained (peek is `true`).
|
||||
=====
|
||||
|
||||
*IMAP IDLE and lost connection*
|
||||
==== IMAP `idle` and Lost Connections
|
||||
|
||||
When using IMAP IDLE channel adapter there might be situations where connection to the server may be lost (e.g., network failure) and since Java Mail documentation explicitly states that the actual IMAP API is EXPERIMENTAL it is important to understand the differences in the API and how to deal with them when configuring IMAP IDLE adapters.
|
||||
Currently Spring Integration Mail adapters was tested with Java Mail 1.4.1 and Java Mail 1.4.3 and depending on which one is used special attention must be payed to some of the java mail properties that needs to be set with regard to auto-reconnect.
|
||||
When using an IMAP `idle` channel adapter, connections to the server may be lost (for example, through network failure) and, since the JavaMail documentation explicitly states that the actual IMAP API is experimental, it is important to understand the differences in the API and how to deal with them when configuring IMAP `idle` adapters.
|
||||
Currently, Spring Integration mail adapters were tested with JavaMail 1.4.1 and JavaMail 1.4.3,.
|
||||
Depending on which one is used, you must pay special attention to some of the JavaMail properties that need to be set with regard to auto-reconnect.
|
||||
|
||||
NOTE: The following behavior was observed with GMAIL but should provide you with some tips on how to solve re-connect
|
||||
issue with other providers, however feedback is always welcome.
|
||||
Again, below notes are based on GMAIL.
|
||||
NOTE: The following behavior was observed with Gmail but should provide you with some tips on how to solve re-connect
|
||||
issue with other providers. However feedback is always welcome.
|
||||
Again, the following notes are based on Gmail.
|
||||
|
||||
With Java Mail 1.4.1 if `mail.imaps.timeout` property is set for a relatively short period of time (e.g., ~ 5 min) then `IMAPFolder.idle()` will throw `FolderClosedException` after this timeout.
|
||||
However if this property is not set (should be indefinite) the behavior that was observed is that `IMAPFolder.idle()` method never returns nor it throws an exception.
|
||||
It will however reconnect automatically if connection was lost for a short period of time (e.g., under 10 min), but if connection was lost for a long period of time (e.g., over 10 min), then `IMAPFolder.idle()` will not throw `FolderClosedException` nor it will re-establish connection and will remain in the blocked state indefinitely, thus leaving you no possibility to reconnect without restarting the adapter.
|
||||
So the only way to make re-connect to work with Java Mail 1.4.1 is to set `mail.imaps.timeout` property explicitly to some value, but it also means that such value should be relatively short (under 10 min) and the connection should be re-established relatively quickly.
|
||||
Again, it may be different with other providers.
|
||||
With Java Mail 1.4.3 there was significant improvements to the API ensuring that there will always be a condition which will force `IMAPFolder.idle()` method to return via `StoreClosedException` or `FolderClosedException` or simply return, thus allowing us to proceed with auto-reconnect.
|
||||
Currently auto-reconnect will run infinitely making attempts to reconnect every 10 sec.
|
||||
With JavaMail 1.4.1, if you set the `mail.imaps.timeout` property to a relatively short period of time (approximately 5 min in our testing), `IMAPFolder.idle()` throws `FolderClosedException` after this timeout.
|
||||
However, if this property is not set (it should be indefinite) the `IMAPFolder.idle()` method never returns and never throws an exception.
|
||||
It does, however, reconnect automatically if the connection was lost for a short period of time (under 10 min in our testing).
|
||||
However, if the connection was lost for a long period of time (over 10 min), `IMAPFolder.idle()`, does not throw `FolderClosedException` and does not re-establish the connection, and remains in the blocked state indefinitely, thus leaving you no possibility to reconnect without restarting the adapter.
|
||||
Consequently, the only way to make re-connecting work with JavaMail 1.4.1 is to set the `mail.imaps.timeout` property explicitly to some value, but it also means that such value should be relatively short (under 10 min) and the connection should be re-established relatively quickly.
|
||||
Again, it may be different with providers other than Gmail.
|
||||
With JavaMail 1.4.3 introduced significant improvements to the API, ensuring that there is always a condition that forces the `IMAPFolder.idle()` method to return `StoreClosedException` or `FolderClosedException` or to simply return, thus letting you proceed with auto-reconnecting.
|
||||
Currently auto-reconnecting runs infinitely making attempts to reconnect every ten seconds.
|
||||
|
||||
IMPORTANT: In both configurations `channel` and `should-delete-messages` are the _REQUIRED_ attributes.
|
||||
The important thing to understand is why `should-delete-messages` is required.
|
||||
The issue is with the POP3 protocol, which does NOT have any knowledge of messages that were READ.
|
||||
It can only know what's been read within a single session.
|
||||
This means that when your POP3 mail adapter is running, emails are successfully consumed as as they become available during each poll
|
||||
and no single email message will be delivered more then once.
|
||||
However, as soon as you restart your adapter and begin a new session all the email messages that might have been retrieved in the previous session will be retrieved again.
|
||||
IMPORTANT: In both configurations, `channel` and `should-delete-messages` are required attributes.
|
||||
You should understand why `should-delete-messages` is required.
|
||||
The issue is with the POP3 protocol, which does not have any knowledge of messages that were read.
|
||||
It can only know what has been read within a single session.
|
||||
This means that, when your POP3 mail adapter runs, emails are successfully consumed as as they become available during each poll
|
||||
and no single email message is delivered more then once.
|
||||
However, as soon as you restart your adapter and begin a new session, all the email messages that might have been retrieved in the previous session are retrieved again.
|
||||
That is the nature of POP3.
|
||||
Some might argue that `should-delete-messages` should be TRUE by default.
|
||||
In other words, there are two valid and mutually exclusive use cases which make it very hard to pick a single "best" default.
|
||||
You may want to configure your adapter as the only email receiver in which case you want to be able to restart such adapter without fear that messages that were delivered before will not be redelivered again.
|
||||
In this case setting `should-delete-messages` to TRUE would make most sense.
|
||||
However, you may have another use case where you may want to have multiple adapters that simply monitor email servers and their content.
|
||||
In other words you just want to 'peek but not touch'.
|
||||
Then setting `should-delete-messages` to FALSE would be much more appropriate.
|
||||
So since it is hard to choose what should be the right default value for the `should-delete-messages` attribute, we simply made it a required attribute, to be set by the user.
|
||||
Leaving it up to the user also means, you will be less likely to end up with unintended behavior.
|
||||
Some might argue that `should-delete-messages` should be `true` by default.
|
||||
In other words, there are two valid and mutually exclusive use that make it very hard to pick a single best default.
|
||||
You may want to configure your adapter as the only email receiver, in which case you want to be able to restart your adapter without fear that previously delivered messages are not delivered again.
|
||||
In this case, setting `should-delete-messages` to `true` would make the most sense.
|
||||
However, you may have another use case where you may want to have multiple adapters monitor email servers and their content.
|
||||
In other words, you want to 'peek but not touch'.
|
||||
Then setting `should-delete-messages` to `false` is much more appropriate.
|
||||
So since it is hard to choose what should be the right default value for the `should-delete-messages` attribute, we made it a required attribute to be set by you.
|
||||
Leaving it up to you also means that you are less likely to end up with unintended behavior.
|
||||
|
||||
NOTE: When configuring a polling email adapter's _should-mark-messages-as-read_ attribute, be aware of the protocol you are configuring to retrieve messages.
|
||||
For example POP3 does not support this flag which means setting it to either value will have no effect as messages will NOT be marked as read.
|
||||
NOTE: When configuring a polling email adapter's `should-mark-messages-as-read` attribute, you should be aware of the protocol you are configuring to retrieve messages.
|
||||
For example, POP3 does not support this flag, which means setting it to either value has no effect, as messages are not marked as read.
|
||||
|
||||
[IMPORTANT]
|
||||
=====
|
||||
It is important to understand that that these actions (marking messages read, and deleting messages) are performed after the messages are received, but before they are processed.
|
||||
You should understand that that these actions (marking messages read and deleting messages) are performed after the messages are received but before they are processed.
|
||||
This can cause messages to be lost.
|
||||
|
||||
You may wish to consider using transaction synchronization instead - see <<mail-tx-sync>>
|
||||
You may wish to consider using transaction synchronization instead.
|
||||
See "`<<mail-tx-sync>>`".
|
||||
=====
|
||||
|
||||
The `<imap-idle-channel-adapter/>` also accepts the 'error-channel' attribute.
|
||||
If a downstream exception is thrown and an 'error-channel' is specified, a MessagingException message containing the failed message and original exception, will be sent to this channel.
|
||||
Otherwise, if the downstream channels are synchronous, any such exception will simply be logged as a warning by the channel adapter.
|
||||
If a downstream exception is thrown and an 'error-channel' is specified, a `MessagingException` message containing the failed message and the original exception is sent to this channel.
|
||||
Otherwise, if the downstream channels are synchronous, any such exception is logged as a warning by the channel adapter.
|
||||
|
||||
NOTE: Beginning with the 3.0 release, the IMAP idle adapter emits application events (specifically `ImapIdleExceptionEvent` s) when exceptions occur.
|
||||
NOTE: Beginning with the 3.0 release, the IMAP `idle` adapter emits application events (specifically `ImapIdleExceptionEvent` instances) when exceptions occur.
|
||||
This allows applications to detect and act on those exceptions.
|
||||
The events can be obtained using an `<int-event:inbound-channel-adapter>` or any `ApplicationListener` configured to receive an `ImapIdleExceptionEvent` or one of its super classes.
|
||||
You can obtain the events by using an `<int-event:inbound-channel-adapter>` or any `ApplicationListener` configured to receive an `ImapIdleExceptionEvent` or one of its super classes.
|
||||
|
||||
[[imap-seen]]
|
||||
=== Marking IMAP Messages When \Recent is Not Supported
|
||||
=== Marking IMAP Messages When `\Recent` Is Not Supported
|
||||
|
||||
If `shouldMarkMessagesAsRead` is true, the IMAP adapters set the `\Seen` flag.
|
||||
|
||||
In addition, when an email server does not support the `\Recent` flag, the IMAP adapters mark messages with a user
|
||||
flag (`spring-integration-mail-adapter` by default) as long as the server supports user flags.
|
||||
flag (by default, `spring-integration-mail-adapter`), as long as the server supports user flags.
|
||||
If not, `Flag.FLAGGED` is set to `true`.
|
||||
These flags are applied regardless of the `shouldMarkMessagesRead` setting.
|
||||
|
||||
As discussed in <<search-term>>, the default `SearchTermStrategy` will ignore messages so flagged.
|
||||
As discussed in "`<<search-term>>`", the default `SearchTermStrategy` ignore messages that are so flagged.
|
||||
|
||||
Starting with _version 4.2.2_, the name of the user flag can be set using `setUserFlag` on the `MailReceiver` - this
|
||||
allows multiple receivers to use a different flag (as long as the mail server supports user flags).
|
||||
The attribute `user-flag` is available when configuring the adapter with the namespace.
|
||||
Starting with version 4.2.2, you can set the name of the user flag by using `setUserFlag` on the `MailReceiver`.
|
||||
Doing so lets multiple receivers use a different flag (as long as the mail server supports user flags).
|
||||
The `user-flag` attribute is available when configuring the adapter with the namespace.
|
||||
|
||||
[[mail-filtering]]
|
||||
=== Email Message Filtering
|
||||
|
||||
Very often you may encounter a requirement to filter incoming messages (e.g., You want to only read emails that have 'Spring Integration' in the _Subject_ line).
|
||||
This could be easily accomplished by connecting Inbound Mail adapter with an expression-based _Filter_.
|
||||
Very often, you may encounter a requirement to filter incoming messages (for example, you want to read only emails that have 'Spring Integration' in the `Subject` line).
|
||||
You can accomplish this by connecting an inbound mail adapter with an expression-based `Filter`.
|
||||
Although it would work, there is a downside to this approach.
|
||||
Since messages would be filtered after going through inbound mail adapter all such messages would be marked as read (SEEN) or Un-read (depending on the value of `should-mark-messages-as-read` attribute).
|
||||
However in reality what would be more useful is to mark messages as SEEN only if they passed the filtering criteria.
|
||||
This is very similar to looking at your email client while scrolling through all the messages in the preview pane, but only flagging messages as SEEN that were actually opened and read.
|
||||
Since messages would be filtered after going through the inbound mail adapter, all such messages would be marked as read (`SEEN`) or unread (depending on the value of `should-mark-messages-as-read` attribute).
|
||||
However, in reality, it be more useful to mark messages as `SEEN` only if they pass the filtering criteria.
|
||||
This is similar to looking at your email client while scrolling through all the messages in the preview pane, but only flagging messages that were actually opened and read as `SEEN`.
|
||||
|
||||
In Spring Integration 2.0.4 we've introduced `mail-filter-expression` attribute on `inbound-channel-adapter` and `imap-idle-channel-adapter`.
|
||||
This attribute allows you to provide an expression which is a combination of SpEL and Regular Expression.
|
||||
For example if you would like to read only emails that contain 'Spring Integration' in the Subject line, you would configure `mail-filter-expression` attribute like this this: `mail-filter-expression="subject matches '(?i).*Spring Integration.*"`
|
||||
Spring Integration 2.0.4 introduced the `mail-filter-expression` attribute on `inbound-channel-adapter` and `imap-idle-channel-adapter`.
|
||||
This attribute lets you provide an expression that is a combination of SpEL and a regular expression.
|
||||
For example if you would like to read only emails that contain 'Spring Integration' in the subject line, you would configure the `mail-filter-expression` attribute like as follows: `mail-filter-expression="subject matches '(?i).*Spring Integration.*"`
|
||||
|
||||
Since `javax.mail.internet.MimeMessage` is the root context of SpEL Evaluation Context, you can filter on any value available through MimeMessage including the actual body of the message.
|
||||
This one is particularly important since reading the body of the message would typically result in such message to be marked as SEEN by default, but since we now setting PEAK flag of every incomming message to 'true', only messages that were explicitly marked as SEEN will be seen as read.
|
||||
Since `javax.mail.internet.MimeMessage` is the root context of the SpEL evaluation context, you can filter on any value available through `MimeMessage`, including the actual body of the message.
|
||||
This one is particularly important, since reading the body of the message typically results in such messages being marked as `SEEN` by default.
|
||||
However, since we now set the `PEEK` flag of every incoming message to 'true', only messages that were explicitly marked as `SEEN` are marked as read.
|
||||
|
||||
So in the below example only messages that match the filter expression will be output by this adapter and only those messages will be marked as SEEN.
|
||||
In this case based on the `mail-filter-expression` only messages that contain 'Spring Integration' in the subject line will be produced by this adapter.
|
||||
So, in the following example, only messages that match the filter expression are output by this adapter and only those messages are marked as read:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-mail:imap-idle-channel-adapter id="customAdapter"
|
||||
@@ -400,31 +445,36 @@ In this case based on the `mail-filter-expression` only messages that contain 'S
|
||||
java-mail-properties="javaMailProperties"
|
||||
mail-filter-expression="subject matches '(?i).*Spring Integration.*'"/>
|
||||
----
|
||||
====
|
||||
|
||||
Another reasonable question is what happens on the next poll, or idle event, or what happens when such adapter is restarted.
|
||||
Will there be a potential duplication of massages to be filtered? In other words if on the last retrieval where you had 5 new messages and only 1 passed the filter what would happen with the other 4.
|
||||
Would they go through the filtering logic again on the next poll or idle? After all they were not marked as SEEN.
|
||||
The actual answer is no.
|
||||
They would not be subject of duplicate processing due to another flag (RECENT) that is set by the Email server and is used by Spring Integration mail search filter.
|
||||
Folder implementations set this flag to indicate that this message is new to this folder, that is, it has arrived since the last time this folder was opened.
|
||||
In other while our adapter may peek at the email it also lets the email server know that such email was touched and therefore will be marked as RECENT by the email server.
|
||||
In the preceding example, thanks to the `mail-filter-expression` attribute, only messages that contain 'Spring Integration' in the subject line are produced by this adapter.
|
||||
|
||||
Another reasonable question is what happens on the next poll or idle event or what happens when such an adapter is restarted.
|
||||
Can there be duplication of massages to be filtered? In other words, if, on the last retrieval where you had five new messages and only one passed the filter, what would happen with the other four?
|
||||
Would they go through the filtering logic again on the next poll or idle? After all, they were not marked as `SEEN`.
|
||||
The answer is no.
|
||||
They would not be subject to duplicate processing due to another flag (`RECENT`) that is set by the email server and is used by the Spring Integration mail search filter.
|
||||
Folder implementations set this flag to indicate that this message is new to this folder.
|
||||
That is, it has arrived since the last time this folder was opened.
|
||||
In other words, while our adapter may peek at the email, it also lets the email server know that such email was touched and should therefore be marked as `RECENT` by the email server.
|
||||
|
||||
[[mail-tx-sync]]
|
||||
=== Transaction Synchronization
|
||||
|
||||
Transaction synchronization for inbound adapters allows you to take different actions after a transaction commits, or rolls back.
|
||||
Transaction synchronization is enabled by adding a `<transactional/>` element to the poller for the polled `<inbound-adapter/>`, or to the `<imap-idle-inbound-adapter/>`.
|
||||
Transaction synchronization for inbound adapters lets you take different actions after a transaction commits or rolls back.
|
||||
You can enable transaction synchronization by adding a `<transactional/>` element to the poller for the polled `<inbound-adapter/>` or to the `<imap-idle-inbound-adapter/>`.
|
||||
Even if there is no 'real' transaction involved, you can still enable this feature by using a `PseudoTransactionManager` with the `<transactional/>` element.
|
||||
For more information, see <<transaction-synchronization>>.
|
||||
For more information, see "`<<transaction-synchronization>>`".
|
||||
|
||||
Because of the many different mail servers, and specifically the limitations that some have, at this time we only provide a strategy for these transaction synchronizations.
|
||||
You can send the messages to some other Spring Integration components, or invoke a custom bean to perform some action.
|
||||
Because of the many different mail servers and specifically the limitations that some have, at this time we provide only a strategy for these transaction synchronizations.
|
||||
You can send the messages to some other Spring Integration components or invoke a custom bean to perform some action.
|
||||
For example, to move an IMAP message to a different folder after the transaction commits, you might use something similar to the following:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-mail:imap-idle-channel-adapter id="customAdapter"
|
||||
store-uri="imaps://foo.com:password@imap.foo.com/INBOX"
|
||||
store-uri="imaps://something.com:password@imap.something.com/INBOX"
|
||||
channel="receiveChannel"
|
||||
auto-startup="true"
|
||||
should-delete-messages="false"
|
||||
@@ -436,9 +486,13 @@ For example, to move an IMAP message to a different folder after the transaction
|
||||
<int:after-commit expression="@syncProcessor.process(payload)"/>
|
||||
</int:transaction-synchronization-factory>
|
||||
|
||||
<bean id="syncProcessor" class="foo.bar.Mover"/>
|
||||
<bean id="syncProcessor" class="thing1.thing2.Mover"/>
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows what the `Mover` class might look like:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class Mover {
|
||||
@@ -461,13 +515,14 @@ public class Mover {
|
||||
}
|
||||
}
|
||||
|
||||
Folder fooFolder = store.getFolder("FOO"));
|
||||
fooFolder.appendMessages(new MimeMessage[]{message});
|
||||
Folder somethingFolder = store.getFolder("SOMETHING"));
|
||||
somethingFolder.appendMessages(new MimeMessage[]{message});
|
||||
folder.expunge();
|
||||
folder.close(true);
|
||||
fooFolder.close(false);
|
||||
somethingFolder.close(false);
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
IMPORTANT: For the message to be still available for manipulation after the transaction, _should-delete-messages_ must be set to 'false'.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
[[messaging-construction-chapter]]
|
||||
== Message Construction
|
||||
|
||||
include::./message.adoc[]
|
||||
@@ -1,28 +1,33 @@
|
||||
[[message-history]]
|
||||
=== Message History
|
||||
|
||||
The key benefit of a messaging architecture is loose coupling where participating components do not maintain any awareness about one another.
|
||||
This fact alone makes your application extremely flexible, allowing you to change components without affecting the rest of the flow, change messaging routes, message consuming styles (polling vs event driven), and so on.
|
||||
The key benefit of a messaging architecture is loose coupling such that participating components do not maintain any awareness about one another.
|
||||
This fact alone makes an application extremely flexible, letting you change components without affecting the rest of the flow, change messaging routes, change message consuming styles (polling versus event driven), and so on.
|
||||
However, this unassuming style of architecture could prove to be difficult when things go wrong.
|
||||
When debugging, you would probably like to get as much information about the message as you can (its origin, channels it has traversed, etc.)
|
||||
When debugging, you probably want as much information (its origin, the channels it has traversed, and other details) about the message as you can get.
|
||||
|
||||
Message History is one of those patterns that helps by giving you an option to maintain some level of awareness of a message path either for debugging purposes or to maintain an audit trail.
|
||||
Spring integration provides a simple way to configure your message flows to maintain the Message History by adding a header to the Message and updating that header every time a message passes through a tracked component.
|
||||
Message history is one of those patterns that helps by giving you an option to maintain some level of awareness of a message path either for debugging purposes or for maintaining an audit trail.
|
||||
Spring integration provides a simple way to configure your message flows to maintain the message history by adding a header to the message and updating that header every time a message passes through a tracked component.
|
||||
|
||||
[[message-history-config]]
|
||||
==== Message History Configuration
|
||||
|
||||
To enable Message History all you need is to define the `message-history` element in your configuration.
|
||||
To enable message history, you need only define the `message-history` element in your configuration, as shown in the following example:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:message-history/>
|
||||
----
|
||||
====
|
||||
|
||||
Now every named component (component that has an 'id' defined) will be tracked.
|
||||
The framework will set the 'history' header in your Message.
|
||||
Its value is very simple - `List<Properties>`.
|
||||
Now every named component (component that has an 'id' defined) is tracked.
|
||||
The framework sets the 'history' header in your message.
|
||||
Its value a `List<Properties>`.
|
||||
|
||||
Consider the following configuration example:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:gateway id="sampleGateway"
|
||||
@@ -35,16 +40,19 @@ Its value is very simple - `List<Properties>`.
|
||||
</int:header-enricher>
|
||||
</int:chain>
|
||||
----
|
||||
====
|
||||
|
||||
The above configuration will produce a very simple Message History structure:
|
||||
The preceding configuration produces a simple message history structure, with output similar to the following:
|
||||
[source,java]
|
||||
----
|
||||
[{name=sampleGateway, type=gateway, timestamp=1283281668091},
|
||||
{name=sampleChain, type=chain, timestamp=1283281668094}]
|
||||
----
|
||||
|
||||
To get access to Message History all you need is access the MessageHistory header.
|
||||
For example:
|
||||
To get access to message history, you need only access the `MessageHistory` header.
|
||||
The folloiwng example shows how to do so:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Iterator<Properties> historyIterator =
|
||||
@@ -56,26 +64,31 @@ assertTrue(historyIterator.hasNext());
|
||||
Properties chainHistory = historyIterator.next();
|
||||
assertEquals("sampleChain", chainHistory.get("name"));
|
||||
----
|
||||
====
|
||||
|
||||
You might not want to track all of the components.
|
||||
To limit the history to certain components based on their names, all you need is provide the `tracked-components` attribute and specify a comma-delimited list of component names and/or patterns that match the components you want to track.
|
||||
To limit the history to certain components based on their names, you can provide the `tracked-components` attribute and specify a comma-delimited list of component names and patterns that match the components you want to track.
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:message-history tracked-components="*Gateway, sample*, foo"/>
|
||||
<int:message-history tracked-components="*Gateway, sample*, aName"/>
|
||||
----
|
||||
====
|
||||
|
||||
In the above example, Message History will only be maintained for all of the components that end with 'Gateway', start with 'sample', or match the name 'foo' exactly.
|
||||
In the preceding example, message history is maintained only for the components that end with 'Gateway', start with 'sample', or match the name, 'aName', exactly.
|
||||
|
||||
Starting with _version 4.0_, you can also use the `@EnableMessageHistory` annotation in a `@Configuration` class.
|
||||
In addition, the `MessageHistoryConfigurer` bean is now exposed as a JMX MBean by the `IntegrationMBeanExporter` (see <<jmx-mbean-exporter>>), allowing the patterns to be changed at runtime.
|
||||
Starting with version 4.0, you can also use the `@EnableMessageHistory` annotation in a `@Configuration` class.
|
||||
In addition, the `MessageHistoryConfigurer` bean is now exposed as a JMX MBean by the `IntegrationMBeanExporter` (see "`<<jmx-mbean-exporter>>`"), letting you change the patterns at runtime.
|
||||
Note, however, that the bean must be stopped (turning off message history) in order to change the patterns.
|
||||
This feature might be useful to temporarily turn on history to analyze a system.
|
||||
The MBean's object name is `"<domain>:name=messageHistoryConfigurer,type=MessageHistoryConfigurer"`.
|
||||
The MBean's object name is `<domain>:name=messageHistoryConfigurer,type=MessageHistoryConfigurer`.
|
||||
|
||||
IMPORTANT: If multiple beans (declared by `@EnableMessageHistory` and/or `<message-history/>`) they all must have identical component name patterns (when trimmed and sorted).
|
||||
*Do not use a generic `<bean/>` definition for the `MessageHistoryConfigurer`*.
|
||||
IMPORTANT: If multiple beans (declared by `@EnableMessageHistory` and `<message-history/>`) exist, they must all have identical component name patterns (when trimmed and sorted).
|
||||
Do not use a generic `<bean/>` definition for the `MessageHistoryConfigurer`.
|
||||
|
||||
NOTE: Remember that by definition the Message History header is immutable (you can't re-write history, although some try).
|
||||
Therefore, when writing Message History values, the components are either creating brand new Messages (when the component is an origin), or they are copying the history from a request Message, modifying it and setting the new list on a reply Message.
|
||||
In either case, the values can be appended even if the Message itself is crossing thread boundaries.
|
||||
NOTE: By definition, the message history header is immutable (you cannot re-write history).
|
||||
Therefore, when writing message history values, the components either create new messages (when the component is an origin) or they copy the history from a request message, modifying it and setting the new list on a reply message.
|
||||
In either case, the values can be appended even if the message itself is crossing thread boundaries.
|
||||
That means that the history values can greatly simplify debugging in an asynchronous message flow.
|
||||
|
||||
@@ -1,37 +1,36 @@
|
||||
[[message-publishing]]
|
||||
== Message Publishing
|
||||
|
||||
The AOP Message Publishing feature allows you to construct and send a message as a by-product of a method invocation.
|
||||
For example, imagine you have a component and every time the state of this component changes you would like to be notified via a Message.
|
||||
The easiest way to send such notifications would be to send a message to a dedicated channel, but how would you connect the method invocation that changes the state of the object to a message sending process, and how should the notification Message be structured?
|
||||
The AOP Message Publishing feature handles these responsibilities with a configuration-driven approach.
|
||||
The (Aspect-oriented Programming) AOP message publishing feature lets you construct and send a message as a by-product of a method invocation.
|
||||
For example, imagine you have a component and, every time the state of this component changes, you want to be notified by a message.
|
||||
The easiest way to send such notifications is to send a message to a dedicated channel, but how would you connect the method invocation that changes the state of the object to a message sending process, and how should the notification message be structured?
|
||||
The AOP message publishing feature handles these responsibilities with a configuration-driven approach.
|
||||
|
||||
[[message-publishing-config]]
|
||||
=== Message Publishing Configuration
|
||||
|
||||
Spring Integration provides two approaches: XML and Annotation-driven.
|
||||
Spring Integration provides two approaches: XML configuration and annotation-driven (Java) configuration.
|
||||
|
||||
[[publisher-annotation]]
|
||||
==== Annotation-driven approach via @Publisher annotation
|
||||
==== Annotation-driven Configuration with the `@Publisher` Annotation
|
||||
|
||||
The annotation-driven approach allows you to annotate any method with the `@Publisher` annotation, specifying a 'channel' attribute.
|
||||
Starting with _version 5.1_, to switch this functionality on, the `@EnablePublisher` annotation must be provided on some `@Configuration` class.
|
||||
See <<configuration-enable-integration>> for more information.
|
||||
The Message will be constructed from the return value of the method invocation and sent to a channel specified by the 'channel' attribute.
|
||||
The annotation-driven approach lets you annotate any method with the `@Publisher` annotation to specify a 'channel' attribute.
|
||||
Starting with version 5.1, to switch this functionality on, you must use the `@EnablePublisher` annotation on some `@Configuration` class.
|
||||
See "`<<configuration-enable-integration>>`" for more information.
|
||||
The message is constructed from the return value of the method invocation and sent to the channel specified by the 'channel' attribute.
|
||||
To further manage message structure, you can also use a combination of both `@Payload` and `@Header` annotations.
|
||||
|
||||
Internally this message publishing feature of Spring Integration uses both Spring AOP by defining `PublisherAnnotationAdvisor` and Spring 3.0's Expression Language (SpEL) support, giving you considerable flexibility and control over the structure of the _Message_ it will publish.
|
||||
Internally, this message publishing feature of Spring Integration uses both Spring AOP by defining `PublisherAnnotationAdvisor` and the Spring Expression Language (SpEL), giving you considerable flexibility and control over the structure of the `Message` it publishes.
|
||||
|
||||
The `PublisherAnnotationAdvisor` defines and binds the following variables:
|
||||
|
||||
* _#return_ - will bind to a return value allowing you to reference it or its attributes (e.g., _#return.foo_ where 'foo' is an attribute of the object bound to _#return_)
|
||||
* `#return`: Binds to a return value, letting you reference it or its attributes (for example, `#return.something`, where 'something' is an attribute of the object bound to `#return`)
|
||||
* `#exception`: Binds to an exception if one is thrown by the method invocation
|
||||
* `#args`: Binds to method arguments so that you can extract individual arguments by name (for example, `#args.fname`)
|
||||
|
||||
* _#exception_ - will bind to an exception if one is thrown by the method invocation.
|
||||
|
||||
* _#args_ - will bind to method arguments, so individual arguments could be extracted by name (e.g., _#args.fname_ as in the above method)
|
||||
|
||||
Let's look at a couple of examples:
|
||||
Consider the following example:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Publisher
|
||||
@@ -39,14 +38,17 @@ public String defaultPayload(String fname, String lname) {
|
||||
return fname + " " + lname;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
In the above example the Message will be constructed with the following structure:
|
||||
In the preceding example, the message is constructed with the following structure:
|
||||
|
||||
* Message payload - will be the return type and value of the method.
|
||||
* The message payload is the return type and value of the method.
|
||||
This is the default.
|
||||
* A newly constructed message is sent to a default publisher channel that is configured with an annotation post processor (covered later in this section).
|
||||
|
||||
* A newly constructed message will be sent to a default publisher channel configured with an annotation post processor (see the end of this section).
|
||||
The following example is the same as the preceding example, except that it does not use a default publishing channel:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Publisher(channel="testChannel")
|
||||
@@ -54,12 +56,15 @@ public String defaultPayload(String fname, @Header("last") String lname) {
|
||||
return fname + " " + lname;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
In this example everything is the same as above, except that we are not using a default publishing channel.
|
||||
Instead we are specifying the publishing channel via the 'channel' attribute of the `@Publisher` annotation.
|
||||
We are also adding a `@Header` annotation which results in the Message header named 'last' having the same value as the 'lname' method parameter.
|
||||
That header will be added to the newly constructed Message.
|
||||
Instead of using a default publishing channel, we specify the publishing channel by setting the 'channel' attribute of the `@Publisher` annotation.
|
||||
We also add a `@Header` annotation, which results in the message header named 'last' having the same value as the 'lname' method parameter.
|
||||
That header is added to the newly constructed message.
|
||||
|
||||
The following example is almost identical to the preceding example:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Publisher(channel="testChannel")
|
||||
@@ -68,10 +73,13 @@ public String defaultPayloadButExplicitAnnotation(String fname, @Header String l
|
||||
return fname + " " + lname;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The above example is almost identical to the previous one.
|
||||
The only difference here is that we are using a `@Payload` annotation on the method, thus explicitly specifying that the return value of the method should be used as the payload of the Message.
|
||||
The only difference is that we use a `@Payload` annotation on the method to explicitly specify that the return value of the method should be used as the payload of the message.
|
||||
|
||||
The following example expands on the previous configuration by using the Spring Expression Language in the `@Payload` annotation to further instruct the framework about how the message should be constructed:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Publisher(channel="testChannel")
|
||||
@@ -80,12 +88,13 @@ public String setName(String fname, String lname, @Header("x") int num) {
|
||||
return fname + " " + lname;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Here we are expanding on the previous configuration by using the Spring Expression Language in the `@Payload` annotation to further instruct the framework how the message should be constructed.
|
||||
In this particular case the message will be a concatenation of the return value of the method invocation and the 'lname' input argument.
|
||||
The Message header named 'x' will have its value determined by the 'num' input argument.
|
||||
That header will be added to the newly constructed Message.
|
||||
In the preceding example, the message is a concatenation of the return value of the method invocation and the 'lname' input argument.
|
||||
The Message header named 'x' has its value determined by the 'num' input argument.
|
||||
That header is added to the newly constructed message.
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Publisher(channel="testChannel")
|
||||
@@ -93,27 +102,35 @@ public String argumentAsPayload(@Payload String fname, @Header String lname) {
|
||||
return fname + " " + lname;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
In the above example you see another usage of the `@Payload` annotation.
|
||||
Here we are annotating a method argument which will become the payload of the newly constructed message.
|
||||
In the preceding example, you see another usage of the `@Payload` annotation.
|
||||
Here, we annotate a method argument that becomes the payload of the newly constructed message.
|
||||
|
||||
As with most other annotation-driven features in Spring, you will need to register a post-processor (`PublisherAnnotationBeanPostProcessor`).
|
||||
As with most other annotation-driven features in Spring, you need to register a post-processor (`PublisherAnnotationBeanPostProcessor`).
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<bean class="org.springframework.integration.aop.PublisherAnnotationBeanPostProcessor"/>
|
||||
----
|
||||
====
|
||||
|
||||
You can instead use namespace support for a more concise configuration:
|
||||
For a more concise configuration, you can instead use namespace support, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:annotation-config>
|
||||
<int:enable-publisher default-publisher-channel="defaultChannel"/>
|
||||
</int:annotation-config>
|
||||
----
|
||||
====
|
||||
|
||||
For Java & Annotation Spring configuration, the `@EnablePublisher` annotation must be used:
|
||||
For Java configuration, you must use the `@EnablePublisher` annotation, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
@@ -123,37 +140,46 @@ public class IntegrationConfiguration {
|
||||
...
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Similar to other Spring annotations (`@Component`, `@Scheduled`, etc.), `@Publisher` can also be used as a meta-annotation.
|
||||
That means you can define your own annotations that will be treated in the same way as the `@Publisher` itself.
|
||||
Similar to other Spring annotations (`@Component`, `@Scheduled`, and so on), you can also use `@Publisher` as a meta-annotation.
|
||||
This means that you can define your own annotations that are treated in the same way as the `@Publisher` itself.
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Publisher(channel="auditChannel")
|
||||
public @interface Audit {
|
||||
...
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Here we defined the `@Audit` annotation which itself is annotated with `@Publisher`.
|
||||
Also note that you can define a `channel` attribute on the meta-annotation thus encapsulating the behavior of where messages will be sent inside of this annotation.
|
||||
Now you can annotate any method:
|
||||
In the preceding example, we define the `@Audit` annotation, which itself is annotated with `@Publisher`.
|
||||
Also note that you can define a `channel` attribute on the meta-annotation to encapsulate where messages are sent inside of this annotation.
|
||||
Now you can annotate any method with the `@Audit` annotation, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Audit
|
||||
public String test() {
|
||||
return "foo";
|
||||
return "Hello";
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
In the above example every invocation of the `test()` method will result in a Message with a payload created from its return value.
|
||||
Each Message will be sent to the channel named _auditChannel_.
|
||||
In the preceding example, every invocation of the `test()` method results in a message with a payload created from its return value.
|
||||
Each message is sent to the channel named `auditChannel`.
|
||||
One of the benefits of this technique is that you can avoid the duplication of the same channel name across multiple annotations.
|
||||
You also can provide a level of indirection between your own, potentially domain-specific annotations and those provided by the framework.
|
||||
You also can provide a level of indirection between your own, potentially domain-specific, annotations and those provided by the framework.
|
||||
|
||||
You can also annotate the class which would mean that the properties of this annotation will be applied on every public method of that class.
|
||||
You can also annotate the class, which lets you apply the properties of this annotation on every public method of that class, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Audit
|
||||
@@ -170,19 +196,21 @@ static class BankingOperationsImpl implements BankingOperations {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[[aop-based-interceptor]]
|
||||
==== XML-based approach via the <publishing-interceptor> element
|
||||
==== XML-based Approach with the `<publishing-interceptor>` element
|
||||
|
||||
The XML-based approach allows you to configure the same AOP-based Message Publishing functionality with simple namespace-based configuration of a `MessagePublishingInterceptor`.
|
||||
It certainly has some benefits over the annotation-driven approach since it allows you to use AOP pointcut expressions, thus possibly intercepting multiple methods at once or intercepting and publishing methods to which you don't have the source code.
|
||||
The XML-based approach lets you configure the same AOP-based message publishing functionality as a namespace-based configuration of a `MessagePublishingInterceptor`.
|
||||
It certainly has some benefits over the annotation-driven approach, since it lets you use AOP pointcut expressions, thus possibly intercepting multiple methods at once or intercepting and publishing methods to which you do not have the source code.
|
||||
|
||||
To configure Message Publishing via XML, you only need to do the following two things:
|
||||
|
||||
* Provide configuration for `MessagePublishingInterceptor` via the `<publishing-interceptor>` XML element.
|
||||
To configure message publishing with XML, you need only do the following two things:
|
||||
|
||||
* Provide configuration for `MessagePublishingInterceptor` by using the `<publishing-interceptor>` XML element.
|
||||
* Provide AOP configuration to apply the `MessagePublishingInterceptor` to managed objects.
|
||||
|
||||
The following example shows how to configure a `publishing-interceptor` element:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<aop:config>
|
||||
@@ -190,74 +218,77 @@ To configure Message Publishing via XML, you only need to do the following two t
|
||||
</aop:config>
|
||||
<publishing-interceptor id="interceptor" default-channel="defaultChannel">
|
||||
<method pattern="echo" payload="'Echoing: ' + #return" channel="echoChannel">
|
||||
<header name="foo" value="bar"/>
|
||||
<header name="things" value="something"/>
|
||||
</method>
|
||||
<method pattern="repl*" payload="'Echoing: ' + #return" channel="echoChannel">
|
||||
<header name="foo" expression="'bar'.toUpperCase()"/>
|
||||
<header name="things" expression="'something'.toUpperCase()"/>
|
||||
</method>
|
||||
<method pattern="echoDef*" payload="#return"/>
|
||||
</publishing-interceptor>
|
||||
----
|
||||
|
||||
As you can see the `<publishing-interceptor>` configuration looks rather similar to the Annotation-based approach, and it also utilizes the power of the Spring 3.0 Expression Language.
|
||||
The `<publishing-interceptor>` configuration looks rather similar to the annotation-based approach, and it also uses the power of the Spring Expression Language.
|
||||
|
||||
In the above example the execution of the `echo` method of a `testBean` will render a _Message_ with the following structure:
|
||||
In the preceding example, the execution of the `echo` method of a `testBean` renders a `Message` with the following structure:
|
||||
|
||||
* The Message payload will be of type String with the content "Echoing: [value]" where `value` is the value returned by an executed method.
|
||||
|
||||
* The Message will have a header with the name "foo" and value "bar".
|
||||
|
||||
* The Message will be sent to `echoChannel`.
|
||||
* The `Message` payload is of type `String` with the following content: `Echoing: [value]`, where `value` is the value returned by an executed method.
|
||||
* The `Message` has a header with a name of `things` and a value of `something`.
|
||||
* The `Message` is sent to `echoChannel`.
|
||||
|
||||
The second method is very similar to the first.
|
||||
Here every method that begins with 'repl' will render a Message with the following structure:
|
||||
Here, every method that begins with 'repl' renders a `Message` with the following structure:
|
||||
|
||||
* The Message payload will be the same as in the above sample
|
||||
* The `Message` payload is the same as in the preceding sample.
|
||||
* The `Message` has a header named `things` whose value is the result of the SpEL expression `'something'.toUpperCase()`.
|
||||
* The `Message` is sent to `echoChannel`.
|
||||
|
||||
* The Message will have a header named "foo" whose value is the result of the SpEL expression `'bar'.toUpperCase()` .
|
||||
The second method, mapping the execution of any method that begins with `echoDef`, produces a `Message` with the following structure:
|
||||
|
||||
* The Message will be sent to `echoChannel`.
|
||||
* The `Message` payload is the value returned by an executed method.
|
||||
* Since the `channel` attribute is not provided, the `Message` is sent to the `defaultChannel` defined by the `publisher`.
|
||||
|
||||
The second method, mapping the execution of any method that begins with `echoDef` of `testBean`, will produce a Message with the following structure.
|
||||
For simple mapping rules you can rely on the `publisher` defaults, as the following example shows:
|
||||
|
||||
* The Message payload will be the value returned by an executed method.
|
||||
|
||||
* Since the `channel` attribute is not provided explicitly, the Message will be sent to the `defaultChannel` defined by the _publisher_.
|
||||
|
||||
For simple mapping rules you can rely on the _publisher_ defaults.
|
||||
For example:
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
|
||||
<publishing-interceptor id="anotherInterceptor"/>
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
This will map the return value of every method that matches the pointcut expression to a payload and will be sent to a _default-channel_.
|
||||
If the _defaultChannel_is not specified (as above) the messages will be sent to the global _nullChannel_.
|
||||
The preceding example maps the return value of every method that matches the pointcut expression to a payload and is sent to a `default-channel`.
|
||||
If you do not specify the `defaultChannel` (as the preceding example does not do), the messages are sent to the global `nullChannel` (the equivalent of `/dev/null`).
|
||||
|
||||
_Async Publishing_
|
||||
===== Asynchronous Publishing
|
||||
|
||||
One important thing to understand is that publishing occurs in the same thread as your component's execution.
|
||||
So by default in is synchronous.
|
||||
This means that the entire message flow would have to wait until the publisher's flow completes. However, quite often you want the complete opposite and that is to use this Message publishing feature to initiate asynchronous sub-flows.
|
||||
For example, you might host a service (HTTP, WS etc.) which receives a remote request.You may want to send this request internally into a process that might take a while.
|
||||
Publishing occurs in the same thread as your component's execution.
|
||||
So, by default, it is synchronous.
|
||||
This means that the entire message flow has to wait until the publisher's flow completes.
|
||||
However, developers often want the complete opposite: to use this message-publishing feature to initiate asynchronous flows.
|
||||
For example, you might host a service (HTTP, WS, and so on) which receives a remote request.
|
||||
You may want to send this request internally into a process that might take a while.
|
||||
However you may also want to reply to the user right away.
|
||||
So, instead of sending inbound requests for processing via the output channel (the conventional way), you can simply use 'output-channel' or a 'replyChannel' header to send a simple acknowledgment-like reply back to the caller while using the Message publisher feature to initiate a complex flow.
|
||||
So, instead of sending inbound requests for processing to the output channel (the conventional way), you can use 'output-channel' or a 'replyChannel' header to send a simple acknowledgment-like reply back to the caller while using the message-publisher feature to initiate a complex flow.
|
||||
|
||||
EXAMPLE: Here is the simple service that receives a complex payload, which needs to be sent further for processing, but it also needs to reply to the caller with a simple acknowledgment.
|
||||
The service in the following example receives a complex payload (which needs to be sent further for processing), but it also needs to reply to the caller with a simple acknowledgment:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public String echo(Object complexPayload) {
|
||||
return "ACK";
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
So instead of hooking up the complex flow to the output channel we use the Message publishing feature instead.
|
||||
We configure it to create a new Message using the input argument of the service method (above) and send that to the 'localProcessChannel'.
|
||||
And to make sure this sub-flow is asynchronous all we need to do is send it to any type of asynchronous channel (ExecutorChannel in this example).
|
||||
So, instead of hooking up the complex flow to the output channel, we use the message-publishing feature instead.
|
||||
We configure it to create a new message, by using the input argument of the service method (shown in the preceding example), and send that to the 'localProcessChannel'.
|
||||
To make sure this flow is asynchronous, all we need to do is send it to any type of asynchronous channel (`ExecutorChannel` in the next example).
|
||||
The following example shows how to an asynchronous `publishing-interceptor`:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:service-activator input-channel="inputChannel" output-channel="outputChannel" ref="sampleservice"/>
|
||||
@@ -280,21 +311,23 @@ And to make sure this sub-flow is asynchronous all we need to do is send it to a
|
||||
|
||||
<task:executor id="executor" pool-size="5"/>
|
||||
----
|
||||
====
|
||||
|
||||
Another way of handling this type of scenario is with a wire-tap.
|
||||
Another way of handling this type of scenario is with a wire-tap. See "`<<channel-wiretap>>`".
|
||||
|
||||
[[scheduled-producer]]
|
||||
==== Producing and publishing messages based on a scheduled trigger
|
||||
==== Producing and Publishing Messages Based on a Scheduled Trigger
|
||||
|
||||
In the above sections we looked at the Message publishing feature of Spring Integration which constructs and publishes messages as by-products of Method invocations.
|
||||
However in those cases, you are still responsible for invoking the method.
|
||||
In Spring Integration 2.0 we've added another related useful feature: support for scheduled Message producers/publishers via the new "expression" attribute on the 'inbound-channel-adapter' element.
|
||||
Scheduling could be based on several triggers, any one of which may be configured on the 'poller' sub-element.
|
||||
Currently we support `cron`, `fixed-rate`, `fixed-delay` as well as any custom trigger implemented by you and referenced by the 'trigger' attribute value.
|
||||
In the preceding sections, we looked at the message-publishing feature, which constructs and publishes messages as by-products of method invocations.
|
||||
However, in those cases, you are still responsible for invoking the method.
|
||||
Spring Integration 2.0 added support for scheduled message producers and publishers with the new `expression` attribute on the 'inbound-channel-adapter' element.
|
||||
You can schedul based on several triggers, any one of which can be configured on the 'poller' element.
|
||||
Currently, we support `cron`, `fixed-rate`, `fixed-delay` and any custom trigger implemented by you and referenced by the 'trigger' attribute value.
|
||||
|
||||
As mentioned above, support for scheduled producers/publishers is provided via the _<inbound-channel-adapter>_ xml element.
|
||||
Let's look at couple of examples:
|
||||
As mentioned earlier, support for scheduled producers and publishers is provided via the `<inbound-channel-adapter>` XML element.
|
||||
Consider the following example:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:inbound-channel-adapter id="fixedDelayProducer"
|
||||
@@ -303,10 +336,14 @@ Let's look at couple of examples:
|
||||
<int:poller fixed-delay="1000"/>
|
||||
</int:inbound-channel-adapter>
|
||||
----
|
||||
====
|
||||
|
||||
In the above example an inbound Channel Adapter will be created which will construct a Message with its payload being the result of the expression defined in the `expression` attribute.
|
||||
Such messages will be created and sent every time the delay specified by the `fixed-delay` attribute occurs.
|
||||
The preceding example creates an inbound channel adapter that constructs a `Message`, with its payload being the result of the expression defined in the `expression` attribute.
|
||||
Such messages are created and sent every time the delay specified by the `fixed-delay` attribute occurs.
|
||||
|
||||
The following example is similar to the preceding example, except that it uses the `fixed-rate` attribute:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:inbound-channel-adapter id="fixedRateProducer"
|
||||
@@ -315,9 +352,13 @@ Such messages will be created and sent every time the delay specified by the `fi
|
||||
<int:poller fixed-rate="1000"/>
|
||||
</int:inbound-channel-adapter>
|
||||
----
|
||||
====
|
||||
|
||||
This example is very similar to the previous one, except that we are using the `fixed-rate` attribute which will allow us to send messages at a fixed rate (measuring from the start time of each task).
|
||||
The `fixed-rate` attribute lets you send messages at a fixed rate (measuring from the start time of each task).
|
||||
|
||||
The following example shows how you can apply a Cron trigger with a value specified in the `cron` attribute:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:inbound-channel-adapter id="cronProducer"
|
||||
@@ -326,8 +367,9 @@ This example is very similar to the previous one, except that we are using the `
|
||||
<int:poller cron="7 6 5 4 3 ?"/>
|
||||
</int:inbound-channel-adapter>
|
||||
----
|
||||
====
|
||||
|
||||
This example demonstrates how you can apply a Cron trigger with a value specified in the `cron` attribute.
|
||||
The following example shows how to insert additional headers into the message:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
@@ -341,10 +383,12 @@ This example demonstrates how you can apply a Cron trigger with a value specifie
|
||||
</int:inbound-channel-adapter>
|
||||
----
|
||||
|
||||
Here you can see that in a way very similar to the Message publishing feature we are enriching a newly constructed Message with extra Message headers which can take scalar values or the results of evaluating Spring expressions.
|
||||
The additional message headers can take scalar values or the results of evaluating Spring expressions.
|
||||
|
||||
If you need to implement your own custom trigger you can use the `trigger` attribute to provide a reference to any spring configured bean which implements the `org.springframework.scheduling.Trigger` interface.
|
||||
If you need to implement your own custom trigger, you can use the `trigger` attribute to provide a reference to any spring configured bean that implements the `org.springframework.scheduling.Trigger` interface.
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:inbound-channel-adapter id="triggerRefProducer"
|
||||
@@ -356,3 +400,4 @@ If you need to implement your own custom trigger you can use the `trigger` attri
|
||||
<beans:constructor-arg value="9999"/>
|
||||
</beans:bean>
|
||||
----
|
||||
====
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
[[messaging-routing-chapter]]
|
||||
== Message Routing
|
||||
|
||||
This chapter covers the details of using Spring Integration to route messages.
|
||||
|
||||
// BE SURE TO PRECEDE ALL include:: with a blank line - see https://github.com/asciidoctor/asciidoctor/issues/1297
|
||||
include::./router.adoc[]
|
||||
|
||||
|
||||
@@ -1,68 +1,80 @@
|
||||
[[message-store]]
|
||||
=== Message Store
|
||||
|
||||
Enterprise Integration Patterns (EIP) identifies several patterns that have the capability to buffer messages.
|
||||
For example, an _Aggregator_ buffers messages until they can be released and a _QueueChannel_ buffers messages until consumers explicitly receive those messages from that channel.
|
||||
The http://www.eaipatterns.com[_Enterprise Integration Patterns_] (EIP) book identifies several patterns that have the ability to buffer messages.
|
||||
For example, an aggregator buffers messages until they can be released, and a `QueueChannel` buffers messages until consumers explicitly receive those messages from that channel.
|
||||
Because of the failures that can occur at any point within your message flow, EIP components that buffer messages also introduce a point where messages could be lost.
|
||||
|
||||
To mitigate the risk of losing Messages, EIP defines the http://eaipatterns.com/MessageStore.html[Message Store] pattern which allows EIP components to store _Messages_ typically in some type of persistent store (e.g.
|
||||
RDBMS).
|
||||
To mitigate the risk of losing messages, EIP defines the http://eaipatterns.com/MessageStore.html[message store] pattern, which lets EIP components store messages, typically in some type of persistent store (such as an RDBMS).
|
||||
|
||||
Spring Integration provides support for the _Message Store_ pattern by a) defining a `org.springframework.integration.store.MessageStore` strategy interface, b) providing several implementations of this interface, and c) exposing a `message-store` attribute on all components that have the capability to buffer messages so that you can inject any instance that implements the `MessageStore` interface.
|
||||
Spring Integration provides support for the message store pattern by:
|
||||
|
||||
Details on how to configure a specific _Message Store_ implementation and/or how to inject a `MessageStore` implementation into a specific buffering component are described throughout the manual (see the specific component, such as <<channel-configuration-queuechannel,_QueueChannel_>>, <<aggregator,_Aggregator_>>, <<delayer,_Delayer_>> etc.), but here are a couple of samples to give you an idea:
|
||||
* Defining an `org.springframework.integration.store.MessageStore` strategy interface
|
||||
* Providing several implementations of this interface
|
||||
* Exposing a `message-store` attribute on all components that have the capability to buffer messages so that you can inject any instance that implements the `MessageStore` interface.
|
||||
|
||||
QueueChannel
|
||||
Details on how to configure a specific message store implementation and how to inject a `MessageStore` implementation into a specific buffering component are described throughout the manual (see the specific component, such as "`<<channel-configuration-queuechannel,QueueChannel>>`", "`<<aggregator,Aggregator>>`", "`<<delayer,Delayer>>`", and others).
|
||||
The following pair of examples show how to add a reference to a message store for a `QueueChannel` and for an aggregator:
|
||||
|
||||
.QueueChannel
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:channel id="myQueueChannel">
|
||||
<int:queue message-store="refToMessageStore"/>
|
||||
<int:channel>
|
||||
----
|
||||
====
|
||||
|
||||
Aggregator
|
||||
.Aggregator
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:aggregator … message-store="refToMessageStore"/>
|
||||
----
|
||||
====
|
||||
|
||||
By default _Messages_ are stored in-memory using `org.springframework.integration.store.SimpleMessageStore`, an implementation of `MessageStore`.
|
||||
By default, messages are stored in-memory by using `o.s.i.store.SimpleMessageStore`, an implementation of `MessageStore`.
|
||||
That might be fine for development or simple low-volume environments where the potential loss of non-persistent messages is not a concern.
|
||||
However, the typical production application will need a more robust option, not only to mitigate the risk of message loss but also to avoid potential out-of-memory errors.
|
||||
Therefore, we also provide MessageStore implementations for a variety of data-stores.
|
||||
Below is a complete list of supported implementations:
|
||||
However, the typical production application needs a more robust option, not only to mitigate the risk of message loss but also to avoid potential out-of-memory errors.
|
||||
Therefore, we also provide `MessageStore` implementations for a variety of data-stores.
|
||||
The following is a complete list of supported implementations:
|
||||
|
||||
* <<jdbc-message-store>> - uses RDBMS to store Messages
|
||||
* <<redis-message-store>> - uses Redis key/value datastore to store Messages
|
||||
* <<mongodb-message-store>> - uses MongoDB document store to store Messages
|
||||
* <<gemfire-message-store>> - uses Gemfire distributed cache to store Messages
|
||||
* <<jdbc-message-store>>: Uses an RDBMS to store messages
|
||||
* <<redis-message-store>>: Uses a Redis key/value datastore to store messages
|
||||
* <<mongodb-message-store>>: Uses a MongoDB document store to store messages
|
||||
* <<gemfire-message-store>>: Uses a Gemfire distributed cache to store messages
|
||||
|
||||
[IMPORTANT]
|
||||
=====
|
||||
However be aware of some limitations while using persistent implementations of the `MessageStore`.
|
||||
However, be aware of some limitations while using persistent implementations of the `MessageStore`.
|
||||
|
||||
The Message data (payload and headers) is _serialized_ and _deserialized_ using different serialization strategies depending on the implementation of the `MessageStore`.
|
||||
The Message data (payload and headers) is serialized and deserialized by using different serialization strategies, depending on the implementation of the `MessageStore`.
|
||||
For example, when using `JdbcMessageStore`, only `Serializable` data is persisted by default.
|
||||
In this case non-Serializable headers are removed before serialization occurs.
|
||||
Also be aware of the protocol specific headers that are injected by transport adapters (e.g., FTP, HTTP, JMS etc.).
|
||||
For example, `<http:inbound-channel-adapter/>` maps HTTP-headers into Message Headers and one of them is an `ArrayList` of non-Serializable `org.springframework.http.MediaType` instances.
|
||||
However you are able to inject your own implementation of the `Serializer` and/or `Deserializer` strategy interfaces into some `MessageStore` implementations (such as JdbcMessageStore) to change the behaviour of serialization and deserialization.
|
||||
In this case, non-Serializable headers are removed before serialization occurs.
|
||||
Also, be aware of the protocol-specific headers that are injected by transport adapters (such as FTP, HTTP, JMS, and others).
|
||||
For example, `<http:inbound-channel-adapter/>` maps HTTP headers into message headers, and one of them is an `ArrayList` of non-serializable `org.springframework.http.MediaType` instances.
|
||||
However, you can inject your own implementation of the `Serializer` and `Deserializer` strategy interfaces into some `MessageStore` implementations (such as `JdbcMessageStore`) to change the behavior of serialization and deserialization.
|
||||
|
||||
Special attention must be paid to the headers that represent certain types of data.
|
||||
For example, if one of the headers contains an instance of some _Spring Bean_, upon deserialization you may end up with a different instance of that bean, which directly affects some of the implicit headers created by the framework (e.g., REPLY_CHANNEL or ERROR_CHANNEL).
|
||||
Currently they are not serializable, but even if they were, the deserialized channel would not represent the expected instance.
|
||||
Pay special attention to the headers that represent certain types of data.
|
||||
For example, if one of the headers contains an instance of some Spring bean, upon deserialization, you may end up with a different instance of that bean, which directly affects some of the implicit headers created by the framework (such as `REPLY_CHANNEL` or `ERROR_CHANNEL`).
|
||||
Currently, they are not serializable, but, even if they were, the deserialized channel would not represent the expected instance.
|
||||
|
||||
Beginning with _Spring Integration version 3.0_, this issue can be resolved with a header enricher, configured to replace these headers with a name after registering the channel with the `HeaderChannelRegistry`.
|
||||
Beginning with Spring Integration version 3.0, you can resolve this issue with a header enricher configured to replace these headers with a name after registering the channel with the `HeaderChannelRegistry`.
|
||||
|
||||
Also when configuring a message-flow like this: _gateway -> queue-channel (backed by a persistent Message Store) -> service-activator_ That gateway creates a _Temporary Reply Channel_, and it will be lost by the time the service-activator's poller reads from the queue.
|
||||
Again, you can use the header enricher to replace the headers with a String representation.
|
||||
Also, consider what happens when you configure a message-flow as follows: gateway -> queue-channel (backed by a persistent Message Store) -> service-activator. That gateway creates a temporary reply channel, which is lost by the time the service-activator's poller reads from the queue.
|
||||
Again, you can use the header enricher to replace the headers with a `String` representation.
|
||||
|
||||
For more information, refer to the <<header-enricher>>.
|
||||
For more information, see "`<<header-enricher>>`".
|
||||
=====
|
||||
|
||||
_Spring Integration 4.0_ introduced two new interfaces `ChannelMessageStore` - to implement operations specific for `QueueChannel` s, `PriorityCapableChannelMessageStore` - to mark `MessageStore` implementation to be used for `PriorityChannel` s and to provide _priority_ order for persisted Messages.
|
||||
The real behaviour depends on implementation.
|
||||
The Framework provides these implementations, which can be used as a persistent `MessageStore` for `QueueChannel` and `PriorityChannel`:
|
||||
Spring Integration 4.0 introduced two new interfaces:
|
||||
|
||||
* `ChannelMessageStore`: To implement operations specific for `QueueChannel` instances
|
||||
* `PriorityCapableChannelMessageStore`: To mark `MessageStore` implementations to be used for `PriorityChannel` instances and to provide priority order for persisted messages.
|
||||
|
||||
The real behavior depends on the implementation.
|
||||
The framework provides the following implementations, which can be used as a persistent `MessageStore` for `QueueChannel` and `PriorityChannel`:
|
||||
|
||||
* <<redis-cms>>
|
||||
* <<mongodb-priority-channel-message-store>>
|
||||
@@ -70,65 +82,67 @@ The Framework provides these implementations, which can be used as a persistent
|
||||
|
||||
[[sms-caution]]
|
||||
[WARNING]
|
||||
.Caution with SimpleMessageStore
|
||||
.Caution about `SimpleMessageStore`
|
||||
=====
|
||||
|
||||
Starting with _version 4.1_, the `SimpleMessageStore` no longer copies the message group when calling `getMessageGroup()`.
|
||||
Starting with version 4.1, the `SimpleMessageStore` no longer copies the message group when calling `getMessageGroup()`.
|
||||
For large message groups, this was a significant performance problem.
|
||||
4.0.1 introduced a boolean `copyOnGet` allowing this to be controlled.
|
||||
When used internally by the aggregator, this was set to false to improve performance.
|
||||
It is now false by default.
|
||||
4.0.1 introduced a boolean `copyOnGet` property that lets you control this behavior.
|
||||
When used internally by the aggregator, this property was set to `false` to improve performance.
|
||||
It is now `false` by default.
|
||||
|
||||
Users accessing the group store outside of components such as aggregators, will now get a direct reference to the group being used by the aggregator, instead of a copy.
|
||||
Users accessing the group store outside of components such as aggregators now get a direct reference to the group being used by the aggregator instead of a copy.
|
||||
Manipulation of the group outside of the aggregator may cause unpredictable results.
|
||||
|
||||
For this reason, users should not perform such manipulation, or set the `copyOnGet` property to `true`.
|
||||
For this reason, you should either not perform such manipulation or set the `copyOnGet` property to `true`.
|
||||
=====
|
||||
|
||||
[[message-group-factory]]
|
||||
==== MessageGroupFactory
|
||||
==== Using `MessageGroupFactory`
|
||||
|
||||
Starting with _version 4.3_, some `MessageGroupStore` implementations can be injected with a custom
|
||||
`MessageGroupFactory` strategy to create/customize the `MessageGroup` instances used by the `MessageGroupStore`.
|
||||
This defaults to a `SimpleMessageGroupFactory` which produces `SimpleMessageGroup` s based on the `GroupType.HASH_SET`
|
||||
Starting with version 4.3, some `MessageGroupStore` implementations can be injected with a custom
|
||||
`MessageGroupFactory` strategy to create and customize the `MessageGroup` instances used by the `MessageGroupStore`.
|
||||
This defaults to a `SimpleMessageGroupFactory`, which produces `SimpleMessageGroup` instances based on the `GroupType.HASH_SET`
|
||||
(`LinkedHashSet`) internal collection.
|
||||
Other possible options are `SYNCHRONISED_SET` and `BLOCKING_QUEUE`, where the last one can be used to reinstate the
|
||||
previous `SimpleMessageGroup` behavior.
|
||||
Also the `PERSISTENT` option is available. See the next section for more information.
|
||||
Starting with __version 5.0.1_, the `LIST` option is also available for use-cases when the order and uniqueness of messages in the group doesn't matter.
|
||||
Starting with version 5.0.1, the `LIST` option is also available for when the order and uniqueness of messages in the group does not matter.
|
||||
|
||||
[[lazy-load-message-group]]
|
||||
==== Persistence MessageGroupStore and Lazy-Load
|
||||
==== Persistent `MessageGroupStore` and Lazy-load
|
||||
|
||||
Starting with _version 4.3_, all persistence `MessageGroupStore` s retrieve `MessageGroup` s and their `messages`
|
||||
from the store with the _Lazy-Load_ manner.
|
||||
In most cases it is useful for the Correlation `MessageHandler` s (<<aggregator>> and <<resequencer>>),
|
||||
when it would be an overhead to load entire `MessageGroup` from the store on each correlation operation.
|
||||
Starting with version 4.3, all persistent `MessageGroupStore` instances retrieve `MessageGroup` instances and their `messages`
|
||||
from the store in the lazy-load manner.
|
||||
In most cases, it is useful for the correlation `MessageHandler` instances (see "`<<aggregator>>`" and "`<<resequencer>>`"),
|
||||
when it would add overhead to load entire the `MessageGroup` from the store on each correlation operation.
|
||||
|
||||
To switch off the lazy-load behavior the `AbstractMessageGroupStore.setLazyLoadMessageGroups(false)` option
|
||||
can be used from the configuration.
|
||||
You can use the `AbstractMessageGroupStore.setLazyLoadMessageGroups(false)` option to switch off the lazy-load behavior from the configuration.
|
||||
|
||||
Our performance tests for _lazy-load_ on MongoDB `MessageStore` (<<mongodb-message-store>>) and
|
||||
Our performance tests for lazy-load on MongoDB `MessageStore` (<<mongodb-message-store>>) and
|
||||
`<aggregator>` (<<aggregator>>)
|
||||
with custom `release-strategy` like:
|
||||
use a custom `release-strategy` similar to the following:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:aggregator input-channel="inputChannel"
|
||||
output-channel="outputChannel"
|
||||
message-store="mongoStore"
|
||||
release-strategy-expression="size() == 1000"/>
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
demonstrate this results for 1000 simple messages:
|
||||
It produces results similar to the following for 1000 simple messages:
|
||||
|
||||
....
|
||||
====
|
||||
----
|
||||
...
|
||||
StopWatch 'Lazy-Load Performance': running time (millis) = 38918
|
||||
-----------------------------------------
|
||||
ms % Task name
|
||||
-----------------------------------------
|
||||
02652 007% Lazy-Load
|
||||
36266 093% Eager
|
||||
....
|
||||
|
||||
...
|
||||
----
|
||||
====
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
=== Message
|
||||
|
||||
The Spring Integration `Message` is a generic container for data.
|
||||
Any object can be provided as the payload, and each `Message` also includes headers containing user-extensible properties as key-value pairs.
|
||||
Any object can be provided as the payload, and each `Message` instance includes headers containing user-extensible properties as key-value pairs.
|
||||
|
||||
[[message-interface]]
|
||||
==== The Message Interface
|
||||
==== The `Message` Interface
|
||||
|
||||
Here is the definition of the `Message` interface:
|
||||
The following listing shows the definition of the `Message` interface:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public interface Message<T> {
|
||||
@@ -18,258 +20,144 @@ public interface Message<T> {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The `Message` is obviously a very important part of the API.
|
||||
The `Message` interface is a core part of the API.
|
||||
By encapsulating the data in a generic wrapper, the messaging system can pass it around without any knowledge of the data's type.
|
||||
As an application evolves to support new types, or when the types themselves are modified and/or extended, the messaging system will not be affected by such changes.
|
||||
On the other hand, when some component in the messaging system _does_ require access to information about the `Message`, such metadata can typically be stored to and retrieved from the metadata in the Message Headers.
|
||||
As an application evolves to support new types or when the types themselves are modified or extended, the messaging system is not affected.
|
||||
On the other hand, when some component in the messaging system does require access to information about the `Message`, such metadata can typically be stored to and retrieved from the metadata in the message headers.
|
||||
|
||||
[[message-headers]]
|
||||
==== Message Headers
|
||||
|
||||
Just as Spring Integration allows any Object to be used as the payload of a Message, it also supports any Object types as header values.
|
||||
In fact, the `MessageHeaders` class implements the _java.util.Map_ interface:
|
||||
Just as Spring Integration lets any `Object` be used as the payload of a `Message`, it also supports any `Object` types as header values.
|
||||
In fact, the `MessageHeaders` class implements the `java.util.Map_ interface`, as the following class definition shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public final class MessageHeaders implements Map<String, Object>, Serializable {
|
||||
...
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: Even though the MessageHeaders implements Map, it is effectively a read-only implementation.
|
||||
Any attempt to _put_ a value in the Map will result in an `UnsupportedOperationException`.
|
||||
The same applies for _remove_ and _clear_.
|
||||
Since Messages may be passed to multiple consumers, the structure of the Map cannot be modified.
|
||||
Likewise, the Message's payload Object can not be _set_ after the initial creation.
|
||||
NOTE: Even though the `MessageHeaders` class implements `Map`, it is effectively a read-only implementation.
|
||||
Any attempt to `put` a value in the Map results in an `UnsupportedOperationException`.
|
||||
The same applies for `remove` and `clear`.
|
||||
Since messages may be passed to multiple consumers, the structure of the `Map` cannot be modified.
|
||||
Likewise, the message's payload `Object` can not be `set` after the initial creation.
|
||||
However, the mutability of the header values themselves (or the payload Object) is intentionally left as a decision for the framework user.
|
||||
|
||||
As an implementation of Map, the headers can obviously be retrieved by calling `get(..)` with the name of the header.
|
||||
Alternatively, you can provide the expected _Class_ as an additional parameter.
|
||||
As an implementation of `Map`, the headers can be retrieved by calling `get(..)` with the name of the header.
|
||||
Alternatively, you can provide the expected `Class` as an additional parameter.
|
||||
Even better, when retrieving one of the pre-defined values, convenient getters are available.
|
||||
Here is an example of each of these three options:
|
||||
The following example shows each of these three options:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Object someValue = message.getHeaders().get("someKey");
|
||||
Object someValue = message.getHeaders().get("someKey");
|
||||
|
||||
CustomerId customerId = message.getHeaders().get("customerId", CustomerId.class);
|
||||
|
||||
Long timestamp = message.getHeaders().getTimestamp();
|
||||
CustomerId customerId = message.getHeaders().get("customerId", CustomerId.class);
|
||||
|
||||
Long timestamp = message.getHeaders().getTimestamp();
|
||||
----
|
||||
====
|
||||
|
||||
The following Message headers are pre-defined:
|
||||
|
||||
The following table describes the pre-defined message headers:
|
||||
|
||||
.Pre-defined Message Headers
|
||||
|
||||
[cols="2l,2l,5", options="header"]
|
||||
|===
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| Header Name
|
||||
|
||||
|
||||
| Header Type
|
||||
|
||||
|
||||
| Usage
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| MessageHeaders.ID
|
||||
|
||||
|
||||
| `MessageHeaders.ID`
|
||||
| java.util.UUID
|
||||
|
||||
|
||||
| An identifier for this message instance.
|
||||
Changes each time a message is mutated.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| MessageHeaders.
|
||||
TIMESTAMP
|
||||
|
||||
|
||||
| `MessageHeaders.TIMESTAMP`
|
||||
| java.lang.Long
|
||||
|
||||
|
||||
| The time the message was created.
|
||||
Changes each time a message is mutated.
|
||||
|
||||
| `MessageHeaders.REPLY_CHANNEL`
|
||||
| `java.lang.Object` (`String` or `MessageChannel`)
|
||||
| A channel to which a reply (if any) is sent when no explicit output channel is configured and there is no `ROUTING_SLIP` or the `ROUTING_SLIP` is exhausted.
|
||||
If the value is a `String`, it must represent a bean name or have been generated by a `ChannelRegistry.`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| MessageHeaders.
|
||||
REPLY_CHANNEL
|
||||
|
||||
|
||||
| java.lang.Object
|
||||
(String or MessageChannel)
|
||||
|
||||
|
||||
| A channel to which a reply (if any) will be sent when no explicit output channel is configured and there is no `ROUTING_SLIP` or the `ROUTING_SLIP` is exhausted.
|
||||
If the value is a `String` it must represent a bean name, or have been generated by a `ChannelRegistry.`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| MessageHeaders.
|
||||
ERROR_CHANNEL
|
||||
|
||||
|
||||
| java.lang.Object
|
||||
(String or MessageChannel)
|
||||
|
||||
|
||||
| A channel to which errors will be sent.
|
||||
If the value is a `String` it must represent a bean name, or have been generated by a `ChannelRegistry.`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| MessageHeaders.ERROR_CHANNEL
|
||||
| `java.lang.Object` (`String` or `MessageChannel`)
|
||||
| A channel to which errors are sent.
|
||||
If the value is a `String`, it must represent a bean name or have been generated by a `ChannelRegistry.`
|
||||
|===
|
||||
|
||||
Many inbound and outbound adapter implementations will also provide and/or expect certain headers, and additional user-defined headers can also be configured.
|
||||
Constants for these headers can be found in those modules where such headers exist, for example `AmqpHeaders`, `JmsHeaders` etc.
|
||||
Many inbound and outbound adapter implementations also provide or expect certain headers, and you can configure additional user-defined headers.
|
||||
Constants for these headers can be found in those modules where such headers exist -- for example. `AmqpHeaders`, `JmsHeaders`, and so on.
|
||||
|
||||
[[message-header-accessor]]
|
||||
===== MessageHeaderAccessor API
|
||||
|
||||
Starting with Spring Framework 4.0 and Spring Integration 4.0, the core Messaging abstraction has been moved to the _spring-messaging_ module and the new `MessageHeaderAccessor` API has been introduced to provide additional abstraction over Messaging implementations.
|
||||
All (core) Spring Integration specific Message Headers constants are now declared in the `IntegrationMessageHeaderAccessor` class:
|
||||
===== `MessageHeaderAccessor` API
|
||||
|
||||
Starting with Spring Framework 4.0 and Spring Integration 4.0, the core messaging abstraction has been moved to the `spring-messaging` module, and the `MessageHeaderAccessor` API has been introduced to provide additional abstraction over messaging implementations.
|
||||
All (core) Spring Integration-specific message headers constants are now declared in the `IntegrationMessageHeaderAccessor` class.
|
||||
The following table describes the pre-defined message headers:
|
||||
|
||||
.Pre-defined Message Headers
|
||||
|
||||
[cols="5l,3l,5", options="header"]
|
||||
|===
|
||||
| Header Name
|
||||
|
||||
|
||||
| Header Type
|
||||
|
||||
|
||||
| Usage
|
||||
|
||||
|
||||
| IntegrationMessageHeaderAccessor.
|
||||
CORRELATION_ID
|
||||
|
||||
|
||||
| java.lang.Object
|
||||
|
||||
|
||||
| `IntegrationMessageHeaderAccessor.CORRELATION_ID`
|
||||
| `java.lang.Object`
|
||||
| Used to correlate two or more messages.
|
||||
|
||||
|
||||
| IntegrationMessageHeaderAccessor.
|
||||
SEQUENCE_NUMBER
|
||||
|
||||
|
||||
| java.lang.Integer
|
||||
|
||||
|
||||
| `IntegrationMessageHeaderAccessor.SEQUENCE_NUMBER`
|
||||
| `java.lang.Integer`
|
||||
| Usually a sequence number with a group of messages with a `SEQUENCE_SIZE` but can also be used in a `<resequencer/>` to resequence an unbounded group of messages.
|
||||
|
||||
|
||||
| IntegrationMessageHeaderAccessor.
|
||||
SEQUENCE_SIZE
|
||||
|
||||
|
||||
| java.lang.Integer
|
||||
|
||||
|
||||
| `IntegrationMessageHeaderAccessor.SEQUENCE_SIZE`
|
||||
| `java.lang.Integer`
|
||||
| The number of messages within a group of correlated messages.
|
||||
|
||||
|
||||
| IntegrationMessageHeaderAccessor.
|
||||
EXPIRATION_DATE
|
||||
|
||||
|
||||
| java.lang.Long
|
||||
|
||||
|
||||
| `IntegrationMessageHeaderAccessor.EXPIRATION_DATE`
|
||||
| `java.lang.Long`
|
||||
| Indicates when a message is expired.
|
||||
Not used by the framework directly but can be set with a header enricher and used in a `<filter/>` configured with an `UnexpiredMessageSelector`.
|
||||
|
||||
|
||||
| IntegrationMessageHeaderAccessor.
|
||||
PRIORITY
|
||||
|
||||
|
||||
| java.lang.Integer
|
||||
|
||||
|
||||
| Message priority; for example within a `PriorityChannel`
|
||||
|
||||
|
||||
| IntegrationMessageHeaderAccessor.
|
||||
DUPLICATE_MESSAGE
|
||||
|
||||
|
||||
| java.lang.Boolean
|
||||
Not used by the framework directly but can be set with a header enricher and used in a `<filter/>` that is configured with an `UnexpiredMessageSelector`.
|
||||
|
||||
| `IntegrationMessageHeaderAccessor.PRIORITY`
|
||||
| `java.lang.Integer`
|
||||
| Message priority -- for example, within a `PriorityChannel`.
|
||||
|
||||
|
||||
| `IntegrationMessageHeaderAccessor.DUPLICATE_MESSAGE`
|
||||
| `java.lang.Boolean`
|
||||
| True if a message was detected as a duplicate by an idempotent receiver interceptor.
|
||||
See <<idempotent-receiver>>.
|
||||
See "`<<idempotent-receiver>>`".
|
||||
|
||||
| IntegrationMessageHeaderAccessor.
|
||||
CLOSEABLE_RESOURCE
|
||||
| `IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE`
|
||||
| `java.io.Closeable`
|
||||
| This header is present if the message is associated with a `Closeable` that should be closed when message processing is complete.
|
||||
An example is the `Session` associated with a streamed file transfer using FTP, SFTP, and so on.
|
||||
|
||||
| `IntegrationMessageHeaderAccessor.DELIVERY_ATTEMPT`
|
||||
| `java.lang.AtomicInteger`
|
||||
| If a message-driven channel adapter supports the configuration of a `RetryTemplate`, this header contains the current delivery attempt.
|
||||
|
||||
| java.io.Closeable
|
||||
|
||||
|
||||
| This header is present if the message is associated with a `Closeable` which should be closed when message processing is complete.
|
||||
An example is the `Session` associated with a streamed file transfer using FTP, SFTP, etc.
|
||||
|
||||
| IntegrationMessageHeaderAccessor.
|
||||
DELIVERY_ATTEMPT
|
||||
|
||||
|
||||
| java.lang.
|
||||
AtomicInteger
|
||||
|
||||
|
||||
| If a message-driven channel adapter supports the configuration of a `RetryTemplate` this header contains the current delivery attempt.
|
||||
|
||||
| IntegrationMessageHeaderAccessor.
|
||||
ACKNOWLEDGMENT_CALLBACK
|
||||
|
||||
|
||||
| o.s.i.support.
|
||||
Acknowledgment
|
||||
Callback
|
||||
|
||||
| If a message source supports it, a call back to accept, reject, or requeue a message - see <<deferred-acks-message-source>>.
|
||||
|
||||
| `IntegrationMessageHeaderAccessor.ACKNOWLEDGMENT_CALLBACK`
|
||||
| `o.s.i.support.AcknowledgmentCallback`
|
||||
| If a message source supports it, a call back to accept, reject, or requeue a message.
|
||||
See "`<<deferred-acks-message-source>>`".
|
||||
|===
|
||||
|
||||
Convenient typed getters for some of these headers are provided on the `IntegrationMessageHeaderAccessor` class:
|
||||
Convenient typed getters for some of these headers are provided on the `IntegrationMessageHeaderAccessor` class, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
IntegrationMessageHeaderAccessor accessor = new IntegrationMessageHeaderAccessor(message);
|
||||
@@ -277,164 +165,135 @@ int sequenceNumber = accessor.getSequenceNumber();
|
||||
Object correlationId = accessor.getCorrelationId();
|
||||
...
|
||||
----
|
||||
====
|
||||
|
||||
The following headers also appear in the `IntegrationMessageHeaderAccessor` but are generally not used by user code; their inclusion here is for completeness:
|
||||
|
||||
The following table describes headers that also appear in the `IntegrationMessageHeaderAccessor` but are generally not used by user code (that is, they are generally used by internal parts of Spring Integration -- their inclusion here is for completeness):
|
||||
|
||||
.Pre-defined Message Headers
|
||||
|
||||
[cols="5l,3l,5", options="header"]
|
||||
|===
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| Header Name
|
||||
|
||||
|
||||
| Header Type
|
||||
|
||||
|
||||
| Usage
|
||||
|
||||
| `IntegrationMessageHeaderAccessor.SEQUENCE_DETAILS`
|
||||
| `java.util.List<List<Object>>`
|
||||
| A stack of correlation data used when nested correlation is needed (for example, `splitter->...->splitter->...->aggregator->...->aggregator`).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| IntegrationMessageHeaderAccessor.
|
||||
SEQUENCE_DETAILS
|
||||
|
||||
|
||||
| java.util.List<
|
||||
List<Object>>
|
||||
|
||||
|
||||
| A stack of correlation data used when nested correlation is needed (e.g.
|
||||
`splitter->...->splitter->...->aggregator->...->aggregator`).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| IntegrationMessageHeaderAccessor.
|
||||
ROUTING_SLIP
|
||||
|
||||
|
||||
| java.util.Map<
|
||||
List<Object>, Integer>
|
||||
|
||||
|
||||
| See <<routing-slip>>.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| `IntegrationMessageHeaderAccessor.ROUTING_SLIP`
|
||||
| `java.util.Map<List<Object>, Integer>`
|
||||
| See "`<<routing-slip>>`".
|
||||
|===
|
||||
|
||||
[[message-id-generation]]
|
||||
===== Message ID Generation
|
||||
|
||||
When a message transitions through an application, each time it is mutated (e.g.
|
||||
by a transformer) a new message id is assigned.
|
||||
The message id is a `UUID`.
|
||||
Beginning with _Spring Integration 3.0_, the default strategy used for id generation is more efficient than the previous `java.util.UUID.randomUUID()` implementation.
|
||||
It uses simple random numbers based on a secure random seed, instead of creating a secure random number each time.
|
||||
When a message transitions through an application, each time it is mutated (for example,
|
||||
by a transformer) a new message ID is assigned.
|
||||
The message ID is a `UUID`.
|
||||
Beginning with Spring Integration 3.0, the default strategy used for IS generation is more efficient than the previous `java.util.UUID.randomUUID()` implementation.
|
||||
It uses simple random numbers based on a secure random seed instead of creating a secure random number each time.
|
||||
|
||||
A different UUID generation strategy can be selected by declaring a bean that implements `org.springframework.util.IdGenerator` in the application context.
|
||||
|
||||
IMPORTANT: Only one UUID generation strategy can be used in a classloader.
|
||||
This means that if two or more application contexts are running in the same classloader, they will share the same strategy.
|
||||
If one of the contexts changes the strategy, it will be used by all contexts.
|
||||
If two or more contexts in the same classloader declare a bean of type `org.springframework.util.IdGenerator`, they must all be an instance of the same class, otherwise the context attempting to replace a custom strategy will fail to initialize.
|
||||
If the strategy is the same, but parameterized, the strategy in the first context to initialize will be used.
|
||||
This means that, if two or more application contexts run in the same classloader, they share the same strategy.
|
||||
If one of the contexts changes the strategy, it is used by all contexts.
|
||||
If two or more contexts in the same classloader declare a bean of type `org.springframework.util.IdGenerator`, they must all be an instance of the same class.
|
||||
Otherwise, the context attempting to replace a custom strategy fails to initialize.
|
||||
If the strategy is the same, but parameterized, the strategy in the first context to be initialized is used.
|
||||
|
||||
In addition to the default strategy, two additional `IdGenerators` are provided; `org.springframework.util.JdkIdGenerator` uses the previous `UUID.randomUUID()` mechanism; `o.s.i.support.IdGenerators.SimpleIncrementingIdGenerator` can be used in cases where a UUID is not really needed and a simple incrementing value is sufficient.
|
||||
In addition to the default strategy, two additional `IdGenerators` are provided.
|
||||
`org.springframework.util.JdkIdGenerator` uses the previous `UUID.randomUUID()` mechanism.
|
||||
You can use `o.s.i.support.IdGenerators.SimpleIncrementingIdGenerator` when a UUID is not really needed and a simple incrementing value is sufficient.
|
||||
|
||||
[[read-only-headers]]
|
||||
===== Read-only Headers
|
||||
|
||||
The `MessageHeaders.ID` and `MessageHeaders.TIMESTAMP` are read-only headers and they cannot be overridden.
|
||||
The `MessageHeaders.ID` and `MessageHeaders.TIMESTAMP` are read-only headers and cannot be overridden.
|
||||
|
||||
Since _version 4.3.2_, the `MessageBuilder` provides the `readOnlyHeaders(String... readOnlyHeaders)` API to customize a list of headers which should not be copied from an upstream `Message`.
|
||||
Just the `MessageHeaders.ID` and `MessageHeaders.TIMESTAMP` are read only by default.
|
||||
The global `spring.integration.readOnly.headers` property (see <<global-properties>>) is provided to customize `DefaultMessageBuilderFactory` for Framework components.
|
||||
This can be useful when you would like do not populate some out-of-the-box headers, like `contentType` by the `ObjectToJsonTransformer` (see <<json-transformers>>).
|
||||
Since version 4.3.2, the `MessageBuilder` provides the `readOnlyHeaders(String... readOnlyHeaders)` API to customize a list of headers that should not be copied from an upstream `Message`.
|
||||
Only the `MessageHeaders.ID` and `MessageHeaders.TIMESTAMP` are read only by default.
|
||||
The global `spring.integration.readOnly.headers` property (see <<global-properties>>) is provided to customize `DefaultMessageBuilderFactory` for framework components.
|
||||
This can be useful when you would like do not populate some out-of-the-box headers, such as `contentType` by the `ObjectToJsonTransformer` (see <<json-transformers>>).
|
||||
|
||||
When you try to build a new message using `MessageBuilder`, this kind of headers are ignored and particular `INFO` message is emitted to logs.
|
||||
When you try to build a new message using `MessageBuilder`, this kind of header is ignored and a particular `INFO` message is emitted to logs.
|
||||
|
||||
Starting with _version 5.0_, <<gateway,Messaging Gateway>>, <<header-enricher,Header Enricher>>, <<payload-enricher,Content Enricher>> and <<header-filter, Header Filter>> don't allow to configure `MessageHeaders.ID` and `MessageHeaders.TIMESTAMP` header names when `DefaultMessageBuilderFactory` is used and they throw `BeanInitializationException`.
|
||||
Starting with version 5.0, <<gateway,Messaging Gateway>>, <<header-enricher,Header Enricher>>, <<payload-enricher,Content Enricher>> and <<header-filter,Header Filter>> do not let you configure the `MessageHeaders.ID` and `MessageHeaders.TIMESTAMP` header names when `DefaultMessageBuilderFactory` is used, and they throw `BeanInitializationException`.
|
||||
|
||||
[[header-propagation]]
|
||||
===== Header Propagation
|
||||
|
||||
When messages are processed (and modified) by message-producing endpoints (such as a <<service-activator, service activator>>), in general, inbound headers are propagated to the outbound message.
|
||||
One exception to this is a <<transformer, transformer>>, when a complete message is returned to the framework; in that case, the user code is responsible for the entire outbound message.
|
||||
When a transformer just returns the payload; the inbound headers **are** propagated.
|
||||
Also, a header is only propagated if it does not already exist in the outbound message, allowing user code to change header values as needed.
|
||||
When messages are processed (and modified) by message-producing endpoints (such as a <<service-activator,service activator>>), in general, inbound headers are propagated to the outbound message.
|
||||
One exception to this is a <<transformer,transformer>>, when a complete message is returned to the framework.
|
||||
In that case, the user code is responsible for the entire outbound message.
|
||||
When a transformer just returns the payload, the inbound headers are propagated.
|
||||
Also, a header is only propagated if it does not already exist in the outbound message, letting you change header values as needed.
|
||||
|
||||
Starting with _version 4.3.10_, you can configure message handlers (that modify messages and produce output) to suppress the propagation of specific headers.
|
||||
Call the `setNotPropagatedHeaders()` or `addNotPropagatedHeaders()` methods on the `MessageProducingMessageHandler` abstract class, to configure the header(s) you don't want to be copied.
|
||||
Starting with version 4.3.10, you can configure message handlers (that modify messages and produce output) to suppress the propagation of specific headers.
|
||||
To configure the header(s) you do not want to be copied, call the `setNotPropagatedHeaders()` or `addNotPropagatedHeaders()` methods on the `MessageProducingMessageHandler` abstract class.
|
||||
|
||||
You can also globally suppress propagation of specific message headers by setting the `readOnlyHeaders` property in `META-INF/spring.integration.properties` to a comma-delimited list of headers.
|
||||
|
||||
Starting with _version 5.0_, the `setNotPropagatedHeaders()` implementation on the `AbstractMessageProducingHandler` applies the simple patterns (`xxx*`, `*xxx`, `*xxx*` or `xxx*yyy`) to allow filtering headers with a common suffix or prefix.
|
||||
See `PatternMatchUtils` JavaDocs for more information.
|
||||
When one of the patterns is `*` (asterisk), no headers are propagated; all other patterns are ignored.
|
||||
In this case the Service Activator behaves the same way as Transformer and any required headers must be supplied in the `Message` returned from the service method.
|
||||
The option `notPropagatedHeaders()` is available in the `ConsumerEndpointSpec` for Java DSL, as well as for XML configuration of the `<service-activator>` component as a `not-propagated-headers` attribute.
|
||||
|
||||
IMPORTANT: Header propagation suppression does not apply to those endpoints that don't modify the message, e.g. <<bridge, bridges>> and <<router, routers>>.
|
||||
Starting with version 5.0, the `setNotPropagatedHeaders()` implementation on the `AbstractMessageProducingHandler` applies simple patterns (`xxx*`, `*xxx`, `*xxx*`, or `xxx*yyy`) to allow filtering headers with a common suffix or prefix.
|
||||
See https://docs.spring.io/spring-integration/api/org/springframework/integration/util/PatternMatchUtils.html[`PatternMatchUtils` Javadoc] for more information.
|
||||
When one of the patterns is `*` (asterisk), no headers are propagated.
|
||||
All other patterns are ignored.
|
||||
In that case, the service activator behaves the same way as a transformer and any required headers must be supplied in the `Message` returned from the service method.
|
||||
The `notPropagatedHeaders()` option is available in the `ConsumerEndpointSpec` for the Java DSL
|
||||
It is also available for XML configuration of the `<service-activator>` component as a `not-propagated-headers` attribute.
|
||||
|
||||
IMPORTANT: Header propagation suppression does not apply to those endpoints that do not modify the message, such as <<bridge,bridges>> and <<router,routers>>.
|
||||
|
||||
[[message-implementations]]
|
||||
==== Message Implementations
|
||||
|
||||
The base implementation of the `Message` interface is `GenericMessage<T>`, and it provides two constructors:
|
||||
The base implementation of the `Message` interface is `GenericMessage<T>`, and it provides two constructors, shown in the following listing:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
new GenericMessage<T>(T payload);
|
||||
|
||||
new GenericMessage<T>(T payload, Map<String, Object> headers)
|
||||
----
|
||||
====
|
||||
|
||||
When a Message is created, a random unique id will be generated.
|
||||
The constructor that accepts a Map of headers will copy the provided headers to the newly created Message.
|
||||
When a `Message` is created, a random unique ID is generated.
|
||||
The constructor that accepts a `Map` of headers copies the provided headers to the newly created `Message`.
|
||||
|
||||
There is also a convenient implementation of `Message` designed to communicate error conditions.
|
||||
This implementation takes `Throwable` object as its payload:
|
||||
This implementation takes a `Throwable` object as its payload, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
ErrorMessage message = new ErrorMessage(someThrowable);
|
||||
|
||||
Throwable t = message.getPayload();
|
||||
----
|
||||
====
|
||||
|
||||
Notice that this implementation takes advantage of the fact that the `GenericMessage` base class is parameterized.
|
||||
Therefore, as shown in both examples, no casting is necessary when retrieving the Message payload Object.
|
||||
Note that this implementation takes advantage of the fact that the `GenericMessage` base class is parameterized.
|
||||
Therefore, as shown in both examples, no casting is necessary when retrieving the `Message` payload `Object`.
|
||||
|
||||
[[message-builder]]
|
||||
==== The MessageBuilder Helper Class
|
||||
==== The `MessageBuilder` Helper Class
|
||||
|
||||
You may notice that the Message interface defines retrieval methods for its payload and headers but no setters.
|
||||
The reason for this is that a Message cannot be modified after its initial creation.
|
||||
Therefore, when a Message instance is sent to multiple consumers (e.g.
|
||||
through a Publish Subscribe Channel), if one of those consumers needs to send a reply with a different payload type, it will need to create a new Message.
|
||||
You may notice that the `Message` interface defines retrieval methods for its payload and headers but provides no setters.
|
||||
The reason for this is that a `Message` cannot be modified after its initial creation.
|
||||
Therefore, when a `Message` instance is sent to multiple consumers (for example,
|
||||
through a publish-subscribe Channel), if one of those consumers needs to send a reply with a different payload type, it must create a new `Message`.
|
||||
As a result, the other consumers are not affected by those changes.
|
||||
Keep in mind, that multiple consumers may access the same payload instance or header value, and whether such an instance is itself immutable is a decision left to the developer.
|
||||
In other words, the contract for Messages is similar to that of an _unmodifiable Collection_, and the MessageHeaders' map further exemplifies that; even though the MessageHeaders class implements `java.util.Map`, any attempt to invoke a _put_ operation (or 'remove' or 'clear') on the MessageHeaders will result in an `UnsupportedOperationException`.
|
||||
Keep in mind that multiple consumers may access the same payload instance or header value, and whether such an instance is itself immutable is a decision left to you.
|
||||
In other words, the contract for `Message` instances is similar to that of an unmodifiable `Collection`, and the `MessageHeaders` map further exemplifies that.
|
||||
Even though the `MessageHeaders` class implements `java.util.Map`, any attempt to invoke a `put` operation (or 'remove' or 'clear') on a `MessageHeaders` instance results in an `UnsupportedOperationException`.
|
||||
|
||||
Rather than requiring the creation and population of a Map to pass into the GenericMessage constructor, Spring Integration does provide a far more convenient way to construct Messages: `MessageBuilder`.
|
||||
The MessageBuilder provides two factory methods for creating Messages from either an existing Message or with a payload Object.
|
||||
When building from an existing Message, the headers _and payload_ of that Message will be copied to the new Message:
|
||||
The `MessageBuilder` provides two factory methods for creating `Message` instances from either an existing `Message` or with a payload `Object`.
|
||||
When building from an existing `Message`, the headers and payload of that `Message` are copied to the new `Message`, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Message<String> message1 = MessageBuilder.withPayload("test")
|
||||
@@ -446,8 +305,9 @@ Message<String> message2 = MessageBuilder.fromMessage(message1).build();
|
||||
assertEquals("test", message2.getPayload());
|
||||
assertEquals("bar", message2.getHeaders().get("foo"));
|
||||
----
|
||||
====
|
||||
|
||||
If you need to create a Message with a new payload but still want to copy the headers from an existing Message, you can use one of the 'copy' methods.
|
||||
If you need to create a `Message` with a new payload but still want to copy the headers from an existing `Message`, you can use one of the 'copy' methods, as the following example shows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -464,9 +324,11 @@ assertEquals("bar", message3.getHeaders().get("foo"));
|
||||
assertEquals(123, message4.getHeaders().get("foo"));
|
||||
----
|
||||
|
||||
Notice that the `copyHeadersIfAbsent` does not overwrite existing values.
|
||||
Also, in the second example above, you can see how to set any user-defined header with `setHeader`.
|
||||
Finally, there are set methods available for the predefined headers as well as a non-destructive method for setting any header (MessageHeaders also defines constants for the pre-defined header names).
|
||||
Note that the `copyHeadersIfAbsent` method does not overwrite existing values.
|
||||
Also, in the preceding example, you can see how to set any user-defined header with `setHeader`.
|
||||
Finally, there are `set` methods available for the predefined headers as well as a non-destructive method for setting any header (`MessageHeaders` also defines constants for the pre-defined header names).
|
||||
|
||||
You can also use `MessageBuilder` to set the priority of messages, as the following example shows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -484,5 +346,5 @@ assertEquals(2, lessImportantMessage.getHeaders().getPriority());
|
||||
|
||||
----
|
||||
|
||||
The `priority` header is only considered when using a `PriorityChannel` (as described in the next chapter).
|
||||
It is defined as _java.lang.Integer_.
|
||||
The `priority` header is considered only when using a `PriorityChannel` (as described in the next chapter).
|
||||
It is defined as a `java.lang.Integer`.
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
[[metadata-store]]
|
||||
=== Metadata Store
|
||||
|
||||
Many external systems, services or resources aren't transactional (Twitter, RSS, file system etc.) and there is no any ability to mark the data as read.
|
||||
Or there is just need to implement the Enterprise Integration Pattern http://eaipatterns.com/IdempotentReceiver.html[Idempotent Receiver] in some integration solutions.
|
||||
To achieve this goal and store some previous state of the Endpoint before the next interaction with external system, or deal with the next Message, Spring Integration provides the _Metadata Store_ component being an implementation of the `org.springframework.integration.metadata.MetadataStore` interface with a general _key-value_ contract.
|
||||
Many external systems, services, or resources are not transactional (Twitter, RSS, file systems, and so on), and there is no any ability to mark the data as read.
|
||||
Also, sometimes, you may need to implement the Enterprise Integration Pattern http://eaipatterns.com/IdempotentReceiver.html[idempotent receiver] in some integration solutions.
|
||||
To achieve this goal and store some previous state of the endpoint before the next interaction with external system or to deal with the next message, Spring Integration provides the metadata store component as an an implementation of the `org.springframework.integration.metadata.MetadataStore` interface with a general key-value contract.
|
||||
|
||||
The _Metadata Store_ is designed to store various types of generic meta-data (e.g., published date of the last feed entry that has been processed) to help components such as the Feed adapter deal with duplicates.
|
||||
If a component is not directly provided with a reference to a `MetadataStore`, the algorithm for locating a metadata store is as follows: First, look for a bean with id `metadataStore` in the ApplicationContext.
|
||||
If one is found then it will be used, otherwise it will create a new instance of `SimpleMetadataStore` which is an in-memory implementation that will only persist metadata within the lifecycle of the currently running Application Context.
|
||||
This means that upon restart you may end up with duplicate entries.
|
||||
The metadata store is designed to store various types of generic metadata (for example, the published date of the last feed entry that has been processed) to help components such as the feed adapter deal with duplicates.
|
||||
If a component is not directly provided with a reference to a `MetadataStore`, the algorithm for locating a metadata store is as follows: First, look for a bean with a `metadataStore` ID in the application context.
|
||||
If one is found, use it.
|
||||
Otherwise, create a new instance of `SimpleMetadataStore`, which is an in-memory implementation that persists only metadata within the lifecycle of the currently running application context.
|
||||
This means that, upon restart, you may end up with duplicate entries.
|
||||
|
||||
If you need to persist metadata between Application Context restarts, these persistent `MetadataStores` are provided by
|
||||
the framework:
|
||||
If you need to persist metadata between application context restarts, the framework provides the following persistent `MetadataStores`:
|
||||
|
||||
* `PropertiesPersistingMetadataStore`
|
||||
* <<gemfire-metadata-store>>
|
||||
@@ -20,12 +20,11 @@ the framework:
|
||||
* <<redis-metadata-store>>
|
||||
* <<zk-metadata-store>>
|
||||
|
||||
The `PropertiesPersistingMetadataStore` is backed by a properties file and a http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/PropertiesPersister.html[`PropertiesPersister`].
|
||||
|
||||
|
||||
The `PropertiesPersistingMetadataStore` is backed by a properties file and a http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/PropertiesPersister.html[PropertiesPersister].
|
||||
|
||||
By default, it only persists the state when the application context is closed normally. It implements `Flushable` so you
|
||||
can persist the state at will, be invoking `flush()`.
|
||||
By default, it persists only the state when the application context is closed normally. It implements `Flushable` so that you
|
||||
can persist the state at will, by invoking `flush()`.
|
||||
The following example shows how to configure a 'PropertiesPersistingMetadataStore' with XML:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
@@ -33,18 +32,19 @@ can persist the state at will, be invoking `flush()`.
|
||||
class="org.springframework.integration.metadata.PropertiesPersistingMetadataStore"/>
|
||||
----
|
||||
|
||||
Alternatively, you can provide your own implementation of the `MetadataStore` interface (e.g.
|
||||
JdbcMetadataStore) and configure it as a bean in the Application Context.
|
||||
Alternatively, you can provide your own implementation of the `MetadataStore` interface (for example,
|
||||
`JdbcMetadataStore`) and configure it as a bean in the application context.
|
||||
|
||||
Starting with _version 4.0_, `SimpleMetadataStore`, `PropertiesPersistingMetadataStore` and `RedisMetadataStore` implement `ConcurrentMetadataStore`.
|
||||
Starting with version 4.0, `SimpleMetadataStore`, `PropertiesPersistingMetadataStore`, and `RedisMetadataStore` implement `ConcurrentMetadataStore`.
|
||||
These provide for atomic updates and can be used across multiple component or application instances.
|
||||
|
||||
[[idempotent-receiver-pattern]]
|
||||
==== Idempotent Receiver and Metadata Store
|
||||
|
||||
The _Metadata Store_ is useful for implementing the EIP http://eaipatterns.com/IdempotentReceiver.html[Idempotent Receiver] pattern, when there is need to _filter_ an incoming Message if it has already been processed, and just discard it or perform some other logic on discarding.
|
||||
The following configuration is an example of how to do this:
|
||||
The metadata store is useful for implementing the EIP http://eaipatterns.com/IdempotentReceiver.html[idempotent receiver] pattern when there is need to filter an incoming message if it has already been processed and you can discard it or perform some other logic on discarding.
|
||||
The following configuration shows an example of how to do so:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:filter input-channel="serviceChannel"
|
||||
@@ -59,16 +59,18 @@ The following configuration is an example of how to do this:
|
||||
|
||||
<int:service-activator input-channel="idempotentServiceChannel" ref="service"/>
|
||||
----
|
||||
====
|
||||
|
||||
The `value` of the idempotent entry may be some expiration date, after which that entry should be removed from _Metadata Store_ by some scheduled reaper.
|
||||
The `value` of the idempotent entry may be an expiration date, after which that entry should be removed from metadata store by some scheduled reaper.
|
||||
|
||||
Also see <<idempotent-receiver>>.
|
||||
See also "`<<idempotent-receiver>>`".
|
||||
|
||||
[[metadatastore-listener]]
|
||||
==== MetadataStoreListener
|
||||
==== `MetadataStoreListener`
|
||||
|
||||
Some metadata stores (currently only zookeeper) support registering a listener to receive events when items change.
|
||||
Some metadata stores (currently only zookeeper) support registering a listener to receive events when items change, as the following example shows:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
public interface MetadataStoreListener {
|
||||
@@ -80,6 +82,7 @@ public interface MetadataStoreListener {
|
||||
void onUpdate(String key, String newValue);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
See the javadocs for more information.
|
||||
The `MetadataStoreListenerAdapter` can be subclassed if you are only interested in a subset of events.
|
||||
See the https://docs.spring.io/spring-integration/api/org/springframework/integration/metadata/MetadataStoreListenerAdapter.html[Javadoc] for more information.
|
||||
The `MetadataStoreListenerAdapter` can be subclassed if you are interested only in a subset of events.
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
[[metrics-management]]
|
||||
=== Metrics and Management
|
||||
|
||||
This section describes how to capture metrics for Spring Integration. In recent versions, we have relied more on Micrometer (see http://micrometer.io), and we plan to use Micrometer even more in future releases.
|
||||
|
||||
[[configuring-metrics-capture]]
|
||||
==== Configuring Metrics Capture
|
||||
|
||||
NOTE: Prior to _version 4.2_ metrics were only available when JMX was enabled.
|
||||
See <<jmx>>.
|
||||
NOTE: Prior to version 4.2, metrics were only available when JMX was enabled.
|
||||
See "`<<jmx>>`".
|
||||
|
||||
To enable `MessageSource`, `MessageChannel` and `MessageHandler` metrics, add an `<int:management/>` bean to the
|
||||
application context, or annotate one of your `@Configuration` classes with `@EnableIntegrationManagement`.
|
||||
`MessageSource` s only maintain counts, `MessageChannel` s and `MessageHandler` s maintain duration statistics in
|
||||
addition to counts.
|
||||
See <<mgmt-channel-features>> and <<mgmt-handler-features>> below.
|
||||
To enable `MessageSource`, `MessageChannel`, and `MessageHandler` metrics, add an `<int:management/>` bean to the application context (in XML) or annotate one of your `@Configuration` classes with `@EnableIntegrationManagement` (in Java).
|
||||
`MessageSource` instances maintain only counts, `MessageChannel` instances and `MessageHandler` instances maintain duration statistics in addition to counts.
|
||||
See "`<<mgmt-channel-features>>`" and "`<<mgmt-handler-features>>`", later in this chapter.
|
||||
|
||||
This causes the automatic registration of the `IntegrationManagementConfigurer` bean in the application context.
|
||||
Only one such bean can exist in the context and it must have the bean name `integrationManagementConfigurer`
|
||||
if registered manually via a `<bean/>` definition.
|
||||
This bean applies it's configuration to beans after all beans in the context have been instantiated.
|
||||
Doing so causes the automatic registration of the `IntegrationManagementConfigurer` bean in the application context.
|
||||
Only one such bean can exist in the context, and, if registered manually via a `<bean/>` definition, it must have the bean name set to `integrationManagementConfigurer`.
|
||||
This bean applies its configuration to beans after all beans in the context have been instantiated.
|
||||
|
||||
In addition to metrics, you can control *debug* logging in the main message flow.
|
||||
It has been found that in very high volume applications, even calls to `isDebugEnabled()` can be quite expensive with
|
||||
some logging subsystems.
|
||||
You can disable all such logging to avoid this overhead; exception logging (debug or otherwise) are not affected
|
||||
by this setting.
|
||||
In addition to metrics, you can control debug logging in the main message flow.
|
||||
In very high volume applications, even calls to `isDebugEnabled()` can be quite expensive with some logging subsystems.
|
||||
You can disable all such logging to avoid this overhead.
|
||||
Exception logging (debug or otherwise) is not affected by this setting.
|
||||
|
||||
A number of options are available:
|
||||
The following listing shows the available options for controlling logging:
|
||||
|
||||
====
|
||||
[source, xml]
|
||||
----
|
||||
<int:management
|
||||
@@ -51,128 +51,126 @@ public static class ContextConfiguration {
|
||||
...
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
<1> Set to `false` to disable all logging in the main message flow, regardless of the log system category settings.
|
||||
Set to 'true' to enable debug logging (if also enabled by the logging subsystem).
|
||||
Only applied if you have not explicitly configured the setting in a bean definition.
|
||||
Default `true`.
|
||||
The default is `true`.
|
||||
|
||||
<2> Enable or disable count metrics for components not matching one of the patterns in <4>.
|
||||
<2> Enable or disable count metrics for components that do not match one of the patterns in <4>.
|
||||
Only applied if you have not explicitly configured the setting in a bean definition.
|
||||
Default `false`.
|
||||
The default is `false`.
|
||||
|
||||
<3> Enable or disable statistical metrics for components not matching one of the patterns in <5>.
|
||||
<3> Enable or disable statistical metrics for components that do not match one of the patterns in <5>.
|
||||
Only applied if you have not explicitly configured the setting in a bean definition.
|
||||
Default 'false'.
|
||||
The default is 'false'.
|
||||
|
||||
<4> A comma-delimited list of patterns for beans for which counts should be enabled; negate the pattern with `!`.
|
||||
First match wins (positive or negative).
|
||||
In the unlikely event that you have a bean name starting with `!`, escape the `!` in the pattern: `\!foo` positively
|
||||
matches a bean named `!foo`.
|
||||
<4> A comma-delimited list of patterns for beans for which counts should be enabled.
|
||||
You can negate the pattern with `!`.
|
||||
First match (positive or negative) wins.
|
||||
In the unlikely event that you have a bean name starting with `!`, escape the `!` in the pattern.
|
||||
For example, `\!something` positively matches a bean named `!something`.
|
||||
|
||||
<5> A comma-delimited list of patterns for beans for which statistical metrics should be enabled; negate the pattern
|
||||
with `!`.
|
||||
First match wins (positive or negative).
|
||||
In the unlikely event that you have a bean name starting with `!`, escape the `!` in the pattern: `\!foo` positively
|
||||
matches a bean named `!foo`.
|
||||
Stats implies counts.
|
||||
<5> A comma-delimited list of patterns for beans for which statistical metrics should be enabled.
|
||||
You can negate the pattern\ with `!`.
|
||||
First match (positive or negative) wins.
|
||||
In the unlikely event that you have a bean name starting with `!`, escape the `!` in the pattern.
|
||||
`\!something` positively matches a bean named `!something`.
|
||||
The collection of statistics implies the collection of counts.
|
||||
|
||||
<6> A reference to a `MetricsFactory`.
|
||||
See <<mgmt-metrics-factory>>.
|
||||
See "`<<mgmt-metrics-factory>>`".
|
||||
|
||||
At runtime, counts and statistics can be obtained by calling `IntegrationManagementConfigurer` `getChannelMetrics`,
|
||||
`getHandlerMetrics` and `getSourceMetrics`, returning `MessageChannelMetrics`, `MessageHandlerMetrics` and
|
||||
`MessageSourceMetrics` respectively.
|
||||
At runtime, counts and statistics can be obtained by calling `getChannelMetrics`, `getHandlerMetrics` and `getSourceMetrics` (all from the `IntegrationManagementConfigurer` class), which return `MessageChannelMetrics`, `MessageHandlerMetrics`, and `MessageSourceMetrics`, respectively.
|
||||
|
||||
See the javadocs for complete information about these classes.
|
||||
See the https://docs.spring.io/spring-integration/api/index.html[Javadoc] for complete information about these classes.
|
||||
|
||||
When JMX is enabled (see <<jmx>>), these metrics are also exposed by the `IntegrationMBeanExporter`.
|
||||
When JMX is enabled (see "`<<jmx>>`"), `IntegrationMBeanExporter` also exposes these metrics.
|
||||
|
||||
[IMPORTANT]
|
||||
====
|
||||
`defaultLoggingEnabled`, `defaultCountsEnabled`, and `defaultStatsEnabled` are only applied if you have not explicitly configured the corresponding setting in a bean definition.
|
||||
====
|
||||
IMPORTANT:
|
||||
`defaultLoggingEnabled`, `defaultCountsEnabled`, and `defaultStatsEnabled` are applied only if you have not explicitly configured the corresponding setting in a bean definition.
|
||||
|
||||
Starting with _version 5.0.2_, the framework will automatically detect if there is a single `MetricsFactory` bean in the application context and use it instead of the default metrics factory.
|
||||
Starting with version 5.0.2, the framework automatically detects whether the application context has a single `MetricsFactory` bean and, if so, uses it instead of the default metrics factory.
|
||||
|
||||
[[micrometer-integration]]
|
||||
==== Micrometer Integration
|
||||
|
||||
Starting with _version 5.0.3_, the presence of a https://micrometer.io/[Micrometer] `MeterRegistry` in the application context will trigger support for Micrometer metrics in addition to the inbuilt metrics (inbuilt metrics will be removed in a future release).
|
||||
Starting with version 5.0.3, the presence of a https://micrometer.io/[Micrometer] `MeterRegistry` in the application context triggers support for Micrometer metrics in addition to the built-in metrics (note that built-in metrics will be removed in a future release).
|
||||
|
||||
IMPORTANT: Micrometer was first supported in _version 5.0.2_, but 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 using Micrometer, a minimum of version 5.0.4 is recommended since some of the changes in 5.0.4 were breaking API changes.
|
||||
IMPORTANT: Micrometer was first supported in version 5.0.2, but 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, a minimum of version 5.0.4 is recommended, since some of the changes in 5.0.4 were breaking API changes.
|
||||
|
||||
Simply add a `MeterRegistry` bean of choice to the application context.
|
||||
If the `IntegrationManagementConfigurer` detects exactly one `MeterRegistry` bean, it will configure a `MicrometerMetricsCaptor` bean with name `integrationMicrometerMetricsCaptor`.
|
||||
To use Micrometer, add one of the `MeterRegistry` beans to the application context.
|
||||
If the `IntegrationManagementConfigurer` detects exactly one `MeterRegistry` bean, it configures a `MicrometerMetricsCaptor` bean with a name of `integrationMicrometerMetricsCaptor`.
|
||||
|
||||
For each `MessageHandler` and `MessageChannel`, timers are registered.
|
||||
For each `MessageSource`, a counter is registered.
|
||||
|
||||
This only applies to objects that extend `AbstractMessageHandler`, `AbstractMessageChannel` and `AbstractMessageSource` respectively (which is the case for most framework components).
|
||||
This only applies to objects that extend `AbstractMessageHandler`, `AbstractMessageChannel`, and `AbstractMessageSource` (which is the case for most framework components).
|
||||
|
||||
With Micrometer metrics, the `statsEnabled` flag takes no effect, since statistics capture is delegated to Micrometer.
|
||||
The `countsEnabled` flag controls whether the Micrometer `Meter` s are updated when processing each message.
|
||||
With Micrometer metrics, the `statsEnabled` flag has no effect, since statistics capture is delegated to Micrometer.
|
||||
The `countsEnabled` flag controls whether the Micrometer `Meter` instances are updated when processing each message.
|
||||
|
||||
The `Timer` Meters for send operations on message channels have the following name/tags:
|
||||
The `Timer` Meters for send operations on message channels have the following names or tags:
|
||||
|
||||
- `name` : `spring.integration.send`
|
||||
- `tag` : `type:channel`
|
||||
- `tag` : `name:<componentName>`
|
||||
- `tag` : `result:(success|failure)`
|
||||
- `tag` : `exception:(none|exception simple class name)`
|
||||
- `description` : `Send processing time`
|
||||
* `name`: `spring.integration.send`
|
||||
* `tag`: `type:channel`
|
||||
* `tag`: `name:<componentName>`
|
||||
* `tag`: `result:(success|failure)`
|
||||
* `tag`: `exception:(none|exception simple class name)`
|
||||
* `description`: `Send processing time`
|
||||
|
||||
(A `failure` result with a `none` exception means the channel `send()` operation returned `false`).
|
||||
(A `failure` result with a `none` exception means the channel's `send()` operation returned `false`.)
|
||||
|
||||
The `Counter` Meters for receive operations on pollable message channels have the following names/tags:
|
||||
The `Counter` Meters for receive operations on pollable message channels have the following names or tags:
|
||||
|
||||
- `name` : `spring.integration.receive`
|
||||
- `tag` : `type:channel`
|
||||
- `tag` : `name:<componentName>`
|
||||
- `tag` : `result:(success|failure)`
|
||||
- `tag` : `exception:(none|exception simple class name)`
|
||||
- `description` : `Messages received`
|
||||
* `name`: `spring.integration.receive`
|
||||
* `tag`: `type:channel`
|
||||
* `tag`: `name:<componentName>`
|
||||
* `tag`: `result:(success|failure)`
|
||||
* `tag`: `exception:(none|exception simple class name)`
|
||||
* `description`: `Messages received`
|
||||
|
||||
The `Timer` Meters for operations on message handlers have the following name/tags:
|
||||
The `Timer` Meters for operations on message handlers have the following names or tags:
|
||||
|
||||
- `name` : `spring.integration.send`
|
||||
- `tag` : `type:handler`
|
||||
- `tag` : `name:<componentName>`
|
||||
- `tag` : `result:(success|failure)`
|
||||
- `tag` : `exception:(none|exception simple class name)`
|
||||
- `description` : `Send processing time`
|
||||
* `name`: `spring.integration.send`
|
||||
* `tag`: `type:handler`
|
||||
* `tag`: `name:<componentName>`
|
||||
* `tag`: `result:(success|failure)`
|
||||
* `tag`: `exception:(none|exception simple class name)`
|
||||
* `description`: `Send processing time`
|
||||
|
||||
The `Counter` meters for message sources have the following names/tags:
|
||||
|
||||
- `name` : `spring.integration.receive`
|
||||
- `tag` : `type:source`
|
||||
- `tag` : `name:<componentName>`
|
||||
- `tag` : `result:success`
|
||||
- `tag` : `exception:none`
|
||||
- `description` : `Messages received`
|
||||
* `name`: `spring.integration.receive`
|
||||
* `tag`: `type:source`
|
||||
* `tag`: `name:<componentName>`
|
||||
* `tag`: `result:success`
|
||||
* `tag`: `exception:none`
|
||||
* `description`: `Messages received`
|
||||
|
||||
In addition, there are three `Gauge` Meters:
|
||||
|
||||
`spring.integration.channels` - the number of `MessageChannels` in the application.
|
||||
`spring.integration.handlers` - the number of `MessageHandlers` in the application.
|
||||
`spring.integration.sources` - the number of `MessageSources` in the application.
|
||||
* `spring.integration.channels`: The number of `MessageChannels` in the application.
|
||||
* `spring.integration.handlers`: The number of `MessageHandlers` in the application.
|
||||
* `spring.integration.sources`: The number of `MessageSources` in the application.
|
||||
|
||||
|
||||
[[mgmt-channel-features]]
|
||||
==== MessageChannel Metric Features
|
||||
==== `MessageChannel` Metric Features
|
||||
|
||||
These legacy metrics will be removed in a future release; see <<micrometer-integration>>.
|
||||
These legacy metrics will be removed in a future release.
|
||||
See "`<<micrometer-integration>>`".
|
||||
|
||||
Message channels report metrics according to their concrete type.
|
||||
If you are looking at a `DirectChannel`, you will see statistics for the send operation.
|
||||
If it is a `QueueChannel`, you will also see statistics for the receive operation, as well as the count of messages that are currently buffered by this `QueueChannel`.
|
||||
In both cases there are some metrics that are simple counters (message count and error count), and some that are estimates of averages of interesting quantities.
|
||||
The algorithms used to calculate these estimates are described briefly in the section below.
|
||||
If you are looking at a `DirectChannel`, you see statistics for the send operation.
|
||||
If it is a `QueueChannel`, you also see statistics for the receive operation as well as the count of messages that are currently buffered by this `QueueChannel`.
|
||||
In both cases, some metrics are simple counters (message count and error count), and some are estimates of averages of interesting quantities.
|
||||
The algorithms used to calculate these estimates are described briefly in the following table.
|
||||
|
||||
.MessageChannel Metrics
|
||||
|
||||
|
||||
[cols="1,2,3", options="header"]
|
||||
|===
|
||||
| Metric Type
|
||||
@@ -191,35 +189,34 @@ Increases by one when an send results in an error.
|
||||
|
||||
| Duration
|
||||
| Send Duration (method execution time in milliseconds)
|
||||
| Exponential Moving Average with decay factor (10 by default).
|
||||
Average of the method execution time over roughly the last 10 (default) measurements.
|
||||
| Exponential moving average with decay factor (ten by default).
|
||||
Average of the method execution time over roughly the last ten (by default) measurements.
|
||||
|
||||
| Rate
|
||||
| Send Rate (number of operations per second)
|
||||
| Inverse of Exponential Moving Average of the interval between events with decay in time (lapsing over 60 seconds by default) and per measurement (last 10 events by default).
|
||||
| Inverse of Exponential moving average of the interval between events with decay in time (lapsing over 60 seconds by default) and per measurement (last ten events by default).
|
||||
|
||||
| Error Rate
|
||||
| Send Error Rate (number of errors per second)
|
||||
| Inverse of Exponential Moving Average of the interval between error events with decay in time (lapsing over 60 seconds by default) and per measurement (last 10 events by default).
|
||||
| Inverse of exponential moving average of the interval between error events with decay in time (lapsing over 60 seconds by default) and per measurement (last ten events by default).
|
||||
|
||||
| Ratio
|
||||
| Send Success Ratio (ratio of successful to total sends)
|
||||
| Estimate the success ratio as the Exponential Moving Average of the series composed of values 1 for success and 0 for failure (decaying as per the rate measurement over time and events by default).
|
||||
Error ratio is 1 - success ratio.
|
||||
| Estimate the success ratio as the exponential moving average of the series composed of values (1 for success and 0 for failure, decaying as per the rate measurement over time and events by default).
|
||||
The error ratio is: 1 - success ratio.
|
||||
|
||||
|===
|
||||
|
||||
[[mgmt-handler-features]]
|
||||
==== MessageHandler Metric Features
|
||||
|
||||
These legacy metrics will be removed in a future release; see <<micrometer-integration>>.
|
||||
These legacy metrics will be removed in a future release. See <<micrometer-integration>>.
|
||||
|
||||
The following table shows the statistics maintained for message handlers.
|
||||
Some metrics are simple counters (message count and error count), and one is an estimate of averages of send duration.
|
||||
The algorithms used to calculate these estimates are described briefly in the table below:
|
||||
The algorithms used to calculate these estimates are described briefly in the following table:
|
||||
|
||||
.MessageHandlerMetrics
|
||||
|
||||
[cols="1,2,3", options="header"]
|
||||
|===
|
||||
| Metric Type
|
||||
@@ -242,8 +239,8 @@ Increases by one when an invocation results in an error.
|
||||
|
||||
| Duration
|
||||
| Handle Duration (method execution time in milliseconds)
|
||||
| Exponential Moving Average with decay factor (10 by default).
|
||||
Average of the method execution time over roughly the last 10 (default) measurements.
|
||||
| Exponential moving average with decay factor (ten by default).
|
||||
Average of the method execution time over roughly the last ten (default) measurements.
|
||||
|
||||
|===
|
||||
|
||||
@@ -251,44 +248,40 @@ Average of the method execution time over roughly the last 10 (default) measurem
|
||||
==== Time-Based Average Estimates
|
||||
|
||||
A feature of the time-based average estimates is that they decay with time if no new measurements arrive.
|
||||
To help interpret the behaviour over time, the time (in seconds) since the last measurement is also exposed as a metric.
|
||||
To help interpret the behavior over time, the time (in seconds) since the last measurement is also exposed as a metric.
|
||||
|
||||
There are two basic exponential models: decay per measurement (appropriate for duration and anything where the number of measurements is part of the metric), and decay per time unit (more suitable for rate measurements where the time in between measurements is part of the metric).
|
||||
Both models depend on the fact that
|
||||
|
||||
`S(n) = sum(i=0,i=n) w(i) x(i)` has a special form when `w(i) = r^i`, with `r=constant`:
|
||||
|
||||
`S(n) = x(n) + r S(n-1)` (so you only have to store `S(n-1)`, not the whole series `x(i)`, to generate a new metric estimate from the last measurement).
|
||||
There are two basic exponential models: decay per measurement (appropriate for duration and anything where the number of measurements is part of the metric) and decay per time unit (more suitable for rate measurements where the time in between measurements is part of the metric).
|
||||
Both models depend on the fact that `S(n) = sum(i=0,i=n) w(i) x(i)` has a special form when `w(i) = r^i`, with `r=constant`: `S(n) = x(n) + r S(n-1)` (so you only have to store `S(n-1)` (not the whole series `x(i)`) to generate a new metric estimate from the last measurement).
|
||||
The algorithms used in the duration metrics use `r=exp(-1/M)` with `M=10`.
|
||||
The net effect is that the estimate `S(n)` is more heavily weighted to recent measurements and is composed roughly of the last `M` measurements.
|
||||
So `M` is the "window" or lapse rate of the estimate In the case of the vanilla moving average, `i` is a counter over the number of measurements.
|
||||
In the case of the rate we interpret `i` as the elapsed time, or a combination of elapsed time and a counter (so the metric estimate contains contributions roughly from the last `M` measurements and the last `T` seconds).
|
||||
|
||||
The net effect is that the estimate, `S(n)`, is more heavily weighted to recent measurements and is composed roughly of the last `M` measurements.
|
||||
So `M` is the "`window`" or lapse rate of the estimate.
|
||||
For the vanilla moving average, `i` is a counter over the number of measurements.
|
||||
For the rate, we interpret `i` as the elapsed time or a combination of elapsed time and a counter (so the metric estimate contains contributions roughly from the last `M` measurements and the last `T` seconds).
|
||||
|
||||
[[mgmt-metrics-factory]]
|
||||
==== Metrics Factory
|
||||
|
||||
A strategy interface `MetricsFactory` has been introduced allowing you to provide custom channel metrics for your
|
||||
`MessageChannel` s and `MessageHandler` s.
|
||||
By default, a `DefaultMetricsFactory` provides default implementation of `MessageChannelMetrics` and
|
||||
`MessageHandlerMetrics` which are described above.
|
||||
To override the default `MetricsFactory` configure it as described above, by providing a reference to your
|
||||
`MetricsFactory` bean instance.
|
||||
You can either customize the default implementations as described in the next bullet, or provide completely different
|
||||
implementations by extending `AbstractMessageChannelMetrics` and/or `AbstractMessageHandlerMetrics`.
|
||||
A strategy interface `MetricsFactory` has been introduced to let you provide custom channel metrics for your `MessageChannel` instances and `MessageHandler` instances.
|
||||
By default, a `DefaultMetricsFactory` provides a default implementation of `MessageChannelMetrics` and `MessageHandlerMetrics`, <<configuring-metrics-capture,described earlier>>.
|
||||
To override the default `MetricsFactory`, configure it as <<configuring-metrics-capture,described earlier>>, by providing a reference to your `MetricsFactory` bean instance.
|
||||
You can either customize the default implementations, as described in the next section, or provide completely different
|
||||
implementations by extending `AbstractMessageChannelMetrics` or `AbstractMessageHandlerMetrics`.
|
||||
|
||||
Also see <<micrometer-integration>>.
|
||||
See also "`<<micrometer-integration>>`".
|
||||
|
||||
In addition to the default metrics factory described above, the framework provides the `AggregatingMetricsFactory`.
|
||||
This factory creates `AggregatingMessageChannelMetrics` and `AggregatingMessageHandlerMetrics`.
|
||||
In very high volume scenarios, the cost of capturing statistics can be prohibitive (2 calls to the system time and
|
||||
storing the data for each message).
|
||||
In addition to the default metrics factory <<configuring-metrics-capture,described earlier>>, the framework provides the `AggregatingMetricsFactory`.
|
||||
This factory creates `AggregatingMessageChannelMetrics` and `AggregatingMessageHandlerMetrics` instances.
|
||||
In very high volume scenarios, the cost of capturing statistics can be prohibitive (the time to make two calls to the system and
|
||||
store the data for each message).
|
||||
The aggregating metrics aggregate the response time over a sample of messages.
|
||||
This can save significant CPU time.
|
||||
|
||||
CAUTION: The statistics will be skewed if messages arrive in bursts.
|
||||
CAUTION: The statistics are likely to be skewed if messages arrive in bursts.
|
||||
These metrics are intended for use with high, constant-volume, message rates.
|
||||
|
||||
The following example shows how to define an aggregrating metrics factory:
|
||||
|
||||
====
|
||||
[source, xml]
|
||||
----
|
||||
<bean id="aggregatingMetricsFactory"
|
||||
@@ -296,23 +289,24 @@ These metrics are intended for use with high, constant-volume, message rates.
|
||||
<constructor-arg value="1000" /> <!-- sample size -->
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The above configuration aggregates the duration over 1000 messages.
|
||||
Counts (send, error) are maintained per-message but the statistics are per 1000 messages.
|
||||
The preceding configuration aggregates the duration over 1000 messages.
|
||||
Counts (send and error) are maintained per-message, but the statistics are per 1000 messages.
|
||||
|
||||
* *Customizing the Default Channel/Handler Statistics*
|
||||
===== Customizing the Default Channel and Handler Statistics
|
||||
|
||||
See <<mgmt-statistics>> and the Javadocs for the `ExponentialMovingAverage*` classes for more information about these
|
||||
values.
|
||||
See "`<<mgmt-statistics>>`" and the https://docs.spring.io/spring-integration/api/index.html[Javadoc] for the `ExponentialMovingAverage*` classes for more information about these values.
|
||||
|
||||
By default, the `DefaultMessageChannelMetrics` and `DefaultMessageHandlerMetrics` use a `window` of 10 measurements,
|
||||
a rate period of 1 second (rate per second) and a decay lapse period of 1 minute.
|
||||
By default, the `DefaultMessageChannelMetrics` and `DefaultMessageHandlerMetrics` use a "`window`" of ten measurements,
|
||||
a rate period of one second (meaning rate per second) and a decay lapse period of one minute.
|
||||
|
||||
If you wish to override these defaults, you can provide a custom `MetricsFactory` that returns appropriately configured
|
||||
metrics and provide a reference to it to the MBean exporter as described above.
|
||||
metrics and provide a reference to it in the MBean exporter, as <<mgmt-metrics-factory,described earlier>>.
|
||||
|
||||
Example:
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public static class CustomMetrics implements MetricsFactory {
|
||||
@@ -333,21 +327,20 @@ public static class CustomMetrics implements MetricsFactory {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
===== Advanced Customization
|
||||
|
||||
* *Advanced Customization*
|
||||
The customizations described earlier are wholesale and apply to all appropriate beans exported by the MBean exporter.
|
||||
This is the extent of customization available when you use XML configuration.
|
||||
|
||||
The customizations described above are wholesale and will apply to all appropriate beans exported by the MBean exporter.
|
||||
This is the extent of customization available using XML configuration.
|
||||
|
||||
Individual beans can be provided with different implementations using java `@Configuration` or programmatically at
|
||||
runtime, after the application context has been refreshed, by invoking the `configureMetrics` methods on
|
||||
Individual beans can be provided with different implementations using by Java `@Configuration` or programmatically at
|
||||
runtime (after the application context has been refreshed) by invoking the `configureMetrics` methods on
|
||||
`AbstractMessageChannel` and `AbstractMessageHandler`.
|
||||
|
||||
===== Performance Improvement
|
||||
|
||||
* *Performance Improvement*
|
||||
|
||||
Previously, the time-based metrics (see <<mgmt-statistics>>) were calculated in real time.
|
||||
Previously, the time-based metrics (see "`<<mgmt-statistics>>`") were calculated in real time.
|
||||
The statistics are now calculated when retrieved instead.
|
||||
This resulted in a significant performance improvement, at the expense of a small amount of additional memory for each statistic.
|
||||
As discussed in the bullet above, the statistics can be disabled altogether, while retaining the MBean allowing the invocation of `Lifecycle` methods.
|
||||
As <<configuring-metrics-capture,discussed earlier>>, you can disable the statistics altogether while retaining the MBean that allows the invocation of `Lifecycle` methods.
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
[[mongodb]]
|
||||
== MongoDb Support
|
||||
|
||||
As of version 2.1 Spring Integration introduces support for http://www.mongodb.org/[MongoDB]: a _"high-performance, open source, document-oriented database"_.
|
||||
This support comes in the form of a MongoDB-based MessageStore.
|
||||
Version 2.1 introduced support for http://www.mongodb.org/[MongoDB]: a "`high-performance, open source, document-oriented database`".
|
||||
This support comes in the form of a MongoDB-based `MessageStore`.
|
||||
|
||||
[[mongodb-intro]]
|
||||
=== Introduction
|
||||
|
||||
To download, install, and run MongoDB please refer to the http://www.mongodb.org/downloads[MongoDB documentation].
|
||||
To download, install, and run MongoDB, see the http://www.mongodb.org/downloads[MongoDB documentation].
|
||||
|
||||
[[mongodb-connection]]
|
||||
=== Connecting to MongoDb
|
||||
|
||||
To begin interacting with MongoDB you first need to connect to it.
|
||||
Spring Integration builds on the support provided by another Spring project, http://projects.spring.io/spring-data-mongodb/[Spring Data MongoDB], which provides a factory class called `MongoDbFactory` that simplifies integration with the MongoDB Client API.
|
||||
To begin interacting with MongoDB, you first need to connect to it.
|
||||
Spring Integration builds on the support provided by another Spring project, http://projects.spring.io/spring-data-mongodb/[Spring Data MongoDB].
|
||||
It provides a factory class called `MongoDbFactory`, which simplifies integration with the MongoDB Client API.
|
||||
|
||||
_MongoDbFactory_
|
||||
==== Using `MongoDbFactory`
|
||||
|
||||
To connect to MongoDB you can use an implementation of the `MongoDbFactory` interface:
|
||||
To connect to MongoDB you can use an implementation of the `MongoDbFactory` interface.
|
||||
The following listing shows the `MongoDbFactory` interface:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public interface MongoDbFactory {
|
||||
@@ -41,16 +42,20 @@ public interface MongoDbFactory {
|
||||
DB getDb(String dbName) throws DataAccessException;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The example below shows `SimpleMongoDbFactory`, the out-of-the-box implementation:
|
||||
The following example shows how to use `SimpleMongoDbFactory`, the out-of-the-box implementation, in Java:
|
||||
|
||||
In Java:
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
MongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(new Mongo(), "test");
|
||||
----
|
||||
====
|
||||
|
||||
Or in Spring's XML configuration:
|
||||
The following example shows how to use `SimpleMongoDbFactory` in XML configuration:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="mongoDbFactory" class="o.s.data.mongodb.core.SimpleMongoDbFactory">
|
||||
@@ -60,20 +65,24 @@ Or in Spring's XML configuration:
|
||||
<constructor-arg value="test"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
As you can see `SimpleMongoDbFactory` takes two arguments: 1) a `Mongo` instance and 2) a String specifying the name of the database.
|
||||
If you need to configure properties such as `host`, `port`, etc, you can pass those using one of the constructors provided by the underlying `Mongo` class.
|
||||
For more information on how to configure MongoDB, please refer to the http://docs.spring.io/spring-data/data-mongo/docs/current/reference/html/[Spring-Data-MongoDB] reference.
|
||||
`SimpleMongoDbFactory` takes two arguments: a `Mongo` instance and a `String` that specifies the name of the database.
|
||||
If you need to configure properties such as `host`, `port`, and others, you can pass those by using one of the constructors provided by the underlying `Mongo` class.
|
||||
For more information on how to configure MongoDB, see the http://docs.spring.io/spring-data/data-mongo/docs/current/reference/html/[Spring-Data-MongoDB] reference.
|
||||
|
||||
[[mongodb-message-store]]
|
||||
=== MongoDB Message Store
|
||||
|
||||
As described in EIP, a http://www.eaipatterns.com/MessageStore.html[Message Store] allows you to persist Messages.
|
||||
This can be very useful when dealing with components that have a capability to buffer messages (_QueueChannel, Aggregator, Resequencer_, etc.) if reliability is a concern.
|
||||
In Spring Integration, the `MessageStore` strategy also provides the foundation for the http://www.eaipatterns.com/StoreInLibrary.html[ClaimCheck] pattern, which is described in EIP as well.
|
||||
As described in the _Enterprise Integration Patterns_ (EIP) book, a http://www.eaipatterns.com/MessageStore.html[Message Store] lets you persist messages.
|
||||
Doing so can be useful when dealing with components that have the ability to buffer messages (`QueueChannel`, `aggregator`, `resequencer`, and others.) if reliability is a concern.
|
||||
In Spring Integration, the `MessageStore` strategy also provides the foundation for the http://www.eaipatterns.com/StoreInLibrary.html[claim check] pattern, which is described in EIP as well.
|
||||
|
||||
Spring Integration's MongoDB module provides the `MongoDbMessageStore` which is an implementation of both the `MessageStore` strategy (mainly used by the _ClaimCheck_ pattern) and the `MessageGroupStore` strategy (mainly used by the _Aggregator_ and _Resequencer_ patterns).
|
||||
Spring Integration's MongoDB module provides the `MongoDbMessageStore`, which is an implementation of both the `MessageStore` strategy (mainly used by the claim check pattern) and the `MessageGroupStore` strategy (mainly used by the aggregator and resequencer patterns).
|
||||
|
||||
The following example configures a `MongoDbMessageStore` to use a `QueueChannel` and an `aggregator`:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="mongoDbMessageStore" class="o.s.i.mongodb.store.MongoDbMessageStore">
|
||||
@@ -87,40 +96,43 @@ Spring Integration's MongoDB module provides the `MongoDbMessageStore` which is
|
||||
<int:aggregator input-channel="inputChannel" output-channel="outputChannel"
|
||||
message-store="mongoDbMessageStore"/>
|
||||
----
|
||||
====
|
||||
|
||||
Above is a sample `MongoDbMessageStore` configuration that shows its usage by a _QueueChannel_ and an _Aggregator_.
|
||||
As you can see it is a simple bean configuration, and it expects a `MongoDbFactory` as a constructor argument.
|
||||
The preceding example is a simple bean configuration, and it expects a `MongoDbFactory` as a constructor argument.
|
||||
|
||||
The `MongoDbMessageStore` expands the `Message` as a Mongo document with all nested properties using the Spring Data Mongo Mapping mechanism.
|
||||
It is useful when you need to have access to the `payload` or `headers` for auditing or analytics, for example, against stored messages.
|
||||
The `MongoDbMessageStore` expands the `Message` as a Mongo document with all nested properties by using the Spring Data Mongo mapping mechanism.
|
||||
It is useful when you need to have access to the `payload` or `headers` for auditing or analytics -- for example, against stored messages.
|
||||
|
||||
IMPORTANT: The `MongoDbMessageStore` uses a custom `MappingMongoConverter` implementation to store `Message` s as MongoDB documents and there are some limitations for the properties (`payload` and `header` values) of the `Message`.
|
||||
For example, there is no ability to configure custom converters for complex domain `payload` s or `header` values.
|
||||
Or to provide a custom `MongoTemplate` (or `MappingMongoConverter`).
|
||||
To achieve these capabilities, an alternative MongoDB `MessageStore` implementation has been introduced; see next paragraph.
|
||||
IMPORTANT: The `MongoDbMessageStore` uses a custom `MappingMongoConverter` implementation to store `Message` instances as MongoDB documents, and there are some limitations for the properties (`payload` and `header` values) of the `Message`.
|
||||
For example, there is no ability to configure custom converters for complex domain `payload` instances or `header` values.
|
||||
There is also no way to provide a custom `MongoTemplate` (or `MappingMongoConverter`).
|
||||
To achieve these capabilities, an alternative MongoDB `MessageStore` implementation has been introduced (we describe it next).
|
||||
|
||||
_Spring Integration 3.0_ introduced the `ConfigurableMongoDbMessageStore` - `MessageStore` and `MessageGroupStore` implementation.
|
||||
This class can receive, as a constructor argument, a `MongoTemplate`, with which you can configure with a custom `WriteConcern`, for example.
|
||||
Another constructor requires a `MappingMongoConverter`, and a `MongoDbFactory`, which allows you to provide some custom conversions for `Message` s and their properties.
|
||||
Note, by default, the `ConfigurableMongoDbMessageStore` uses standard Java serialization to write/read `Message` s to/from MongoDB (see `MongoDbMessageBytesConverter`) and relies on default values for other properties from `MongoTemplate`, which is built from the provided `MongoDbFactory` and `MappingMongoConverter`.
|
||||
Spring Integration 3.0 introduced the `ConfigurableMongoDbMessageStore`. It implements both the `MessageStore` and `MessageGroupStore` interfaces.
|
||||
This class can receive, as a constructor argument, a `MongoTemplate`, with which you can, for example, configure a custom `WriteConcern`.
|
||||
Another constructor requires a `MappingMongoConverter` and a `MongoDbFactory`, which lets you provide some custom conversions for `Message` instances and their properties.
|
||||
Note that, by default, the `ConfigurableMongoDbMessageStore` uses standard Java serialization to write and read `Message` instances to and from MongoDB (see `MongoDbMessageBytesConverter`) and relies on default values for other properties from `MongoTemplate`.
|
||||
It builds a `MongoTemplate` from the provided `MongoDbFactory` and `MappingMongoConverter`.
|
||||
The default name for the collection stored by the `ConfigurableMongoDbMessageStore` is `configurableStoreMessages`.
|
||||
It is recommended to use this implementation for robust and flexible solutions when messages contain complex data types.
|
||||
We recommend using this implementation to create robust and flexible solutions when messages contain complex data types.
|
||||
|
||||
[[mongodb-priority-channel-message-store]]
|
||||
==== MongoDB Channel Message Store
|
||||
|
||||
Starting with _version 4.0_, the new `MongoDbChannelMessageStore` has been introduced; it is an optimized `MessageGroupStore` for use in `QueueChannel` s.
|
||||
With `priorityEnabled = true`, it can be used in `<int:priority-queue>` s to achieve _priority_ order polling for persisted messages.
|
||||
The _priority_ MongoDB document field is populated from the `IntegrationMessageHeaderAccessor.PRIORITY` (`priority`) message header.
|
||||
Version 4.0 introduced the new `MongoDbChannelMessageStore`.
|
||||
It is an optimized `MessageGroupStore` for use in `QueueChannel` instances.
|
||||
With `priorityEnabled = true`, you can use it in `<int:priority-queue>` instances to achieve priority-order polling for persisted messages.
|
||||
The priority MongoDB document field is populated from the `IntegrationMessageHeaderAccessor.PRIORITY` (`priority`) message header.
|
||||
|
||||
In addition, all MongoDB `MessageStore` s now have a `sequence` field for MessageGroup documents.
|
||||
In addition, all MongoDB `MessageStore` instances now have a `sequence` field for `MessageGroup` documents.
|
||||
The `sequence` value is the result of an `$inc` operation for a simple `sequence` document from the same collection, which is created on demand.
|
||||
The `sequence` field is used in `poll` operations to provide first-in-first-out (FIFO) message order (within priority if configured) when messages are stored within the same millisecond.
|
||||
The `sequence` field is used in `poll` operations to provide first-in-first-out (FIFO) message order (within priority, if configured) when messages are stored within the same millisecond.
|
||||
|
||||
NOTE: It is not recommended to use the same `MongoDbChannelMessageStore` bean for priority and non-priority, because the `priorityEnabled` option applies to the entire store.
|
||||
NOTE: We do not recommend using the same `MongoDbChannelMessageStore` bean for priority and non-priority, because the `priorityEnabled` option applies to the entire store.
|
||||
However, the same `collection` can be used for both `MongoDbChannelMessageStore` types, because message polling from the store is sorted and uses indexes.
|
||||
To configure that scenario, simply extend one message store bean from the other:
|
||||
To configure that scenario, you can extend one message store bean from the other, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="channelStore" class="o.s.i.mongodb.store.MongoDbChannelMessageStore">
|
||||
@@ -139,24 +151,26 @@ To configure that scenario, simply extend one message store bean from the other:
|
||||
<int:priority-queue message-store="priorityStore"/>
|
||||
</int:channel>
|
||||
----
|
||||
====
|
||||
|
||||
[[mongodb-metadata-store]]
|
||||
==== MongoDB Metadata Store
|
||||
|
||||
As of _Spring Integration 4.2_, a new MongoDB-based `MetadataStore` (<<metadata-store>>) implementation is available.
|
||||
The `MongoDbMetadataStore` can be used to maintain metadata state across application restarts.
|
||||
This new `MetadataStore` implementation can be used with adapters such as:
|
||||
Spring Integration 4.2 introduced a new MongoDB-based `MetadataStore` (see "`<<metadata-store>>`") implementation.
|
||||
You can use the `MongoDbMetadataStore` to maintain metadata state across application restarts.
|
||||
You can use this new `MetadataStore` implementation with adapters such as:
|
||||
|
||||
* <<twitter-inbound>>
|
||||
* <<feed-inbound-channel-adapter>>
|
||||
* <<file-reading>>
|
||||
* <<ftp-inbound>>
|
||||
* <<sftp-inbound>>
|
||||
* <<twitter-inbound,Twitter>>
|
||||
* <<feed-inbound-channel-adapter,Feed>>
|
||||
* <<file-reading,File>>
|
||||
* <<ftp-inbound,FTP>>
|
||||
* <<sftp-inbound,SFTP>>
|
||||
|
||||
In order to instruct these adapters to use the new `MongoDbMetadataStore`, simply declare a Spring bean using the
|
||||
bean name *metadataStore*. The _Twitter Inbound Channel Adapter_ and the _Feed Inbound Channel Adapter_ will both
|
||||
automatically pick up and use the declared `MongoDbMetadataStore`:
|
||||
To instruct these adapters to use the new `MongoDbMetadataStore`, declare a Spring bean with a bean name of `metadataStore`.
|
||||
The Twitter inbound channel adapter and the feed inbound channel adapter both automatically pick up and use the declared `MongoDbMetadataStore`.
|
||||
The following example shows how to declare a bean with a name of `metadataStore`:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
@@ -164,16 +178,18 @@ public MetadataStore metadataStore(MongoDbFactory factory) {
|
||||
return new MongoDbMetadataStore(factory, "integrationMetadataStore");
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The `MongoDbMetadataStore` also implements `ConcurrentMetadataStore`, allowing it to be reliably shared across multiple
|
||||
application instances where only one instance will be allowed to store or modify a key's value.
|
||||
All these operations are _atomic_ via MongoDB guarantees.
|
||||
The `MongoDbMetadataStore` also implements `ConcurrentMetadataStore`, letting it be reliably shared across multiple application instances, where only one instance is allowed to store or modify a key's value.
|
||||
All these operations are atomic, thanks to MongoDB guarantees.
|
||||
|
||||
[[mongodb-inbound-channel-adapter]]
|
||||
=== MongoDB Inbound Channel Adapter
|
||||
|
||||
The _MongoDb Inbound Channel Adapter_ is a polling consumer that reads data from MongoDb and sends it as a Message payload.
|
||||
The MongoDB inbound channel adapter is a polling consumer that reads data from MongoDB and sends it as a `Message` payload.
|
||||
The following example shows how to configure a MongoDB inbound channel adapter:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-mongodb:inbound-channel-adapter id="mongoInboundAdapter"
|
||||
@@ -184,30 +200,29 @@ The _MongoDb Inbound Channel Adapter_ is a polling consumer that reads data from
|
||||
<int:poller fixed-rate="100"/>
|
||||
</int-mongodb:inbound-channel-adapter>
|
||||
----
|
||||
====
|
||||
|
||||
As you can see from the configuration above, you configure a _MongoDb Inbound Channel Adapter_ using the `inbound-channel-adapter` element, providing values for various attributes such as:
|
||||
As the preceding configuration shows, you configure a MongoDb inbound channel adapter by using the `inbound-channel-adapter` element and providing values for various attributes, such as:
|
||||
|
||||
* `query` - a JSON query (see http://www.mongodb.org/display/DOCS/Querying[MongoDb Querying])
|
||||
* `query-expression` - A SpEL expression that is evaluated to a JSON query String (as the `query` attribute above), or to an instance of `o.s.data.mongodb.core.query.Query`. Mutually exclusive with `query` attribute.
|
||||
* `entity-class` - the type of the payload object; if not supplied, a `com.mongodb.DBObject` will be returned.
|
||||
* `collection-name` or `collection-name-expression` - Identifies the name of the MongoDb collection to use.
|
||||
* `mongodb-factory` - reference to an instance of `o.s.data.mongodb.MongoDbFactory`
|
||||
* `mongo-template` - reference to an instance of `o.s.data.mongodb.core.MongoTemplate`
|
||||
|
||||
|
||||
|
||||
and other attributes that are common across all other inbound adapters (e.g., 'channel').
|
||||
* `query`: A JSON query (see http://www.mongodb.org/display/DOCS/Querying[MongoDB Querying])
|
||||
* `query-expression`: A SpEL expression that is evaluated to a JSON query string (as the `query` attribute above) or to an instance of `o.s.data.mongodb.core.query.Query`. Mutually exclusive with the `query` attribute.
|
||||
* `entity-class`: The type of the payload object. If not supplied, a `com.mongodb.DBObject` is returned.
|
||||
* `collection-name` or `collection-name-expression`: Identifies the name of the MongoDB collection to use.
|
||||
* `mongodb-factory`: Reference to an instance of `o.s.data.mongodb.MongoDbFactory`
|
||||
* `mongo-template`: Reference to an instance of `o.s.data.mongodb.core.MongoTemplate`
|
||||
* Other attributes that are common across all other inbound adapters (such as 'channel').
|
||||
|
||||
NOTE: You cannot set both `mongo-template` and `mongodb-factory`.
|
||||
|
||||
The example above is relatively simple and static since it has a literal value for the `query` and uses the default name for a `collection`.
|
||||
Sometimes you may need to change those values at runtime, based on some condition.
|
||||
To do that, simply use their `-expression` equivalents (`query-expression` and `collection-name-expression`) where the provided expression can be any valid SpEL expression.
|
||||
The preceding example is relatively simple and static, since it has a literal value for the `query` and uses the default name for a `collection`.
|
||||
Sometimes, you may need to change those values at runtime, based on some condition.
|
||||
To do so, use their `-expression` equivalents (`query-expression` and `collection-name-expression`), where the provided expression can be any valid SpEL expression.
|
||||
|
||||
Also, you may wish to do some post-processing to the successfully processed data that was read from the MongoDb.
|
||||
For example; you may want to move or remove a document after its been processed.
|
||||
You can do this using Transaction Synchronization feature that was added with Spring Integration 2.2.
|
||||
Also, you may wish to do some post-processing to the successfully processed data that was read from the MongoDB.
|
||||
For example; you may want to move or remove a document after it has been processed.
|
||||
You can do so by using that transaction synchronization feature Spring Integration 2.2 added, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-mongodb:inbound-channel-adapter id="mongoInboundAdapter"
|
||||
@@ -226,11 +241,15 @@ You can do this using Transaction Synchronization feature that was added with Sp
|
||||
channe="someChannel"/>
|
||||
</int:transaction-synchronization-factory>
|
||||
|
||||
<bean id="documentCleaner" class="foo.bar.DocumentCleaner"/>
|
||||
<bean id="documentCleaner" class="thing1.thing2.DocumentCleaner"/>
|
||||
|
||||
<bean id="transactionManager" class="o.s.i.transaction.PseudoTransactionManager"/>
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows the `DocumentCleaner` referenced in the preceding example:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class DocumentCleaner {
|
||||
@@ -244,30 +263,32 @@ public class DocumentCleaner {
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
As you can see from the above, all you need to do is declare your poller to be transactional with a `transactional` element.
|
||||
This element can reference a real transaction manager (for example if some other part of your flow invokes JDBC).
|
||||
If you don't have a 'real' transaction, you can use a `org.springframework.integration.transaction.PseudoTransactionManager` which is an implementation of Spring's `PlatformTransactionManager` and enables the use of the transaction synchronization features of the mongo adapter when there is no actual transaction.
|
||||
You can declare your poller to be transactional by using the `transactional` element.
|
||||
This element can reference a real transaction manager (for example, if some other part of your flow invokes JDBC).
|
||||
If you do not have a "`real`" transaction, you can use an instance of `o.s.i.transaction.PseudoTransactionManager`, which is an implementation of Spring's `PlatformTransactionManager` and enables the use of the transaction synchronization features of the Mongo adapter when there is no actual transaction.
|
||||
|
||||
IMPORTANT: This does NOT make MongoDB itself transactional, it simply allows the synchronization of actions to be taken before/after success (commit) or after failure (rollback).
|
||||
IMPORTANT: Doing so does not make MongoDB itself transactional. It lets the synchronization of actions be taken before or after success (commit) or after failure (rollback).
|
||||
|
||||
Once your poller is transactional all you need to do is set an instance of the `o.s.i.transaction.TransactionSynchronizationFactory` on the `transactional` element.
|
||||
`TransactionSynchronizationFactory` will create an instance of the `TransactionSynchronization`.
|
||||
For your convenience, we've exposed a default SpEL-based `TransactionSynchronizationFactory` which allows you to configure SpEL expressions, with their execution being coordinated (synchronized) with a transaction.
|
||||
Expressions for before-commit, after-commit, and after-rollback are supported, together with a channel for each where the evaluation result (if any) will be sent.
|
||||
For each sub-element you can specify `expression` and/or `channel` attributes.
|
||||
If only the `channel` attribute is present the received Message will be sent there as part of the particular synchronization scenario.
|
||||
If only the `expression` attribute is present and the result of an expression is a non-Null value, a Message with the result as the payload will be generated and sent to a default channel (NullChannel) and will appear in the logs (DEBUG).
|
||||
If you want the evaluation result to go to a specific channel add a `channel` attribute.
|
||||
If the result of an expression is null or void, no Message will be generated.
|
||||
Once your poller is transactional, you can set an instance of the `o.s.i.transaction.TransactionSynchronizationFactory` on the `transactional` element.
|
||||
`TransactionSynchronizationFactory` creates an instance of the `TransactionSynchronization`.
|
||||
For your convenience, we have exposed a default SpEL-based `TransactionSynchronizationFactory` that lets you configure SpEL expressions, with their execution being coordinated (synchronized) with a transaction.
|
||||
Expressions for before-commit, after-commit, and after-rollback events are supported, together with a channel for each event where the evaluation result (if any) is sent.
|
||||
For each child element, you can specify `expression` and `channel` attributes.
|
||||
If only the `channel` attribute is present, the received message is sent there as part of the particular synchronization scenario.
|
||||
If only the `expression` attribute is present and the result of an expression is a non-null value, a message with the result as the payload is generated and sent to a default channel (`NullChannel`) and appears in the logs (on the `DEBUG` level).
|
||||
If you want the evaluation result to go to a specific channel, add a `channel` attribute.
|
||||
If the result of an expression is null or void, no message is generated.
|
||||
|
||||
For more information about transaction synchronization, see <<transaction-synchronization>>.
|
||||
For more information about transaction synchronization, see "`<<transaction-synchronization>>`".
|
||||
|
||||
[[mongodb-outbound-channel-adapter]]
|
||||
=== MongoDB Outbound Channel Adapter
|
||||
|
||||
The _MongoDb Outbound Channel Adapter_ allows you to write the Message payload to a MongoDb document store
|
||||
The MongoDB outbound channel adapter lets you write the message payload to a MongoDB document store, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-mongodb:outbound-channel-adapter id="fullConfigWithCollectionExpression"
|
||||
@@ -275,56 +296,62 @@ The _MongoDb Outbound Channel Adapter_ allows you to write the Message payload t
|
||||
mongo-converter="mongoConverter"
|
||||
mongodb-factory="mongoDbFactory" />
|
||||
----
|
||||
====
|
||||
|
||||
As you can see from the configuration above, you configure a _MongoDb Outbound Channel Adapter_ using the `outbound-channel-adapter` element, providing values for various attributes such as:
|
||||
As the preceding configuration shows, you can configure a MongoDB outbound channel adapter by using the `outbound-channel-adapter` element, providing values for various attributes, such as:
|
||||
|
||||
* `collection-name` or `collection-name-expression` - Identifies the name of the MongoDb collection to use.
|
||||
* `mongo-converter` - reference to an instance of `o.s.data.mongodb.core.convert.MongoConverter` to assist with converting a raw java object to a JSON document representation
|
||||
* `mongodb-factory` - reference to an instance of `o.s.data.mongodb.MongoDbFactory`
|
||||
* `mongo-template` - reference to an instance of `o.s.data.mongodb.core.MongoTemplate` (NOTE: you can not have both mongo-template and mongodb-factory set)
|
||||
* `collection-name` or `collection-name-expression`: Identifies the name of the MongoDb collection to use.
|
||||
* `mongo-converter`: Reference to an instance of `o.s.data.mongodb.core.convert.MongoConverter` that assists with converting a raw Java object to a JSON document representation.
|
||||
* `mongodb-factory`: Reference to an instance of `o.s.data.mongodb.MongoDbFactory`.
|
||||
* `mongo-template`: Reference to an instance of `o.s.data.mongodb.core.MongoTemplate`.
|
||||
NOTE: you can not have both mongo-template and mongodb-factory set.
|
||||
* Other attributes that are common across all other inbound adapters (such as 'channel').
|
||||
|
||||
|
||||
|
||||
and other attributes that are common across all other inbound adapters (e.g., 'channel').
|
||||
|
||||
The example above is relatively simple and static since it has a literal value for the `collection-name`.
|
||||
Sometimes you may need to change this value at runtime based on some condition.
|
||||
To do that, simply use `collection-name-expression` where the provided expression can be any valid SpEL expression.
|
||||
The preceding example is relatively simple and static, since it has a literal value for the `collection-name`.
|
||||
Sometimes, you may need to change this value at runtime, based on some condition.
|
||||
To do that, use `collection-name-expression`, where the provided expression is any valid SpEL expression.
|
||||
|
||||
[[mongodb-outbound-gateway]]
|
||||
=== MongoDB Outbound Gateway
|
||||
|
||||
Starting with _version 5.0_, the MongoDb Outbound Gateway is provided and it allows you to query a database by sending a Message to its request channel.
|
||||
The gateway will then send the response to the reply channel.
|
||||
The Message payload and headers can be used to specify the query, as well as collection name.
|
||||
Version 5.0 introduced the MongoDB outbound gateway.
|
||||
It allows you query a database by sending a message to its request channel.
|
||||
The gateway thens send the response to the reply channel.
|
||||
You can use the message payload and headers to specify the query and the collection name, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-mongodb:outbound-gateway id="gatewayQuery"
|
||||
mongodb-factory="mongoDbFactory"
|
||||
mongo-converter="mongoConverter"
|
||||
query="{firstName: 'Bob'}"
|
||||
collection-name="foo"
|
||||
collection-name="myCollection"
|
||||
request-channel="in"
|
||||
reply-channel="out"
|
||||
entity-class="org.springframework.integration.mongodb.test.entity$Person"/>
|
||||
----
|
||||
====
|
||||
|
||||
* `collection-name` or `collection-name-expression` - identifies the name of the MongoDb collection to use;
|
||||
* `mongo-converter` - reference to an instance of `o.s.data.mongodb.core.convert.MongoConverter` to assist with converting a raw java object to a JSON document representation
|
||||
* `mongodb-factory` - reference to an instance of `o.s.data.mongodb.MongoDbFactory`
|
||||
* `mongo-template` - reference to an instance of `o.s.data.mongodb.core.MongoTemplate` (NOTE: you can not have both mongo-template and mongodb-factory set)
|
||||
* `entity-class` - the fully qualified name of the entity class to be passed to `find(..)` or `findOne(..)` method in MongoTemplate.
|
||||
If this attribute is not provided the default value is `org.bson.Document`;
|
||||
* `query` or `query-expression` - specifies the MongoDb query.
|
||||
Please refer to http://www.mongodb.org/display/DOCS/Querying[MongoDB documentation] for more query samples.
|
||||
* `collection-callback` - reference to an instance of `org.springframework.data.mongodb.core.CollectionCallback`
|
||||
(NOTE: you can not have both collection-callback and any of the query attributes).
|
||||
You can use the following attributes with a MongoDB outbound Gateway:
|
||||
|
||||
* `collection-name` or `collection-name-expression`: Identifies the name of the MongoDB collection to use.
|
||||
* `mongo-converter`: Reference to an instance of `o.s.data.mongodb.core.convert.MongoConverter` that assists with converting a raw Java object to a JSON document representation.
|
||||
* `mongodb-factory`: Reference to an instance of `o.s.data.mongodb.MongoDbFactory`.
|
||||
* `mongo-template`: Reference to an instance of `o.s.data.mongodb.core.MongoTemplate`.
|
||||
NOTE: you can not set both `mongo-template` and `mongodb-factory`.
|
||||
* `entity-class`: The fully qualified name of the entity class to be passed to the `find(..)` and `findOne(..)` methods in MongoTemplate.
|
||||
If this attribute is not provided, the default value is `org.bson.Document`.
|
||||
* `query` or `query-expression`: Specifies the MongoDB query.
|
||||
See the http://www.mongodb.org/display/DOCS/Querying[MongoDB documentation] for more query samples.
|
||||
* `collection-callback`: Reference to an instance of `org.springframework.data.mongodb.core.CollectionCallback`.
|
||||
NOTE: You can not have both `collection-callback` and any of the query attributes.
|
||||
|
||||
==== Configuring with Java Configuration
|
||||
|
||||
The following Spring Boot application provides an example of configuring the outbound gateway using Java configuration:
|
||||
The following Spring Boot application shows an example of how to configure the outbound gateway with Java configuration:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@@ -343,7 +370,7 @@ public class MongoDbJavaApplication {
|
||||
@ServiceActivator(inputChannel = "requestChannel")
|
||||
public MessageHandler mongoDbOutboundGateway() {
|
||||
MongoDbOutboundGateway gateway = new MongoDbOutboundGateway(this.mongoDbFactory);
|
||||
gateway.setCollectionNameExpressionString("'foo'");
|
||||
gateway.setCollectionNameExpressionString("'myCollection'");
|
||||
gateway.setQueryExpressionString("'{''name'' : ''Bob''}'");
|
||||
gateway.setExpectSingleResult(true);
|
||||
gateway.setEntityClass(Person.class);
|
||||
@@ -358,11 +385,13 @@ public class MongoDbJavaApplication {
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
==== Configuring with the Java DSL
|
||||
|
||||
The following Spring Boot application provides an example of configuring the Outbound Gateway using the Java DSL:
|
||||
The following Spring Boot application show an example of how to configure the outbound gateway with the Java DSL:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@@ -398,16 +427,18 @@ public class MongoDbJavaApplication {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Alternatively to the `query` and `query-expression` properties, you can specify other database operations through
|
||||
the `collectionCallback` property.
|
||||
As an alternate to the `query` and `query-expression` properties, you can specify other database operations by using the `collectionCallback` property.
|
||||
The following example specifies a count operation:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
private MongoDbOutboundGatewaySpec collectionCallbackOutboundGateway() {
|
||||
return MongoDb.outboundGateway(this.mongoDbFactory, this.mongoConverter)
|
||||
.collectionCallback(MongoCollection::count)
|
||||
.collectionName("foo");
|
||||
.collectionName("myCollection");
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
[[mqtt]]
|
||||
== MQTT Support
|
||||
|
||||
[[mqtt-intro]]
|
||||
=== Introduction
|
||||
|
||||
Spring Integration provides inbound and outbound channel adapters supporting the MQ Telemetry Transport (MQTT) protocol.
|
||||
Spring Integration provides inbound and outbound channel adapters to support the Message Queueing Telemetry Transport (MQTT) protocol.
|
||||
The current implementation uses the http://www.eclipse.org/paho/[Eclipse Paho MQTT Client] library.
|
||||
|
||||
Configuration of both adapters is achieved using the `DefaultMqttPahoClientFactory`.
|
||||
Refer to the Paho documentation for more information about configuration options.
|
||||
|
||||
NOTE: It is preferred to configure an `MqttConnectOptions` object and inject it into the factory, instead of setting the (deprecated) options on the factory itself.
|
||||
NOTE: We recommend configuring an `MqttConnectOptions` object and injecting it into the factory, instead of setting the (deprecated) options on the factory itself.
|
||||
|
||||
[[mqtt-inbound]]
|
||||
=== Inbound (message-driven) Channel Adapter
|
||||
=== Inbound (Message-driven) Channel Adapter
|
||||
|
||||
The inbound channel adapter is implemented by the `MqttPahoMessageDrivenChannelAdapter`.
|
||||
For convenience, it can be configured using the namespace.
|
||||
A minimal configuration might be:
|
||||
For convenience, you can configure it by using the namespace.
|
||||
A minimal configuration might be as follows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="clientFactory"
|
||||
@@ -38,9 +36,11 @@ A minimal configuration might be:
|
||||
client-factory="clientFactory"
|
||||
channel="output"/>
|
||||
----
|
||||
====
|
||||
|
||||
Attributes:
|
||||
The following listing shows the available attributes:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-mqtt:message-driven-channel-adapter id="oneTopicAdapter"
|
||||
@@ -56,85 +56,80 @@ Attributes:
|
||||
channel="out" />
|
||||
----
|
||||
|
||||
<1> The client id.
|
||||
|
||||
|
||||
<1> The client ID.
|
||||
<2> The broker URL.
|
||||
|
||||
<3> A comma delimited list of topics from which this adapter will receive messages.
|
||||
|
||||
|
||||
<4> A comma delimited list of QoS values.
|
||||
Can be a single value that is applied to all topics, or a value for each topic (in which case the lists must the same length).
|
||||
|
||||
|
||||
<3> A comma-separated list of topics from which this adapter receives messages.
|
||||
<4> A commap-separated list of QoS values.
|
||||
It can be a single value that is applied to all topics or a value for each topic (in which case, the lists must be the same length).
|
||||
<5> An `MqttMessageConverter` (optional).
|
||||
The default `DefaultPahoMessageConverter` produces a message with a `String` payload (by default) with the following headers: +
|
||||
`mqtt_topic` - the topic from which the message was received +
|
||||
`mqtt_duplicate` - true if the message is a duplicate +
|
||||
`mqtt_qos` - the quality of service +
|
||||
The `DefaultPahoMessageConverter` can be configured to return the raw `byte[]` in the payload by declaring it as a `<bean/>` and setting the `payloadAsBytes` property.
|
||||
|
||||
|
||||
By default, the default `DefaultPahoMessageConverter` produces a message with a `String` payload with the following headers:
|
||||
* `mqtt_topic`: The topic from which the message was received
|
||||
* `mqtt_duplicate`: `true` if the message is a duplicate
|
||||
* `mqtt_qos`: The quality of service
|
||||
You can configure the `DefaultPahoMessageConverter` to return the raw `byte[]` in the payload by declaring it as a `<bean/>` and setting the `payloadAsBytes` property to `true`.
|
||||
<6> The client factory.
|
||||
<7> The send timeout.
|
||||
It applies only if the channel might block (such as a bounded `QueueChannel` that is currently full).
|
||||
<8> The error channel.
|
||||
Downstream exceptions are sent to this channel, if supplied, in an `ErrorMessage`.
|
||||
The payload is a `MessagingException` that contains the failed message and cause.
|
||||
<9> The recovery interval.
|
||||
It controls the interval at which the adapter attempts to reconnect after a failure. It defaults to `10000ms` (ten seconds).
|
||||
====
|
||||
|
||||
NOTE: Starting with version 4.1, you can omit the URL.
|
||||
Instead, you can provide the server URIs in the `serverURIs` property of the `DefaultMqttPahoClientFactory`.
|
||||
Doing so enables, for example, connection to a highly available (HA) cluster.
|
||||
|
||||
<7> The send timeout - only applies if the channel might block (such as a bounded `QueueChannel` that is currently full).
|
||||
|
||||
|
||||
<8> The error channel - downstream exceptions will be sent to this channel, if supplied, in an `ErrorMessage`; the payload is a `MessagingException` containing the failed message and cause.
|
||||
|
||||
<9> The recovery interval - controls the interval at which the adapter will attempt to reconnect after
|
||||
a failure; it defaults to `10000ms` (ten seconds).
|
||||
|
||||
NOTE: Starting with _version 4.1_ the url can be omitted and, instead, the server URIs can be provided in the `serverURIs` property of the `DefaultMqttPahoClientFactory`.
|
||||
This enables, for example, connection to a highly available (HA) cluster.
|
||||
|
||||
Starting with _version 4.2.2_, an `MqttSubscribedEvent` is published when the adapter successfully subscribes to the
|
||||
topic(s).
|
||||
`MqttConnectionFailedEvent` s are published when the connection/subscription fails.
|
||||
Starting with version 4.2.2, an `MqttSubscribedEvent` is published when the adapter successfully subscribes to the
|
||||
topics.
|
||||
`MqttConnectionFailedEvent` events are published when the connection or subscription fails.
|
||||
These events can be received by a bean that implements `ApplicationListener`.
|
||||
|
||||
Also, a new property `recoveryInterval` controls the interval at which the adapter will attempt to reconnect after
|
||||
a failure; it defaults to `10000ms` (ten seconds).
|
||||
|
||||
Also, a new property called `recoveryInterval` controls the interval at which the adapter attempts to reconnect after
|
||||
a failure.
|
||||
It defaults to `10000ms` (ten seconds).
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Prior to _version 4.2.3_, the client always unsubscribed when the adapter was stopped.
|
||||
This was incorrect because if the client QOS is > 0, we need to keep the subscription active so that messages arriving
|
||||
while the adapter is stopped will be delivered on the next start.
|
||||
This also requires setting the `cleanSession` property on the client factory to `false` - it defaults to `true`.
|
||||
Prior to version 4.2.3, the client always unsubscribed when the adapter was stopped.
|
||||
This was incorrect because, if the client QOS is greater than 0, we need to keep the subscription active so that messages arriving
|
||||
while the adapter is stopped are delivered on the next start.
|
||||
This also requires setting the `cleanSession` property on the client factory to `false`.
|
||||
It defaults to `true`.
|
||||
|
||||
Starting with _version 4.2.3_, the adapter will not unsubscribe (by default) if the `cleanSession` property is `false`.
|
||||
Starting with version 4.2.3, the adapter does not unsubscribe (by default) if the `cleanSession` property is `false`.
|
||||
|
||||
This behavior can be overridden by setting the `consumerCloseAction` property on the factory.
|
||||
It can have values: `UNSUBSCRIBE_ALWAYS`, `UNSUBSCRIBE_NEVER`, and `UNSUBSCRIBE_CLEAN`.
|
||||
The latter (the default) will unsubscribe only if the `cleanSession` property is `true`.
|
||||
The latter (the default) unsubscribes only if the `cleanSession` property is `true`.
|
||||
|
||||
To revert to the pre-4.2.3 behavior, use `UNSUBSCRIBE_ALWAYS`.
|
||||
====
|
||||
|
||||
[IMPORTANT]
|
||||
====
|
||||
Starting with _version 5.0_, the `topic`, `qos` and `retained` properties are mapped to `.RECEIVED_...` headers (`MqttHeaders.RECEIVED_TOPIC`, `MqttHeaders.RECEIVED_QOS`, and `MqttHeaders.RECEIVED_RETAINED`), to avoid inadvertent propagation to an outbound message which (by default) uses the `MqttHeaders.TOPIC`, `MqttHeaders.QOS`, and `MqttHeaders.RETAINED` headers.
|
||||
Starting with version 5.0, the `topic`, `qos`, and `retained` properties are mapped to `.RECEIVED_...` headers (`MqttHeaders.RECEIVED_TOPIC`, `MqttHeaders.RECEIVED_QOS`, and `MqttHeaders.RECEIVED_RETAINED`), to avoid inadvertent propagation to an outbound message that (by default) uses the `MqttHeaders.TOPIC`, `MqttHeaders.QOS`, and `MqttHeaders.RETAINED` headers.
|
||||
====
|
||||
|
||||
==== Adding/Removing Topics at Runtime
|
||||
==== Adding and Removing Topics at Runtime
|
||||
|
||||
Starting with _version 4.1_, it is possible to programmatically change the topics to which the adapter is subscribed.
|
||||
Methods `addTopic()` and `removeTopic()` are provided.
|
||||
Starting with version 4.1, you can programmatically change the topics to which the adapter is subscribed.
|
||||
Spring Integration provides the `addTopic()` and `removeTopic()` methods.
|
||||
When adding topics, you can optionally specify the `QoS` (default: 1).
|
||||
You can also modify the topics by sending an appropriate message to a `<control-bus/>` with an appropriate payload: `"myMqttAdapter.addTopic('foo', 1)"`.
|
||||
You can also modify the topics by sending an appropriate message to a `<control-bus/>` with an appropriate payload -- for example: `"myMqttAdapter.addTopic('foo', 1)"`.
|
||||
|
||||
Stopping/starting the adapter has no effect on the topic list (it does *not* revert to the original settings in the configuration).
|
||||
The changes are not retained beyond the life cycle of the application context; a new application context will revert to the configured settings.
|
||||
Stopping and starting the adapter has no effect on the topic list (it does not revert to the original settings in the configuration).
|
||||
The changes are not retained beyond the life cycle of the application context.
|
||||
A new application context reverts to the configured settings.
|
||||
|
||||
Changing the topics while the adapter is stopped (or disconnected from the broker) will take effect the next time a connection is established.
|
||||
Changing the topics while the adapter is stopped (or disconnected from the broker) takes effect the next time a connection is established.
|
||||
|
||||
==== Configuring with Java Configuration
|
||||
|
||||
The following Spring Boot application provides an example of configuring the inbound adapter using Java configuration:
|
||||
The following Spring Boot application shows an example of how to configure the inbound adapter with Java configuration:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@@ -178,18 +173,20 @@ public class MqttJavaApplication {
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
====
|
||||
|
||||
[[mqtt-outbound]]
|
||||
=== Outbound Channel Adapter
|
||||
|
||||
The outbound channel adapter is implemented by the `MqttPahoMessageHandler` which is wrapped in a `ConsumerEndpoint`.
|
||||
For convenience, it can be configured using the namespace.
|
||||
The outbound channel adapter is implemented by the `MqttPahoMessageHandler`, which is wrapped in a `ConsumerEndpoint`.
|
||||
For convenience, you can configure it by using the namespace.
|
||||
|
||||
Starting with _version 4.1_, the adapter supports asynchronous sends, avoiding blocking until the delivery is confirmed; application events can be emitted to enable applications to confirm delivery if desired.
|
||||
Starting with version 4.1, the adapter supports asynchronous send operations, avoiding blocking until the delivery is confirmed.
|
||||
You can emit application events to enable applications to confirm delivery if desired.
|
||||
|
||||
Attributes:
|
||||
The following listing shows the attributes available for an outbound channel adapter:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-mqtt:outbound-channel-adapter id="withConverter"
|
||||
@@ -208,52 +205,44 @@ Attributes:
|
||||
channel="target" />
|
||||
----
|
||||
|
||||
<1> The client id.
|
||||
|
||||
|
||||
<1> The client ID.
|
||||
<2> The broker URL.
|
||||
|
||||
|
||||
<3> An `MqttMessageConverter` (optional).
|
||||
The default `DefaultPahoMessageConverter` recognizes the following headers: +
|
||||
`mqtt_topic` - the topic to which the message will be sent +
|
||||
`mqtt_retained` - true if the message is to be retained +
|
||||
`mqtt_qos` - the quality of service
|
||||
|
||||
|
||||
The default `DefaultPahoMessageConverter` recognizes the following headers:
|
||||
* `mqtt_topic`: The topic to which the message will be sent
|
||||
* `mqtt_retained`: `true` if the message is to be retained
|
||||
* `mqtt_qos`: The quality of service
|
||||
<4> The client factory.
|
||||
<5> The default quality of service. It is used if no `mqtt_qos` header is found or the `qos-expression` returns `null`.
|
||||
It is not used if you supply a custom `converter`.
|
||||
<6> An expression to evaluate to determine the qos.
|
||||
The default is `headers[mqtt_qos]`.
|
||||
<7> The default value of the retained flag. It is used if no `mqtt_retained` header is found.
|
||||
It is not used if a custom `converter` is supplied.
|
||||
<8> An expression to evaluate to determine the retained boolean.
|
||||
The default is `headers[mqtt_retained]`.
|
||||
<9> The default topic to which the message is sent (used if no `mqtt_topic` header is found).
|
||||
<10> An expression to evaluate to determine the destination topic.
|
||||
The default is `headers['topic']`.
|
||||
<11> When `true`, the caller does not block. Rather, it waits for delivery confirmation when a message is sent.
|
||||
The default is `false` (the send blocks until delivery is confirmed).
|
||||
<12> When `async` and `async-events` are both `true`, an `MqttMessageSentEvent` is emitted.
|
||||
It contains the message, the topic, the `messageId` generated by the client library, the `clientId`, and the `clientInstance` (incremented each time the client is connected).
|
||||
When the delivery is confirmed by the client library, an `MqttMessageDeliveredEvent` is emitted.
|
||||
It contains the the `messageId`, the `clientId`, and the `clientInstance`, enabling delivery to be correlated with the send.
|
||||
Any `ApplicationListener` or an event inbound channel adapter can received these events.
|
||||
Note that it is possible for the `MqttMessageDeliveredEvent` to be received before the `MqttMessageSentEvent`.
|
||||
The default is `false`.
|
||||
====
|
||||
|
||||
|
||||
<5> The default quality of service (used if no `mqtt_qos` header is found or the `qos-expression` returns `null`.
|
||||
Not used if a custom `converter` is supplied.
|
||||
|
||||
<6> An expression to evaluate to determine the qos; default `headers[mqtt_qos]`.
|
||||
|
||||
<7> The default value of the retained flag (used if no `mqtt_retained` header is found).
|
||||
Not used if a custom `converter` is supplied.
|
||||
|
||||
<8> An expression to evaluate to determine the retained boolean; default `headers[mqtt_retained]`.
|
||||
|
||||
<9> The default topic to which the message will be sent (used if no `mqtt_topic` header is found).
|
||||
|
||||
<10> An expression to evaluate to determine the destination topic; default `headers['topic']`.
|
||||
|
||||
<11> When `true`, the caller will not block waiting for delivery confirmation when a message is sent.
|
||||
Default:false (the send blocks until delivery is confirmed).
|
||||
|
||||
|
||||
<12> When `async` and `async-events` are both `true`, an `MqttMessageSentEvent` is emitted, containing the message, the topic, the `messageId` generated by the client library, the `clientId` and the `clientInstance` (incremented each time the client is connected).
|
||||
When the delivery is confirmed by the client library, an `MqttMessageDeliveredEvent` is emitted, containing the the `messageId`, `clientId` and the `clientInstance`, enabling delivery to be correlated with the send.
|
||||
These events can be received by any `ApplicationListener`, or by an event inbound channel adapter.
|
||||
Note that it is possible that the `MqttMessageDeliveredEvent` might be received before the `MqttMessageSentEvent`.
|
||||
Default: `false`.
|
||||
|
||||
NOTE: Starting with _version 4.1_ the url can be omitted and, instead, the server URIs can be provided in the `serverURIs` property of the `DefaultMqttPahoClientFactory`.
|
||||
NOTE: Starting with version 4.1, the URL can be omitted. Instead, the server URIs can be provided in the `serverURIs` property of the `DefaultMqttPahoClientFactory`.
|
||||
This enables, for example, connection to a highly available (HA) cluster.
|
||||
|
||||
==== Configuring with Java Configuration
|
||||
|
||||
The following Spring Boot application provides an example of configuring the outbound adapter using Java configuration:
|
||||
The following Spring Boot application show an example of how to configure the outbound adapter with Java configuration:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@@ -304,3 +293,4 @@ public class MqttJavaApplication {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
@@ -1,29 +1,33 @@
|
||||
[[overview]]
|
||||
== Spring Integration Overview
|
||||
|
||||
This chapter provides a high-level introduction to Spring Integration's core concepts and components.
|
||||
It includes some programming tips to help you make the most of Spring Integration.
|
||||
|
||||
[[overview-background]]
|
||||
=== Background
|
||||
|
||||
One of the key themes of the Spring Framework is _inversion of control_.
|
||||
One of the key themes of the Spring Framework is Inversion of Control (IoC).
|
||||
In its broadest sense, this means that the framework handles responsibilities on behalf of the components that are managed within its context.
|
||||
The components themselves are simplified since they are relieved of those responsibilities.
|
||||
For example, _dependency injection_ relieves the components of the responsibility of locating or creating their dependencies.
|
||||
Likewise, _aspect-oriented programming_ relieves business components of generic cross-cutting concerns by modularizing them into reusable aspects.
|
||||
The components themselves are simplified, because they are relieved of those responsibilities.
|
||||
For example, dependency injection relieves the components of the responsibility of locating or creating their dependencies.
|
||||
Likewise, aspect-oriented programming relieves business components of generic cross-cutting concerns by modularizing them into reusable aspects.
|
||||
In each case, the end result is a system that is easier to test, understand, maintain, and extend.
|
||||
|
||||
Furthermore, the Spring framework and portfolio provide a comprehensive programming model for building enterprise applications.
|
||||
Developers benefit from the consistency of this model and especially the fact that it is based upon well-established best practices such as programming to interfaces and favoring composition over inheritance.
|
||||
Developers benefit from the consistency of this model and especially from the fact that it is based upon well established best practices, such as programming to interfaces and favoring composition over inheritance.
|
||||
Spring's simplified abstractions and powerful support libraries boost developer productivity while simultaneously increasing the level of testability and portability.
|
||||
|
||||
Spring Integration is motivated by these same goals and principles.
|
||||
It extends the Spring programming model into the messaging domain and builds upon Spring's existing enterprise integration support to provide an even higher level of abstraction.
|
||||
It supports message-driven architectures where inversion of control applies to runtime concerns, such as _when_ certain business logic should execute and _where_ the response should be sent.
|
||||
It supports message-driven architectures where inversion of control applies to runtime concerns, such as when certain business logic should run and where the response should be sent.
|
||||
It supports routing and transformation of messages so that different transports and different data formats can be integrated without impacting testability.
|
||||
In other words, the messaging and integration concerns are handled by the framework, so business components are further isolated from the infrastructure and developers are relieved of complex integration responsibilities.
|
||||
In other words, the messaging and integration concerns are handled by the framework.
|
||||
Business components are further isolated from the infrastructure, and developers are relieved of complex integration responsibilities.
|
||||
|
||||
As an extension of the Spring programming model, Spring Integration provides a wide variety of configuration options including annotations, XML with namespace support, XML with generic "bean" elements, and of course direct usage of the underlying API.
|
||||
That API is based upon well-defined strategy interfaces and non-invasive, delegating adapters.
|
||||
Spring Integration's design is inspired by the recognition of a strong affinity between common patterns within Spring and the well-known http://www.eaipatterns.com[Enterprise Integration Patterns] as described in the book of the same name by Gregor Hohpe and Bobby Woolf (Addison Wesley, 2004).
|
||||
As an extension of the Spring programming model, Spring Integration provides a wide variety of configuration options, including annotations, XML with namespace support, XML with generic "`bean`" elements, and direct usage of the underlying API.
|
||||
That API is based upon well defined strategy interfaces and non-invasive, delegating adapters.
|
||||
Spring Integration's design is inspired by the recognition of a strong affinity between common patterns within Spring and the well known patterns described in http://www.eaipatterns.com[_Enterprise Integration Patterns_], by Gregor Hohpe and Bobby Woolf (Addison Wesley, 2004).
|
||||
Developers who have read that book should be immediately comfortable with the Spring Integration concepts and terminology.
|
||||
|
||||
[[overview-goalsandprinciples]]
|
||||
@@ -32,40 +36,36 @@ Developers who have read that book should be immediately comfortable with the Sp
|
||||
Spring Integration is motivated by the following goals:
|
||||
|
||||
* Provide a simple model for implementing complex enterprise integration solutions.
|
||||
|
||||
* Facilitate asynchronous, message-driven behavior within a Spring-based application.
|
||||
|
||||
* Promote intuitive, incremental adoption for existing Spring users.
|
||||
|
||||
Spring Integration is guided by the following principles:
|
||||
|
||||
* Components should be _loosely coupled_ for modularity and testability.
|
||||
|
||||
* The framework should enforce _separation of concerns_ between business logic and integration logic.
|
||||
|
||||
* Extension points should be abstract in nature but within well-defined boundaries to promote _reuse_ and _portability_.
|
||||
* Components should be loosely coupled for modularity and testability.
|
||||
* The framework should enforce separation of concerns between business logic and integration logic.
|
||||
* Extension points should be abstract in nature (but within well-defined boundaries) to promote reuse and portability.
|
||||
|
||||
[[overview-components]]
|
||||
=== Main Components
|
||||
|
||||
From the _vertical_ perspective, a layered architecture facilitates separation of concerns, and interface-based contracts between layers promote loose coupling.
|
||||
Spring-based applications are typically designed this way, and the Spring framework and portfolio provide a strong foundation for following this best practice for the full-stack of an enterprise application.
|
||||
Message-driven architectures add a _horizontal_ perspective, yet these same goals are still relevant.
|
||||
Just as "layered architecture" is an extremely generic and abstract paradigm, messaging systems typically follow the similarly abstract "pipes-and-filters" model.
|
||||
The "filters" represent any component that is capable of producing and/or consuming messages, and the "pipes" transport the messages between filters so that the components themselves remain loosely-coupled.
|
||||
From a vertical perspective, a layered architecture facilitates separation of concerns, and interface-based contracts between layers promote loose coupling.
|
||||
Spring-based applications are typically designed this way, and the Spring framework and portfolio provide a strong foundation for following this best practice for the full stack of an enterprise application.
|
||||
Message-driven architectures add a horizontal perspective, yet these same goals are still relevant.
|
||||
Just as "`layered architecture`" is an extremely generic and abstract paradigm, messaging systems typically follow the similarly abstract "`pipes-and-filters`" model.
|
||||
The "`filters`" represent any components capable of producing or consuming messages, and the "`pipes`" transport the messages between filters so that the components themselves remain loosely-coupled.
|
||||
It is important to note that these two high-level paradigms are not mutually exclusive.
|
||||
The underlying messaging infrastructure that supports the "pipes" should still be encapsulated in a layer whose contracts are defined as interfaces.
|
||||
Likewise, the "filters" themselves would typically be managed within a layer that is logically above the application's service layer, interacting with those services through interfaces much in the same way that a web-tier would.
|
||||
The underlying messaging infrastructure that supports the "`pipes`" should still be encapsulated in a layer whose contracts are defined as interfaces.
|
||||
Likewise, the "`filters`" themselves should be managed within a layer that is logically above the application's service layer, interacting with those services through interfaces in much the same way that a web tier would.
|
||||
|
||||
[[overview-components-message]]
|
||||
==== Message
|
||||
|
||||
In Spring Integration, a Message is a generic wrapper for any Java object combined with metadata used by the framework while handling that object.
|
||||
In Spring Integration, a message is a generic wrapper for any Java object combined with metadata used by the framework while handling that object.
|
||||
It consists of a payload and headers.
|
||||
The payload can be of any type and the headers hold commonly required information such as id, timestamp, correlation id, and return address.
|
||||
The payload can be of any type, and the headers hold commonly required information such as ID, timestamp, correlation ID, and return address.
|
||||
Headers are also used for passing values to and from connected transports.
|
||||
For example, when creating a Message from a received File, the file name may be stored in a header to be accessed by downstream components.
|
||||
Likewise, if a Message's content is ultimately going to be sent by an outbound Mail adapter, the various properties (to, from, cc, subject, etc.) may be configured as Message header values by an upstream component.
|
||||
For example, when creating a message from a received file, the file name may be stored in a header to be accessed by downstream components.
|
||||
Likewise, if a message's content is ultimately going to be sent by an outbound mail adapter, the various properties (to, from, cc, subject, and others) may be configured as message header values by an upstream component.
|
||||
Developers can also store any arbitrary key-value pairs in the headers.
|
||||
|
||||
.Message
|
||||
@@ -74,165 +74,168 @@ image::images/message.jpg["Message", align="center"]
|
||||
[[overview-components-channel]]
|
||||
==== Message Channel
|
||||
|
||||
A Message Channel represents the "pipe" of a pipes-and-filters architecture.
|
||||
Producers send Messages to a channel, and consumers receive Messages from a channel.
|
||||
The Message Channel therefore decouples the messaging components, and also provides a convenient point for interception and monitoring of Messages.
|
||||
A message channel represents the "`pipe`" of a pipes-and-filters architecture.
|
||||
Producers send messages to a channel, and consumers receive messages from a channel.
|
||||
The message channel therefore decouples the messaging components and also provides a convenient point for interception and monitoring of messages.
|
||||
|
||||
.Message Channel
|
||||
|
||||
image::images/channel.jpg["Message Channel", align="center"]
|
||||
|
||||
A Message Channel may follow either Point-to-Point or Publish/Subscribe semantics.
|
||||
With a Point-to-Point channel, at most one consumer can receive each Message sent to the channel.
|
||||
Publish/Subscribe channels, on the other hand, will attempt to broadcast each Message to all of its subscribers.
|
||||
Spring Integration supports both of these.
|
||||
A message channel may follow either point-to-point or publish-subscribe semantics.
|
||||
With a point-to-point channel, no more than one consumer can receive each message sent to the channel.
|
||||
Publish-subscribe channels, on the other hand, attempt to broadcast each message to all subscribers on the channel.
|
||||
Spring Integration supports both of these models.
|
||||
|
||||
Whereas "Point-to-Point" and "Publish/Subscribe" define the two options for _how many_ consumers will ultimately receive each Message, there is another important consideration: should the channel buffer messages? In Spring Integration, _Pollable Channels_ are capable of buffering Messages within a queue.
|
||||
The advantage of buffering is that it allows for throttling the inbound Messages and thereby prevents overloading a consumer.
|
||||
However, as the name suggests, this also adds some complexity, since a consumer can only receive the Messages from such a channel if a _poller_ is configured.
|
||||
On the other hand, a consumer connected to a _Subscribable Channel_ is simply Message-driven.
|
||||
The variety of channel implementations available in Spring Integration will be discussed in detail in <<channel-implementations>>.
|
||||
Whereas "`point-to-point`" and "publish-subscribe" define the two options for how many consumers ultimately receive each message, there is another important consideration: Should the channel buffer messages?
|
||||
In Spring Integration, pollable channels are capable of buffering Messages within a queue.
|
||||
The advantage of buffering is that it allows for throttling the inbound messages and thereby prevents overloading a consumer.
|
||||
However, as the name suggests, this also adds some complexity, since a consumer can only receive the messages from such a channel if a poller is configured.
|
||||
On the other hand, a consumer connected to a subscribable channel is simply message-driven.
|
||||
"`<<channel-implementations>>`" has a detailed discussion of the variety of channel implementations available in Spring Integration.
|
||||
|
||||
[[overview-components-endpoint]]
|
||||
==== Message Endpoint
|
||||
|
||||
One of the primary goals of Spring Integration is to simplify the development of enterprise integration solutions through _inversion of control_.
|
||||
This means that you should not have to implement consumers and producers directly, and you should not even have to build Messages and invoke send or receive operations on a Message Channel.
|
||||
Instead, you should be able to focus on your specific domain model with an implementation based on plain Objects.
|
||||
Then, by providing declarative configuration, you can "connect" your domain-specific code to the messaging infrastructure provided by Spring Integration.
|
||||
The components responsible for these connections are Message Endpoints.
|
||||
This does not mean that you will necessarily connect your existing application code directly.
|
||||
Any real-world enterprise integration solution will require some amount of code focused upon integration concerns such as _routing_ and _transformation_.
|
||||
The important thing is to achieve separation of concerns between such integration logic and business logic.
|
||||
In other words, as with the Model-View-Controller paradigm for web applications, the goal should be to provide a thin but dedicated layer that translates inbound requests into service layer invocations, and then translates service layer return values into outbound replies.
|
||||
The next section will provide an overview of the Message Endpoint types that handle these responsibilities, and in upcoming chapters, you will see how Spring Integration's declarative configuration options provide a non-invasive way to use each of these.
|
||||
One of the primary goals of Spring Integration is to simplify the development of enterprise integration solutions through inversion of control.
|
||||
This means that you should not have to implement consumers and producers directly, and you should not even have to build messages and invoke send or receive operations on a message channel.
|
||||
Instead, you should be able to focus on your specific domain model with an implementation based on plain objects.
|
||||
Then, by providing declarative configuration, you can "`connect`" your domain-specific code to the messaging infrastructure provided by Spring Integration.
|
||||
The components responsible for these connections are message endpoints.
|
||||
This does not mean that you should necessarily connect your existing application code directly.
|
||||
Any real-world enterprise integration solution requires some amount of code focused upon integration concerns such as routing and transformation.
|
||||
The important thing is to achieve separation of concerns between the integration logic and the business logic.
|
||||
In other words, as with the Model-View-Controller (MVC) paradigm for web applications, the goal should be to provide a thin but dedicated layer that translates inbound requests into service layer invocations and then translates service layer return values into outbound replies.
|
||||
The next section provides an overview of the message endpoint types that handle these responsibilities, and, in upcoming chapters, you can see how Spring Integration's declarative configuration options provide a non-invasive way to use each of these.
|
||||
|
||||
[[overview-endpoints]]
|
||||
=== Message Endpoints
|
||||
|
||||
A Message Endpoint represents the "filter" of a pipes-and-filters architecture.
|
||||
As mentioned above, the endpoint's primary role is to connect application code to the messaging framework and to do so in a non-invasive manner.
|
||||
In other words, the application code should ideally have no awareness of the Message objects or the Message Channels.
|
||||
This is similar to the role of a Controller in the MVC paradigm.
|
||||
Just as a Controller handles HTTP requests, the Message Endpoint handles Messages.
|
||||
Just as Controllers are mapped to URL patterns, Message Endpoints are mapped to Message Channels.
|
||||
A Message Endpoint represents the "`filter`" of a pipes-and-filters architecture.
|
||||
As mentioned earlier, the endpoint's primary role is to connect application code to the messaging framework and to do so in a non-invasive manner.
|
||||
In other words, the application code should ideally have no awareness of the message objects or the message channels.
|
||||
This is similar to the role of a controller in the MVC paradigm.
|
||||
Just as a controller handles HTTP requests, the message endpoint handles messages.
|
||||
Just as controllers are mapped to URL patterns, message endpoints are mapped to message channels.
|
||||
The goal is the same in both cases: isolate application code from the infrastructure.
|
||||
These concepts are discussed at length along with all of the patterns that follow in the http://www.eaipatterns.com[Enterprise Integration Patterns] book.
|
||||
Here, we provide only a high-level description of the main endpoint types supported by Spring Integration and their roles.
|
||||
The chapters that follow will elaborate and provide sample code as well as configuration examples.
|
||||
These concepts and all of the patterns that follow are discussed at length in the http://www.eaipatterns.com[_Enterprise Integration Patterns_] book.
|
||||
Here, we provide only a high-level description of the main endpoint types supported by Spring Integration and the roles associated with those types.
|
||||
The chapters that follow elaborate and provide sample code as well as configuration examples.
|
||||
|
||||
[[overview-endpoints-transformer]]
|
||||
==== Transformer
|
||||
==== Message Transformer
|
||||
|
||||
A Message Transformer is responsible for converting a Message's content or structure and returning the modified Message.
|
||||
Probably the most common type of transformer is one that converts the payload of the Message from one format to another (e.g.
|
||||
from XML Document to java.lang.String).
|
||||
Similarly, a transformer may be used to add, remove, or modify the Message's header values.
|
||||
A message transformer is responsible for converting a message's content or structure and returning the modified message.
|
||||
Probably the most common type of transformer is one that converts the payload of the message from one format to another (such as
|
||||
from XML to `java.lang.String`).
|
||||
Similarly, a transformer can add, remove, or modify the message's header values.
|
||||
|
||||
[[overview-endpoints-filter]]
|
||||
==== Filter
|
||||
==== Message Filter
|
||||
|
||||
A Message Filter determines whether a Message should be passed to an output channel at all.
|
||||
This simply requires a boolean test method that may check for a particular payload content type, a property value, the presence of a header, etc.
|
||||
If the Message is accepted, it is sent to the output channel, but if not it will be dropped (or for a more severe implementation, an Exception could be thrown).
|
||||
Message Filters are often used in conjunction with a Publish Subscribe channel, where multiple consumers may receive the same Message and use the filter to narrow down the set of Messages to be processed based on some criteria.
|
||||
A message filter determines whether a message should be passed to an output channel at all.
|
||||
This simply requires a boolean test method that may check for a particular payload content type, a property value, the presence of a header, or other conditions.
|
||||
If the message is accepted, it is sent to the output channel.
|
||||
If not, it is dropped (or, for a more severe implementation, an `Exception` could be thrown).
|
||||
Message filters are often used in conjunction with a publish-subscribe channel, where multiple consumers may receive the same message and use the criteria of the filter to narrow down the set of messages to be processed.
|
||||
|
||||
NOTE: Be careful not to confuse the generic use of "filter" within the Pipes-and-Filters architectural pattern with this specific endpoint type that selectively narrows down the Messages flowing between two channels.
|
||||
The Pipes-and-Filters concept of "filter" matches more closely with Spring Integration's Message Endpoint: any component that can be connected to Message Channel(s) in order to send and/or receive Messages.
|
||||
NOTE: Be careful not to confuse the generic use of "`filter`" within the pipes-and-filters architectural pattern with this specific endpoint type that selectively narrows down the messages flowing between two channels.
|
||||
The pipes-and-filters concept of a "`filter`" matches more closely with Spring Integration's message endpoint: any component that can be connected to a message channel in order to send or receive messages.
|
||||
|
||||
[[overview-endpoints-router]]
|
||||
==== Router
|
||||
==== Message Router
|
||||
|
||||
A Message Router is responsible for deciding what channel or channels should receive the Message next (if any).
|
||||
Typically the decision is based upon the Message's content and/or metadata available in the Message Headers.
|
||||
A Message Router is often used as a dynamic alternative to a statically configured output channel on a Service Activator or other endpoint capable of sending reply Messages.
|
||||
Likewise, a Message Router provides a proactive alternative to the reactive Message Filters used by multiple subscribers as described above.
|
||||
|
||||
.Router
|
||||
A message router is responsible for deciding what channel or channels (if any) should receive the message next.
|
||||
Typically, the decision is based upon the message's content or the metadata available in the message headers.
|
||||
A message router is often used as a dynamic alternative to a statically configured output channel on a service activator or other endpoint capable of sending reply messages.
|
||||
Likewise, a message router provides a proactive alternative to the reactive message filters used by multiple subscribers, as described earlier.
|
||||
|
||||
.Message Router
|
||||
image::images/router.jpg["Router", align="center"]
|
||||
|
||||
[[overview-endpoints-splitter]]
|
||||
==== Splitter
|
||||
|
||||
A Splitter is another type of Message Endpoint whose responsibility is to accept a Message from its input channel, split that Message into multiple Messages, and then send each of those to its output channel.
|
||||
This is typically used for dividing a "composite" payload object into a group of Messages containing the sub-divided payloads.
|
||||
A splitter is another type of message endpoint whose responsibility is to accept a message from its input channel, split that message into multiple messages, and send each of those to its output channel.
|
||||
This is typically used for dividing a "`composite`" payload object into a group of messages containing the subdivided payloads.
|
||||
|
||||
[[overview-endpoints-aggregator]]
|
||||
==== Aggregator
|
||||
|
||||
Basically a mirror-image of the Splitter, the Aggregator is a type of Message Endpoint that receives multiple Messages and combines them into a single Message.
|
||||
In fact, Aggregators are often downstream consumers in a pipeline that includes a Splitter.
|
||||
Technically, the Aggregator is more complex than a Splitter, because it is required to maintain state (the Messages to-be-aggregated), to decide when the complete group of Messages is available, and to timeout if necessary.
|
||||
Furthermore, in case of a timeout, the Aggregator needs to know whether to send the partial results or to discard them to a separate channel.
|
||||
Spring Integration provides a `CorrelationStrategy`, a `ReleaseStrategy` and configurable settings for: timeout, whether
|
||||
Basically a mirror-image of the splitter, the aggregator is a type of message endpoint that receives multiple messages and combines them into a single message.
|
||||
In fact, aggregators are often downstream consumers in a pipeline that includes a splitter.
|
||||
Technically, the aggregator is more complex than a splitter, because it is required to maintain state (the messages to be aggregated), to decide when the complete group of messages is available, and to timeout if necessary.
|
||||
Furthermore, in case of a timeout, the aggregator needs to know whether to send the partial results, discard them, or send them to a separate channel.
|
||||
Spring Integration provides a `CorrelationStrategy`, a `ReleaseStrategy`, and configurable settings for timeout, whether
|
||||
to send partial results upon timeout, and a discard channel.
|
||||
|
||||
[[overview-endpoints-service-activator]]
|
||||
==== Service Activator
|
||||
|
||||
A Service Activator is a generic endpoint for connecting a service instance to the messaging system.
|
||||
The input Message Channel must be configured, and if the service method to be invoked is capable of returning a value, an output Message Channel may also be provided.
|
||||
The input message channel must be configured, and, if the service method to be invoked is capable of returning a value, an output message Channel may also be provided.
|
||||
|
||||
NOTE: The output channel is optional, since each Message may also provide its own 'Return Address' header.
|
||||
NOTE: The output channel is optional, since each message may also provide its own 'Return Address' header.
|
||||
This same rule applies for all consumer endpoints.
|
||||
|
||||
The Service Activator invokes an operation on some service object to process the request Message, extracting the request Message's payload and converting if necessary (if the method does not expect a Message-typed parameter).
|
||||
Whenever the service object's method returns a value, that return value will likewise be converted to a reply Message if necessary (if it's not already a Message).
|
||||
That reply Message is sent to the output channel.
|
||||
If no output channel has been configured, then the reply will be sent to the channel specified in the Message's "return address" if available.
|
||||
The service activator invokes an operation on some service object to process the request message, extracting the request message's payload and converting (if the method does not expect a message-typed parameter).
|
||||
Whenever the service object's method returns a value, that return value is likewise converted to a reply message if necessary (if it is not already a message type).
|
||||
That reply message is sent to the output channel.
|
||||
If no output channel has been configured, the reply is sent to the channel specified in the message's "`return address`", if available.
|
||||
|
||||
A request-reply "Service Activator" endpoint connects a target object's method to input and output Message Channels.
|
||||
A request-reply service activator endpoint connects a target object's method to input and output Message Channels.
|
||||
|
||||
.Service Activator
|
||||
|
||||
image::images/handler-endpoint.jpg[align="center", scaledwidth=100%]
|
||||
|
||||
NOTE: As discussed in <<overview-components-channel, Message Channel>> above, channels can be _Pollable_ or _Subscribable_; in this diagram, this is depicted by the "clock" symbol and the solid arrow (poll) and the dotted arrow (subscribe).
|
||||
NOTE: As discussed earlier, in <<overview-components-channel, Message Channel>>, channels can be pollable or subscribable.
|
||||
In the preceding diagram, this is depicted by the "`clock`" symbol and the solid arrow (poll) and the dotted arrow (subscribe).
|
||||
|
||||
[[overview-endpoints-channeladapter]]
|
||||
==== Channel Adapter
|
||||
|
||||
A Channel Adapter is an endpoint that connects a Message Channel to some other system or transport.
|
||||
Channel Adapters may be either inbound or outbound.
|
||||
Typically, the Channel Adapter will do some mapping between the Message and whatever object or resource is received-from or sent-to the other system (File, HTTP Request, JMS Message, etc).
|
||||
Depending on the transport, the Channel Adapter may also populate or extract Message header values.
|
||||
Spring Integration provides a number of Channel Adapters, and they will be described in upcoming chapters.
|
||||
|
||||
.An inbound "Channel Adapter" endpoint connects a source system to a MessageChannel.
|
||||
A channel adapter is an endpoint that connects a message channel to some other system or transport.
|
||||
Channel adapters may be either inbound or outbound.
|
||||
Typically, the channel adapter does some mapping between the message and whatever object or resource is received from or sent to the other system (file, HTTP Request, JMS message, and others).
|
||||
Depending on the transport, the channel adapter may also populate or extract message header values.
|
||||
Spring Integration provides a number of channel adapters, which are described in upcoming chapters.
|
||||
|
||||
.An inbound channel adapter endpoint connects a source system to a `MessageChannel`.
|
||||
image::images/source-endpoint.jpg[align="center", scaledwidth=100%]
|
||||
|
||||
NOTE: Message sources can be _Pollable_ (e.g. POP3) or _Message-Driven_ (e.g. IMAP Idle); in this diagram, this is depicted by the "clock" symbol and the solid arrow (poll) and the dotted arrow (message-driven).
|
||||
|
||||
.An outbound "Channel Adapter" endpoint connects a MessageChannel to a target system.
|
||||
NOTE: Message sources can be pollable (for example, POP3) or message-driven_ (for example, IMAP Idle).
|
||||
In the preceding diagram, this is depicted by the "`clock`" symbol and the solid arrow (poll) and the dotted arrow (message-driven).
|
||||
|
||||
.An outbound channel adapter endpoint connects a `MessageChannel` to a target system.
|
||||
image::images/target-endpoint.jpg[align="center", scaledwidth=100%]
|
||||
|
||||
NOTE: As discussed in <<overview-components-channel, Message Channel>> above, channels can be _Pollable_ or _Subscribable_; in this diagram, this is depicted by the "clock" symbol and the solid arrow (poll) and the dotted arrow (subscribe).
|
||||
NOTE: As discussed earlier in <<overview-components-channel, Message Channel>>, channels can be pollable or subscribable.
|
||||
In the preceding diagram, this is depicted by the "`clock`" symbol and the solid arrow (poll) and the dotted arrow (subscribe).
|
||||
|
||||
[[endpoint-bean-names]]
|
||||
==== Endpoint Bean Names
|
||||
|
||||
Consuming endpoints (anything with an `inputChannel`) consist of two beans, the consumer and message handler.
|
||||
Consuming endpoints (anything with an `inputChannel`) consist of two beans, the consumer and the message handler.
|
||||
The consumer has a reference to the message handler and invokes it as messages arrive.
|
||||
|
||||
When configuring with XML:
|
||||
Consider the following XML example:
|
||||
|
||||
====
|
||||
[source, xml]
|
||||
----
|
||||
<int:service-activator id = "someService" ... />
|
||||
----
|
||||
====
|
||||
|
||||
the bean names will be as follows:
|
||||
Given the preceding example, the bean names are as follows:
|
||||
|
||||
- Consumer: `someService` (the `id`)
|
||||
- Handler: `someService.handler`
|
||||
* Consumer: `someService` (the `id`)
|
||||
* Handler: `someService.handler`
|
||||
|
||||
When using EIP annotations, the names depend on several factors.
|
||||
|
||||
**When Annotating POJO Methods**
|
||||
When using Enterprise Integration Pattern (EIP) annotations, the names depend on several factors.
|
||||
Consider the following example of an annotated POJO:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Component
|
||||
@@ -245,14 +248,16 @@ public class SomeComponent {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
the bean names will be as follows:
|
||||
Given the preceding example, the bean names are as follows:
|
||||
|
||||
- Consumer: `someComponent.someMethod.serviceActivator`
|
||||
- Handler: `someComponent.someMethod.serviceActivator.handler`
|
||||
* Consumer: `someComponent.someMethod.serviceActivator`
|
||||
* Handler: `someComponent.someMethod.serviceActivator.handler`
|
||||
|
||||
Starting with _version 5.0.4_, these names can be modified using the `@EndpointId` annotation:
|
||||
Starting with version 5.0.4, you can modify these names by using the `@EndpointId` annotation, as the following example shows:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Component
|
||||
@@ -266,17 +271,17 @@ public class SomeComponent {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
the bean names will be as follows:
|
||||
Given the preceding example, the bean names are as follows:
|
||||
|
||||
- Consumer: `someService`
|
||||
- Handler: `someService.handler`
|
||||
* Consumer: `someService`
|
||||
* Handler: `someService.handler`
|
||||
|
||||
i.e. `@EndpointId` creates names as created by the `id` attribute with XML configuration.
|
||||
|
||||
|
||||
**When Annotating @Beans**
|
||||
The `@EndpointId` creates names as created by the `id` attribute with XML configuration.
|
||||
Consider the following example of an annotated bean:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Configuratiom
|
||||
@@ -290,21 +295,23 @@ public class SomeConfiguration {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
the bean names will be as follows:
|
||||
Given the preceding example, the bean names are as follows:
|
||||
|
||||
- Consumer: `someConfiguration.someHandler.serviceActivator`
|
||||
- Handler: `someHandler` (the `@Bean` name)
|
||||
* Consumer: `someConfiguration.someHandler.serviceActivator`
|
||||
* Handler: `someHandler` (the `@Bean` name)
|
||||
|
||||
Starting with _version 5.0.4_, these names can be modified using the `@EndpointId` annotation:
|
||||
Starting with version 5.0.4, you can modify these names by using the `@EndpointId` annotation, as the following example shows:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Configuratiom
|
||||
public class SomeConfiguration {
|
||||
|
||||
@Bean("someService.handler")
|
||||
@EndpointId("someService")
|
||||
@Bean("someService.handler") <1>
|
||||
@EndpointId("someService") <2>
|
||||
@ServiceActivator(inputChannel = ...)
|
||||
public MessageHandler someHandler() {
|
||||
...
|
||||
@@ -313,33 +320,35 @@ public class SomeConfiguration {
|
||||
}
|
||||
----
|
||||
|
||||
- Consumer: `someService`
|
||||
- Handler: `someService.handler`
|
||||
<1> Handler: `someService.handler` (the bean name)
|
||||
<2> Consumer: `someService` (the endpoint ID)
|
||||
====
|
||||
|
||||
i.e. `@EndpointId` creates names as created by the `id` attribute with XML configuration, as long as you use the convention of appending `.handler` to the `@Bean` name.
|
||||
The `@EndpointId` annotation creates names as created by the `id` attribute with XML configuration, as long as you use the convention of appending `.handler` to the `@Bean` name.
|
||||
|
||||
There is one special case where a third bean is created; for architectural reasons, if a `MessageHandler` `@Bean` does not define an `AbstractReplyProducingMessageHandler`, the framework wraps the provided bean in a `ReplyProducingMessageHandlerWrapper`.
|
||||
This wrapper supports request handler advice handling as well as emitting the normal 'produced no reply' debug log messages.
|
||||
Its bean name is the handler bean name plus `.wrapper` (when there is an `@EndpointId`, otherwise it's the normal generated handler name).
|
||||
|
||||
**Message Sources**
|
||||
There is one special case where a third bean is created: For architectural reasons, if a `MessageHandler` `@Bean` does not define an `AbstractReplyProducingMessageHandler`, the framework wraps the provided bean in a `ReplyProducingMessageHandlerWrapper`.
|
||||
This wrapper supports request handler advice handling and emits the normal 'produced no reply' debug log messages.
|
||||
Its bean name is the handler bean name plus `.wrapper` (when there is an `@EndpointId` -- otherwise, it is the normal generated handler name).
|
||||
|
||||
Similarly <<pollable-message-source, Pollable Message Sources>> create two beans, a `SourcePollingChannelAdapter` (SPCA) and a `MessageSource`.
|
||||
|
||||
When configuring with XML:
|
||||
Consider the following XML configuration:
|
||||
|
||||
====
|
||||
[source, xml]
|
||||
----
|
||||
<int:inbound-channel-adapter id = "someAdapter" ... />
|
||||
----
|
||||
====
|
||||
|
||||
the bean names will be as follows:
|
||||
Given the preceding XML configuration, the bean names are as follows:
|
||||
|
||||
- SPCA: `someAdapter` (the `id`)
|
||||
- Handler: `someAdapter.source`
|
||||
* SPCA: `someAdapter` (the `id`)
|
||||
* Handler: `someAdapter.source`
|
||||
|
||||
Using `@EndpointId` with Java configuration:
|
||||
Consider the following Java configuration of a POJO to define an `@EndpointId`:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@EndpointId("someAdapter")
|
||||
@@ -348,12 +357,16 @@ public String pojoSource() {
|
||||
...
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
the bean names will be as follows:
|
||||
Given the preceding Java configuration example, the bean names are as follows:
|
||||
|
||||
- SPCA: `someAdapter`
|
||||
- Handler: `someAdapter.source`
|
||||
* SPCA: `someAdapter`
|
||||
* Handler: `someAdapter.source`
|
||||
|
||||
Consider the following Java configuration of a bean to define an `@EndpointID`:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean("someAdapter.source")
|
||||
@@ -365,101 +378,102 @@ public MessageSource<?> source() {
|
||||
};
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
the bean names will be as follows:
|
||||
Given the preceding example, the bean names are as follows:
|
||||
|
||||
- SPCA: `someAdapter`
|
||||
- Handler: `someAdapter.source` (as long as you use the convention of appending `.source` to the `@Bean` name)
|
||||
* SPCA: `someAdapter`
|
||||
* Handler: `someAdapter.source` (as long as you use the convention of appending `.source` to the `@Bean` name)
|
||||
|
||||
|
||||
[[configuration-enable-integration]]
|
||||
=== Configuration and @EnableIntegration
|
||||
=== Configuration and `@EnableIntegration`
|
||||
|
||||
Throughout this document you will see references to XML namespace support for declaring elements in a Spring Integration flow.
|
||||
Throughout this document, you can see references to XML namespace support for declaring elements in a Spring Integration flow.
|
||||
This support is provided by a series of namespace parsers that generate appropriate bean definitions to implement a particular component.
|
||||
For example, many endpoints consist of a `MessageHandler` bean and a `ConsumerEndpointFactoryBean` into which the handler and an input channel name are injected.
|
||||
|
||||
The first time a Spring Integration namespace element is encountered, the framework automatically declares a number of beans that are used to support the runtime environment (task scheduler, implicit channel creator, etc).
|
||||
The first time a Spring Integration namespace element is encountered, the framework automatically declares a number of beans (a task scheduler, an implicit channel creator, and others) that are used to support the runtime environment.
|
||||
|
||||
IMPORTANT: Starting with _version 4.0_, the `@EnableIntegration` annotation has been introduced, to allow the
|
||||
registration of Spring Integration infrastructure beans (see
|
||||
http://docs.spring.io/spring-integration/docs/latest-ga/api/org/springframework/integration/config/EnableIntegration.html[JavaDocs]).
|
||||
This annotation is required when only Java & Annotation configuration is used, e.g. with Spring Boot and/or
|
||||
Spring Integration Messaging Annotation support and Spring Integration Java DSL with no XML integration configuration.
|
||||
IMPORTANT: Version 4.0 introduced the `@EnableIntegration` annotation, to allow the
|
||||
registration of Spring Integration infrastructure beans (see the
|
||||
http://docs.spring.io/spring-integration/docs/latest-ga/api/org/springframework/integration/config/EnableIntegration.html[Javadoc]).
|
||||
This annotation is required when only Java configuration is used -- for example with Spring Boot or Spring Integration Messaging Annotation support and Spring Integration Java DSL with no XML integration configuration.
|
||||
|
||||
The `@EnableIntegration` annotation is also useful when you have a parent context with no Spring Integration components
|
||||
and 2 or more child contexts that use Spring Integration.
|
||||
It enables these common components to be declared once only, in the parent context.
|
||||
and two or more child contexts that use Spring Integration.
|
||||
It lets these common components be declared once only, in the parent context.
|
||||
|
||||
The `@EnableIntegration` annotation registers many infrastructure components with the application context:
|
||||
The `@EnableIntegration` annotation registers many infrastructure components with the application context.
|
||||
In particular, it:
|
||||
|
||||
- Registers some built-in beans, e.g. `errorChannel` and its `LoggingHandler`, `taskScheduler` for pollers,
|
||||
`jsonPath` SpEL-function etc.;
|
||||
- Adds several `BeanFactoryPostProcessor` s to enhance the `BeanFactory` for global and default integration environment;
|
||||
- Adds several `BeanPostProcessor` s to enhance and/or convert and wrap particular beans for integration purposes;
|
||||
- Adds annotations processors to parse Messaging Annotations and registers components for them with the application
|
||||
context.
|
||||
* Registers some built-in beans, such as `errorChannel` and its `LoggingHandler`, `taskScheduler` for pollers, `jsonPath` SpEL-function, and others.
|
||||
* Adds several `BeanFactoryPostProcessor` instances to enhance the `BeanFactory` for global and default integration environment.
|
||||
* Adds several `BeanPostProcessor` instances to enhance or convert and wrap particular beans for integration purposes.
|
||||
* Adds annotation processors to parse messaging annotations and registers components for them with the application context.
|
||||
|
||||
The `@IntegrationComponentScan` annotation has also been introduced to permit classpath scanning.
|
||||
This annotation plays a similar role as the standard Spring Framework `@ComponentScan` annotation, but it is restricted just to Spring Integration specific components and annotations, which aren't reachable by the standard Spring Framework component scan mechanism.
|
||||
For example <<messaging-gateway-annotation>>.
|
||||
The `@IntegrationComponentScan` annotation also permits classpath scanning.
|
||||
This annotation plays a similar role as the standard Spring Framework `@ComponentScan` annotation, but it is restricted to components and annotations that are specific to Spring Integration, which the standard Spring Framework component scan mechanism cannot reach.
|
||||
For an example, see "`<<messaging-gateway-annotation>>`".
|
||||
|
||||
The `@EnablePublisher` annotation has been introduced to register a `PublisherAnnotationBeanPostProcessor` bean and configure the `default-publisher-channel` for those `@Publisher` annotations which are provided without a `channel` attribute.
|
||||
The `@EnablePublisher` annotation registers a `PublisherAnnotationBeanPostProcessor` bean and configures the `default-publisher-channel` for those `@Publisher` annotations that are provided without a `channel` attribute.
|
||||
If more than one `@EnablePublisher` annotation is found, they must all have the same value for the default channel.
|
||||
See <<publisher-annotation>> for more information.
|
||||
See "`<<publisher-annotation>>`" for more information.
|
||||
|
||||
The `@GlobalChannelInterceptor` annotation has been introduced to mark `ChannelInterceptor` beans for global channel interception.
|
||||
This annotation is an analogue of the `<int:channel-interceptor>` xml element (see <<global-channel-configuration-interceptors>>).
|
||||
`@GlobalChannelInterceptor` annotations can be placed at the class level (with a `@Component` stereotype annotation), or on `@Bean` methods within `@Configuration` classes.
|
||||
In either case, the bean *must* be a `ChannelInterceptor`.
|
||||
This annotation is an analogue of the `<int:channel-interceptor>` XML element (see "`<<global-channel-configuration-interceptors>>`").
|
||||
`@GlobalChannelInterceptor` annotations can be placed at the class level (with a `@Component` stereotype annotation) or on `@Bean` methods within `@Configuration` classes.
|
||||
In either case, the bean must implement `ChannelInterceptor`.
|
||||
|
||||
Starting with _version 5.1_, global channel interceptors are now applied to channels registered dynamically - such as via the `IntegrationFlowContext` when using the Java DSL, or beans that are initialized using `beanFactory.initializeBean()`.
|
||||
Starting with version 5.1, global channel interceptors apply to dynamically registered channels -- such as beans that are initialized by using `beanFactory.initializeBean()` or through the `IntegrationFlowContext` when using the Java DSL.
|
||||
Previously, interceptors were not applied when beans were created after the application context was refreshed.
|
||||
|
||||
The `@IntegrationConverter` annotation has been introduced to mark `Converter`, `GenericConverter` or `ConverterFactory` beans as candidate converters for `integrationConversionService`.
|
||||
This annotation is an analogue of the `<int:converter>` xml element (see <<payload-type-conversion>>).
|
||||
`@IntegrationConverter` annotations can be placed at the class level (with a `@Component` stereotype annotation), or on `@Bean` methods within `@Configuration` classes.
|
||||
The `@IntegrationConverter` annotation marks `Converter`, `GenericConverter`, or `ConverterFactory` beans as candidate converters for `integrationConversionService`.
|
||||
This annotation is an analogue of the `<int:converter>` XML element (see "`<<payload-type-conversion>>`").
|
||||
You can place `@IntegrationConverter` annotations at the class level (with a `@Component` stereotype annotation) or on `@Bean` methods within `@Configuration` classes.
|
||||
|
||||
Also see <<annotations>> for more information about Messaging Annotations.
|
||||
See "`<<annotations>>`" for more information about messaging mnnotations.
|
||||
|
||||
[[programming-considerations]]
|
||||
=== Programming Considerations
|
||||
|
||||
It is generally recommended that you use plain old java objects (POJOs) whenever possible and only expose the framework in your code when absolutely necessary.
|
||||
See <<pojo-invocation>> for more information.
|
||||
You should use plain old java objects (POJOs) whenever possible and only expose the framework in your code when absolutely necessary.
|
||||
See "`<<pojo-invocation>>`" for more information.
|
||||
|
||||
If you do expose the framework to your classes, there are some considerations that need to be taken into account, especially during application startup; some of these are listed here.
|
||||
If you do expose the framework to your classes, there are some considerations that need to be taken into account, especially during application startup:
|
||||
|
||||
* If your component is `ApplicationContextAware`, you should generally not "use" the `ApplicationContext` in the `setApplicationContext()` method; just store a reference and defer such uses until later in the context lifecycle.
|
||||
* If your component is `ApplicationContextAware`, you should generally not use the `ApplicationContext` in the `setApplicationContext()` method.
|
||||
Instead, store a reference and defer such uses until later in the context lifecycle.
|
||||
|
||||
* If your component is an `InitializingBean` or uses `@PostConstruct` methods, do not send any messages from these initialization methods - the application context is not yet initialized when these methods are called, and sending such messages will likely fail.
|
||||
* If your component is an `InitializingBean` or uses `@PostConstruct` methods, do not send any messages from these initialization methods.
|
||||
The application context is not yet initialized when these methods are called, and sending such messages is likely to fail.
|
||||
If you need to send a messages during startup, implement `ApplicationListener` and wait for the `ContextRefreshedEvent`.
|
||||
Alternatively, implement `SmartLifecycle`, put your bean in a late phase, and send the messages from the `start()` method.
|
||||
|
||||
|
||||
[[shaded]]
|
||||
=== Considerations When using Packaged (e.g. Shaded) Jars
|
||||
==== Considerations When Using Packaged (for example, Shaded) Jars
|
||||
|
||||
Spring Integration bootstraps certain features using Spring Framework's `SpringFactories` mechanism to load several `IntegrationConfigurationInitializer` classes.
|
||||
This includes the `-core` jar as well as certain others such as `-http`, `-jmx`, etc.
|
||||
The information for this process is stored in a file `META-INF/spring.factories` in each jar.
|
||||
Spring Integration bootstraps certain features by using Spring Framework's `SpringFactories` mechanism to load several `IntegrationConfigurationInitializer` classes.
|
||||
This includes the `-core` jar as well as certain others, including `-http` and `-jmx`.
|
||||
The information for this process is stored in a `META-INF/spring.factories` file in each jar.
|
||||
|
||||
Some developers prefer to repackage their application and all dependencies into a single jar using well-known tools, such as the https://maven.apache.org/plugins/maven-shade-plugin/[Apache Maven Shade Plugin].
|
||||
Some developers prefer to repackage their application and all dependencies into a single jar by using well known tools, such as the https://maven.apache.org/plugins/maven-shade-plugin/[Apache Maven Shade Plugin].
|
||||
|
||||
By default, the shade plugin will not merge the `spring.factories` files when producing the shaded jar.
|
||||
By default, the shade plugin does not merge the `spring.factories` files when producing the shaded jar.
|
||||
|
||||
In addition to `spring.factories`, there are other `META-INF` files (`spring.handlers`, `spring.schemas`) used for XML configuration.
|
||||
These also need to be merged.
|
||||
In addition to `spring.factories`, other `META-INF` files (`spring.handlers` and `spring.schemas`) are used for XML configuration.
|
||||
These files also need to be merged.
|
||||
|
||||
IMPORTANT: https://docs.spring.io/spring-boot/docs/current/reference/html/executable-jar.html[Spring Boot's executable jar mechanism] takes a different approach in that it nests the jars, thus retaining each `spring.factories` file on the class path.
|
||||
So, with a Spring Boot application, nothing more is needed, if you use its default executable jar format.
|
||||
IMPORTANT: https://docs.spring.io/spring-boot/docs/current/reference/html/executable-jar.html[Spring Boot's executable jar mechanism] takes a different approach, in that it nests the jars, thus retaining each `spring.factories` file on the class path.
|
||||
So, with a Spring Boot application, nothing more is needed if you use its default executable jar format.
|
||||
|
||||
Even if you are not using Spring Boot, you can still use tooling provided by Boot to enhance the shade plugin by adding transformers for the above mentioned files.
|
||||
Even if you do not use Spring Boot, you can still use the tooling provided by Boot to enhance the shade plugin by adding transformers for the above mentioned files.
|
||||
|
||||
The following is an example configuration for the plugin at the time of writing.
|
||||
You may wish to consult the current https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-starters/spring-boot-starter-parent/pom.xml[spring-boot-starter-parent pom] to see the current settings that boot uses.
|
||||
The following example shows how to configure the plugin:
|
||||
|
||||
.pom.xml
|
||||
====
|
||||
[source, xml]
|
||||
----
|
||||
...
|
||||
@@ -511,24 +525,28 @@ You may wish to consult the current https://github.com/spring-projects/spring-bo
|
||||
|
||||
Specifically,
|
||||
|
||||
<1> add the `spring-boot-maven-plugin` as a dependency
|
||||
<1> Add the `spring-boot-maven-plugin` as a dependency.
|
||||
|
||||
<2> configure the transformers
|
||||
<2> Configure the transformers.
|
||||
====
|
||||
|
||||
Add a property for `${spring.boot.version}` or use a version explicitly there.
|
||||
You can add a property for `${spring.boot.version}` or use an explicit version.
|
||||
|
||||
[[programming-tips]]
|
||||
=== Programming Tips and Tricks
|
||||
|
||||
This section documents some of the ways to get the most from Spring Integration.
|
||||
|
||||
==== XML Schemas
|
||||
|
||||
When using XML configuration, to avoid getting false schema validation errors, you should use a "Spring-aware" IDE, such as the Spring Tool Suite (STS) (or eclipse with the Spring IDE plugins) or IntelliJ IDEA, for example.
|
||||
These IDEs know how to resolve the correct XML schema from the classpath (using the `META-INF/spring.schemas` file in the jar(s)).
|
||||
When using STS, or eclipse with the plugin, be sure to enable `Spring Project Nature` on the project.
|
||||
When using XML configuration, to avoid getting false schema validation errors, you should use a "`Spring-aware`" IDE, such as the Spring Tool Suite (STS), Eclipse with the Spring IDE plugins, or IntelliJ IDEA.
|
||||
These IDEs know how to resolve the correct XML schema from the classpath (by using the `META-INF/spring.schemas` file in the jars).
|
||||
When using STS or Eclipse with the plugin, you must enable `Spring Project Nature` on the project.
|
||||
|
||||
The schemas hosted on the internet for certain legacy modules (those that existed in version 1.0) are the 1.0 versions for compatibility reasons; if your IDE uses these schemas, you will likely see false errors.
|
||||
The schemas hosted on the internet for certain legacy modules (those that existed in version 1.0) are the 1.0 versions for compatibility reasons.
|
||||
If your IDE uses these schemas, you are likely to see false errors.
|
||||
|
||||
Each of these online schemas has a warning similar to this:
|
||||
Each of these online schemas has a warning similar to the following:
|
||||
|
||||
[IMPORTANT]
|
||||
====
|
||||
@@ -542,58 +560,50 @@ https://github.com/spring-projects/spring-integration/tree/master/spring-integra
|
||||
|
||||
The affected modules are
|
||||
|
||||
- core (`spring-integration.xsd`)
|
||||
- file
|
||||
- http
|
||||
- jms
|
||||
- mail
|
||||
- rmi
|
||||
- security
|
||||
- stream
|
||||
- ws
|
||||
- xml
|
||||
* `core` (`spring-integration.xsd`)
|
||||
* `file`
|
||||
* `http`
|
||||
* `jms`
|
||||
* `mail`
|
||||
* `rmi`
|
||||
* `security`
|
||||
* `stream`
|
||||
* `ws`
|
||||
* `xml`
|
||||
|
||||
==== Finding Class Names for Java and DSL Configuration
|
||||
|
||||
With XML configuration and Spring Integration Namespace support, the XML Parsers hide how
|
||||
target beans are declared and wired together.
|
||||
For Java & Annotation Configuration, it is important to understand the Framework API for target end-user
|
||||
applications.
|
||||
With XML configuration and Spring Integration Namespace support, the XML parsers hide how target beans are declared and wired together.
|
||||
For Java configuration, it is important to understand the Framework API for target end-user applications.
|
||||
|
||||
The first class citizens for EIP implementation are `Message`, `Channel` and `Endpoint` (see <<overview-components>>
|
||||
above).
|
||||
The first-class citizens for EIP implementation are `Message`, `Channel`, and `Endpoint` (see <<overview-components>>, earlier in this chapter).
|
||||
Their implementations (contracts) are:
|
||||
|
||||
- `org.springframework.messaging.Message` - see <<message>>;
|
||||
- `org.springframework.messaging.MessageChannel` - see <<channel>>;
|
||||
- `org.springframework.integration.endpoint.AbstractEndpoint` - see <<polling-consumer>>.
|
||||
* `org.springframework.messaging.Message`: See "`<<message>>`";
|
||||
* `org.springframework.messaging.MessageChannel`: See "`<<channel>>`";
|
||||
* `org.springframework.integration.endpoint.AbstractEndpoint`: See "`<<polling-consumer>>`".
|
||||
|
||||
The first two are simple enough to understand how to implement, configure and use, respectively;
|
||||
the last one deserves more review.
|
||||
The first two are simple enough to understand how to implement, configure, and use.
|
||||
The last one deserves more attention
|
||||
|
||||
The `AbstractEndpoint` is widely used throughout the Framework for different component implementations;
|
||||
its main implementations are:
|
||||
The `AbstractEndpoint` is widely used throughout the Spring Framework for different component implementations.
|
||||
Its main implementations are:
|
||||
|
||||
- `EventDrivenConsumer`, when we subscribe to a `SubscribableChannel` to _listen_ for messages;
|
||||
- `PollingConsumer`, when we _poll_ for messages from a `PollableChannel`.
|
||||
* `EventDrivenConsumer`, used when we subscribe to a `SubscribableChannel` to listen for messages.
|
||||
* `PollingConsumer`, used when we poll for messages from a `PollableChannel`.
|
||||
|
||||
Using Messaging Annotations and/or Java DSL, you shouldn't worry about these components, because the Framework produces
|
||||
them automatically via appropriate annotations and `BeanPostProcessor` s.
|
||||
When building components manually, the `ConsumerEndpointFactoryBean` should be used to help to determine the target
|
||||
`AbstractEndpoint` consumer implementation to create, based on the provided `inputChannel` property.
|
||||
When you use messaging annotations or the Java DSL, you need ot worry about these components, because the Framework automatically produces them with appropriate annotations and `BeanPostProcessor` implementations.
|
||||
When building components manually, you should use the `ConsumerEndpointFactoryBean` to help determine the target `AbstractEndpoint` consumer implementation to create, based on the provided `inputChannel` property.
|
||||
|
||||
On the other hand, the `ConsumerEndpointFactoryBean` delegates to an another first class citizen in the Framework -
|
||||
On the other hand, the `ConsumerEndpointFactoryBean` delegates to an another first class citizen in the Framework:
|
||||
`org.springframework.messaging.MessageHandler`.
|
||||
The goal of the implementation of this interface is to _handle the message consumed by the endpoint from the channel_.
|
||||
All EIP components in Spring Integration are `MessageHandler` implementations,
|
||||
e.g. `AggregatingMessageHandler`, `MessageTransformingHandler`, `AbstractMessageSplitter` etc.; as well as the target
|
||||
protocol outbound adapters are implementations too, e.g. `FileWritingMessageHandler`,
|
||||
`HttpRequestExecutingMessageHandler`, `AbstractMqttMessageHandler` etc.
|
||||
When you develop Spring Integration applications with Java & Annotation Configuration, you should take a look into the
|
||||
Spring Integration module to find an appropriate `MessageHandler` implementation to be used for the `@ServiceActivator`
|
||||
configuration.
|
||||
For example to send an XMPP message (see <<xmpp>>) we should configure something like this:
|
||||
The goal of the implementation of this interface is to handle the message consumed by the endpoint from the channel.
|
||||
All EIP components in Spring Integration are `MessageHandler` implementations (for example, `AggregatingMessageHandler`, `MessageTransformingHandler`, `AbstractMessageSplitter`, and others).
|
||||
The target protocol outbound adapters (`FileWritingMessageHandler`, `HttpRequestExecutingMessageHandler`, `AbstractMqttMessageHandler`, and others) are also `MessageHandler` implementations.
|
||||
When you develop Spring Integration applications with Java configuration, you should look into the Spring Integration module to find an appropriate `MessageHandler` implementation to use for the `@ServiceActivator`\ configuration.
|
||||
For example, to send an XMPP message (see "`<<xmpp>>`") you should configure something like the following:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
@@ -608,32 +618,29 @@ public MessageHandler sendChatMessageHandler(XMPPConnection xmppConnection) {
|
||||
return handler;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The `MessageHandler` implementations represent the _outbound_ and _processing_ part of the message flow.
|
||||
The `MessageHandler` implementations represent the outbound and processing part of the message flow.
|
||||
|
||||
The _inbound_ message flow side has its own components, which are divided to _polling_ and _listening_ behaviors.
|
||||
The inbound message flow side has its own components, which are divided into polling and listening behaviors.
|
||||
The listening (message-driven) components are simple and typically require only one target class implementation to be ready to
|
||||
produce messages.
|
||||
Listening components can be one-way `MessageProducerSupport` implementations,
|
||||
e.g. `AbstractMqttMessageDrivenChannelAdapter` and `ImapIdleChannelAdapter`; and request-reply -
|
||||
`MessagingGatewaySupport` implementations, e.g. `AmqpInboundGateway` and `AbstractWebServiceInboundGateway`.
|
||||
Listening components can be one-way `MessageProducerSupport` implementations, (such as `AbstractMqttMessageDrivenChannelAdapter` and `ImapIdleChannelAdapter`) or request-reply `MessagingGatewaySupport` implementations (such as `AmqpInboundGateway` and `AbstractWebServiceInboundGateway`).
|
||||
|
||||
_Polling_ inbound endpoints are for those protocols which don't provide a listener API or aren't intended for
|
||||
such a behavior.
|
||||
For example any File based protocol, as an FTP, any data bases (RDBMS or NoSQL) etc.
|
||||
Polling inbound endpoints are for those protocols that do not provide a listener API or are not intended for
|
||||
such a behavior, including any file based protocol (such as FTP), any data bases (RDBMS or NoSQL), and others.
|
||||
|
||||
These inbound endpoints consist with two components: the poller configuration, to initiate the polling task periodically,
|
||||
and message source class to read data from the target protocol and produce a message for the downstream integration flow.
|
||||
The first class, for the poller configuration, is a `SourcePollingChannelAdapter`.
|
||||
It is one more `AbstractEndpoint` implementation, but especially for polling to initiate an integration
|
||||
flow.
|
||||
Typically, with the Messaging Annotations or Java DSL, you shouldn't worry about this class, the Framework produces
|
||||
a bean for it, based on the `@InboundChannelAdapter` configuration or a Java DSL Builder spec.
|
||||
These inbound endpoints consist of two components: the poller configuration, to initiate the polling task periodically,
|
||||
and a message source class to read data from the target protocol and produce a message for the downstream integration flow.
|
||||
The first class for the poller configuration is a `SourcePollingChannelAdapter`.
|
||||
It is one more `AbstractEndpoint` implementation, but especially for polling to initiate an integration flow.
|
||||
Typically, with the messaging annotations or Java DSL, you should not worry about this class.
|
||||
The Framework produces a bean for it, based on the `@InboundChannelAdapter` configuration or a Java DSL builder spec.
|
||||
|
||||
_Message source_ components are more important for the target application development and they all implement
|
||||
the `MessageSource` interface, e.g. `MongoDbMessageSource` and `AbstractTwitterMessageSource`.
|
||||
With that in mind, our config for reading data from an RDBMS table with JDBC may look like:
|
||||
Message source components are more important for the target application development, and they all implement the `MessageSource` interface (for example, `MongoDbMessageSource` and `AbstractTwitterMessageSource`).
|
||||
With that in mind, our config for reading data from an RDBMS table with JDBC could resemble the following:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
@@ -642,19 +649,18 @@ public MessageSource<?> storedProc(DataSource dataSource) {
|
||||
return new JdbcPollingChannelAdapter(dataSource, "SELECT * FROM foo where status = 0");
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
All the required _inbound_ and _outbound_ classes for the target protocols you can find in the particular Spring
|
||||
Integration module, in most cases in the respective package.
|
||||
For example `spring-integration-websocket` adapters are:
|
||||
You can find all the required inbound and outbound classes for the target protocols in the particular Spring Integration module (in most cases, in the respective package).
|
||||
For example, the `spring-integration-websocket` adapters are:
|
||||
|
||||
- `o.s.i.websocket.inbound.WebSocketInboundChannelAdapter` - implements `MessageProducerSupport`
|
||||
implementation to listen frames on the socket and produce message to the channel;
|
||||
- `o.s.i.websocket.outbound.WebSocketOutboundMessageHandler` - the one-way
|
||||
`AbstractMessageHandler` implementation to convert incoming messages to the appropriate frame and send over websocket.
|
||||
* `o.s.i.websocket.inbound.WebSocketInboundChannelAdapter`: Implements `MessageProducerSupport` to listen for frames on the socket and produce message to the channel.
|
||||
* `o.s.i.websocket.outbound.WebSocketOutboundMessageHandler`: The one-way `AbstractMessageHandler` implementation to convert incoming messages to the appropriate frame and send over websocket.
|
||||
|
||||
If you are familiar with Spring Integration XML configuration, starting with _version 4.3_, we provide information in the
|
||||
XSD element definitions about which target classes are used to declare beans for the adapter or gateway, for example:
|
||||
If you are familiar with Spring Integration XML configuration, starting with version 4.3, we provide information in the
|
||||
XSD element definitions about which target classes are used to declare beans for the adapter or gateway, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<xsd:element name="outbound-async-gateway">
|
||||
@@ -666,22 +672,25 @@ The sending thread returns immediately; the reply is sent asynchronously; uses '
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
----
|
||||
====
|
||||
|
||||
[[pojo-invocation]]
|
||||
=== POJO Method invocation
|
||||
|
||||
As discussed in <<programming-considerations>>, it is generally recommended to use a POJO programming style.
|
||||
For example,
|
||||
As discussed in "`<<programming-considerations>>`", we recommend using a POJO programming style, as the following example shows:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@ServiceActivator
|
||||
public String myService(String payload) { ... }
|
||||
----
|
||||
====
|
||||
|
||||
In this case, the framework will extract a String payload, invoke your method, and wrap the result in a message to send to the next component in the flow (the original headers will be copied to the new message).
|
||||
In fact, if you are using XML configuration, you don't even need the `@ServiceActivator` annotation:
|
||||
In this case, the framework extracts a `String` payload, invokes your method, and wraps the result in a message to send to the next component in the flow (the original headers are copied to the new message).
|
||||
In fact, if you use XML configuration, you do not even need the `@ServiceActivator` annotation, as the following paired examples show:
|
||||
|
||||
====
|
||||
[source, xml]
|
||||
----
|
||||
<int:service-activator ... ref="myPojo" method="myService" />
|
||||
@@ -691,44 +700,51 @@ In fact, if you are using XML configuration, you don't even need the `@ServiceAc
|
||||
----
|
||||
public String myService(String payload) { ... }
|
||||
----
|
||||
====
|
||||
|
||||
You can omit the `method` attribute as long as there is no ambiguity in the public methods on the class.
|
||||
|
||||
Some further observations:
|
||||
|
||||
You can obtain header information in your POJO methods:
|
||||
You can also obtain header information in your POJO methods, as the following example shows:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@ServiceActivator
|
||||
public String myService(@Payload String payload, @Header("foo") String fooHeader) { ... }
|
||||
----
|
||||
====
|
||||
|
||||
You can dereference properties on the message:
|
||||
You can also dereference properties on the message, as the following example shows:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@ServiceActivator
|
||||
public String myService(@Payload("payload.foo") String foo, @Header("bar.baz") String barbaz) { ... }
|
||||
----
|
||||
====
|
||||
|
||||
Because many any varied POJO method invocations are available, versions prior to _5.0_ used SpEL to invoke the POJO methods.
|
||||
SpEL (even interpreted) is usually "fast enough" for these operations, when compared to the actual work usually done in the methods.
|
||||
However, starting with _version 5.0_, the `org.springframework.messaging.handler.invocation.InvocableHandlerMethod` is used by default, when possible.
|
||||
Because various POJO method invocations are available, versions prior to 5.0 used SpEL (Spring Expression Language) to invoke the POJO methods.
|
||||
SpEL (even interpreted) is usually "`fast enough`" for these operations, when compared to the actual work usually done in the methods.
|
||||
However, starting with version 5.0, the `org.springframework.messaging.handler.invocation.InvocableHandlerMethod` is used by default whenever possible.
|
||||
This technique is usually faster to execute than interpreted SpEL and is consistent with other Spring messaging projects.
|
||||
The `InvocableHandlerMethod` is similar to the technique used to invoke controller methods in Spring MVC.
|
||||
There are certain methods that are still always invoked using SpEL; examples include annotated parameters with dereferenced properties as discussed above.
|
||||
There are certain methods that are still always invoked when using SpEL.
|
||||
Examples include annotated parameters with dereferenced properties, as discussed earlier.
|
||||
This is because SpEL has the capability to navigate a property path.
|
||||
|
||||
There may be some other corner cases that we haven't considered that also won't work with `InvocableHandlerMethod` s.
|
||||
For this reason, we automatically fall-back to using SpEL in those cases.
|
||||
There may be some other corner cases that we have not considered that also do not work with `InvocableHandlerMethod` instances.
|
||||
For this reason, we automatically fall back to using SpEL in those cases.
|
||||
|
||||
If you wish, you can also set up your POJO method such that it always uses SpEL, with the `UseSpelInvoker` annotation:
|
||||
If you wish, you can also set up your POJO method such that it always uses SpEL, with the `UseSpelInvoker` annotation, as the following example shows:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@UseSpelInvoker(compilerMode = "IMMEDIATE")
|
||||
public void bar(String bar) { ... }
|
||||
----
|
||||
====
|
||||
|
||||
If the `compilerMode` property is omitted, the `spring.expression.compiler.mode` system property will determine the compiler mode - see http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/expressions.html#expressions-spel-compilation[SpEL compilation] for more information about compiled SpEL.
|
||||
If the `compilerMode` property is omitted, the `spring.expression.compiler.mode` system property determines the compiler mode.
|
||||
See http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/expressions.html#expressions-spel-compilation[SpEL compilation] for more information about compiled SpEL.
|
||||
|
||||
@@ -1,73 +1,67 @@
|
||||
[[polling-consumer]]
|
||||
=== Poller
|
||||
|
||||
This section describes how polling works in Spring Integration.
|
||||
|
||||
==== Polling Consumer
|
||||
|
||||
When Message Endpoints (Channel Adapters) are connected to channels and instantiated, they produce one of the following 2 instances:
|
||||
When Message Endpoints (Channel Adapters) are connected to channels and instantiated, they produce one of the following instances:
|
||||
|
||||
* http://static.springsource.org/spring-integration/api/org/springframework/integration/endpoint/PollingConsumer.html[PollingConsumer]
|
||||
* http://static.springsource.org/spring-integration/api/org/springframework/integration/endpoint/EventDrivenConsumer.html[EventDrivenConsumer]
|
||||
* http://static.springsource.org/spring-integration/api/org/springframework/integration/endpoint/PollingConsumer.html[`PollingConsumer`]
|
||||
* http://static.springsource.org/spring-integration/api/org/springframework/integration/endpoint/EventDrivenConsumer.html[`EventDrivenConsumer`]
|
||||
|
||||
The actual implementation depends on the type of channel to which these endpoints connect.
|
||||
A channel adapter connected to a channel that implements the http://docs.spring.io/spring/docs/current/javadoc-api/index.html?org/springframework/messaging/SubscribableChannel.html[`org.springframework.messaging.SubscribableChannel`] interface produces an instance of `EventDrivenConsumer`.
|
||||
On the other hand, a channel adapter connected to a channel that implements the http://docs.spring.io/spring/docs/current/javadoc-api/index.html?org/springframework/messaging/PollableChannel.html[`org.springframework.messaging.PollableChannel`] interface (such as a `QueueChannel`) produces an instance of `PollingConsumer`.
|
||||
|
||||
Polling consumers let Spring Integration components actively poll for Messages rather than process messages in an event-driven manner.
|
||||
|
||||
The actual implementation depends on which type of channel these Endpoints are connected to.
|
||||
A channel adapter connected to a channel that implements the http://docs.spring.io/spring/docs/current/javadoc-api/index.html?org/springframework/messaging/SubscribableChannel.html[org.springframework.messaging.SubscribableChannel] interface will produce an instance of `EventDrivenConsumer`.
|
||||
On the other hand, a channel adapter connected to a channel that implements the http://docs.spring.io/spring/docs/current/javadoc-api/index.html?org/springframework/messaging/PollableChannel.html[org.springframework.messaging.PollableChannel] interface (e.g. a QueueChannel) will produce an instance of `PollingConsumer`.
|
||||
|
||||
Polling Consumers allow Spring Integration components to actively poll for Messages, rather than to process Messages in an event-driven manner.
|
||||
|
||||
They represent a critical cross cutting concern in many messaging scenarios.
|
||||
In Spring Integration, Polling Consumers are based on the pattern with the same name, which is described in the book "Enterprise Integration Patterns" by Gregor Hohpe and Bobby Woolf.
|
||||
You can find a description of the pattern on the book's website at:
|
||||
|
||||
http://www.enterpriseintegrationpatterns.com/PollingConsumer.html[http://www.enterpriseintegrationpatterns.com/PollingConsumer.html]
|
||||
They represent a critical cross-cutting concern in many messaging scenarios.
|
||||
In Spring Integration, polling consumers are based on the pattern with the same name, which is described in the book _Enterprise Integration Patterns_, by Gregor Hohpe and Bobby Woolf.
|
||||
You can find a description of the pattern on the http://www.enterpriseintegrationpatterns.com/PollingConsumer.html[book's website].
|
||||
|
||||
[[pollable-message-source]]
|
||||
==== Pollable Message Source
|
||||
|
||||
Furthermore, in Spring Integration a second variation of the Polling Consumer pattern exists.
|
||||
When Inbound Channel Adapters are being used, these adapters are often wrapped by a `SourcePollingChannelAdapter`.
|
||||
For example, when retrieving messages from a remote FTP Server location, the adapter described in <<ftp-inbound>> is configured with a _poller_ to retrieve messages periodically.
|
||||
So, when components are configured with Pollers, the resulting instances are of one of the following types:
|
||||
Spring Integration offers a second variation of the polling consumer pattern.
|
||||
When inbound channel adapters are used, these adapters are often wrapped by a `SourcePollingChannelAdapter`.
|
||||
For example, when retrieving messages from a remote FTP Server location, the adapter described in "`<<ftp-inbound>>`" is configured with a poller to periodically retrieve messages.
|
||||
So, when components are configured with pollers, the resulting instances are of one of the following types:
|
||||
|
||||
* http://static.springsource.org/spring-integration/api/org/springframework/integration/endpoint/PollingConsumer.html[PollingConsumer]
|
||||
* http://static.springsource.org/spring-integration/api/org/springframework/integration/endpoint/SourcePollingChannelAdapter.html[SourcePollingChannelAdapter]
|
||||
* http://static.springsource.org/spring-integration/api/org/springframework/integration/endpoint/PollingConsumer.html[`PollingConsumer`]
|
||||
* http://static.springsource.org/spring-integration/api/org/springframework/integration/endpoint/SourcePollingChannelAdapter.html[`SourcePollingChannelAdapter`]
|
||||
|
||||
This means that pollers are used in both inbound and outbound messaging scenarios.
|
||||
Here are some use cases in which pollers are used:
|
||||
|
||||
* Polling certain external systems, such as FTP Servers, Databases, and Web Services
|
||||
* Polling internal (pollable) message channels
|
||||
* Polling internal services (such as repeatedly executing methods on a Java class)
|
||||
|
||||
This means, Pollers are used in both inbound and outbound messaging scenarios.
|
||||
Here are some use-cases that illustrate the scenarios in which Pollers are used:
|
||||
|
||||
* Polling certain external systems such as FTP Servers, Databases, Web Services
|
||||
* Polling internal (pollable) Message Channels
|
||||
* Polling internal services (E.g.
|
||||
repeatedly execute methods on a Java class)
|
||||
|
||||
|
||||
|
||||
NOTE: AOP Advice classes can be applied to pollers, in an `advice-chain`.
|
||||
An example being a transaction advice to start a transaction.
|
||||
Starting with _version 4.1_ a `PollSkipAdvice` is provided.
|
||||
NOTE: AOP advice classes can be applied to pollers, in an `advice-chain`, such as a transaction advice to start a transaction.
|
||||
Starting with version 4.1, a `PollSkipAdvice` is provided.
|
||||
Pollers use triggers to determine the time of the next poll.
|
||||
The `PollSkipAdvice` can be used to suppress (skip) a poll, perhaps because there is some downstream condition that would prevent the message to be processed properly.
|
||||
The `PollSkipAdvice` can be used to suppress (skip) a poll, perhaps because there is some downstream condition that would prevent the message being processed.
|
||||
To use this advice, you have to provide it with an implementation of a `PollSkipStrategy`.
|
||||
Starting with _version 4.2.5_, a `SimplePollSkipStrategy` is provided.
|
||||
Add an instance as a bean to the application context, inject it into a `PollSkipAdvice` and add that to the poller's
|
||||
Starting with version 4.2.5, a `SimplePollSkipStrategy` is provided.
|
||||
To use it, you can add an instance as a bean to the application context, inject it into a `PollSkipAdvice`, and add that to the poller's
|
||||
advice chain.
|
||||
To skip polling, call `skipPolls()`, to resume polling, call `reset()`.
|
||||
_Version 4.2_ added more flexibility in this area - see <<conditional-pollers>>.
|
||||
To skip polling, call `skipPolls()`.
|
||||
To resume polling, call `reset()`.
|
||||
Version 4.2 added more flexibility in this area.
|
||||
See "`<<conditional-pollers>>`".
|
||||
|
||||
This chapter is meant to only give a high-level overview regarding Polling Consumers and how they fit into the concept of message channels - <<channel>> and channel adapters - <<channel-adapter>>.
|
||||
For more in-depth information regarding Messaging Endpoints in general and Polling Consumers in particular, please see <<endpoint>>.
|
||||
This chapter is meant to only give a high-level overview of polling consumers and how they fit into the concept of message channels (see "`<<channel>>`") and channel adapters (see "`<<channel-adapter>>`").
|
||||
For more information regarding messaging endpoints in general and polling consumers in particular, see "`<<endpoint>>`".
|
||||
|
||||
[[deferred-acks-message-source]]
|
||||
==== Deferred Acknowledgment Pollable Message Source
|
||||
|
||||
Starting with _version 5.0.1_, certain modules provide `MessageSource` implementations that support deferring acknowledgment until the downstream flow completes (or hands off the message to another thread).
|
||||
This is currently limited to the `AmqpMessageSource` and the `KafkaMessageSource` provided by the https://github.com/spring-projects/spring-integration-kafka[spring-kafka-integration extension project, version 3.0.1 or higher].
|
||||
Starting with version 5.0.1, certain modules provide `MessageSource` implementations that support deferring acknowledgment until the downstream flow completes (or hands off the message to another thread).
|
||||
This is currently limited to the `AmqpMessageSource` and the `KafkaMessageSource` provided by the https://github.com/spring-projects/spring-integration-kafka[`spring-kafka-integration` extension project, version 3.0.1 or higher].
|
||||
|
||||
With these message sources, the `IntegrationMessageHeaderAccessor.ACKNOWLEDGMENT_CALLBACK` header (see <<message-header-accessor>>) is added to the message.
|
||||
The value of the header is an instance of `AcknowledgmentCallback`:
|
||||
With these message sources, the `IntegrationMessageHeaderAccessor.ACKNOWLEDGMENT_CALLBACK` header (see "`<<message-header-accessor>>`") is added to the message.
|
||||
The value of the header is an instance of `AcknowledgmentCallback`, as the following example shows:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@@ -104,9 +98,10 @@ public interface AcknowledgmentCallback {
|
||||
}
|
||||
----
|
||||
|
||||
Not all message sources (e.g. Kafka) support the `REJECT` status; it is treated the same as `ACCEPT`.
|
||||
Not all message sources (for example, Kafka) support the `REJECT` status.
|
||||
It is treated the same as `ACCEPT`.
|
||||
|
||||
Applications can acknowledge a message at any time:
|
||||
Applications can acknowledge a message at any time, as the following example shows:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@@ -118,9 +113,12 @@ StaticMessageHeaderAccessor.getAcknowledgmentCallback(received)
|
||||
.acknowledge(Status.ACCEPT);
|
||||
----
|
||||
|
||||
If the `MessageSource` is wired into a `SourcePollingChannelAdapter`, when the poller thread returns to the adapter after the downstream flow completes, the adapter will check if the acknowledgment has already been acknowledged and, if not, `ACCEPT` it (or `REJECT` it if the flow throws an exception).
|
||||
If the `MessageSource` is wired into a `SourcePollingChannelAdapter`, when the poller thread returns to the adapter after the downstream flow completes, the adapter checks whether the acknowledgment has already been acknowledged and, if not, sets its status to `ACCEPT` it (or `REJECT` if the flow throws an exception).
|
||||
The status values are defined in the https://docs.spring.io/spring-integration/api/org/springframework/integration/support/AcknowledgmentCallback.Status.html[`AcknowledgmentCallback.Status` enumeration].
|
||||
|
||||
To perform ad-hoc polling of a `MessageSource` a `MessageSourcePollingTemplate` is provided; this, too will take care of `ACCEPT` ing or `REJECT` ing the `AcknowledgmentCallback` when the `MessageHandler` callback returns (or throws an exception).
|
||||
Spring Integration provides `MessageSourcePollingTemplate` to perform ad-hoc polling of a `MessageSource`.
|
||||
This, too, takes care of setting `ACCEPT` or `REJECT` on the `AcknowledgmentCallback` when the `MessageHandler` callback returns (or throws an exception).
|
||||
The following example shows how to poll with the `MessageSourcePollingTemplate`:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@@ -133,82 +131,85 @@ template.poll(h -> {
|
||||
|
||||
In both cases (`SourcePollingChannelAdapter` and `MessageSourcePollingTemplate`), you can disable auto ack/nack by calling `noAutoAck()` on the callback.
|
||||
You might do this if you hand off the message to another thread and wish to acknowledge later.
|
||||
Not all implementations support this (for example Apache Kafka because the offset commit has to be performed on the same thread).
|
||||
Not all implementations support this (for example, Apache Kafka does not, because the offset commit has to be performed on the same thread).
|
||||
|
||||
|
||||
[[conditional-pollers]]
|
||||
==== Conditional Pollers for Message Sources
|
||||
|
||||
This section covers how to use conditional pollers.
|
||||
|
||||
===== Background
|
||||
|
||||
`Advice` objects, in an `advice-chain` on a poller, advise the whole polling task (message retrieval and processing).
|
||||
These "around advice" methods do not have access to any context for the poll, just the poll itself.
|
||||
This is fine for requirements such as making a task transactional, or skipping a poll due to some external condition as discussed above.
|
||||
What if we wish to take some action depending on the result of the `receive` part of the poll, or if we want to adjust the poller depending on conditions?
|
||||
`Advice` objects, in an `advice-chain` on a poller, advise the whole polling task (both message retrieval and processing).
|
||||
These "`around advice`" methods do not have access to any context for the poll -- only the poll itself.
|
||||
This is fine for requirements such as making a task transactional or skipping a poll due to some external condition, as discussed earlier.
|
||||
What if we wish to take some action depending on the result of the `receive` part of the poll or if we want to adjust the poller depending on conditions? For those instances, Spring Integration offers "`Smart`" Polling.
|
||||
|
||||
===== "Smart" Polling
|
||||
===== "`Smart`" Polling
|
||||
|
||||
_Version 4.2_ introduced the `AbstractMessageSourceAdvice`.
|
||||
Any `Advice` objects in the `advice-chain` that subclass this class, are applied to just the receive operation.
|
||||
Version 4.2 introduced the `AbstractMessageSourceAdvice`.
|
||||
Any `Advice` objects in the `advice-chain` that subclass this class are applied only to the receive operation.
|
||||
Such classes implement the following methods:
|
||||
|
||||
[source, java]
|
||||
beforeReceive(MessageSource<?> source)
|
||||
|
||||
* `beforeReceive(MessageSource<?> source)`
|
||||
This method is called before the `MessageSource.receive()` method.
|
||||
It enables you to examine and or reconfigure the source at this time. Returning `false` cancels this poll (similar to the `PollSkipAdvice` mentioned above).
|
||||
It lets you examine and reconfigure the source. Returning `false` cancels this poll (similar to the `PollSkipAdvice` mentioned earlier).
|
||||
|
||||
[source, java]
|
||||
Message<?> afterReceive(Message<?> result, MessageSource<?> source)
|
||||
|
||||
This method is called after the `receive()` method; again, you can reconfigure the source, or take any action perhaps depending on the result (which can be `null` if there was no message created by the source).
|
||||
* `Message<?> afterReceive(Message<?> result, MessageSource<?> source)`
|
||||
This method is called after the `receive()` method.
|
||||
Again, you can reconfigure the source or take any action (perhaps depending on the result, which can be `null` if there was no message created by the source).
|
||||
You can even return a different message
|
||||
|
||||
.Thread safety
|
||||
[IMPORTANT]
|
||||
====
|
||||
You should not configure the poller with a `TaskExecutor` if an advice mutates the `MessageSource`.
|
||||
If an advice mutates the `MessageSource`, you should not configure the poller with a `TaskExecutor`.
|
||||
If an advice mutates the source, such mutations are not thread safe and could cause unexpected results, especially with high frequency pollers.
|
||||
Consider using a downstream `ExecutorChannel` instead of adding an executor to the poller if you need to process poll results concurrently.
|
||||
If you need to process poll results concurrently, consider using a downstream `ExecutorChannel` instead of adding an executor to the poller.
|
||||
====
|
||||
|
||||
.Advice Chain Ordering
|
||||
[IMPORTANT]
|
||||
=====
|
||||
It is important to understand how the advice chain is processed during initialization.
|
||||
`Advice` objects that do not extend `AbstractMessageSourceAdvice` are applied to the whole poll process and are all invoked first, in order, before any `AbstractMessageSourceAdvice`; then `AbstractMessageSourceAdvice` objects are invoked in order around the `MessageSource` `receive()` method.
|
||||
If you have, say `Advice` objects `a, b, c, d`, where `b` and `d` are `AbstractMessageSourceAdvice`, they will be applied in the order `a, c, b, d`.
|
||||
Also, if a `MessageSource` is already a `Proxy`, the `AbstractMessageSourceAdvice` will be invoked after any existing `Advice` objects.
|
||||
If you wish to change the order, you should wire up the proxy yourself.
|
||||
You should understand how the advice chain is processed during initialization.
|
||||
`Advice` objects that do not extend `AbstractMessageSourceAdvice` are applied to the whole poll process and are all invoked first, in order, before any `AbstractMessageSourceAdvice`.
|
||||
Then `AbstractMessageSourceAdvice` objects are invoked in order around the `MessageSource` `receive()` method.
|
||||
If you have, for example, `Advice` objects `a, b, c, d`, where `b` and `d` are `AbstractMessageSourceAdvice`, the objects are applied in the following order: `a, c, b, d`.
|
||||
Also, if a `MessageSource` is already a `Proxy`, the `AbstractMessageSourceAdvice` is invoked after any existing `Advice` objects.
|
||||
If you wish to change the order, you must wire up the proxy yourself.
|
||||
=====
|
||||
|
||||
===== SimpleActiveIdleMessageSourceAdvice
|
||||
===== `SimpleActiveIdleMessageSourceAdvice`
|
||||
|
||||
This advice is a simple implementation of `AbstractMessageSourceAdvice`, when used in conjunction with a `DynamicPeriodicTrigger`, it adjusts the polling frequency depending on whether or not the previous poll resulted in a message or not.
|
||||
This advice is a simple implementation of `AbstractMessageSourceAdvice`.
|
||||
When used in conjunction with a `DynamicPeriodicTrigger`, it adjusts the polling frequency, depending on whether or not the previous poll resulted in a message or not.
|
||||
The poller must also have a reference to the same `DynamicPeriodicTrigger`.
|
||||
|
||||
.Important: Async Handoff
|
||||
IMPORTANT: This advice modifies the trigger based on the `receive()` result.
|
||||
This will only work if the advice is called on the poller thread.
|
||||
It will *not* work if the poller has a `task-executor`.
|
||||
IMPORTANT: `SimpleActiveIdleMessageSourceAdvice` modifies the trigger based on the `receive()` result.
|
||||
This works only if the advice is called on the poller thread.
|
||||
It does not work if the poller has a `task-executor`.
|
||||
To use this advice where you wish to use async operations after the result of a poll, do the async handoff later, perhaps by using an `ExecutorChannel`.
|
||||
|
||||
===== CompoundTriggerAdvice
|
||||
===== `CompoundTriggerAdvice`
|
||||
|
||||
This advice allows the selection of one of two triggers based on whether a poll returns a message or not.
|
||||
Consider a poller that uses a `CronTrigger`; `CronTrigger` s are immutable so cannot be altered once constructed.
|
||||
Consider a poller that uses a `CronTrigger`.
|
||||
`CronTrigger` instances are immutable, so they cannot be altered once constructed.
|
||||
Consider a use case where we want to use a cron expression to trigger a poll once each hour but, if no message is
|
||||
received, poll once per minute and, when a message is retrieved, revert to using the cron expression.
|
||||
|
||||
The advice (and poller) use a `CompoundTrigger` for this purpose.
|
||||
The trigger's `primary` trigger can be a `CronTrigger`.
|
||||
When the advice detects that no message is received, it adds the secondary trigger to the `CompoundTrigger`.
|
||||
When the `CompoundTrigger` 's `nextExecutionTime` method is invoked, it will delegate to the secondary trigger, if
|
||||
present; otherwise the primary trigger.
|
||||
When the `CompoundTrigger` instance's `nextExecutionTime` method is invoked, it delegates to the secondary trigger, if
|
||||
present.
|
||||
Otherwise, it delegates to the primary trigger.
|
||||
|
||||
The poller must also have a reference to the same `CompoundTrigger`.
|
||||
|
||||
The following shows the configuration for the hourly cron expression with fall-back to every minute...
|
||||
The following example shows the configuration for the hourly cron expression with a fallback to every minute:
|
||||
|
||||
[source, xml]
|
||||
----
|
||||
@@ -238,7 +239,7 @@ The following shows the configuration for the hourly cron expression with fall-b
|
||||
----
|
||||
|
||||
.Important: Async Handoff
|
||||
IMPORTANT: This advice modifies the trigger based on the `receive()` result.
|
||||
This will only work if the advice is called on the poller thread.
|
||||
It will *not* work if the poller has a `task-executor`.
|
||||
IMPORTANT: `CompoundTriggerAdvice` modifies the trigger based on the `receive()` result.
|
||||
This works only if the advice is called on the poller thread.
|
||||
It does not work if the poller has a `task-executor`.
|
||||
To use this advice where you wish to use async operations after the result of a poll, do the async handoff later, perhaps by using an `ExecutorChannel`.
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
= Preface
|
||||
[preface]
|
||||
|
||||
This chapter includes:
|
||||
|
||||
* <<system-requirements>>
|
||||
* <<code-conventions>>
|
||||
* <<guide-conventions>>
|
||||
|
||||
[[system-requirements]]
|
||||
== Requirements
|
||||
|
||||
@@ -10,25 +16,26 @@ This section details the compatible http://www.oracle.com/technetwork/java/javas
|
||||
[[supported-java-versions]]
|
||||
=== Compatible Java Versions
|
||||
|
||||
For _Spring Integration_ *5.0.x*, the *minimum* compatible Java version is *Java SE 8*.
|
||||
For Spring Integration 5.0.x, the minimum compatible Java version is Java SE 8.
|
||||
Older versions of Java are not supported.
|
||||
|
||||
[[supported-spring-versions]]
|
||||
=== Compatible Versions of the Spring Framework
|
||||
|
||||
_Spring Integration_ *5.0.x* requires _Spring Framework_ *5.0* or later.
|
||||
Spring Integration 5.0.x requires Spring Framework 5.0 or later.
|
||||
|
||||
[[code-conventions]]
|
||||
=== Code Conventions
|
||||
== Code Conventions
|
||||
|
||||
The Spring Framework 2.0 introduced support for namespaces, which simplifies the XML configuration of the application context, and consequently Spring Integration provides broad namespace support.
|
||||
This reference guide applies the following conventions for all code examples that use namespace support:
|
||||
Spring Framework 2.0 introduced support for namespaces, which simplifies the XML configuration of the application context and lets Spring Integration provide broad namespace support.
|
||||
|
||||
The *int* namespace prefix will be used for Spring Integration's core namespace support.
|
||||
Each Spring Integration adapter type (module) will provide its own namespace, which is configured using the following convention:
|
||||
In this reference guide, the `int` namespace prefix is used for Spring Integration's core namespace support.
|
||||
Each Spring Integration adapter type (also called a module) provides its own namespace, which is configured by using the following convention:
|
||||
|
||||
*int-* followed by the name of the module, e.g.
|
||||
*int-twitter*, *int-stream*, …
|
||||
`int-` followed by the name of the module -- for example, `int-twitter`, `int-stream`, and so on.
|
||||
The following example shows the `int`, `int-twitter`, and `int-stream` namespaces in use:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
@@ -49,17 +56,18 @@ Each Spring Integration adapter type (module) will provide its own namespace, wh
|
||||
…
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
For a detailed explanation regarding Spring Integration's namespace support see _<<configuration-namespace>>_.
|
||||
For a detailed explanation regarding Spring Integration's namespace support, see "`<<configuration-namespace>>`".
|
||||
|
||||
NOTE: Please note that the namespace prefix can be freely chosen.
|
||||
NOTE: The namespace prefix can be freely chosen.
|
||||
You may even choose not to use any namespace prefixes at all.
|
||||
Therefore, apply the convention that suits your application needs best.
|
||||
Therefore, you should apply the convention that best suits your application.
|
||||
Be aware, though, that SpringSource Tool Suite™ (STS) uses the same namespace conventions for Spring Integration as used in this reference guide.
|
||||
|
||||
[[guide-conventions]]
|
||||
== Conventions in This Guide
|
||||
|
||||
== Conventions in this Book
|
||||
|
||||
In some cases, to aid formatting, when specifying long fully-qualified class names, we shorten
|
||||
the package `org.springframework` to `o.s` and `org.springframework.integration` to `o.s.i`, such as with
|
||||
In some cases, to aid formatting when specifying long fully qualified class names, we shorten
|
||||
`org.springframework` to `o.s` and `org.springframework.integration` to `o.s.i`, such as with
|
||||
`o.s.i.transaction.TransactionSynchronizationFactory`.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,29 +1,28 @@
|
||||
[[resequencer]]
|
||||
=== Resequencer
|
||||
|
||||
==== Introduction
|
||||
|
||||
Related to the Aggregator, albeit different from a functional standpoint, is the Resequencer.
|
||||
The resequencer is related to the aggregator but serves a different purpose. While the aggregator combines messages, the resequencer passes messages through without changing them.
|
||||
|
||||
[[resequencer-functionality]]
|
||||
==== Functionality
|
||||
|
||||
The Resequencer works in a similar way to the Aggregator, in the sense that it uses the `CORRELATION_ID` to store messages in groups, the difference being that the Resequencer does not process the messages in any way.
|
||||
It simply releases them in the order of their `SEQUENCE_NUMBER` header values.
|
||||
The resequencer works in a similar way to the aggregator, in the sense that it uses the `CORRELATION_ID` to store messages in groups. he difference is that the Resequencer does not process the messages in any way.
|
||||
Instead, it releases them in the order of their `SEQUENCE_NUMBER` header values.
|
||||
|
||||
With respect to that, the user might opt to release all messages at once (after the whole sequence, according to the `SEQUENCE_SIZE`, has been released), or as soon as a valid sequence is available.
|
||||
With respect to that, you can opt to release all messages at once (after the whole sequence, according to the `SEQUENCE_SIZE`, and other possibilities) or as soon as a valid sequence is available. (We cover what we mean by "a valid sequence" later in this chapter.)
|
||||
|
||||
IMPORTANT: The resequencer is intended to resequence relatively short sequences of messages with small gaps.
|
||||
If you have a large number of disjoint sequences with many gaps, you may experience performance issues.
|
||||
|
||||
==== Configuring a Resequencer
|
||||
|
||||
See <<java-dsl-aggregators>> for configuring a Resequencer in Java DSL.
|
||||
See "`<<java-dsl-aggregators>>`" for configuring a resequencer in Java DSL.
|
||||
|
||||
Configuring a resequencer requires only including the appropriate element in XML.
|
||||
|
||||
A sample resequencer configuration is shown below.
|
||||
The following example shows a resequencer configuration:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:channel id="inputChannel"/>
|
||||
@@ -40,7 +39,7 @@ A sample resequencer configuration is shown below.
|
||||
send-timeout="86420000" <8>
|
||||
correlation-strategy="correlationStrategyBean" <9>
|
||||
correlation-strategy-method="correlate" <10>
|
||||
correlation-strategy-expression="headers['foo']" <11>
|
||||
correlation-strategy-expression="headers['something']" <11>
|
||||
release-strategy="releaseStrategyBean" <12>
|
||||
release-strategy-method="release" <13>
|
||||
release-strategy-expression="size() == 10" <14>
|
||||
@@ -54,116 +53,65 @@ A sample resequencer configuration is shown below.
|
||||
expire-group-upon-timeout="false" /> <20>
|
||||
----
|
||||
|
||||
<1> The id of the resequencer is _optional_.
|
||||
|
||||
|
||||
|
||||
<1> The id of the resequencer is optional.
|
||||
<2> The input channel of the resequencer.
|
||||
_Required_.
|
||||
|
||||
|
||||
|
||||
<3> The channel to which the resequencer will send the reordered messages.
|
||||
_Optional_.
|
||||
|
||||
|
||||
|
||||
<4> The channel to which the resequencer will send the messages that timed out (if `send-partial-result-on-timeout` is _false)_.
|
||||
_Optional_.
|
||||
|
||||
|
||||
|
||||
<5> Whether to send out ordered sequences as soon as they are available, or only after the whole message group arrives.
|
||||
_Optional (false by default)_.
|
||||
|
||||
|
||||
|
||||
Required.
|
||||
<3> The channel to which the resequencer sends the reordered messages.
|
||||
Optional.
|
||||
<4> The channel to which the resequencer sends the messages that timed out (if `send-partial-result-on-timeout` is set to `false`).
|
||||
Optional.
|
||||
<5> Whether to send out ordered sequences as soon as they are available or only after the whole message group arrives.
|
||||
Optional. (The default is `false`.)
|
||||
<6> A reference to a `MessageGroupStore` that can be used to store groups of messages under their correlation key until they are complete.
|
||||
_Optional_ with default a volatile in-memory store.
|
||||
|
||||
|
||||
|
||||
Optional. (The default is a volatile in-memory store.)
|
||||
<7> Whether, upon the expiration of the group, the ordered group should be sent out (even if some of the messages are missing).
|
||||
_Optional (false by default)_.
|
||||
See <<reaper>>.
|
||||
|
||||
|
||||
|
||||
Optional. (The default is false.)
|
||||
See "`<<reaper>>`".
|
||||
<8> The timeout interval to wait when sending a reply `Message` to the `output-channel` or `discard-channel`.
|
||||
Defaults to `-1` - blocking indefinitely.
|
||||
It is applied only if the output channel has some 'sending' limitations, e.g.
|
||||
`QueueChannel` with a fixed 'capacity'.
|
||||
In this case a `MessageDeliveryException` is thrown.
|
||||
The `send-timeout` is ignored in case of `AbstractSubscribableChannel` implementations.
|
||||
In case of `group-timeout(-expression)` the `MessageDeliveryException` from the scheduled expire task leads this task to be rescheduled.
|
||||
_Optional_.
|
||||
|
||||
|
||||
|
||||
Defaults to `-1`, which blocks indefinitely.
|
||||
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.
|
||||
The `send-timeout` is ignored for `AbstractSubscribableChannel` implementations.
|
||||
For `group-timeout(-expression)`, the `MessageDeliveryException` from the scheduled expire task leads this task to be rescheduled.
|
||||
Optional.
|
||||
<9> 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 will use the `IntegrationMessageHeaderAccessor.CORRELATION_ID` header)_.
|
||||
|
||||
|
||||
|
||||
<10> A method defined on the bean referenced by `correlation-strategy`, that implements the correlation decision algorithm.
|
||||
_Optional, with restrictions (requires `correlation-strategy` to be present)._
|
||||
|
||||
|
||||
|
||||
In the latter case, the `correlation-strategy-method` attribute must also be defined.
|
||||
Optional. (By default, the aggregator uses the `IntegrationMessageHeaderAccessor.CORRELATION_ID` header.)
|
||||
<10> A method that is defined on the bean referenced by `correlation-strategy` and that implements the correlation decision algorithm.
|
||||
Optional, with restrictions (requires `correlation-strategy` to be present).
|
||||
<11> A SpEL expression representing the correlation strategy.
|
||||
Example: `"headers['foo']"`.
|
||||
Example: `"headers['something']"`.
|
||||
Only one of `correlation-strategy` or `correlation-strategy-expression` is allowed.
|
||||
|
||||
|
||||
|
||||
<12> 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 will use the `IntegrationMessageHeaderAccessor.SEQUENCE_SIZE` header attribute)_.
|
||||
|
||||
|
||||
|
||||
<13> A method defined on the bean referenced by `release-strategy`, that implements the completion decision algorithm.
|
||||
_Optional, with restrictions (requires `release-strategy` to be present)._
|
||||
|
||||
|
||||
|
||||
<14> A SpEL expression representing the release strategy; the root object for the expression is a `MessageGroup`.
|
||||
In the latter case, the `release-strategy-method` attribute must also be defined.
|
||||
Optional (by default, the aggregator will use the `IntegrationMessageHeaderAccessor.SEQUENCE_SIZE` header attribute).
|
||||
<13> A method that is defined on the bean referenced by `release-strategy` and that implements the completion decision algorithm.
|
||||
Optional, with restrictions (requires `release-strategy` to be present).
|
||||
<14> 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.
|
||||
|
||||
|
||||
|
||||
<15> Only applies if a `MessageGroupStoreReaper` is configured for the `<resequencer>` `MessageStore`.
|
||||
By default, when a `MessageGroupStoreReaper` is configured to expire partial groups, empty groups are also removed.
|
||||
Empty groups exist after a group is released normally.
|
||||
This is to 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 will then not be 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 will also be affected by the reaper's _timeout_ property and it could be as much as this value plus the timeout.
|
||||
|
||||
|
||||
<16> See <<aggregator-xml>>.
|
||||
|
||||
|
||||
|
||||
<17> See <<aggregator-xml>>.
|
||||
|
||||
|
||||
<18> See <<aggregator-xml>>.
|
||||
|
||||
|
||||
<19> See <<aggregator-xml>>.
|
||||
|
||||
|
||||
<20> When a group is completed due to a timeout (or by a `MessageGroupStoreReaper`), the empty group's metadata is retained by default.
|
||||
Late arriving messages will be immediately discarded.
|
||||
Set this to `true` to remove the group completely; then, late arriving messages will start a new group and won't be discarded until the group again times out.
|
||||
The new group will never be released normally because of the "hole" in the sequence range that caused the timeout.
|
||||
Empty groups can be expired (completely removed) later using a `MessageGroupStoreReaper` together with the `empty-group-min-timeout` attribute.
|
||||
Starting with _version 5.0_ empty groups are also scheduled for removal after `empty-group-min-timeout`.
|
||||
Default: 'false'.
|
||||
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.
|
||||
<16> See "`<<aggregator-xml>>`".
|
||||
<17> See "`<<aggregator-xml>>`".
|
||||
<18> See "`<<aggregator-xml>>`".
|
||||
<19> See "`<<aggregator-xml>>`".
|
||||
<20> By default, when a group is completed due to a timeout (or by a `MessageGroupStoreReaper`), the empty group's metadata is retained.
|
||||
Late arriving messages are immediately discarded.
|
||||
Set this to `true` to remove the group completely.
|
||||
Then, late arriving messages start a new group and are not be discarded until the group again times out.
|
||||
The new group is never released normally because of the "`hole`" in the sequence range that caused the timeout.
|
||||
Empty groups can be expired (completely removed) later by using a `MessageGroupStoreReaper` together with the `empty-group-min-timeout` attribute.
|
||||
Starting with version 5.0, empty groups are also scheduled for removal after the `empty-group-min-timeout` elapses.
|
||||
The default is 'false'.
|
||||
====
|
||||
|
||||
NOTE: Since there is no custom behavior to be implemented in Java classes for resequencers, there is no annotation support for it.
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
[[resources]]
|
||||
== Additional Resources
|
||||
|
||||
[[resources-home]]
|
||||
=== Spring Integration Home
|
||||
|
||||
The definitive source of information about Spring Integration is the http://projects.spring.io/spring-integration/[Spring Integration Home] at http://spring.io[http://spring.io].
|
||||
That site serves as a hub of information and is the best place to find up-to-date announcements about the project as well as links to articles, blogs, and new sample applications.
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
[[rmi]]
|
||||
== RMI Support
|
||||
|
||||
[[rmi-intro]]
|
||||
=== Introduction
|
||||
|
||||
This Chapter explains how to use RMI specific channel adapters to distribute a system over multiple JVMs.
|
||||
The first section will deal with sending messages over RMI.
|
||||
This chapter explains how to use channel adapters that are specific to RMI (Remote Method Invocation) to distribute a system over multiple JVMs.
|
||||
The first section deals with sending messages over RMI.
|
||||
The second section shows how to receive messages over RMI.
|
||||
The last section shows how to define rmi channel adapters through the namespace support.
|
||||
The last section shows how to define RMI channel adapters by using the namespace support.
|
||||
|
||||
[[rmi-outbound]]
|
||||
=== Outbound RMI
|
||||
|
||||
To send messages from a channel over RMI, simply define an `RmiOutboundGateway`.
|
||||
This gateway will use Spring's RmiProxyFactoryBean internally to create a proxy for a remote gateway.
|
||||
Note that to invoke a remote interface that doesn't use Spring Integration you should use a service activator in combination with Spring's RmiProxyFactoryBean.
|
||||
To send messages from a channel over RMI, you can define an `RmiOutboundGateway`.
|
||||
This gateway uses Spring's `RmiProxyFactoryBean` internally to create a proxy for a remote gateway.
|
||||
Note that, to invoke a remote interface that does not use Spring Integration, you should use a service activator in combination with Spring's RmiProxyFactoryBean.
|
||||
|
||||
To configure the outbound gateway write a bean definition like this:
|
||||
To configure the outbound gateway, you can write a bean definition similar the following:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="rmiOutGateway" class=org.spf.integration.rmi.RmiOutboundGateway>
|
||||
@@ -24,30 +24,35 @@ To configure the outbound gateway write a bean definition like this:
|
||||
<property name="replyChannel" value="replies"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
[[rmi-inbound]]
|
||||
=== Inbound RMI
|
||||
|
||||
To receive messages over RMI you need to use a `RmiInboundGateway`.
|
||||
This gateway can be configured like this
|
||||
To receive messages over RMI, you need to use an `RmiInboundGateway`.
|
||||
You can configure the gateway as shown in the following example:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="rmiInGateway" class=org.spf.integration.rmi.RmiInboundGateway>
|
||||
<property name="requestChannel" value="requests"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
IMPORTANT: If you use an `errorChannel` on an inbound gateway, it would be normal for the error flow to return a result (or throw an exception).
|
||||
IMPORTANT: If you use an `errorChannel` on an inbound gateway, it the error flow normally returns a result or throws an exception.
|
||||
This is because it is likely that there is a corresponding outbound gateway waiting for a response of some kind.
|
||||
Consuming a message on the error flow, and not replying, will result in no reply at the inbound gateway.
|
||||
Exceptions (on the main flow when there is no errorChannel, or on the error flow) will be propagated to the corresponding inbound gateway.
|
||||
Consuming a message on the error flow and not replying results in no reply for the inbound gateway.
|
||||
Exceptions (on the main flow when there is no `errorChannel` or on the error flow) propagate to the corresponding inbound gateway.
|
||||
|
||||
[[rmi-namespace]]
|
||||
=== RMI namespace support
|
||||
|
||||
To configure the inbound gateway you can choose to use the namespace support for it.
|
||||
The following code snippet shows the different configuration options that are supported.
|
||||
To configure the inbound gateway, you can use the namespace support for it.
|
||||
The following code snippet shows the different configuration options that are supported:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-rmi:inbound-gateway id="gatewayWithDefaults" request-channel="testChannel"/>
|
||||
@@ -64,10 +69,12 @@ The following code snippet shows the different configuration options that are su
|
||||
<int-rmi:inbound-gateway id="gatewayWithExecutorRef" request-channel="testChannel"
|
||||
remote-invocation-executor="invocationExecutor"/>
|
||||
----
|
||||
====
|
||||
|
||||
To configure the outbound gateway you can use the namespace support as well.
|
||||
The following code snippet shows the different configuration for an outbound rmi gateway.
|
||||
You can also use the namespace support to configure the outbound gateway.
|
||||
The following code snippet shows the different configuration for an outbound RMI gateway:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-rmi:outbound-gateway id="gateway"
|
||||
@@ -75,9 +82,13 @@ The following code snippet shows the different configuration for an outbound rmi
|
||||
remote-channel="testChannel"
|
||||
host="localhost"/>
|
||||
----
|
||||
====
|
||||
|
||||
=== Configuring with Java Configuration
|
||||
|
||||
The following example shows how to configure an inbound gateway and an outbound gateway with Java:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
@@ -97,9 +108,10 @@ public RmiOutboundGateway outbound() {
|
||||
return gateway;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Starting with _version 4.3_, the outbound gateway has a second constructor that takes a RmiProxyFactoryBeanConfigurer instance along with the service url argument.
|
||||
This allows further configuration before the proxy is created; for example, to inject a Spring Security `ContextPropagatingRemoteInvocationFactory`:
|
||||
As of version 4.3, the outbound gateway has a second constructor that takes an `RmiProxyFactoryBeanConfigurer` instance, along with the service url argument.
|
||||
It allows further configuration before the proxy is created -- for example, to inject a Spring Security `ContextPropagatingRemoteInvocationFactory`, as the following example shows:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@@ -115,4 +127,4 @@ public RmiOutboundGateway outbound() {
|
||||
}
|
||||
----
|
||||
|
||||
Starting with _version 5.0_, this can be set using the XML namespace, using the `configurer` attribute.
|
||||
Starting with version 5.0, you can set this with the XML namespace by using the `configurer` attribute.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,204 +1,209 @@
|
||||
[[samples]]
|
||||
== Spring Integration Samples
|
||||
|
||||
[[samples-introduction]]
|
||||
=== Introduction
|
||||
|
||||
As of Spring Integration 2.0, the _samples_ are no longer included with the Spring Integration distribution.
|
||||
Instead we have switched to a much simpler collaborative model that should promote better community participation and, ideally, more contributions.
|
||||
As of Spring Integration 2.0, the Spring Integration distribution no longer includes the samples.
|
||||
Instead, we have switched to a much simpler collaborative model that should promote better community participation and, ideally, more contributions.
|
||||
Samples now have a dedicated Git repository and a dedicated JIRA Issue Tracking system.
|
||||
Sample development will also have its own lifecycle which is not dependent on the lifecycle of the framework releases, although the repository will still be tagged with each major release for compatibility reasons.
|
||||
Sample development also has its own lifecycle, which is not dependent on the lifecycle of the framework releases, although the repository is still tagged with each major release for compatibility reasons.
|
||||
|
||||
The great benefit to the community is that we can now add more samples and make them available to you right away without waiting for the next release.
|
||||
Having its own JIRA that is not tied to the the actual framework is also a great benefit.
|
||||
You now have a dedicated place to suggest samples as well as report issues with existing samples.
|
||||
Or, _ you may want to submit a sample to us_ as an attachment through the JIRA or, better, through the collaborative model that Git promotes.
|
||||
You can also submit a sample to us as an attachment through JIRA or, better, through the collaborative model that Git promotes.
|
||||
If we believe your sample adds value, we would be more then glad to add it to the 'samples' repository, properly crediting you as the author.
|
||||
|
||||
[[samples-get]]
|
||||
=== Where to get Samples
|
||||
=== Where to Get Samples
|
||||
|
||||
The Spring Integration Samples project is hosted on https://github.com/SpringSource/spring-integration-samples/[GitHub].
|
||||
You can find the repository at:
|
||||
|
||||
https://github.com/SpringSource/spring-integration-samples[https://github.com/SpringSource/spring-integration-samples]
|
||||
|
||||
In order to check out or _clone_ (Git parlance) the samples, please make sure you have a Git client installed on your system.
|
||||
There are several GUI-based products available for many platforms, e.g.
|
||||
http://eclipse.org/egit/[EGit] for the Eclipse IDE.
|
||||
A simple Google search will help you find them.
|
||||
Of course you can also just use the command line interface for <<http://git-scm.com/,Git>>.
|
||||
In order to check out or clone the samples, you must have a Git client installed on your system.
|
||||
There are several GUI-based products available for many platforms (such as http://eclipse.org/egit/[EGit] for the Eclipse IDE).
|
||||
A simple Google search can help you find them.
|
||||
You can also use the command line interface for <<http://git-scm.com/,Git>>.
|
||||
|
||||
NOTE: If you need more information on how to install and/or use Git, please visit: http://git-scm.com/[http://git-scm.com/].
|
||||
NOTE: If you need more information on how to install or use Git, visit: http://git-scm.com/[http://git-scm.com/].
|
||||
|
||||
In order to checkout (clone in Git terms) the Spring Integration samples repository using the Git command line tool, issue the following commands:
|
||||
To clone (check out) the Spring Integration samples repository by using the Git command line tool, issue the following command:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
$ git clone https://github.com/SpringSource/spring-integration-samples.git
|
||||
----
|
||||
====
|
||||
|
||||
That is all you need to do in order to clone the entire samples repository into a directory named _spring-integration-samples_ within the working directory where you issued that _git_ command.
|
||||
Since the samples repository is a live repository, you might want to perform periodic _pulls_ (updates) to get new samples, as well as updates to the existing samples.
|
||||
In order to do so issue the following git _pull_ command:
|
||||
The preceding command clones the entire samples repository into a directory named `spring-integration-samples` within the working directory where you issued that `git` command.
|
||||
Since the samples repository is a live repository, you might want to perform periodic pulls (updates) to get new samples and updates to the existing samples.
|
||||
To do so, issue the following `git pull` command:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
$ git pull
|
||||
----
|
||||
====
|
||||
|
||||
=== Submitting Samples or Sample Requests
|
||||
|
||||
_How can I contribute my own Samples?_
|
||||
You can submit both new samples and requests for samples. We greatly appreciate any effort toward improving the samples, including the sharing of good ideas.
|
||||
|
||||
Github is for social coding: if you want to submit your own code examples to the Spring Integration Samples project, we encourage contributions through http://help.github.com/send-pull-requests/[_pull
|
||||
requests_] from http://help.github.com/fork-a-repo/[_forks_] of this repository.
|
||||
If you want to contribute code this way, please reference, if possible, ahttps://jira.springframework.org/browse/INTSAMPLES[_JIRA Ticket_] that provides some details regarding the provided sample.
|
||||
[[samples-how-can-i-contribute]]
|
||||
==== How Can I Contribute My Own Samples?
|
||||
|
||||
Github is for social coding: if you want to submit your own code examples to the Spring Integration Samples project, we encourage contributions through http://help.github.com/send-pull-requests/[pull requests] from http://help.github.com/fork-a-repo/[forks] of this repository.
|
||||
If you want to contribute code this way, please reference, if possible, a https://jira.springframework.org/browse/INTSAMPLES[JIRA ticket] that provides some details regarding your sample.
|
||||
|
||||
[IMPORTANT]
|
||||
.Sign the contributor license agreement
|
||||
=====
|
||||
|
||||
Very important: before we can accept your Spring Integration sample, we will need you to sign the SpringSource contributor license agreement (CLA).
|
||||
Very important: Before we can accept your Spring Integration sample, we need you to sign the SpringSource contributor license agreement (CLA).
|
||||
Signing the contributor's agreement does not grant anyone commit rights to the main repository, but it does mean that we can accept your contributions, and you will get an author credit if we do.
|
||||
In order to read and sign the CLA, please go to:
|
||||
In order to read and sign the CLA, go to:
|
||||
|
||||
https://support.springsource.com/spring_committer_signup
|
||||
|
||||
From the Project drop down, please select _Spring Integration_.
|
||||
The Project Lead is _Gary Russell_.
|
||||
From the *Project* drop down, select *Spring Integration*.
|
||||
The Project Lead is Gary Russell.
|
||||
=====
|
||||
|
||||
_Code Contribution Process_
|
||||
==== Code Contribution Process
|
||||
|
||||
For the actual code contribution process, please read the the _Contributor Guidelines_ for Spring Integration, they apply for this project as well:
|
||||
|
||||
https://github.com/spring-projects/spring-integration/blob/master/CONTRIBUTING.md
|
||||
For the actual code contribution process, read the the Contributor Guidelines for Spring Integration.
|
||||
They apply for the samples project as well.
|
||||
You can find them at https://github.com/spring-projects/spring-integration/blob/master/CONTRIBUTING.md
|
||||
|
||||
This process ensures that every commit gets peer-reviewed.
|
||||
As a matter of fact, the core committers follow the exact same rules.
|
||||
We are gratefully looking forward to your Spring Integration Samples!
|
||||
We gratefully look forward to your Spring Integration samples!
|
||||
|
||||
_Sample Requests_
|
||||
==== Sample Requests
|
||||
|
||||
As mentioned earlier, the _Spring Integration Samples_ project has a dedicated JIRA Issue tracking system.
|
||||
To submit new sample requests, please visit our JIRA Issue Tracking system:
|
||||
|
||||
https://jira.springframework.org/browse/INTSAMPLES[https://jira.springframework.org/browse/INTSAMPLES].
|
||||
As <<samples-how-can-i-contribute,mentioned earlier>>, the Spring Integration Samples project has a dedicated JIRA issue tracking system.
|
||||
To submit new sample requests, visit the JIRA Issue Tracking system at https://jira.springframework.org/browse/INTSAMPLES.
|
||||
|
||||
[[samples-structure]]
|
||||
=== Samples Structure
|
||||
|
||||
Starting with Spring Integration 2.0, the structure of the _samples_ changed as well.
|
||||
With plans for more samples we realized that some samples have different goals than others.
|
||||
While they all share the common goal of showing you how to apply and work with the Spring Integration framework, they also differ in areas where some samples are meant to concentrate on a technical use case while others focus on a business use case, and some samples are all about showcasing various techniques that could be applied to address certain scenarios (both technical and business).
|
||||
The new categorization of samples will allow us to better organize them based on the problem each sample addresses while giving you a simpler way of finding the right sample for your needs.
|
||||
Starting with Spring Integration 2.0, the structure of the samples has changed.
|
||||
With plans for more samples, we realized that not all samples have the same goals.
|
||||
They all share the common goal of showing you how to apply and work with the Spring Integration framework.
|
||||
However, they differ in that some samples concentrate on a technical use case, while others focus on a business use case.
|
||||
Also, some samples are about showcasing various techniques that could be applied to address certain scenarios (both technical and business).
|
||||
The new categorization of samples lets us better organize them based on the problem each sample addresses while giving you a simpler way of finding the right sample for your needs.
|
||||
|
||||
Currently there are 4 categories.
|
||||
Within the samples repository each category has its own directory which is named after the category name:
|
||||
|
||||
_BASIC (samples/basic)_
|
||||
Currently there are four categories.
|
||||
Within the samples repository, each category has its own directory, which is named after the category name:
|
||||
|
||||
Basic (`samples/basic`)::
|
||||
This is a good place to get started.
|
||||
The samples here are technically motivated and demonstrate the bare minimum with regard to configuration and code.
|
||||
These should help you to get started quickly by introducing you to the basic concepts, API and configuration of Spring Integration as well as Enterprise Integration Patterns (EIP).
|
||||
For example, if you are looking for an answer on how to implement and wire a _Service Activator_ to a _Message Channel_ or how to use a _Messaging Gateway_ as a facade to your message exchange, or how to get started with using MAIL or TCP/UDP modules etc., this would be the right place to find a good sample.
|
||||
The bottom line is this is a good place to get started.
|
||||
These should help you to get started quickly by introducing you to the basic concepts, API, and configuration of Spring Integration as well as Enterprise Integration Patterns (EIP).
|
||||
For example, if you are looking for an answer on how to implement and wire a service activator to a message channel, how to use a messaging gateway as a facade to your message exchange, or how to get started with MAIL, TCP/UDP or other modules, this is the right place to find a good sample.
|
||||
The bottom line is `samples/basic` is a good place to get started.
|
||||
|
||||
_INTERMEDIATE (samples/intermediate)_
|
||||
|
||||
This category targets developers who are already familiar with the Spring Integration framework (past getting started), but need some more guidance while resolving the more advanced technical problems one might deal with after switching to a Messaging architecture.
|
||||
For example, if you are looking for an answer on how to handle errors in various message exchange scenarios or how to properly configure the _Aggregator_ for the situations where some messages might not ever arrive for aggregation, or any other issue that goes beyond a basic implementation and configuration of a particular component and addresses _what else_ types of problems, this would be the right place to find these type of samples.
|
||||
|
||||
_ADVANCED (samples/advanced)_
|
||||
Intermediate (`samples/intermediate`)::
|
||||
This category targets developers who are already familiar with the Spring Integration framework (beyond getting started) but need some more guidance while resolving the more advanced technical problems they might encounter after switching to a messaging architecture.
|
||||
For example, if you are looking for an answer on how to handle errors in various message exchange scenarios or how to properly configure the aggregator for a situation where some messages do not ever arrive for aggregation, or any other issue that goes beyond a basic implementation and configuration of a particular component and exposes "`what else`" types of problems, this is the right place to find these type of samples.
|
||||
|
||||
Advanced (`samples/advanced`)::
|
||||
This category targets developers who are very familiar with the Spring Integration framework but are looking to extend it to address a specific custom need by using Spring Integration's public API.
|
||||
For example, if you are looking for samples showing you how to implement a custom _Channel_ or _Consumer_ (event-based or polling-based), or you are trying to figure out what is the most appropriate way to implement a custom Bean parser on top of the Spring Integration Bean parser hierarchy when implementing your own namespace and schema for a custom component, this would be the right place to look.
|
||||
Here you can also find samples that will help you with _Adapter_ development.
|
||||
Spring Integration comes with an extensive library of adapters to allow you to connect remote systems with the Spring Integration messaging framework.
|
||||
However you might have a need to integrate with a system for which the core framework does not provide an adapter.
|
||||
So, you may decide to implement your own (and potentially contribute it).
|
||||
For example, if you are looking for samples showing you how to implement a custom channel or consumer (event-based or polling-based) or you are trying to figure out the most appropriate way to implement a custom bean parser on top of the Spring Integration bean parser hierarchy (perhaps when implementing your own namespace and schema for a custom component), this is the right place to look.
|
||||
Here you can also find samples that will help you with adapter development.
|
||||
Spring Integration comes with an extensive library of adapters to let you connect remote systems with the Spring Integration messaging framework.
|
||||
However, you might need to integrate with a system for which the core framework does not provide an adapter.
|
||||
If so, you might decide to implement your own (please consider contributing it).
|
||||
This category would include samples showing you how.
|
||||
|
||||
_APPLICATIONS (samples/applications)_
|
||||
|
||||
This category targets developers and architects who have a good understanding of Message-driven architecture and EIP, and an above average understanding of Spring and Spring Integration who are looking for samples that address a particular _business problem_.
|
||||
In other words the emphasis of samples in this category is _business use cases_ and how they can be solved with a Message-Driven Architecture and Spring Integration in particular.
|
||||
For example, if you are interested to see how a _Loan Broker_ or _Travel Agent_ process could be implemented and automated via Spring Integration, this would be the right place to find these types of samples.
|
||||
|
||||
IMPORTANT: Remember: Spring Integration is a community driven framework, therefore community participation is IMPORTANT.
|
||||
That includes Samples; so, if you can't find what you are looking for, let us know!
|
||||
Applications (`samples/applications`)::
|
||||
This category targets developers and architects who have a good understanding of message-driven architecture and EIP and an above-average understanding of Spring and Spring Integration who are looking for samples that address a particular business problem.
|
||||
In other words, the emphasis of the samples in this category is business use cases and how they can be solved with a message-driven architecture and Spring Integration in particular.
|
||||
For example, if you want to see how a loan broker or travel agent process could be implemented and automated with Spring Integration, this is the right place to find these types of samples.
|
||||
|
||||
IMPORTANT: Spring Integration is a community-driven framework. Therefore community participation is IMPORTANT.
|
||||
That includes samples.
|
||||
If you cannot find what you are looking for, let us know!
|
||||
|
||||
[[samples-impl]]
|
||||
=== Samples
|
||||
|
||||
Currently Spring Integration comes with quite a few samples and you can only expect more.
|
||||
To help you better navigate through them, each sample comes with its own `readme.txt` file which covers several details about the sample (e.g., what EIP patterns it addresses, what problem it is trying to solve, how to run sample etc.).
|
||||
Currently, Spring Integration comes with quite a few samples and you can only expect more.
|
||||
To help you better navigate through them, each sample comes with its own `readme.txt` file which covers several details about the sample (for example, what EIP patterns it addresses, what problem it is trying to solve, how to run the sample, and other details).
|
||||
However, certain samples require a more detailed and sometimes graphical explanation.
|
||||
In this section you'll find details on samples that we believe require special attention.
|
||||
In this section, you can find details on samples that we believe require special attention.
|
||||
|
||||
[[samples-loan-broker]]
|
||||
==== Loan Broker
|
||||
|
||||
In this section, we will review the _Loan Broker_ sample application that is included in the Spring Integration samples. This sample is inspired by one of the samples featured in Gregor Hohpe and Bobby Woolf's book, http://www.eaipatterns.com[Enterprise Integration Patterns].
|
||||
This section covers the loan broker sample application that is included in the Spring Integration samples. This sample is inspired by one of the samples featured in Gregor Hohpe and Bobby Woolf's book, http://www.eaipatterns.com[_Enterprise Integration Patterns_].
|
||||
|
||||
The diagram below represents the entire process
|
||||
The following diagram shows the entire process:
|
||||
|
||||
.Loan Broker Sample
|
||||
image::images/loan-broker-eip.png[align="center", scaledwidth=100%]
|
||||
|
||||
Now lets look at this process in more detail
|
||||
|
||||
At the core of an EIP architecture are the very simple yet powerful concepts of Pipes and Filters, and of course: Messages.
|
||||
Endpoints (Filters) are connected with one another via Channels (Pipes).
|
||||
The producing endpoint sends Message to the Channel, and the Message is retrieved by the Consuming endpoint.
|
||||
This architecture is meant to define various mechanisms that describe HOW information is exchanged between the endpoints, without any awareness of WHAT those endpoints are or what information they are exchanging.
|
||||
Thus, it provides for a very loosely coupled and flexible collaboration model while also decoupling Integration concerns from Business concerns.
|
||||
At the core of an EIP architecture are the very simple yet powerful concepts of pipes, filters, and, of course: messages.
|
||||
Endpoints (filters) are connected with one another via channels (pipes).
|
||||
Producing endpoints send messages to the channel, and the consuming endpoint retrieves the messages.
|
||||
This architecture is meant to define various mechanisms that describe how information is exchanged between the endpoints, without any awareness of what those endpoints are or what information they are exchanging.
|
||||
Thus, it provides for a very loosely coupled and flexible collaboration model while also decoupling integration concerns from business concerns.
|
||||
EIP extends this architecture by further defining:
|
||||
|
||||
* The types of pipes (Point-to-Point Channel, Publish-Subscribe Channel, Channel Adapter, etc.)
|
||||
* The types of pipes (point-to-point channel, publish-subscribe channel, channel adapter, and others)
|
||||
|
||||
* The core filters and patterns around how filters collaborate with pipes (Message Router, Splitters and Aggregators, various Message Transformation patterns, etc.)
|
||||
* The core filters and patterns around how filters collaborate with pipes (Message router, splitters and aggregators, various message transformation patterns, and others)
|
||||
|
||||
The details and variations of this use case are very nicely described in Chapter 9 of the EIP Book, but here is the brief summary; A Consumer while shopping for the best Loan Quote(s) subscribes to the services of a Loan Broker, which handles details such as:
|
||||
[[samples-loan-broker-requirements]]
|
||||
Chapter 9 of the EIP book nicely describes the details and variations of this use case, but here is the brief summary: While shopping for the best loan quote, a consumer subscribes to the services of a loan broker, which handles such details as:
|
||||
|
||||
* Consumer pre-screening (e.g., obtain and review the consumer's Credit history)
|
||||
* Consumer pre-screening (for example, obtaining and reviewing the consumer's Credit history)
|
||||
|
||||
* Determine the most appropriate Banks (e.g., based on consumer's credit history/score)
|
||||
* Determining the most appropriate banks (for example, based on the consumer's credit history or score)
|
||||
|
||||
* Send a Loan quote request to each selected Bank
|
||||
* Sending a loan quote request to each selected bank
|
||||
|
||||
* Collect responses from each Bank
|
||||
* Collecting responses from each bank
|
||||
|
||||
* Filter responses and determine the best quote(s), based on consumer's requirements.
|
||||
* Filtering responses and determining the best quotes, based on consumer's requirements.
|
||||
|
||||
* Pass the Loan quote(s) back to the consumer.
|
||||
* Pass the Loan quotes back to the consumer.
|
||||
|
||||
Obviously the real process of obtaining a loan quote is a bit more complex, but since our goal here is to demonstrate how Enterprise Integration Patterns are realized and implemented within SI, the use case has been simplified to concentrate only on the Integration aspects of the process.
|
||||
It is not an attempt to give you an advice in consumer finances.
|
||||
The real process of obtaining a loan quote is generally a bit more complex.
|
||||
However, since our goal is to demonstrate how Enterprise Integration Patterns are realized and implemented within Spring Integration, the use case has been simplified to concentrate only on the integration aspects of the process.
|
||||
It is not an attempt to give you advice in consumer finances.
|
||||
|
||||
As you can see, by hiring a Loan Broker, the consumer is isolated from the details of the Loan Broker's operations, and each Loan Broker's operations may defer from one another to maintain competitive advantage, so whatever we assemble/implement must be flexible so any changes could be introduced quickly and painlessly.
|
||||
Speaking of change, the Loan Broker sample does not actually talk to any 'imaginary' Banks or Credit bureaus.
|
||||
By engaging a loan broker, the consumer is isolated from the details of the loan broker's operations, and each loan broker's operations may defer from one another to maintain competitive advantage, so whatever we assemble and implement must be flexible so that any changes could be introduced quickly and painlessly.
|
||||
|
||||
NOTE: The loan broker sample does not actually talk to any 'imaginary' Banks or Credit bureaus.
|
||||
Those services are stubbed out.
|
||||
Our goal here is to assemble, orchestrate and test the integration aspect of the process as a whole.
|
||||
Only then can we start thinking about wiring such process to the real services.
|
||||
At that time the assembled process and its configuration will not change regardless of the number of Banks a particular Loan Broker is dealing with, or the type of communication media (or protocols) used (JMS, WS, TCP, etc.) to communicate with these Banks.
|
||||
|
||||
_DESIGN_
|
||||
Our goal here is to assemble, orchestrate, and test the integration aspects of the process as a whole.
|
||||
Only then can we start thinking about wiring such processes to the real services.
|
||||
At that time, the assembled process and its configuration do not change regardless of the number of banks with which a particular loan broker deals or the type of communication media (or protocols) used (JMS, WS, TCP, and so on) to communicate with these banks.
|
||||
|
||||
As you analyze the 6 requirements above you'll quickly see that they all fall into the category of Integration concerns.
|
||||
For example, in the consumer pre-screening step we need to gather additional information about the consumer and the consumer's desires and enrich the loan request with additional meta information.
|
||||
We then have to filter such information to select the most appropriate list of Banks, and so on.
|
||||
Enrich, filter, select – these are all integration concerns for which EIP defines a solution in the form of patterns.
|
||||
SI provides an implementation of these patterns.
|
||||
===== Design
|
||||
|
||||
As you analyze the <<samples-loan-broker-requirements,six requirements>> listed earlier, you can see that they are all integration concerns.
|
||||
For example, in the consumer pre-screening step, we need to gather additional information about the consumer and the consumer's desires and enrich the loan request with additional meta-information.
|
||||
We then have to filter such information to select the most appropriate list of banks and so on.
|
||||
Enrich, filter, and select are all integration concerns for which EIP defines a solution in the form of patterns.
|
||||
Spring Integration provides an implementation of these patterns.
|
||||
|
||||
The following image shows a representation of a messaging gateway:
|
||||
|
||||
.Messaging Gateway
|
||||
image::images/gateway.jpg[align="center"]
|
||||
|
||||
The _Messaging Gateway_ pattern provides a simple mechanism to access messaging systems, including our Loan Broker.
|
||||
In SI you define the _Gateway_ as a Plain Old Java Interface (no need to provide an implementation), configure it via the XML _<gateway>_ element or via annotation and use it as any other Spring bean.
|
||||
SI will take care of delegating and mapping method invocations to the Messaging infrastructure by generating a _Message_ (payload is mapped to an input parameter of the method) and sending it to the designated channel.
|
||||
The messaging gateway pattern provides a simple mechanism to access messaging systems, including our loan broker.
|
||||
In Spring Integration, you can define the gateway as a plain old java interface (you need not provide an implementation), configure it with the XML `<gateway>` element or with an annotation in Java, and use it as you would any other Spring bean.
|
||||
Spring Integration takes care of delegating and mapping method invocations to the messaging infrastructure by generating a message (the payload is mapped to an input parameter of the method) and sending it to the designated channel.
|
||||
The following example shows how to define such a gateway with XML:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:gateway id="loanBrokerGateway"
|
||||
@@ -209,47 +214,61 @@ SI will take care of delegating and mapping method invocations to the Messaging
|
||||
</int:method>
|
||||
</int:gateway>
|
||||
----
|
||||
====
|
||||
|
||||
Our current _Gateway_ provides two methods that could be invoked.
|
||||
One that will return the best single quote and another one that will return all quotes.
|
||||
Somehow downstream we need to know what type of reply the caller is looking for.
|
||||
The best way to achieve this in Messaging architecture is to enrich the content of the message with some meta-data describing your intentions.
|
||||
_Content Enricher_ is one of the patterns that addresses this and although Spring Integration does provide a separate configuration element to enrich Message Headers with arbitrary data (we'll see it later), as a convenience, since_Gateway_ element is responsible to construct the initial _Message_ it provides embedded capability to enrich the newly created _Message_ with arbitrary _Message Headers_.
|
||||
In our example we are adding header RESPONSE_TYPE with value 'BEST'' whenever the getBestQuote() method is invoked.
|
||||
For other method we are not adding any header.
|
||||
Now we can check downstream for an existence of this header and based on its presence and its value we can determine what type of reply the caller is looking for.
|
||||
Our current gateway provides two methods that could be invoked.
|
||||
One that returns the best single quote and another one that returns all quotes.
|
||||
Somehow, downstream, we need to know what type of reply the caller needs.
|
||||
The best way to achieve this in messaging architecture is to enrich the content of the message with some metadata that describes your intentions.
|
||||
Content Enricher is one of the patterns that addresses this.
|
||||
Spring Integration does, as a convenience, provide a separate configuration element to enrich message headers with arbitrary data (described later)
|
||||
However, since the `gateway` element is responsible for constructing the initial message, it includes ability to enrich the newly created message with arbitrary message headers.
|
||||
In our example, we add a `RESPONSE_TYPE` header with a value of `BEST` whenever the `getBestQuote()` method is invoked.
|
||||
For other methods, we do not add any header.
|
||||
Now we can check downstream for the existence of this header.
|
||||
Based on its presence and its value, we can determine what type of reply the caller wants.
|
||||
|
||||
Based on the use case we also know there are some pre-screening steps that needs to be performed such as getting and evaluating the consumer's credit score, simply because some premiere Banks will only typically accept quote requests from consumers that meet a minimum credit score requirement.
|
||||
So it would be nice if the _Message_ would be enriched with such information before it is forwarded to the Banks.
|
||||
It would also be nice if when several processes needs to be completed to provide such meta-information, those processes could be grouped in a single unit.
|
||||
In our use case we need to determine credit score and based on the credit score and some rule select a list of _Message Channels_ (Bank Channels) we will sent quote request to.
|
||||
Based on the use case, we also know tat some pre-screening steps need to be performed, such as getting and evaluating the consumer's credit score, because some premiere banks only accept quote requests from consumers that meet a minimum credit score requirement.
|
||||
So it would be nice if the message would be enriched with such information before it is forwarded to the banks.
|
||||
It would also be nice if, when several processes need to be completed to provide such meta-information, those processes could be grouped in a single unit.
|
||||
In our use case, we need to determine the credit score and, based on the credit score and some rule, select a list of message channels (bank channels) to which to send quote request.
|
||||
|
||||
_Composed Message Processor_
|
||||
===== Composed Message Processor
|
||||
|
||||
The _Composed Message Processor_ pattern describes rules around building endpoints that maintain control over message flow which consists of multiple message processors.
|
||||
In Spring Integration _Composed Message Processor_ pattern is implemented via _<chain>_ element.
|
||||
The composed message processor pattern describes rules around building endpoints that maintain control over message flow, which consists of multiple message processors.
|
||||
In Spring Integration, the composed message processor pattern is implemented by the `<chain>` element.
|
||||
|
||||
The following image shows the chain pattern:
|
||||
|
||||
.Chain
|
||||
image::images/chain.png[align="center"]
|
||||
|
||||
As you can see from the above configuration we have a chain with inner header-enricher element which will further enrich the content of the _Message_ with the header CREDIT_SCORE and value that will be determined by the call to a credit service (simple POJO spring bean identified by 'creditBureau' name) and then it will delegate to the _Message Router_
|
||||
The preceding image shows that we have a chain with an inner header-enricher element that further enriches the content of the message with the `CREDIT_SCORE` header and the value (which is determined by the call to a credit service -- a simple POJO spring bean identified by 'creditBureau' name).
|
||||
Then it delegates to the message router.
|
||||
|
||||
The following image shows the message router pattern:
|
||||
|
||||
.Message Router
|
||||
image::images/bank-router.jpg[align="center"]
|
||||
|
||||
There are several implementations of the _Message Routing_ pattern available in Spring Integration.
|
||||
Here we are using a router that will determine a list of channels based on evaluating an expression (Spring Expression Language) which will look at the credit score that was determined is the previous step and will select the list of channels from the Map bean with id 'banks' whose values are 'premier' or 'secondary' based o the value of credit score.
|
||||
Once the list of _Channels_ is selected, the _Message_ will be routed to those _Channels_.
|
||||
Spring Integration offers several implementations of the message routing pattern.
|
||||
In this case, we use a router that determines a list of channels based on evaluating an expression (in Spring Expression Language) that looks at the credit score (determined in the previous step) and selects the list of channels from the `Map` bean with an `id` of `banks` whose values are `premier` or `secondary`, based on the value of credit score.
|
||||
Once the list of channels is selected, the message is routed to those channels.
|
||||
|
||||
Now, one last thing the Loan Broker needs to to is to receive the loan quotes form the banks, aggregate them by consumer (we don't want to show quotes from one consumer to another), assemble the response based on the consumer's selection criteria (single best quote or all quotes) and reply back to the consumer.
|
||||
Now, one last thing the loan broker needs to receive the loan quotes form the banks, aggregate them by consumer (we do not want to show quotes from one consumer to another), assemble the response based on the consumer's selection criteria (single best quote or all quotes) and send the reply to the consumer.
|
||||
|
||||
The following image shows the message aggregator pattern:
|
||||
|
||||
.Message Aggregator
|
||||
image::images/quotes-aggregator.jpg[align="center"]
|
||||
|
||||
An _Aggregator_ pattern describes an endpoint which groups related _Messages_ into a single _Message_.
|
||||
An aggregator pattern describes an endpoint that groups related messages into a single message.
|
||||
Criteria and rules can be provided to determine an aggregation and correlation strategy.
|
||||
SI provides several implementations of the _Aggregator_ pattern as well as a convenient name-space based configuration.
|
||||
Spring Integration provides several implementations of the aggregator pattern as well as a convenient namespace-based configuration.
|
||||
|
||||
The following example shows how to define an aggregator:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:aggregator id="quotesAggregator"
|
||||
@@ -258,51 +277,55 @@ SI provides several implementations of the _Aggregator_ pattern as well as a con
|
||||
<beans:bean class="org.springframework.integration.samples.loanbroker.LoanQuoteAggregator"/>
|
||||
</int:aggregator>
|
||||
----
|
||||
====
|
||||
|
||||
Our Loan Broker defines a 'quotesAggregator' bean via the _<aggregator>_ element which provides a default aggregation and correlation strategy.
|
||||
The default correlation strategy correlates messages based on the `correlationId` header (see _Correlation Identifier_ pattern).
|
||||
What's interesting is that we never provided the value for this header.
|
||||
It was set earlier by the router automatically, when it generated a separate _Message_ for each Bank channel.
|
||||
Our Loan Broker defines a 'quotesAggregator' bean with the `<aggregator>` element, which provides a default aggregation and correlation strategy.
|
||||
The default correlation strategy correlates messages based on the `correlationId` header (see http://www.enterpriseintegrationpatterns.com/patterns/messaging/CorrelationIdentifier.html[the correlation identifier pattern in the EIP book]).
|
||||
Note that we never provided the value for this header.
|
||||
It was automatically set earlier by the router, when it generated a separate message for each bank channel.
|
||||
|
||||
Once the _Messages_ are correlated they are released to the actual _Aggregator_ implementation.
|
||||
Although default _Aggregator_ is provided by SI, its strategy (gather the list of payloads from all _Messages_ and construct a new _Message_ with this List as payload) does not satisfy our requirement.
|
||||
The reason is that our consumer might require a single best quote or all quotes.
|
||||
To communicate the consumer's intention, earlier in the process we set the RESPONSE_TYPE header.
|
||||
Now we have to evaluate this header and return either all the quotes (the default aggregation strategy would work) or the best quote (the default aggregation strategy will not work because we have to determine which loan quote is the best).
|
||||
Once the messages are correlated, they are released to the actual aggregator implementation.
|
||||
Although Spring Integration provides a default aggregator, its strategy (gather the list of payloads from all messages and construct a new message with this list as its payload) does not satisfy our requirement.
|
||||
Having all the results in the message is a problem, because our consumer might require a single best quote or all quotes.
|
||||
To communicate the consumer's intention, earlier in the process we set the `RESPONSE_TYPE` header.
|
||||
Now we have to evaluate this header and return either all the quotes (the default aggregation strategy would work) or the best quote (the default aggregation strategy does not work because we have to determine which loan quote is the best).
|
||||
|
||||
Obviously selecting the best quote could be based on complex criteria and would influence the complexity of the aggregator implementation and configuration, but for now we are making it simple.
|
||||
If consumer wants the best quote we will select a quote with the lowest interest rate.
|
||||
To accomplish that the LoanQuoteAggregator.java will sort all the quotes and return the first one.
|
||||
The `LoanQuote.java` implements `Comparable` which compares quotes based on the rate attribute.
|
||||
Once the response _Message_ is created it is sent to the default-reply-channel of the _Messaging Gateway_ (thus the consumer) which started the process.
|
||||
Our consumer got the Loan Quote!
|
||||
In a more realistic application, selecting the best quote might be based on complex criteria that might influence the complexity of the aggregator implementation and configuration.
|
||||
For now, though, we are making it simple.
|
||||
If the consumer wants the best quote, we select a quote with the lowest interest rate.
|
||||
To accomplish that, the `LoanQuoteAggregator` class sorts all the quotes by interest rate and returns the first one.
|
||||
The `LoanQuote` class implements `Comparable` to compare quotes based on the rate attribute.
|
||||
Once the response message is created, it is sent to the default reply channel of the messaging gateway (and, thus, to the consumer) that started the process.
|
||||
Our consumer got the loan quote!
|
||||
|
||||
Conclusion
|
||||
In conclusion, a rather complex process was assembled based on POJO (that is existing or legacy) logic and a light-weight, embeddable messaging framework (Spring Integration) with a loosely coupled programming model intended to simplify integration of heterogeneous systems without requiring a heavy-weight ESB-like engine or a proprietary development and deployment environment.
|
||||
As a developer, you should not need to port your Swing or console-based application to an ESB-like server or implement proprietary interfaces just because you have an integration concern.
|
||||
|
||||
As you can see a rather complex process was assembled based on POJO (read existing, legacy), light weight, embeddable messaging framework (Spring Integration) with a loosely coupled programming model intended to simplify integration of heterogeneous systems without requiring a heavy-weight ESB-like engine or proprietary development and deployment environment, because as a developer you should not be porting your Swing or console-based application to an ESB-like server or implementing proprietary interfaces just because you have an integration concern.
|
||||
|
||||
This and other samples in this section are built on top of Enterprise Integration Patterns and can be considered "building blocks" for YOUR solution; they are not intended to be complete solutions.
|
||||
Integration concerns exist in all types of application (whether server based or not).
|
||||
It should not require change in design, testing and deployment strategy if such applications need to be integrated.
|
||||
This sample and the other samples in this section are built on top of Enterprise Integration Patterns.
|
||||
You can consider them to be "`building blocks`" for your solution.
|
||||
They are not intended to be complete solutions.
|
||||
Integration concerns exist in all types of application (whether server-based or not).
|
||||
Our goal is to make is so that integrating applications does not require changes in design, testing, and deployment strategy.
|
||||
|
||||
[[samples-cafe]]
|
||||
==== The Cafe Sample
|
||||
|
||||
In this section, we will review a _Cafe_ sample application that is included in the Spring Integration samples. This sample is inspired by another sample featured in Gregor Hohpe's http://www.eaipatterns.com/ramblings.html[Ramblings].
|
||||
This section covers the cafe sample application that is included in the Spring Integration samples. This sample is inspired by another sample featured in Gregor Hohpe's http://www.eaipatterns.com/ramblings.html[Ramblings].
|
||||
|
||||
The domain is that of a Cafe, and the basic flow is depicted in the following diagram:
|
||||
The domain is that of a cafe, and the following diagram depicts the basic flow:
|
||||
|
||||
.Cafe Sample
|
||||
image::images/cafe-eip.png[align="center", scaledwidth=100%]
|
||||
|
||||
The `Order` object may contain multiple `OrderItems`.
|
||||
Once the order is placed, a _Splitter_ will break the composite order message into a single message per drink.
|
||||
Each of these is then processed by a _Router_ that determines whether the drink is hot or cold (checking the `OrderItem` object's 'isIced' property).
|
||||
Once the order is placed, a splitter breaks the composite order message into a single message for each drink.
|
||||
Each of these is then processed by a router that determines whether the drink is hot or cold (by checking the `OrderItem` object's 'isIced' property).
|
||||
The `Barista` prepares each drink, but hot and cold drink preparation are handled by two distinct methods: 'prepareHotDrink' and 'prepareColdDrink'.
|
||||
The prepared drinks are then sent to the Waiter where they are aggregated into a `Delivery` object.
|
||||
The prepared drinks are then sent to the `Waiter` where they are aggregated into a `Delivery` object.
|
||||
|
||||
Here is the XML configuration:
|
||||
The following listing shows the XML configuration:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
@@ -354,12 +377,14 @@ Here is the XML configuration:
|
||||
|
||||
</beans:beans>
|
||||
----
|
||||
====
|
||||
|
||||
As you can see, each Message Endpoint is connected to input and/or output channels.
|
||||
Each endpoint will manage its own Lifecycle (by default endpoints start automatically upon initialization - to prevent that add the "auto-startup" attribute with a value of "false").
|
||||
Each message endpoint connects to input channels, output channels, or both.
|
||||
Each endpoint manages its own lifecycle (by default, endpoints start automatically upon initialization, to prevent that, add the `auto-startup` attribute with a value of `false`).
|
||||
Most importantly, notice that the objects are simple POJOs with strongly typed method arguments.
|
||||
For example, here is the Splitter:
|
||||
The following example shows the Splitter:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class OrderSplitter {
|
||||
@@ -368,10 +393,12 @@ public class OrderSplitter {
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
In the case of the Router, the return value does not have to be a `MessageChannel` instance (although it can be).
|
||||
As you see in this example, a String-value representing the channel name is returned instead.
|
||||
In the case of the router, the return value does not have to be a `MessageChannel` instance (although it can be).
|
||||
In this example, a `String` value that holds the channel name is returned instead, as the following listing shows.
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class DrinkRouter {
|
||||
@@ -380,10 +407,13 @@ public class DrinkRouter {
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Now turning back to the XML, you see that there are two <service-activator> elements.
|
||||
Each of these is delegating to the same `Barista` instance but different methods: 'prepareHotDrink' or 'prepareColdDrink' corresponding to the two channels where order items have been routed.
|
||||
Now, turning back to the XML, you can see that there are two `<service-activator>` elements.
|
||||
Each of these is delegating to the same `Barista` instance but with different methods (`prepareHotDrink` or `prepareColdDrink`), each corresponding to one of the two channels where order items have been routed.
|
||||
The following listing shows the Barista class (which contains the `prepareHotDrink` and `prepareColdDrink` methods)
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class Barista {
|
||||
@@ -435,15 +465,17 @@ public class Barista {
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
As you can see from the code excerpt above, the barista methods have different delays (the hot drinks take 5 times as long to prepare).
|
||||
As you can see from the preceding code excerpt, the `Barista` methods have different delays (the hot drinks take five times as long to prepare).
|
||||
This simulates work being completed at different rates.
|
||||
When the `CafeDemo` 'main' method runs, it will loop 100 times sending a single hot drink and a single cold drink each time.
|
||||
It actually sends the messages by invoking the 'placeOrder' method on the Cafe interface.
|
||||
Above, you will see that the <gateway> element is specified in the configuration file.
|
||||
This triggers the creation of a proxy that implements the given 'service-interface' and connects it to a channel.
|
||||
The channel name is provided on the @Gateway annotation of the `Cafe` interface.
|
||||
When the `CafeDemo` 'main' method runs, it loops 100 times and sends a single hot drink and a single cold drink each time.
|
||||
It actually sends the messages by invoking the 'placeOrder' method on the `Cafe` interface.
|
||||
In the earlier XML configuration, you can see that the `<gateway>` element is specified.
|
||||
This triggers the creation of a proxy that implements the given service interface and connects it to a channel.
|
||||
The channel name is provided on the `@Gateway` annotation of the `Cafe` interface, as the following interface definition shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public interface Cafe {
|
||||
@@ -453,9 +485,11 @@ public interface Cafe {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Finally, have a look at the `main()` method of the `CafeDemo` itself.
|
||||
Finally, have a look at the `main()` method of the `CafeDemo` itself:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public static void main(String[] args) {
|
||||
@@ -475,15 +509,18 @@ public static void main(String[] args) {
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
TIP: To run this sample as well as 8 others, refer to the `README.txt` within the "samples" directory of the main distribution as described at the beginning of this chapter.
|
||||
TIP: To run this sample as well as eight others, refer to the `README.txt` within the `samples` directory of the main distribution (as described at <<samples,the beginning of this chapter>>).
|
||||
|
||||
When you run cafeDemo, you will see that the cold drinks are initially prepared more quickly than the hot drinks.
|
||||
When you run `cafeDemo`, you can see that the cold drinks are initially prepared more quickly than the hot drinks.
|
||||
Because there is an aggregator, the cold drinks are effectively limited by the rate of the hot drink preparation.
|
||||
This is to be expected based on their respective delays of 1000 and 5000 milliseconds.
|
||||
This is to be expected, based on their respective delays of 1000 and 5000 milliseconds.
|
||||
However, by configuring a poller with a concurrent task executor, you can dramatically change the results.
|
||||
For example, you could use a thread pool executor with 5 workers for the hot drink barista while keeping the cold drink barista as it is:
|
||||
For example, you could use a thread pool executor with five workers for the hot drink barista while keeping the cold drink barista as it is.
|
||||
The following listing configures such an arrangement:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:service-activator input-channel="hotDrinks"
|
||||
@@ -500,23 +537,28 @@ For example, you could use a thread pool executor with 5 workers for the hot dri
|
||||
|
||||
<task:executor id="pool" pool-size="5"/>
|
||||
----
|
||||
====
|
||||
|
||||
Also, notice that the worker thread name is displayed with each invocation.
|
||||
You will see that the hot drinks are prepared by the task-executor threads.
|
||||
If you provide a much shorter poller interval (such as 100 milliseconds), then you will notice that occasionally it throttles the input by forcing the task-scheduler (the caller) to invoke the operation.
|
||||
You can see that the hot drinks are prepared by the task-executor threads.
|
||||
If you provide a much shorter poller interval (such as 100 milliseconds), you can see that it occasionally throttles the input by forcing the task scheduler (the caller) to invoke the operation.
|
||||
|
||||
NOTE: In addition to experimenting with the poller's concurrency settings, you can also add the 'transactional' sub-element and then refer to any PlatformTransactionManager instance within the context.
|
||||
NOTE: In addition to experimenting with the poller's concurrency settings, you can also add the 'transactional' child element and then refer to any `PlatformTransactionManager` instance within the context.
|
||||
|
||||
[[samples-xml-messaging]]
|
||||
==== The XML Messaging Sample
|
||||
|
||||
The xml messaging sample in `basic/xml` illustrates how to use some of the provided components which deal with xml payloads.
|
||||
The sample uses the idea of processing an order for books represented as xml.
|
||||
The XML messaging sample in `basic/xml` shows how to use some of the provided components that deal with XML payloads.
|
||||
The sample uses the idea of processing an order for books represented as XML.
|
||||
|
||||
NOTE:This sample shows that the namespace prefix can be whatever you want; while we usually use, `int-xml` for
|
||||
integration XML components, the sample uses `si-xml`.
|
||||
NOTE: This sample shows that the namespace prefix can be whatever you want.
|
||||
While we usually use, `int-xml` for integration XML components, the sample uses `si-xml`.
|
||||
(`int` is short for "`Integration`", and `si` is short for "`Spring Integration`".)
|
||||
|
||||
First the order is split into a number of messages, each one representing a single order item using the XPath splitter component.
|
||||
First, the order is split into a number of messages, each one representing a single order item from the XPath splitter component.
|
||||
The following listing shows the configuration of the splitter:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<si-xml:xpath-splitter id="orderItemSplitter" input-channel="ordersChannel"
|
||||
@@ -525,12 +567,15 @@ First the order is split into a number of messages, each one representing a sing
|
||||
namespace-map="orderNamespaceMap" />
|
||||
</si-xml:xpath-splitter>
|
||||
----
|
||||
====
|
||||
|
||||
A service activator is then used to pass the message into a stock checker POJO.
|
||||
The order item document is enriched with information from the stock checker about order item stock level.
|
||||
A service activator then passes the message into a stock checker POJO.
|
||||
The order item document is enriched with information from the stock checker about the order item stock level.
|
||||
This enriched order item message is then used to route the message.
|
||||
In the case where the order item is in stock the message is routed to the warehouse.
|
||||
In the case where the order item is in stock, the message is routed to the warehouse.
|
||||
The following listing configures the `xpath-router` that routes the messages:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<si-xml:xpath-router id="instockRouter" input-channel="orderRoutingChannel" resolution-required="true">
|
||||
@@ -539,11 +584,16 @@ In the case where the order item is in stock the message is routed to the wareho
|
||||
<si-xml:mapping value="false" channel="outOfStockChannel"/>
|
||||
</si-xml:xpath-router>
|
||||
----
|
||||
====
|
||||
|
||||
Where the order item is not in stock the message is transformed using xslt into a format suitable for sending to the supplier.
|
||||
When the order item is not in stock, the message is transformed with XSLT into a format suitable for sending to the supplier.
|
||||
The following listing configures the XSLT transformer:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<si-xml:xslt-transformer input-channel="outOfStockChannel"
|
||||
output-channel="resupplyOrderChannel"
|
||||
xsl-resource="classpath:org/springframework/integration/samples/xml/bigBooksSupplierTransformer.xsl"/>
|
||||
----
|
||||
====
|
||||
|
||||
@@ -4,48 +4,51 @@
|
||||
[[scatter-gather-introduction]]
|
||||
==== Introduction
|
||||
|
||||
Starting with _version 4.1_, Spring Integration provides an implementation of the http://www.eaipatterns.com/BroadcastAggregate.html[Scatter-Gather] Enterprise Integration Pattern.
|
||||
It is a compound endpoint, where the goal is to send a message to the recipients and aggregate the results.
|
||||
Quoting the EIP Book, it is a component for scenarios like _best quote_, when we need to request information from several suppliers and decide which one provides us with the best term for the requested item.
|
||||
Starting with version 4.1, Spring Integration provides an implementation of the http://www.eaipatterns.com/BroadcastAggregate.html[scatter-gather] enterprise integration pattern.
|
||||
It is a compound endpoint for which the goal is to send a message to the recipients and aggregate the results.
|
||||
As noted in http://www.eaipatterns.com[_Enterprise Integration Patterns_], it is a component for scenarios such as "`best quote`", where we need to request information from several suppliers and decide which one provides us with the best term for the requested item.
|
||||
|
||||
Previously, the pattern could be configured using discrete components, this enhancement brings more convenient configuration.
|
||||
Previously, the pattern could be configured by using discrete components.
|
||||
This enhancement brings more convenient configuration.
|
||||
|
||||
The `ScatterGatherHandler` is a _request-reply_ endpoint that combines a `PublishSubscribeChannel` (or `RecipientListRouter`) and an `AggregatingMessageHandler`.
|
||||
The request message is sent to the `scatter` channel and the `ScatterGatherHandler` waits for the reply from the aggregator to sends to the `outputChannel`.
|
||||
The `ScatterGatherHandler` is a request-reply endpoint that combines a `PublishSubscribeChannel` (or a `RecipientListRouter`) and an `AggregatingMessageHandler`.
|
||||
The request message is sent to the `scatter` channel, and the `ScatterGatherHandler` waits for the reply that the aggregator sends to the `outputChannel`.
|
||||
|
||||
[[scatter-gather-functionality]]
|
||||
==== Functionality
|
||||
|
||||
The `Scatter-Gather` pattern suggests two scenarios - _Auction_ and _Distribution_.
|
||||
In both cases, the `aggregation` function is the same and provides all options available for the `AggregatingMessageHandler`.
|
||||
Actually the `ScatterGatherHandler` just requires an `AggregatingMessageHandler` as a constructor argument.
|
||||
See <<aggregator>> for more information.
|
||||
The `Scatter-Gather` pattern suggests two scenarios: "`auction`" and "`distribution`".
|
||||
In both cases, the `aggregation` function is the same and provides all the options available for the `AggregatingMessageHandler`.
|
||||
(Actually, the `ScatterGatherHandler` requires only an `AggregatingMessageHandler` as a constructor argument.)
|
||||
See "`<<aggregator>>`" for more information.
|
||||
|
||||
_Auction_
|
||||
===== Auction
|
||||
|
||||
The _Auction_ `Scatter-Gather` variant uses `publish-subscribe` logic for the request message, where the `scatter` channel is a `PublishSubscribeChannel` with `apply-sequence="true"`.
|
||||
However, this channel can be any `MessageChannel` implementation as is the case with the `request-channel` in the `ContentEnricher` (see <<content-enricher>>) but, in this case, the end-user should support his own custom `correlationStrategy` for the `aggregation` function.
|
||||
The auction `Scatter-Gather` variant uses "`publish-subscribe`" logic for the request message, where the "`scatter`" channel is a `PublishSubscribeChannel` with `apply-sequence="true"`.
|
||||
However, this channel can be any `MessageChannel` implementation (as is the case with the `request-channel` in the `ContentEnricher` -- see "`<<content-enricher>>`").
|
||||
However, in this case, you should create your own custom `correlationStrategy` for the `aggregation` function.
|
||||
|
||||
_Distribution_
|
||||
===== Distribution
|
||||
|
||||
The _Distribution_ `Scatter-Gather` variant is based on the `RecipientListRouter` (see <<router-implementations-recipientlistrouter>>) with all available options for the `RecipientListRouter`.
|
||||
The distribution `Scatter-Gather` variant is based on the `RecipientListRouter` (see "`<<router-implementations-recipientlistrouter>>`") with all available options for the `RecipientListRouter`.
|
||||
This is the second `ScatterGatherHandler` constructor argument.
|
||||
If you want to rely just on the default `correlationStrategy` for the `recipient-list-router` and the `aggregator`, you should specify `apply-sequence="true"`.
|
||||
Otherwise, a custom `correlationStrategy` should be supplied for the `aggregator`.
|
||||
Unlike the `PublishSubscribeChannel` (_Auction_) variant, having a `recipient-list-router` `selector` option, we can _filter_ target suppliers based on the message.
|
||||
With `apply-sequence="true"` the default `sequenceSize` will be supplied and the `aggregator` will be able to release the group correctly.
|
||||
The _Distribution_ option is mutually exclusive with the _Auction_ option.
|
||||
If you want to rely on only the default `correlationStrategy` for the `recipient-list-router` and the `aggregator`, you should specify `apply-sequence="true"`.
|
||||
Otherwise, you should supply a custom `correlationStrategy` for the `aggregator`.
|
||||
Unlike the `PublishSubscribeChannel` variant (the auction variant), having a `recipient-list-router` `selector` option lets filter target suppliers based on the message.
|
||||
With `apply-sequence="true"`, the default `sequenceSize` is supplied, and the `aggregator` can release the group correctly.
|
||||
The distribution option is mutually exclusive with the auction option.
|
||||
|
||||
In both cases, the request (_scatter_) message is enriched with the `gatherResultChannel` `QueueChannel` header, to wait for a reply message from the `aggregator`.
|
||||
For both the auction and the distribution variants, the request (scatter) message is enriched with the `gatherResultChannel` header to wait for a reply message from the `aggregator`.
|
||||
|
||||
By default, all suppliers should send their result to the `replyChannel` header (usually by omitting the `output-channel` from the ultimate endpoint).
|
||||
However, the `gatherChannel` option is also provided, allowing suppliers to send their reply to that channel for the aggregation.
|
||||
However, the `gatherChannel` option is also provided, letting suppliers send their reply to that channel for the aggregation.
|
||||
|
||||
[[scatter-gather-namespace]]
|
||||
==== Configuring a Scatter-Gather Endpoint
|
||||
|
||||
For Java and Annotation configuration, the bean definition for the `Scatter-Gather` is:
|
||||
The following example shows Java configuration for the bean definition for `Scatter-Gather`:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
@@ -75,13 +78,15 @@ public MessageHandler scatterGatherDistribution() {
|
||||
return handler;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Here, we configure the `RecipientListRouter` `distributor` bean, with `applySequence="true"` and the list of recipient channels.
|
||||
In the preceding example, we configure the `RecipientListRouter` `distributor` bean with `applySequence="true"` and the list of recipient channels.
|
||||
The next bean is for an `AggregatingMessageHandler`.
|
||||
Finally, we inject both those beans into the `ScatterGatherHandler` bean definition and mark it as a `@ServiceActivator` to wire the Scatter-Gather component into the integration flow.
|
||||
Finally, we inject both those beans into the `ScatterGatherHandler` bean definition and mark it as a `@ServiceActivator` to wire the scatter-gather component into the integration flow.
|
||||
|
||||
Configuring the `<scatter-gather>` endpoint using the XML namespace:
|
||||
The following example shows how to configure the `<scatter-gather>` endpoint by using the XML namespace:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<scatter-gather
|
||||
@@ -101,85 +106,53 @@ Configuring the `<scatter-gather>` endpoint using the XML namespace:
|
||||
</scatter-gather>
|
||||
----
|
||||
|
||||
<1> The id of the Endpoint.
|
||||
The `ScatterGatherHandler` bean is registered with `id + '.handler'` alias.
|
||||
The `RecipientListRouter` - with `id + '.scatterer'`.
|
||||
And the `AggregatingMessageHandler` with `id + '.gatherer'`.
|
||||
_Optional_ (a default id is generated value by `BeanFactory`).
|
||||
|
||||
|
||||
|
||||
<2> Lifecycle attribute signaling if the Endpoint should be started during Application Context initialization.
|
||||
In addition, the `ScatterGatherHandler` also implements `Lifecycle` and starts/stops the `gatherEndpoint`, which is created internally if a `gather-channel` is provided.
|
||||
_Optional_ (default is `true`).
|
||||
|
||||
|
||||
|
||||
<3> The channel to receive request messages to handle them in the `ScatterGatherHandler`.
|
||||
_Required_.
|
||||
|
||||
|
||||
|
||||
<4> The channel to which the Scatter-Gather will send the aggregation results.
|
||||
_Optional (because incoming messages can specify a reply channel themselves via `replyChannel` Message Header)_.
|
||||
|
||||
|
||||
|
||||
<5> The channel to send the scatter message for the _Auction_ scenario.
|
||||
_Optional_.
|
||||
Mutually exclusive with `<scatterer>` sub-element.
|
||||
|
||||
|
||||
|
||||
<6> The channel to receive replies from each supplier for the aggregation.
|
||||
is used as the `replyChannel` header in the scatter message.
|
||||
_Optional_.
|
||||
By default the `FixedSubscriberChannel` is created.
|
||||
|
||||
|
||||
|
||||
<7> Order of this component when more than one handler is subscribed to the same DirectChannel (use for load balancing purposes).
|
||||
_Optional_.
|
||||
|
||||
|
||||
|
||||
<8> Specify the phase in which the endpoint 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_.
|
||||
|
||||
|
||||
|
||||
<1> The id of the endpoint.
|
||||
The `ScatterGatherHandler` bean is registered with an alias of `id + '.handler'`.
|
||||
The `RecipientListRouter` bean is registered with an alias of `id + '.scatterer'`.
|
||||
The `AggregatingMessageHandler`bean is registered with an alias of `id + '.gatherer'`.
|
||||
Optional.
|
||||
(The `BeanFactory` generates a default `id` value.)
|
||||
<2> Lifecycle attribute signaling whether the endpoint should be started during application context initialization.
|
||||
In addition, the `ScatterGatherHandler` also implements `Lifecycle` and starts and stops `gatherEndpoint`, which is created internally if a `gather-channel` is provided.
|
||||
Optional.
|
||||
(The default is `true`.)
|
||||
<3> The channel on which to receive request messages to handle them in the `ScatterGatherHandler`.
|
||||
Required.
|
||||
<4> The channel to which the `ScatterGatherHandler` sends the aggregation results.
|
||||
Optional.
|
||||
(Incoming messages can specify a reply channel themselves in the `replyChannel` message header).
|
||||
<5> The channel to which to send the scatter message for the auction scenario.
|
||||
Optional.
|
||||
Mutually exclusive with the `<scatterer>` sub-element.
|
||||
<6> The channel on which to receive replies from each supplier for the aggregation.
|
||||
It is used as the `replyChannel` header in the scatter message.
|
||||
Optional.
|
||||
By default, the `FixedSubscriberChannel` is created.
|
||||
<7> The order of this component when more than one handler is subscribed to the same `DirectChannel` (use for load balancing purposes).
|
||||
Optional.
|
||||
<8> Specifies the phase in which the endpoint should be started and stopped.
|
||||
The startup order proceeds from lowest to highest, and the shutdown order is from highest to lowest.
|
||||
By default, this value is `Integer.MAX_VALUE`, meaning that this container starts as late as possible and stops as soon as possible.
|
||||
Optional.
|
||||
<9> The timeout interval to wait when sending a reply `Message` to the `output-channel`.
|
||||
By default the send will block for one second.
|
||||
It applies only if the output channel has some 'sending' limitations, e.g.
|
||||
a `QueueChannel` with a fixed 'capacity' and is full.
|
||||
By default, the send blocks for one second.
|
||||
It applies only if the output channel has some 'sending' limitations -- for example, a `QueueChannel` with a fixed 'capacity' that is full.
|
||||
In this case, a `MessageDeliveryException` is thrown.
|
||||
The `send-timeout` is ignored in case of `AbstractSubscribableChannel` implementations.
|
||||
In case of `group-timeout(-expression)` the `MessageDeliveryException` from the scheduled expire task leads this task to be rescheduled.
|
||||
_Optional_.
|
||||
|
||||
|
||||
|
||||
<10> Allows you to specify how long the Scatter-Gather will wait for the reply message before returning.
|
||||
By default it will wait indefinitely.
|
||||
The `send-timeout` is ignored for `AbstractSubscribableChannel` implementations.
|
||||
For `group-timeout(-expression)`, the `MessageDeliveryException` from the scheduled expire task leads this task to be rescheduled.
|
||||
Optional.
|
||||
<10> Lets you specify how long the scatter-gather waits for the reply message before returning.
|
||||
By default, it waits indefinitely.
|
||||
'null' is returned if the reply times out.
|
||||
_Optional_.
|
||||
Defaults to `-1` - indefinitely.
|
||||
|
||||
|
||||
|
||||
<11> Specify whether the Scatter-Gather must return a non-null value.
|
||||
This value is `true` by default, hence a `ReplyRequiredException` will be thrown when the underlying aggregator returns a null value after `gather-timeout`.
|
||||
Optional.
|
||||
It defaults to `-1`, meaning to wait indefinitely.
|
||||
<11> Specifies whether the scatter-gather must return a non-null value.
|
||||
This value is `true` by default.
|
||||
Consequently, a `ReplyRequiredException` is thrown when the underlying aggregator returns a null value after `gather-timeout`.
|
||||
Note, if `null` is a possibility, the `gather-timeout` should be specified to avoid an indefinite wait.
|
||||
|
||||
|
||||
|
||||
<12> The `<recipient-list-router>` options.
|
||||
_Optional_.
|
||||
Optional.
|
||||
Mutually exclusive with `scatter-channel` attribute.
|
||||
|
||||
|
||||
|
||||
<13> The `<aggregator>` options.
|
||||
_Required_.
|
||||
Required.
|
||||
====
|
||||
|
||||
@@ -1,34 +1,32 @@
|
||||
[[scripting]]
|
||||
=== Scripting support
|
||||
=== Scripting Support
|
||||
|
||||
With Spring Integration 2.1 we've added support for the http://jcp.org/aboutJava/communityprocess/pr/jsr223/[JSR223 Scripting for Java specification], introduced in Java version 6.
|
||||
This allows you to use scripts written in any supported language including Ruby/JRuby, Javascript and Groovy to provide the logic for various integration components similar to the way the Spring Expression Language (SpEL) is used in Spring Integration.
|
||||
For more information about JSR223 please refer to the http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/[documentation]
|
||||
Spring Integration 2.1 added support for the http://jcp.org/aboutJava/communityprocess/pr/jsr223/[JSR223 Scripting for Java specification], introduced in Java version 6.
|
||||
It lets you use scripts written in any supported language (including Ruby, JRuby, Javascript, and Groovy) to provide the logic for various integration components, similar to the way the Spring Expression Language (SpEL) is used in Spring Integration.
|
||||
For more information about JSR223, see the https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/prog_guide/api.html[documentation]
|
||||
|
||||
IMPORTANT: Note that this feature requires Java 6 or higher.
|
||||
Sun developed a JSR223 reference implementation which works with Java 5 but it is not officially supported and we have not tested it with Spring Integration.
|
||||
|
||||
In order to use a JVM scripting language, a JSR223 implementation for that language must be included in your class path.
|
||||
Java 6 natively supports Javascript.
|
||||
The http://www.groovy-lang.org/[Groovy] and http://jruby.org/[JRuby] projects provide JSR233 support in their standard distribution.
|
||||
Other language implementations may be available or under development.
|
||||
Please refer to the appropriate project website for more information.
|
||||
The http://www.groovy-lang.org/[Groovy] and http://jruby.org/[JRuby] projects provide JSR233 support in their standard distributions.
|
||||
|
||||
IMPORTANT: Various JSR223 language implementations have been developed by third parties.
|
||||
A particular implementation's compatibility with Spring Integration depends on how well it conforms to the specification and/or the implementer's interpretation of the specification.
|
||||
A particular implementation's compatibility with Spring Integration depends on how well it conforms to the specification and the implementer's interpretation of the specification.
|
||||
|
||||
TIP: If you plan to use Groovy as your scripting language, we recommended you use <<groovy,Spring-Integration's Groovy Support>> as it offers additional features specific to Groovy.
|
||||
_However you will find this section relevant as well_.
|
||||
However, this section is relevant as well.
|
||||
|
||||
[[scripting-config]]
|
||||
==== Script configuration
|
||||
==== Script Configuration
|
||||
|
||||
Depending on the complexity of your integration requirements scripts may be provided inline as CDATA in XML configuration or as a reference to a Spring resource containing the script.
|
||||
To enable scripting support Spring Integration defines a `ScriptExecutingMessageProcessor` which will bind the Message Payload to a variable named `payload` and the Message Headers to a `headers` variable, both accessible within the script execution context.
|
||||
All that is left for you to do is write a script that uses these variables.
|
||||
Below are a couple of sample configurations:
|
||||
Depending on the complexity of your integration requirements, scripts may be provided inline as CDATA in XML configuration or as a reference to a Spring resource that contains the script.
|
||||
To enable scripting support, Spring Integration defines a `ScriptExecutingMessageProcessor`, which binds the message payload to a variable named `payload` and the message headers to a `headers` variable, both accessible within the script execution context.
|
||||
All you need to do is write a script that uses these variables.
|
||||
The following pair of examples show sample configurations that create filters:
|
||||
|
||||
_Filter_
|
||||
.Filter
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:filter input-channel="referencedScriptInput">
|
||||
@@ -43,83 +41,104 @@ _Filter_
|
||||
</int-script:script>
|
||||
</int:filter>
|
||||
----
|
||||
====
|
||||
|
||||
Here, you see that the script can be included inline or can reference a resource location via the `location` attribute.
|
||||
Additionally the `lang` attribute corresponds to the language name (or JSR223 alias)
|
||||
As the preceding examples show, the script can be included inline or can be included by reference to a resource location (by using the `location` attribute).
|
||||
Additionally, the `lang` attribute corresponds to the language name (or its JSR223 alias)
|
||||
|
||||
Other Spring Integration endpoint elements which support scripting include _router_, _service-activator_, _transformer_, and _splitter_.
|
||||
Other Spring Integration endpoint elements that support scripting include `router`, `service-activator`, `transformer`, and `splitter`.
|
||||
The scripting configuration in each case would be identical to the above (besides the endpoint element).
|
||||
|
||||
Another useful feature of Scripting support is the ability to update (reload) scripts without having to restart the Application Context.
|
||||
To accomplish this, specify the `refresh-check-delay` attribute on the _script_ element:
|
||||
Another useful feature of scripting support is the ability to update (reload) scripts without having to restart the application context.
|
||||
To do so, specify the `refresh-check-delay` attribute on the `script` element, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-script:script location="..." refresh-check-delay="5000"/>
|
||||
----
|
||||
====
|
||||
|
||||
In the above example, the script location will be checked for updates every 5 seconds.
|
||||
If the script is updated, any invocation that occurs later than 5 seconds since the update will result in execution of the new script.
|
||||
In the preceding example, the script location is checked for updates every 5 seconds.
|
||||
If the script is updated, any invocation that occurs later than 5 seconds since the update results in running the new script.
|
||||
|
||||
Consider the following example:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-script:script location="..." refresh-check-delay="0"/>
|
||||
----
|
||||
====
|
||||
|
||||
In the above example the context will be updated with any script modifications as soon as such modification occurs, providing a simple mechanism for 'real-time' configuration.
|
||||
Any negative number value means the script will not be reloaded after initialization of the application context.
|
||||
In the preceding example, the context is updated with any script modifications as soon as such modification occurs, providing a simple mechanism for 'real-time' configuration.
|
||||
Any negative value means the script is not reloaded after initialization of the application context.
|
||||
This is the default behavior.
|
||||
The following example shows a script that never updates:
|
||||
|
||||
IMPORTANT: Inline scripts can not be reloaded.
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-script:script location="..." refresh-check-delay="-1"/>
|
||||
----
|
||||
====
|
||||
|
||||
_Script variable bindings_
|
||||
IMPORTANT: Inline scripts can not be reloaded.
|
||||
|
||||
[[scripting-script-variable-bindings]]
|
||||
===== Script Variable Bindings
|
||||
|
||||
Variable bindings are required to enable the script to reference variables externally provided to the script's execution context.
|
||||
As we have seen, `payload` and `headers` are used as binding variables by default.
|
||||
You can bind additional variables to a script via `<variable>` sub-elements:
|
||||
By default, `payload` and `headers` are used as binding variables.
|
||||
You can bind additional variables to a script by using `<variable>` elements, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<script:script lang="js" location="foo/bar/MyScript.js">
|
||||
<script:variable name="foo" value="foo"/>
|
||||
<script:variable name="bar" value="bar"/>
|
||||
<script:variable name="foo" value="thing1"/>
|
||||
<script:variable name="bar" value="thing2"/>
|
||||
<script:variable name="date" ref="date"/>
|
||||
</script:script>
|
||||
----
|
||||
====
|
||||
|
||||
As shown in the above example, you can bind a script variable either to a scalar value or a Spring bean reference.
|
||||
Note that `payload` and `headers` will still be included as binding variables.
|
||||
As shown in the preceding example, you can bind a script variable either to a scalar value or to a Spring bean reference.
|
||||
Note that `payload` and `headers` are still included as binding variables.
|
||||
|
||||
With _Spring Integration 3.0_, in addition to the `variable` sub-element, the `variables` attribute has been introduced.
|
||||
This attribute and `variable` sub-elements aren't mutually exclusive and you can combine them within one `script` component.
|
||||
However variables must be unique, regardless of where they are defined.
|
||||
Also, since _Spring Integration 3.0_, variable bindings are allowed for inline scripts too:
|
||||
With Spring Integration 3.0, in addition to the `variable` element, the `variables` attribute has been introduced.
|
||||
This attribute and the `variable` elements are not mutually exclusive, and you can combine them within one `script` component.
|
||||
However, variables must be unique, regardless of where they are defined.
|
||||
Also, since Spring Integration 3.0, variable bindings are allowed for inline scripts, too, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<service-activator input-channel="input">
|
||||
<script:script lang="ruby" variables="foo=FOO, date-ref=dateBean">
|
||||
<script:variable name="bar" ref="barBean"/>
|
||||
<script:variable name="baz" value="bar"/>
|
||||
<script:script lang="ruby" variables="thing1=THING1, date-ref=dateBean">
|
||||
<script:variable name="thing2" ref="thing2Bean"/>
|
||||
<script:variable name="thing3" value="thing2"/>
|
||||
<![CDATA[
|
||||
payload.foo = foo
|
||||
payload.foo = thing1
|
||||
payload.date = date
|
||||
payload.bar = bar
|
||||
payload.baz = baz
|
||||
payload.bar = thing2
|
||||
payload.baz = thing3
|
||||
payload
|
||||
]]>
|
||||
</script:script>
|
||||
</service-activator>
|
||||
----
|
||||
====
|
||||
|
||||
The example above shows a combination of an inline script, a `variable` sub-element and a `variables` attribute.
|
||||
The `variables` attribute is a comma-separated value, where each segment contains an '=' separated pair of the variable and its value.
|
||||
The variable name can be suffixed with `-ref`, as in the `date-ref` variable above.
|
||||
That means that the binding variable will have the name `date`, but the value will be a reference to the `dateBean` bean from the application context.
|
||||
This may be useful when using _Property Placeholder Configuration_ or command line arguments.
|
||||
The preceding example shows a combination of an inline script, a `variable` element, and a `variables` attribute.
|
||||
The `variables` attribute contains a comma-separated value, where each segment contains an '=' separated pair of the variable and its value.
|
||||
The variable name can be suffixed with `-ref`, as in the `date-ref` variable in the preceding example.
|
||||
That means that the binding variable has the name, `date`, but the value is a reference to the `dateBean` bean from the application context.
|
||||
This may be useful when using property placeholder configuration or command-line arguments.
|
||||
|
||||
If you need more control over how variables are generated, you can implement your own Java class using the `ScriptVariableGenerator` strategy:
|
||||
If you need more control over how variables are generated, you can implement your own Java class that uses the `ScriptVariableGenerator` strategy, which is defined by the following interface:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public interface ScriptVariableGenerator {
|
||||
@@ -128,11 +147,14 @@ public interface ScriptVariableGenerator {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
This interface requires you to implement the method `generateScriptVariables(Message)`.
|
||||
The Message argument allows you to access any data available in the Message payload and headers and the return value is the Map of bound variables.
|
||||
This method will be called every time the script is executed for a Message.
|
||||
All you need to do is provide an implementation of `ScriptVariableGenerator` and reference it with the `script-variable-generator` attribute:
|
||||
This interface requires you to implement the `generateScriptVariables(Message)` method.
|
||||
The message argument lets you access any data available in the message payload and headers, and the return value is the `Map` of bound variables.
|
||||
This method is called every time the script is executed for a message.
|
||||
The following example shows how to provide an implementation of `ScriptVariableGenerator` and reference it with the `script-variable-generator` attribute:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-script:script location="foo/bar/MyScript.groovy"
|
||||
@@ -140,7 +162,9 @@ All you need to do is provide an implementation of `ScriptVariableGenerator` and
|
||||
|
||||
<bean id="variableGenerator" class="foo.bar.MyScriptVariableGenerator"/>
|
||||
----
|
||||
====
|
||||
|
||||
If a `script-variable-generator` is not provided, script components use `DefaultScriptVariableGenerator`, which merges any provided `<variable>` s with _payload_ and _headers_ variables from the `Message` in its `generateScriptVariables(Message)` method.
|
||||
If a `script-variable-generator` is not provided, script components use `DefaultScriptVariableGenerator`, which merges any provided `<variable>` elements with `payload` and `headers` variables from the `Message` in its `generateScriptVariables(Message)` method.
|
||||
|
||||
IMPORTANT: You cannot provide both the `script-variable-generator` attribute and `<variable>` sub-element(s) as they are mutually exclusive.
|
||||
IMPORTANT: You cannot provide both the `script-variable-generator` attribute and `<variable>` element(s).
|
||||
They are mutually exclusive.
|
||||
|
||||
@@ -1,32 +1,30 @@
|
||||
[[security]]
|
||||
== Security in Spring Integration
|
||||
|
||||
[[security-intro]]
|
||||
=== Introduction
|
||||
Security is one of the important functions in any modern enterprise (or cloud) application.
|
||||
Moreover, it is critical for distributed systems, such as those built on Enterprise Integration Patterns.
|
||||
Messaging independence and loose coupling let target systems communicate with each other with any type of data in the message's `payload`.
|
||||
We can either trust all those messages or secure our service against "`infecting`" messages.
|
||||
|
||||
Security is one of the important functions in any modern enterprise (or cloud) application,
|
||||
moreover it is critical for distributed systems, such as those built using Enterprise
|
||||
Integration Patterns.
|
||||
Messaging independence and loosely-coupling allow target systems to communicate with each other
|
||||
with any type of data in the message's `payload`.
|
||||
We can either trust all those messages or _secure_ our service against "infecting" messages.
|
||||
|
||||
Spring Integration together with
|
||||
http://projects.spring.io/spring-security/[Spring Security] provide a simple and comprehensive way to
|
||||
Spring Integration, together with
|
||||
http://projects.spring.io/spring-security/[Spring Security], provides a simple and comprehensive way to
|
||||
secure message channels, as well as other part of the integration solution.
|
||||
|
||||
[[securing-channels]]
|
||||
=== Securing channels
|
||||
|
||||
Spring Integration provides the interceptor `ChannelSecurityInterceptor`, which extends `AbstractSecurityInterceptor` and intercepts send and receive calls on the channel.
|
||||
Access decisions are then made with reference to a `ChannelSecurityMetadataSource` which provides the metadata describing the send and receive access policies for certain channels.
|
||||
Spring Integration provides the `ChannelSecurityInterceptor` interceptor, which extends `AbstractSecurityInterceptor` and intercepts send and receive calls on the channel.
|
||||
Access decisions are then made with reference to a `ChannelSecurityMetadataSource`, which provides the metadata that describes the send and receive access policies for certain channels.
|
||||
The interceptor requires that a valid `SecurityContext` has been established by authenticating with Spring Security.
|
||||
See the Spring Security reference documentation for details.
|
||||
See the https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/[Spring Security Reference Guide] for details.
|
||||
|
||||
Namespace support is provided to allow easy configuration of security constraints.
|
||||
This consists of the secured channels tag which allows definition of one or more channel name patterns in conjunction with a definition of the security configuration for send and receive.
|
||||
Spring Integration provides Namespace support to allow easy configuration of security constraints.
|
||||
This support consists of the secured channels tag, which allows definition of one or more channel name patterns in conjunction with a definition of the security configuration for send and receive.
|
||||
The pattern is a `java.util.regexp.Pattern`.
|
||||
|
||||
The following example shows how to configure a bean that includes security and how to set up policies with patterns:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
@@ -48,12 +46,13 @@ The pattern is a `java.util.regexp.Pattern`.
|
||||
<int-security:access-policy pattern="admin.*" send-access="ROLE_ADMIN"/>
|
||||
<int-security:access-policy pattern="user.*" receive-access="ROLE_USER"/>
|
||||
</int-security:secured-channels>
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
By default the secured-channels namespace element expects a bean named _authenticationManager_ which implements `AuthenticationManager` and a bean named _accessDecisionManager_ which implements `AccessDecisionManager`.
|
||||
Where this is not the case references to the appropriate beans can be configured as attributes of the _secured-channels_ element as below.
|
||||
By default, the `secured-channels` namespace element expects a bean named `authenticationManager` (which implements `AuthenticationManager`) and a bean named `accessDecisionManager` (which implements `AccessDecisionManager`).
|
||||
Where this is not the case, references to the appropriate beans can be configured as attributes of the `secured-channels` element, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-security:secured-channels access-decision-manager="customAccessDecisionManager"
|
||||
@@ -61,14 +60,14 @@ Where this is not the case references to the appropriate beans can be configured
|
||||
<int-security:access-policy pattern="admin.*" send-access="ROLE_ADMIN"/>
|
||||
<int-security:access-policy pattern="user.*" receive-access="ROLE_USER"/>
|
||||
</int-security:secured-channels>
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
Starting with _version 4.2_, the `@SecuredChannel` annotation is available for Java & Annotation
|
||||
configuration in `@Configuration` classes.
|
||||
Starting with version 4.2, the `@SecuredChannel` annotation is available for Java configuration in `@Configuration` classes.
|
||||
|
||||
With the `@SecuredChannel` annotation, the Java configuration variant of the XML configuration above is:
|
||||
The following example shows the Java equivalent of the preceding XML examples:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
@@ -99,65 +98,52 @@ public class ContextConfiguration {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[[security-context-propagation]]
|
||||
=== SecurityContext Propagation
|
||||
=== Security Context Propagation
|
||||
|
||||
To be sure that our interaction with the application is secure, according to its security system rules, we should supply
|
||||
some _security context_ with an _authentication_ (principal) object.
|
||||
The Spring Security project provides a flexible, canonical mechanism to authenticate our application clients
|
||||
over HTTP, WebSocket or SOAP protocols (as can be done for any other integration protocol
|
||||
with a simple Spring Security extension) and it provides a `SecurityContext` for further authorization checks on the
|
||||
application objects, such as message channels.
|
||||
By default, the `SecurityContext` is tied with the current `Thread` 's execution state using the
|
||||
(`ThreadLocalSecurityContextHolderStrategy`).
|
||||
It is accessed by an AOP interceptor on secured methods to check if that `principal` of the invocation has
|
||||
sufficient permissions to call that method, for example.
|
||||
This works well with the current thread, but often, processing logic can be performed on another thread or even
|
||||
on several threads, or on to some external system(s).
|
||||
To be sure that our interaction with the application is secure, according to its security system rules, we should supply some security context with an authentication (principal) object.
|
||||
The Spring Security project provides a flexible, canonical mechanism to authenticate our application clients over HTTP, WebSocket, or SOAP protocols (as can be done for any other integration protocol with a simple Spring Security extension).
|
||||
It also provides a `SecurityContext` for further authorization checks on the application objects, such as message channels.
|
||||
By default, the `SecurityContext` is tied to the execution state of the current `Thread` by using the (`ThreadLocalSecurityContextHolderStrategy`).
|
||||
It is accessed by an AOP (Aspect-oriented Programming) interceptor on secured methods to check (for example) whether that `principal` of the invocation has sufficient permissions to call that method.
|
||||
This works well with the current thread.
|
||||
Often, though, processing logic can be performed on another thread, on several threads, or even on external systems.
|
||||
|
||||
Standard thread-bound behavior is easy to configure if our application is built on the Spring Integration components
|
||||
and its message channels.
|
||||
In this case, the secured objects may be any service activator or transformer, secured with a
|
||||
`MethodSecurityInterceptor` in their `<request-handler-advice-chain>` (see <<message-handler-advice-chain>>)
|
||||
or even `MessageChannel` (see <<securing-channels>> above).
|
||||
When using `DirectChannel` communication, the `SecurityContext` is available
|
||||
automatically, because the downstream flow runs on the current thread.
|
||||
But in case of the `QueueChannel`, `ExecutorChannel` and `PublishSubscribeChannel` with an `Executor`, messages are
|
||||
transferred from one thread to another (or several) by the nature of those channels.
|
||||
In order to support such scenarios,
|
||||
we can either transfer an `Authentication` object within the message headers and extract and authenticate it on the
|
||||
other side before secured object access.
|
||||
Or, we can _propagate_ the `SecurityContext` to the thread receiving the transferred message.
|
||||
In this case, the secured objects can be any service activator or transformer, secured with a
|
||||
`MethodSecurityInterceptor` in their `<request-handler-advice-chain>` (see "`<<message-handler-advice-chain>>`") or even `MessageChannel` (see "`<<securing-channels>>`", earlier).
|
||||
When using `DirectChannel` communication, the `SecurityContext` is automatically available, because the downstream flow runs on the current thread.
|
||||
However, in the cases of the `QueueChannel`, `ExecutorChannel`, and `PublishSubscribeChannel` with an `Executor`, messages are transferred from one thread to another (or several) by the nature of those channels.
|
||||
In order to support such scenarios, we have two choices:
|
||||
|
||||
Starting with _version 4.2_ `SecurityContext` propagation has been introduced.
|
||||
It is implemented as a `SecurityContextPropagationChannelInterceptor`, which can simply be added to any `MessageChannel`
|
||||
or configured as a `@GlobalChannelInterceptor`.
|
||||
The logic of this interceptor is based on the `SecurityContext` extraction from the current thread from the `preSend()`
|
||||
method, and its populating to another thread from the `postReceive()` (`beforeHandle()`) method.
|
||||
Actually, this interceptor is an extension of the more generic `ThreadStatePropagationChannelInterceptor`, which wraps
|
||||
the message-to-send together with the state-to-propagate in an internal `Message<?>` extension -
|
||||
`MessageWithThreadState<S>`, - on one side and extracts the original message back and state-to-propagate on another.
|
||||
The `ThreadStatePropagationChannelInterceptor` can be extended for any context propagation use-case and
|
||||
`SecurityContextPropagationChannelInterceptor` is a good sample on the matter.
|
||||
* Transfer an `Authentication` object within the message headers and extract and authenticate it on the other side before secured object access.
|
||||
* Propagate the `SecurityContext` to the thread that receives the transferred message.
|
||||
|
||||
IMPORTANT: Since the logic of the `ThreadStatePropagationChannelInterceptor` is based on message modification
|
||||
(it returns an internal `MessageWithThreadState` object to send), you should be careful when combining this
|
||||
interceptor with any other which is intended to modify messages too, e.g. through the
|
||||
`MessageBuilder.withPayload(...)...build()` - the state-to-propagate may be lost.
|
||||
In most cases to overcome the issue, it's sufficient to order interceptors for the channel and ensure the
|
||||
`ThreadStatePropagationChannelInterceptor` is the last one in the stack.
|
||||
Version 4.2 introduced `SecurityContext` propagation.
|
||||
It is implemented as a `SecurityContextPropagationChannelInterceptor`, which you can add to any `MessageChannel` or configure as a `@GlobalChannelInterceptor`.
|
||||
The logic of this interceptor is based on the `SecurityContext` extraction from the current thread (from the `preSend()` method) and its populating to another thread from the `postReceive()` (`beforeHandle()`) method.
|
||||
Actually, this interceptor is an extension of the more generic `ThreadStatePropagationChannelInterceptor`, which wraps the message to send with the state to propagate in an internal `Message<?>` extension (`MessageWithThreadState<S>`) on one side and extracts the original message and the state to propagate on the other side.
|
||||
You can extend the `ThreadStatePropagationChannelInterceptor` for any context propagation use case, and `SecurityContextPropagationChannelInterceptor` is a good example of doing so.
|
||||
|
||||
IMPORTANT: The logic of the `ThreadStatePropagationChannelInterceptor` is based on message modification (it returns an internal `MessageWithThreadState` object to send).
|
||||
Consequently, you should be careful when combining this interceptor with any other that can also modify messages (for example, through the `MessageBuilder.withPayload(...)...build()`).
|
||||
The state to propagate may be lost.
|
||||
In most cases, to overcome the issue, you can order the interceptors for the channel and ensure the `ThreadStatePropagationChannelInterceptor` is the last one in the stack.
|
||||
|
||||
Propagation and population of `SecurityContext` is just one half of the work.
|
||||
Since the message isn't an owner of the threads in the message flow and we should be sure that we are secure against
|
||||
any incoming messages, we have to _clean up_ the `SecurityContext` from `ThreadLocal`.
|
||||
The `SecurityContextPropagationChannelInterceptor` provides `afterMessageHandled()` interceptor's method
|
||||
implementation to do the clean up operation to free the Thread in the end of invocation from that propagated principal.
|
||||
This means that, when the thread that processes the handed-off message, completes the processing of the message
|
||||
(successfully or otherwise), the context is cleared so that it can't be inadvertently be used when processing another
|
||||
message.
|
||||
Since the message is not an owner of the threads in the message flow and we should be sure that we are secure against any incoming messages, we have to clean up the `SecurityContext` from `ThreadLocal`.
|
||||
The `SecurityContextPropagationChannelInterceptor` provides the `afterMessageHandled()` interceptor method implementation.
|
||||
It cleans up operation by freeing the thread at the end of invocation from that propagated principal.
|
||||
This means that, when the thread that processes the handed-off message finishes processing the message (successful or otherwise), the context is cleared so that it cannot inadvertently be used when processing another message.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
When working with an <<async-gateway,asynchronous gateway>>, you should use an appropriate `AbstractDelegatingSecurityContextSupport` implementation from Spring Security http://docs.spring.io/spring-security/site/docs/current/reference/html/concurrency.html[Concurrency Support], to let security context propagation be ensured over gateway invocation.
|
||||
The following example shows how to do so:
|
||||
|
||||
NOTE: When working with <<async-gateway,Asynchronous Gateway>>, you should use an appropriate `AbstractDelegatingSecurityContextSupport` implementation from Spring Security http://docs.spring.io/spring-security/site/docs/current/reference/html/concurrency.html[Concurrency Support], when security context propagation should be ensured over gateway invocation:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -184,3 +170,4 @@ public interface SecuredGateway {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
@@ -1,78 +1,83 @@
|
||||
[[service-activator]]
|
||||
=== Service Activator
|
||||
|
||||
[[service-activator-introduction]]
|
||||
==== Introduction
|
||||
|
||||
The Service Activator is the endpoint type for connecting any Spring-managed Object to an input channel so that it may play the role of a service.
|
||||
The service activator is the endpoint type for connecting any Spring-managed object to an input channel so that it may play the role of a service.
|
||||
If the service produces output, it may also be connected to an output channel.
|
||||
Alternatively, an output producing service may be located at the end of a processing pipeline or message flow in which case, the inbound Message's "replyChannel" header can be used.
|
||||
This is the default behavior if no output channel is defined and, as with most of the configuration options you'll see here, the same behavior actually applies for most of the other components we have seen.
|
||||
Alternatively, an output-producing service may be located at the end of a processing pipeline or message flow, in which case the inbound message's `replyChannel` header can be used.
|
||||
This is the default behavior if no output channel is defined.
|
||||
As with most of the configuration options described here, the same behavior actually applies for most of the other components.
|
||||
|
||||
[[service-activator-namespace]]
|
||||
==== Configuring Service Activator
|
||||
|
||||
To create a Service Activator, use the 'service-activator' element with the 'input-channel' and 'ref' attributes:
|
||||
To create a service activator, use the 'service-activator' element with the 'input-channel' and 'ref' attributes, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:service-activator input-channel="exampleChannel" ref="exampleHandler"/>
|
||||
----
|
||||
====
|
||||
|
||||
The configuration above selects all methods from the `exampleHandler` which meet one of the Messaging requirements:
|
||||
The preceding configuration selects all the methods from the `exampleHandler` that meet one of the messaging requirements, which are as follows:
|
||||
|
||||
- annotated with `@ServiceActivator`;
|
||||
- is `public`;
|
||||
- not `void` return if `requiresReply == true`.
|
||||
* annotated with `@ServiceActivator`
|
||||
* is `public`
|
||||
* not return `void` if `requiresReply == true`
|
||||
|
||||
The target method for invocation at runtime is selected for each request message by their `payload` type.
|
||||
Or as a fallback to `Message<?>` type if such a method is present on target class.
|
||||
The target method for invocation at runtime is selected for each request message by their `payload` type or as a fallback to the `Message<?>` type if such a method is present on target class.
|
||||
|
||||
Starting with _version 5.0_, one service method can be marked with the `@org.springframework.integration.annotation.Default` as a fallback for all non-matching cases.
|
||||
This can be useful when using <<content-type-conversion>> with the target method being invoked after conversion.
|
||||
Starting with version 5.0, one service method can be marked with the `@org.springframework.integration.annotation.Default` as a fallback for all non-matching cases.
|
||||
This can be useful when using <<content-type-conversion, content-type conversion>> with the target method being invoked after conversion.
|
||||
|
||||
To delegate to an explicitly defined method of any object, simply add the "method" attribute.
|
||||
To delegate to an explicitly defined method of any object, you can add the `method` attribute, as the following example shows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<int:service-activator input-channel="exampleChannel" ref="somePojo" method="someMethod"/>
|
||||
----
|
||||
|
||||
In either case, when the service method returns a non-null value, the endpoint will attempt to send the reply message to an appropriate reply channel.
|
||||
To determine the reply channel, it will first check if an "output-channel" was provided in the endpoint configuration:
|
||||
In either case, when the service method returns a non-null value, the endpoint tries to send the reply message to an appropriate reply channel.
|
||||
To determine the reply channel, it first checks whether an `output-channel` was provided in the endpoint configuration, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:service-activator input-channel="exampleChannel" output-channel="replyChannel"
|
||||
ref="somePojo" method="someMethod"/>
|
||||
----
|
||||
====
|
||||
|
||||
If the method returns a result and no "output-channel" is defined, the framework will then check the request Message's `replyChannel` header value.
|
||||
If that value is available, it will then check its type.
|
||||
If it is a `MessageChannel`, the reply message will be sent to that channel.
|
||||
If it is a `String`, then the endpoint will attempt to resolve the channel name to a channel instance.
|
||||
If the channel cannot be resolved, then a `DestinationResolutionException` will be thrown.
|
||||
It it can be resolved, the Message will be sent there.
|
||||
If the request Message doesn't have `replyChannel` header and and the `reply` object is a `Message`, its `replyChannel` header is consulted for a target destination.
|
||||
This is the technique used for Request Reply messaging in Spring Integration, and it is also an example of the Return Address pattern.
|
||||
If the method returns a result and no `output-channel` is defined, the framework then checks the request message's `replyChannel` header value.
|
||||
If that value is available, it then checks its type.
|
||||
If it is a `MessageChannel`, the reply message is sent to that channel.
|
||||
If it is a `String`, the endpoint tries to resolve the channel name to a channel instance.
|
||||
If the channel cannot be resolved, a `DestinationResolutionException` is thrown.
|
||||
It it can be resolved, the message is sent there.
|
||||
If the request message does not have a `replyChannel` header and the `reply` object is a `Message`, its `replyChannel` header is consulted for a target destination.
|
||||
This is the technique used for request-reply messaging in Spring Integration, and it is also an example of the return address pattern.
|
||||
|
||||
If your method returns a result, and you want to discard it and end the flow, you should configure the `output-channel` to send to a `NullChannel`.
|
||||
For convenience, the framework registers one with the name `nullChannel`.
|
||||
See <<channel-special-channels>> for more information.
|
||||
If your method returns a result and you want to discard it and end the flow, you should configure the `output-channel` to send to a `NullChannel`.
|
||||
For convenience, the framework registers one with the name, `nullChannel`.
|
||||
See "`<<channel-special-channels>>`" for more information.
|
||||
|
||||
The Service Activator is one of those components that is not required to produce a reply message.
|
||||
If your method returns `null` or has a `void` return type, the Service Activator exits after the method invocation, without any signals.
|
||||
This behavior can be controlled by the `AbstractReplyProducingMessageHandler.requiresReply` option, also exposed as `requires-reply` when configuring with the XML namespace.
|
||||
The service activator is one of those components that is not required to produce a reply message.
|
||||
If your method returns `null` or has a `void` return type, the service activator exits after the method invocation, without any signals.
|
||||
This behavior can be controlled by the `AbstractReplyProducingMessageHandler.requiresReply` option, which is also exposed as `requires-reply` when configuring with the XML namespace.
|
||||
If the flag is set to `true` and the method returns null, a `ReplyRequiredException` is thrown.
|
||||
|
||||
The argument in the service method could be either a Message or an arbitrary type.
|
||||
If the latter, then it will be assumed that it is a Message payload, which will be extracted from the message and injected into such service method.
|
||||
This is generally the recommended approach as it follows and promotes a POJO model when working with Spring Integration.
|
||||
Arguments may also have @Header or @Headers annotations as described in <<annotations>>
|
||||
The argument in the service method could be either a message or an arbitrary type.
|
||||
If the latter, then it is assumed to be a message payload, which is extracted from the message and injected into the service method.
|
||||
We generally recommend this approach, as it follows and promotes a POJO model when working with Spring Integration.
|
||||
Arguments may also have `@Header` or `@Headers` annotations, as described in "`<<annotations>>`".
|
||||
|
||||
NOTE: The service method is not required to have any arguments at all, which means you can implement event-style Service Activators, where all you care about is an invocation of the service method, not worrying about the contents of the message.
|
||||
Think of it as a NULL JMS message.
|
||||
An example use-case for such an implementation could be a simple counter/monitor of messages deposited on the input channel.
|
||||
NOTE: The service method is not required to have any arguments, which means you can implement event-style service activators (where all you care about is an invocation of the service method) and not worry about the contents of the message.
|
||||
Think of it as a null JMS message.
|
||||
An example use case for such an implementation is a simple counter or monitor of messages deposited on the input channel.
|
||||
|
||||
Starting with _version 4.1_ the framework correct converts Message properties (`payload` and `headers`) to the Java 8 `Optional` POJO method parameters:
|
||||
Starting with version 4.1, the framework correctly converts message properties (`payload` and `headers`) to the Java 8 `Optional` POJO method parameters, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class MyBean {
|
||||
@@ -90,63 +95,72 @@ public class MyBean {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Using a `ref` attribute is generally recommended if the custom Service Activator handler implementation can be reused in other `<service-activator>` definitions.
|
||||
However if the custom Service Activator handler implementation is only used within a single definition of the `<service-activator>`, you can provide an inner bean definition:
|
||||
We generally recommend using a `ref` attribute if the custom service activator handler implementation can be reused in other `<service-activator>` definitions.
|
||||
However, if the custom service activator handler implementation is only used within a single definition of the `<service-activator>`, you can provide an inner bean definition, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:service-activator id="exampleServiceActivator" input-channel="inChannel"
|
||||
output-channel = "outChannel" method="foo">
|
||||
<beans:bean class="org.foo.ExampleServiceActivator"/>
|
||||
output-channel = "outChannel" method="someMethod">
|
||||
<beans:bean class="org.something.ExampleServiceActivator"/>
|
||||
</int:service-activator>
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: Using both the "ref" attribute and an inner handler definition in the same `<service-activator>` configuration is not allowed, as it creates an ambiguous condition and will result in an Exception being thrown.
|
||||
NOTE: Using both the `ref` attribute and an inner handler definition in the same `<service-activator>` configuration is not allowed, as it creates an ambiguous condition and results in an exception being thrown.
|
||||
|
||||
IMPORTANT: If the "ref" attribute references a bean that extends `AbstractMessageProducingHandler` (such as handlers provided by the framework itself), the configuration is optimized by injecting the output channel into the handler directly.
|
||||
In this case, each "ref" must be to a separate bean instance (or a `prototype`-scoped bean), or use the inner `<bean/>` configuration type.
|
||||
If you inadvertently reference the same message handler from multiple beans, you will get a configuration exception.
|
||||
IMPORTANT: If the `ref` attribute references a bean that extends `AbstractMessageProducingHandler` (such as handlers provided by the framework itself), the configuration is optimized by injecting the output channel into the handler directly.
|
||||
In this case, each `ref` must be to a separate bean instance (or a `prototype`-scoped bean) or use the inner `<bean/>` configuration type.
|
||||
If you inadvertently reference the same message handler from multiple beans, you get a configuration exception.
|
||||
|
||||
_Service Activators and the Spring Expression Language (SpEL)_
|
||||
===== Service Activators and the Spring Expression Language (SpEL)
|
||||
|
||||
Since Spring Integration 2.0, Service Activators can also benefit from SpEL (http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html).
|
||||
Since Spring Integration 2.0, service activators can also benefit from https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#expressions[SpEL].
|
||||
|
||||
For example, you may now invoke any bean method without pointing to the bean via a `ref` attribute or including it as an inner bean definition.
|
||||
For example:
|
||||
For example, you can invoke any bean method without pointing to the bean in a `ref` attribute or including it as an inner bean definition, as follows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:service-activator input-channel="in" output-channel="out"
|
||||
expression="@accountService.processAccount(payload, headers.accountId)"/>
|
||||
|
||||
<bean id="accountService" class="foo.bar.Account"/>
|
||||
<bean id="accountService" class="thing1.thing2.Account"/>
|
||||
----
|
||||
====
|
||||
|
||||
In the above configuration instead of injecting 'accountService' using a `ref` or as an inner bean, we are simply using SpEL's `@beanId` notation and invoking a method which takes a type compatible with Message payload.
|
||||
We are also passing a header value.
|
||||
As you can see, any valid SpEL expression can be evaluated against any content in the Message.
|
||||
For simple scenarios your _Service Activators_ do not even have to reference a bean if all logic can be encapsulated by such an expression.
|
||||
In the preceding configuration, instead of injecting 'accountService' by using a `ref` or as an inner bean, we use SpEL's `@beanId` notation and invoke a method that takes a type compatible with the message payload.
|
||||
We also pass a header value.
|
||||
Any valid SpEL expression can be evaluated against any content in the message.
|
||||
For simple scenarios, your service activators need not reference a bean if all logic can be encapsulated in such an expression, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:service-activator input-channel="in" output-channel="out" expression="payload * 2"/>
|
||||
----
|
||||
====
|
||||
|
||||
In the above configuration our service logic is to simply multiply the payload value by 2, and SpEL lets us handle it relatively easy.
|
||||
In the preceding configuration, our service logic is to multiply the payload value by two.
|
||||
SpEL lets us handle it relatively easily.
|
||||
|
||||
See <<java-dsl-handle>> in Java DSL chapter for more information about configuring Service Activator.
|
||||
See "`<<java-dsl-handle>>`" in the Java DSL chapter for more information about configuring service activator.
|
||||
|
||||
[[async-service-activator]]
|
||||
==== Asynchronous Service Activator
|
||||
|
||||
The service activator is invoked by the calling thread; this would be some upstream thread if the input channel is a
|
||||
`SubscribableChannel`, or a poller thread for a `PollableChannel`.
|
||||
If the service returns a `ListenableFuture<?>` the default action is to send that as the payload of the message sent
|
||||
The service activator is invoked by the calling thread.
|
||||
This is an upstream thread if the input channel is a
|
||||
`SubscribableChannel` or a poller thread for a `PollableChannel`.
|
||||
If the service returns a `ListenableFuture<?>`, the default action is to send that as the payload of the message sent
|
||||
to the output (or reply) channel.
|
||||
Starting with _version 4.3_, you can now set the `async` attribute to true (`setAsync(true)` when using
|
||||
Starting with version 4.3, you can now set the `async` attribute to `true` (by using `setAsync(true)` when using
|
||||
Java configuration).
|
||||
If the service returns a `ListenableFuture<?>` when this is true, the calling thread is released immediately, and the
|
||||
reply message is sent on the thread (from within your service) that completes the future.
|
||||
This is particularly advantageous for long-running services using a `PollableChannel` because the poller thread is
|
||||
freed up to perform other services within the framework.
|
||||
If the service returns a `ListenableFuture<?>` when this the `async` attribute is set to `true`, the calling thread is released immediately and the reply message is sent on the thread (from within your service) that completes the future.
|
||||
This is particularly advantageous for long-running services that use a `PollableChannel`, because the poller thread is
|
||||
released to perform other services within the framework.
|
||||
|
||||
If the service completes the future with an `Exception`, normal error processing will occur - an `ErrorMessage` is
|
||||
sent to the `errorChannel` message header, if present or otherwise to the default `errorChannel` (if available).
|
||||
If the service completes the future with an `Exception`, normal error processing occurs. An `ErrorMessage` is sent to the `errorChannel` message header, if present. Otherwise, an `ErrorMessage` is sent to the default `errorChannel` (if available).
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,38 +1,37 @@
|
||||
[[jmx-shutdown]]
|
||||
=== Orderly Shutdown
|
||||
|
||||
As described in <<jmx-mbean-exporter>>, the MBean exporter provides a JMX operation _stopActiveComponents_, which is used to stop the application in an orderly manner.
|
||||
The operation has a single long parameter.
|
||||
The parameter indicates how long (in milliseconds) the operation will wait to allow in-flight messages to complete.
|
||||
As described in "<<jmx-mbean-exporter>>", the MBean exporter provides a JMX operation called `stopActiveComponents`, which is used to stop the application in an orderly manner.
|
||||
The operation has a single `Long` parameter.
|
||||
The parameter indicates how long (in milliseconds) the operation waits to allow in-flight messages to complete.
|
||||
The operation works as follows:
|
||||
|
||||
The first step calls `beforeShutdown()` on all beans that implement `OrderlyShutdownCapable`.
|
||||
This allows such components to prepare for shutdown.
|
||||
Examples of components that implement this interface, and what they do with this call include: JMS and AMQP message-driven adapters stop their listener containers; TCP server connection factories stop accepting new connections (while keeping existing connections open); TCP inbound endpoints drop (log) any new messages received; http inbound endpoints return _503 - Service Unavailable_ for any new requests.
|
||||
|
||||
The second step stops any active channels, such as JMS- or AMQP-backed channels.
|
||||
|
||||
The third step stops all `MessageSource` s.
|
||||
|
||||
The fourth step stops all inbound `MessageProducer` s (that are not `OrderlyShutdownCapable`).
|
||||
|
||||
The fifth step waits for any remaining time left, as defined by the value of the long parameter passed in to the operation.
|
||||
This is intended to allow any in-flight messages to complete their journeys.
|
||||
. Call `beforeShutdown()` on all beans that implement `OrderlyShutdownCapable`.
|
||||
+
|
||||
Doing so lets such components prepare for shutdown.
|
||||
Examples of components that implement this interface and what they do with this call include JMS and AMQP message-driven adapters that stop their listener containers, TCP server connection factories that stop accepting new connections (while keeping existing connections open), TCP inbound endpoints that drop (log) any new messages received, and HTTP inbound endpoints that return `503 - Service Unavailable` for any new requests.
|
||||
. Stop any active channels, such as JMS- or AMQP-backed channels.
|
||||
. Stop all `MessageSource` instances.
|
||||
. Stop all inbound `MessageProducer` s (that are not `OrderlyShutdownCapable`).
|
||||
. Wait for any remaining time left, as defined by the value of the `Long` parameter passed in to the operation.
|
||||
+
|
||||
Doing so lets any in-flight messages complete their journeys.
|
||||
It is therefore important to select an appropriate timeout when invoking this operation.
|
||||
. Call `afterShutdown()` on all `OrderlyShutdownCapable` components.
|
||||
+
|
||||
Doing so lets such components perform final shutdown tasks (closing all open sockets, for example).
|
||||
|
||||
The sixth step calls `afterShutdown()` on all OrderlyShutdownCapable components.
|
||||
This allows such components to perform final shutdown tasks (closing all open sockets, for example).
|
||||
As discussed in "`<<jmx-mbean-shutdown>>`", this operation can be invoked by using JMX.
|
||||
If you wish to programmatically invoke the method, you need to inject or otherwise get a reference to the `IntegrationMBeanExporter`.
|
||||
If no `id` attribute is provided on the `<int-jmx:mbean-export/>` definition, the bean has a generated name.
|
||||
This name contains a random component to avoid `ObjectName` collisions if multiple Spring Integration contexts exist in the same JVM (`MBeanServer`).
|
||||
|
||||
As discussed in <<jmx-mbean-shutdown>> this operation can be invoked using JMX.
|
||||
If you wish to programmatically invoke the method, you will need to inject, or otherwise get a reference to, the `IntegrationMBeanExporter`.
|
||||
If no `id` attribute is provided on the `<int-jmx:mbean-export/>` definition, the bean will have a generated name.
|
||||
This name contains a random component to avoid `ObjectName` collisions if multiple Spring Integration contexts exist in the same JVM (MBeanServer).
|
||||
For this reason, if you wish to invoke the method programmatically, we recommend that you provide the exporter with an `id` attribute so that you can easily access it in the application context.
|
||||
|
||||
For this reason, if you wish to invoke the method programmatically, it is recommended that you provide the exporter with an `id` attribute so it can easily be accessed in the application context.
|
||||
Finally, the operation can be invoked by using the `<control-bus>` element.
|
||||
See the https://github.com/spring-projects/spring-integration-samples/tree/master/intermediate/monitoring[monitoring Spring Integration sample application] for details.
|
||||
|
||||
Finally, the operation can be invoked using the `<control-bus>`; see the https://github.com/spring-projects/spring-integration-samples/tree/master/intermediate/monitoring[monitoring Spring Integration sample application] for details.
|
||||
|
||||
IMPORTANT: The above algorithm was improved in _version 4.1_.
|
||||
NOTE: The algorithm described earlier was improved in version 4.1.
|
||||
Previously, all task executors and schedulers were stopped.
|
||||
This could cause mid-flow messages in `QueueChannel` s to remain.
|
||||
Now, the shutdown leaves pollers running in order to allow these messages to be drained and processed.
|
||||
This could cause mid-flow messages in `QueueChannel` instances to remain.
|
||||
Now the shutdown leaves pollers running, to let these messages be drained and processed.
|
||||
|
||||
@@ -1,68 +1,72 @@
|
||||
[[spel]]
|
||||
== Spring Expression Language (SpEL)
|
||||
|
||||
[[spel-intro]]
|
||||
=== Introduction
|
||||
You can configure many Spring Integration components by using expressions written in the http://static.springsource.org/spring-framework/docs/current/spring-framework-reference/html/expressions.html[Spring Expression Language].
|
||||
|
||||
Many Spring Integration components can be configured using expressions.
|
||||
These expressions are written in the http://static.springsource.org/spring-framework/docs/current/spring-framework-reference/html/expressions.html[Spring Expression Language].
|
||||
In most cases, the `#root` object is the `Message`, which has two properties (`headers` and `payload`) that allow such expressions as `payload`, `payload.thing`, `headers['my.header']`, and so on.
|
||||
|
||||
In most cases, the _#root_ object is the `Message` which, of course, has two properties - `headers` and `payload` - allowing such expressions as `payload`, `payload.foo`, `headers['my.header']` etc.
|
||||
In some cases, additional variables are provided.
|
||||
For example, `<int-http:inbound-gateway/>` provides `#requestParams` (parameters from the HTTP request) and `#pathVariables` (values from path placeholders in the URI).
|
||||
|
||||
In some cases, additional variables are provided, for example the `<int-http:inbound-gateway/>` provides `#requestParams` (parameters from the HTTP request) and `#pathVariables` (values from path placeholders in the URI).
|
||||
|
||||
For all SpEL expressions, a `BeanResolver` is available, enabling references to any bean in the application context.
|
||||
For example `@myBean.foo(payload)`.
|
||||
In addition, two `PropertyAccessors` are available; a `MapAccessor` enables accessing values in a `Map` using a key, and a `ReflectivePropertyAccessor` which allows access to fields and or JavaBean compliant properties (using getters and setters).
|
||||
This is how the `Message` headers and payload properties are accessible.
|
||||
For all SpEL expressions, a `BeanResolver` is available to enable references to any bean in the application context (for example, `@myBean.foo(payload)`).
|
||||
In addition, two `PropertyAccessors` are available.
|
||||
A `MapAccessor` enables accessing values in a `Map` by using a key and a `ReflectivePropertyAccessor`, which allows access to fields and JavaBean compliant properties (by using getters and setters).
|
||||
This is how you can access the `Message` headers and payload properties.
|
||||
|
||||
[[spel-customization]]
|
||||
=== SpEL Evaluation Context Customization
|
||||
|
||||
Starting with Spring Integration 3.0, it is possible to add additional `PropertyAccessor` s to the SpEL evaluation contexts used by the framework.
|
||||
The framework provides the `JsonPropertyAccessor` which can be used (read-only) to access fields from a `JsonNode`, or JSON in a `String`.
|
||||
Or you can create your own `PropertyAccessor` if you have specific needs.
|
||||
Starting with Spring Integration 3.0, you can add additional `PropertyAccessor` instances to the SpEL evaluation contexts used by the framework.
|
||||
The framework provides the (read-only) `JsonPropertyAccessor`, which you can use to access fields from a `JsonNode` or JSON in a `String`.
|
||||
You can also create your own `PropertyAccessor` if you have specific needs.
|
||||
|
||||
In addition, custom functions can be added.
|
||||
In addition, you can add custom functions.
|
||||
Custom functions are `static` methods declared on a class.
|
||||
Functions and property accessors are available in any SpEL expression used throughout the framework.
|
||||
|
||||
The following configuration shows how to directly configure the `IntegrationEvaluationContextFactoryBean` with custom property accessors and functions.
|
||||
However, for convenience, namespace support is provided for both, as described in the following sections, and the framework will automatically configure the factory bean on your behalf.
|
||||
The following configuration shows how to directly configure the `IntegrationEvaluationContextFactoryBean` with custom property accessors and functions:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="integrationEvaluationContext"
|
||||
class="org.springframework.integration.config.IntegrationEvaluationContextFactoryBean">
|
||||
<property name="propertyAccessors">
|
||||
<util:map>
|
||||
<entry key="foo">
|
||||
<bean class="foo.MyCustomPropertyAccessor"/>
|
||||
<entry key="things">
|
||||
<bean class="things.MyCustomPropertyAccessor"/>
|
||||
</entry>
|
||||
</util:map>
|
||||
</property>
|
||||
<property name="functions">
|
||||
<map>
|
||||
<entry key="barcalc" value="#{T(foo.MyFunctions).getMethod('calc', T(foo.MyBar))}"/>
|
||||
<entry key="barcalc" value="#{T(things.MyFunctions).getMethod('calc', T(things.MyThing))}"/>
|
||||
</map>
|
||||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
This factory bean definition will override the default `integrationEvaluationContext` bean definition, adding the custom accessor to the list (which also includes the standard accessors mentioned above), and one custom function.
|
||||
For convenience, Spring Integration provides namespace support for both property accessors and functions, as described in the following sections.
|
||||
The framework automatically configures the factory bean on your behalf.
|
||||
|
||||
This factory bean definition overrides the default `integrationEvaluationContext` bean definition.
|
||||
It adds the custom accessor and one custom function to the list (which also includes the standard accessors <<spel,mentioned earlier>>).
|
||||
|
||||
Note that custom functions are static methods.
|
||||
In the above example, the custom function is a static method `calc` on class `MyFunctions` and takes a single parameter of type `MyBar`.
|
||||
In the preceding example, the custom function is a static method called `calc` on a class called `MyFunctions` and takes a single parameter of type `MyThing`.
|
||||
|
||||
Say you have a `Message` with a payload that has a type `MyFoo` on which you need to perform some action to create a `MyBar` object from it, and you then want to invoke a custom function `calc` on that object.
|
||||
Suppose you have a `Message` with a payload that has a type of `MyThing`.
|
||||
Further suppose that you need to perform some action to create an object called `MyObject` from `MyThing` and then invoke a custom function called `calc` on that object.
|
||||
|
||||
The standard property accessors wouldn't know how to get a `MyBar` from a `MyFoo` so you could write and configure a custom property accessor to do so.
|
||||
So, your final expression might be `"#barcalc(payload.myBar)"`.
|
||||
The standard property accessors do not know how to get a `MyObject` from a `MyThing`, so you could write and configure a custom property accessor to do so.
|
||||
As a result, your final expression might be `"#barcalc(payload.myObject)"`.
|
||||
|
||||
The factory bean has another property `typeLocator` which allows you to customize the `TypeLocator` used during SpEL evaluation.
|
||||
This might be necessary when running in some environments that use a non-standard `ClassLoader`.
|
||||
In the following example, SpEL expressions will always use the bean factory's class loader:
|
||||
The factory bean has another property (`typeLocator`), which lets you customize the `TypeLocator` used during SpEL evaluation.
|
||||
You might need to do so running in some environments that use a non-standard `ClassLoader`.
|
||||
In the following example, SpEL expressions always use the bean factory's class loader:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="integrationEvaluationContext"
|
||||
@@ -74,39 +78,43 @@ In the following example, SpEL expressions will always use the bean factory's cl
|
||||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
[[spel-functions]]
|
||||
=== SpEL Functions
|
||||
|
||||
Namespace support is provided for easy addition of SpEL custom functions.
|
||||
Spring Integration provides namespace support to let you create SpEL custom functions.
|
||||
You can specify `<spel-function/>` components to provide http://static.springsource.org/spring-framework/docs/current/spring-framework-reference/html/expressions.html#expressions-ref-functions[custom SpEL functions] to the `EvaluationContext` used throughout the framework.
|
||||
Instead of configuring the factory bean above, simply add one or more of these components and the framework will automatically add them to the default _integrationEvaluationContext_ factory bean.
|
||||
Instead of configuring the factory bean shown earlier, you can add one or more of these components, and the framework automatically adds them to the default `integrationEvaluationContext` factory bean.
|
||||
|
||||
For example, assuming we have a useful static method to evaluate XPath:
|
||||
For example, suppose you have a useful static method to evaluate XPath.
|
||||
The following example shows how you can create a custom function to use that method:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:spel-function id="xpath"
|
||||
class="com.foo.test.XPathUtils" method="evaluate(java.lang.String, java.lang.Object)"/>
|
||||
class="com.something.test.XPathUtils" method="evaluate(java.lang.String, java.lang.Object)"/>
|
||||
|
||||
<int:transformer input-channel="in" output-channel="out"
|
||||
expression="#xpath('//foo/@bar', payload)" />
|
||||
|
||||
expression="#xpath('//things/@mythings', payload)" />
|
||||
----
|
||||
====
|
||||
|
||||
With this sample:
|
||||
Given the preceding example:
|
||||
|
||||
* The default `IntegrationEvaluationContextFactoryBean` bean with id _integrationEvaluationContext_ is registered with the application context.
|
||||
* The default `IntegrationEvaluationContextFactoryBean` bean with an ID of `integrationEvaluationContext` is registered with the application context.
|
||||
|
||||
* The `<spel-function/>` is parsed and added to the `functions` Map of _integrationEvaluationContext_ as map entry with `id` as the key and the static `Method` as the value.
|
||||
* The `<spel-function/>` is parsed and added to the `functions` `Map` of `integrationEvaluationContext` as a map entry with its `id` as the key and the static `Method` as the value.
|
||||
|
||||
* The _integrationEvaluationContext_ factory bean creates a new `StandardEvaluationContext` instance, and it is configured with the default `PropertyAccessor` s, `BeanResolver` and the custom functions.
|
||||
* The `integrationEvaluationContext` factory bean creates a new `StandardEvaluationContext` instance, and it is configured with the default `PropertyAccessor` instances, a `BeanResolver`, and the custom functions.
|
||||
|
||||
* That `EvaluationContext` instance is injected into the `ExpressionEvaluatingTransformer` bean.
|
||||
|
||||
To provide a SpEL Function via Java Configuration, you should declare a `SpelFunctionFactoryBean` bean for each function.
|
||||
The sample above can be configured as follows:
|
||||
To provide a SpEL Function by using Java configuration, you can declare a `SpelFunctionFactoryBean` bean for each function.
|
||||
The following example shows how to create a custom function:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
@@ -114,18 +122,20 @@ public SpelFunctionFactoryBean xpath() {
|
||||
return new SpelFunctionFactoryBean(XPathUtils.class, "evaluate");
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: SpEL functions declared in a parent context are also made available in any child context(s).
|
||||
Each context has its own instance of the _integrationEvaluationContext_ factory bean because each needs a different `BeanResolver`, but the function declarations are inherited and can be overridden if needed by declaring a SpEL function with the same name.
|
||||
NOTE: SpEL functions declared in a parent context are also made available in any child contexts.
|
||||
Each context has its own instance of the `integrationEvaluationContext` factory bean, because each needs a different `BeanResolver`, but the function declarations are inherited and can be overridden by declaring a SpEL function with the same name.
|
||||
|
||||
*Built-in SpEL Functions*
|
||||
==== Built-in SpEL Functions
|
||||
|
||||
Spring Integration provides some standard functions, which are registered with the application context automatically on start up:
|
||||
Spring Integration provides the folloiwng standard functions, which are registered with the application context automatically on start up:
|
||||
|
||||
*#jsonPath* - to evaluate a 'jsonPath' on some provided object.
|
||||
This function invokes `JsonPathUtils.evaluate(...)`.
|
||||
This static method delegates to the http://code.google.com/p/json-path[Jayway JsonPath library].
|
||||
The following shows some usage examples:
|
||||
* `#jsonPath`: Evaluates a 'jsonPath' on a specified object.
|
||||
This function invokes `JsonPathUtils.evaluate(...)`, which delegates to the http://code.google.com/p/json-path[Jayway JsonPath library].
|
||||
The following listing shows some usage examples:
|
||||
+
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<transformer expression="#jsonPath(payload, '$.store.book[0].author')"/>
|
||||
@@ -139,38 +149,42 @@ The following shows some usage examples:
|
||||
<mapping channel="output2" value="fiction"/>
|
||||
</router>
|
||||
----
|
||||
|
||||
#jsonPath also supports the third optional parameter - an array of https://github.com/jayway/JsonPath/blob/master/json-path/src/main/java/com/jayway/jsonpath/Filter.java[`com.jayway.jsonpath.Filter`], which could be provided by a reference to a bean or bean method, for example.
|
||||
|
||||
NOTE: Using this function requires the Jayway JsonPath library (json-path.jar) to be on the classpath; otherwise the _#jsonPath_ SpEL function won't be registered.
|
||||
|
||||
====
|
||||
+
|
||||
`#jsonPath` also supports a third (optional) parameter: an array of https://github.com/jayway/JsonPath/blob/master/json-path/src/main/java/com/jayway/jsonpath/Filter.java[`com.jayway.jsonpath.Filter`], which can be provided by a reference to a bean or bean method (for example).
|
||||
+
|
||||
NOTE: Using this function requires the Jayway JsonPath library (`json-path.jar`) to be on the classpath.
|
||||
Otherwise the `#jsonPath` SpEL function is not registered.
|
||||
+
|
||||
For more information regarding JSON see 'JSON Transformers' in <<transformer>>.
|
||||
|
||||
|
||||
*#xpath* - to evaluate an 'xpath' on some provided object.
|
||||
For more information regarding xml and xpath see <<xml>>.
|
||||
* `#xpath`: To evaluate an 'xpath' on some provided object.
|
||||
For more information regarding XML and XPath, see "`<<xml>>`".
|
||||
|
||||
[[spel-property-accessors]]
|
||||
=== PropertyAccessors
|
||||
=== Property Accessors
|
||||
|
||||
Namespace support is provided for the easy addition of SpEL custom http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/expression/PropertyAccessor.html[`PropertyAccessor`] implementations.
|
||||
You can specify the `<spel-property-accessors/>` component to provide a list of custom `PropertyAccessor` s to the `EvaluationContext` used throughout the framework.
|
||||
Instead of configuring the factory bean above, simply add one or more of these components, and the framework will automatically add the accessors to the default _integrationEvaluationContext_ factory bean:
|
||||
Spring Integration provides namespace support to let you create SpEL custom http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/expression/PropertyAccessor.html[`PropertyAccessor`] implementations.
|
||||
You can use the `<spel-property-accessors/>` component to provide a list of custom `PropertyAccessor` instances to the `EvaluationContext` used throughout the framework.
|
||||
Instead of configuring the factory bean shown earlier, you can add one or more of these components, and the framework automatically adds the accessors to the default `integrationEvaluationContext` factory bean.
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:spel-property-accessors>
|
||||
<bean id="jsonPA" class="org.springframework.integration.json.JsonPropertyAccessor"/>
|
||||
<ref bean="fooPropertyAccessor"/>
|
||||
</int:spel-property-accessors>
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
With this sample, two custom `PropertyAccessor` s will be injected to the `EvaluationContext` in the order that they are declared.
|
||||
In the preceding example, two custom `PropertyAccessor` instances are injected into the `EvaluationContext` (in the order in which they are declared).
|
||||
|
||||
To provide `PropertyAccessor` s via Java Configuration, you should declare a `SpelPropertyAccessorRegistrar` bean with the name `spelPropertyAccessorRegistrar` (`IntegrationContextUtils.SPEL_PROPERTY_ACCESSOR_REGISTRAR_BEAN_NAME` constant).
|
||||
The sample above can be configured as follows:
|
||||
To provide `PropertyAccessor` instances by using Java Configuration, you should declare a `SpelPropertyAccessorRegistrar` bean with a name of `spelPropertyAccessorRegistrar` (dictated by the `IntegrationContextUtils.SPEL_PROPERTY_ACCESSOR_REGISTRAR_BEAN_NAME` constant).
|
||||
The following example shows how to configure two custom `PropertyAccessor` instances with Java:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
@@ -179,9 +193,18 @@ public SpelPropertyAccessorRegistrar spelPropertyAccessorRegistrar() {
|
||||
.add(fooPropertyAccessor());
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: Custom `PropertyAccessor` s declared in a parent context are also made available in any child context(s).
|
||||
[NOTE]
|
||||
====
|
||||
Custom `PropertyAccessor` instances declared in a parent context are also made available in any child contexts.
|
||||
They are placed at the end of result list (but before the default `org.springframework.context.expression.MapAccessor` and `o.s.expression.spel.support.ReflectivePropertyAccessor`).
|
||||
If a `PropertyAccessor` with the same bean id is declared in a child context(s), it will override the parent accessor.
|
||||
If you declare a `PropertyAccessor` with the same bean ID in a child context, it overrides the parent accessor.
|
||||
Beans declared within a `<spel-property-accessors/>` must have an 'id' attribute.
|
||||
The final order of usage is: the accessors in the current context, in the order in which they are declared, followed by any from parent contexts, in order, followed by the `MapAccessor` and finally the `ReflectivePropertyAccessor`.
|
||||
The final order of usage is as follows:
|
||||
|
||||
* The accessors in the current context, in the order in which they are declared
|
||||
* Any accessors from parent contexts, in order
|
||||
* The `MapAccessor`
|
||||
* The `ReflectivePropertyAccessor`
|
||||
====
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
[[splitter]]
|
||||
=== Splitter
|
||||
|
||||
[[splitter-annotation]]
|
||||
==== Introduction
|
||||
The splitter is a component whose role is to partition a message into several parts and send the resulting messages to be processed independently.
|
||||
Very often, they are upstream producers in a pipeline that includes an aggregator.
|
||||
|
||||
The Splitter is a component whose role is to partition a message in several parts, and send the resulting messages to be processed independently.
|
||||
Very often, they are upstream producers in a pipeline that includes an Aggregator.
|
||||
==== Programming Model
|
||||
|
||||
==== Programming model
|
||||
The API for performing splitting consists of one base class, `AbstractMessageSplitter`.
|
||||
It is a `MessageHandler` implementation that encapsulates features common to splitters, such as filling in the appropriate message headers (`CORRELATION_ID`, `SEQUENCE_SIZE`, and `SEQUENCE_NUMBER`) on the messages that are produced.
|
||||
This filling enables tracking down the messages and the results of their processing (in a typical scenario, these headers get copied to the messages that are produced by the various transforming endpoints).
|
||||
The values can then be used, for example, by a http://www.eaipatterns.com/DistributionAggregate.html[composed message processor].
|
||||
|
||||
The API for performing splitting consists of one base class, `AbstractMessageSplitter`, which is a `MessageHandler` implementation, encapsulating features which are common to splitters, such as filling in the appropriate message headers `CORRELATION_ID`, `SEQUENCE_SIZE`, and `SEQUENCE_NUMBER` on the messages that are produced.
|
||||
This enables tracking down the messages and the results of their processing (in a typical scenario, these headers would be copied over to the messages that are produced by the various transforming endpoints), and use them, for example, in a http://www.eaipatterns.com/DistributionAggregate.html[Composed Message Processor] scenario.
|
||||
|
||||
An excerpt from `AbstractMessageSplitter` can be seen below:
|
||||
The following example shows an excerpt from `AbstractMessageSplitter`:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -24,26 +23,27 @@ public abstract class AbstractMessageSplitter
|
||||
}
|
||||
----
|
||||
|
||||
To implement a specific Splitter in an application, extend `AbstractMessageSplitter` and implement the `splitMessage` method, which contains logic for splitting the messages.
|
||||
The return value may be one of the following:
|
||||
To implement a specific splitter in an application, you can extend `AbstractMessageSplitter` and implement the `splitMessage` method, which contains logic for splitting the messages.
|
||||
The return value can be one of the following:
|
||||
|
||||
* A `Collection` or an array of Messages, or an `Iterable` (or `Iterator`) that iterates over Messages - in this case the messages will be sent as such (after the `CORRELATION_ID`, `SEQUENCE_SIZE` and `SEQUENCE_NUMBER` are populated).
|
||||
Using this approach gives more control to the developer, for example for populating custom message headers as part of the splitting process.
|
||||
|
||||
* A `Collection` or an array of non-Message objects, or an `Iterable` (or `Iterator`) that iterates over non-Message objects - works like the prior case, except that each collection element will be used as a Message payload.
|
||||
Using this approach allows developers to focus on the domain objects without having to consider the Messaging system and produces code that is easier to test.
|
||||
|
||||
* a `Message` or non-Message object (but not a Collection or an Array) - it works like the previous cases, except a single message will be sent out.
|
||||
* A `Collection` or an array of messages or an `Iterable` (or `Iterator`) that iterates over messages.
|
||||
In this case, the messages are sent as messages (after the `CORRELATION_ID`, `SEQUENCE_SIZE` and `SEQUENCE_NUMBER` are populated).
|
||||
Using this approach gives you more control -- for example, to populate custom message headers as part of the splitting process.
|
||||
|
||||
* A `Collection` or an array of non-message objects or an `Iterable` (or `Iterator`) that iterates over non-message objects.
|
||||
It works like the prior case, except that each collection element is used as a message payload.
|
||||
Using this approach lets you focus on the domain objects without having to consider the messaging system and produces code that is easier to test.
|
||||
|
||||
* a `Message` or non-message object (but not a collection or an array).
|
||||
It works like the previous cases, except that a single message is sent out.
|
||||
|
||||
In Spring Integration, any POJO can implement the splitting algorithm, provided that it defines a method that accepts a single argument and has a return value.
|
||||
In this case, the return value of the method will be interpreted as described above.
|
||||
In this case, the return value of the method is interpreted as described earlier.
|
||||
The input argument might either be a `Message` or a simple POJO.
|
||||
In the latter case, the splitter will receive the payload of the incoming message.
|
||||
Since this decouples the code from the Spring Integration API and will typically be easier to test, it is the recommended approach.
|
||||
In the latter case, the splitter receives the payload of the incoming message.
|
||||
We recommend this approach, because it decouples the code from the Spring Integration API and is typically easier to test.
|
||||
|
||||
*Iterators*
|
||||
===== Iterators
|
||||
|
||||
Starting with _version 4.1_, the `AbstractMessageSplitter` supports the `Iterator` type for the `value` to split.
|
||||
Note, in the case of an `Iterator` (or `Iterable`), we don't have access to the number of underlying items and the `SEQUENCE_SIZE` header is set to `0`.
|
||||
@@ -57,61 +57,49 @@ An `Iterator` object is useful to avoid the need for building an entire collecti
|
||||
For example, when underlying items are populated from some external system (e.g.
|
||||
DataBase or FTP `MGET`) using iterations or streams.
|
||||
|
||||
*Stream and Flux*
|
||||
===== Stream and Flux
|
||||
|
||||
Starting with _version 5.0_, the `AbstractMessageSplitter` supports the Java `Stream` and Reactive Streams `Publisher` types for the `value` to split.
|
||||
In this case the target `Iterator` is built on their iteration functionality.
|
||||
Starting with version 5.0, the `AbstractMessageSplitter` supports the Java `Stream` and Reactive Streams `Publisher` types for the `value` to split.
|
||||
In this case, the target `Iterator` is built on their iteration functionality.
|
||||
|
||||
In addition, if Splitter's output channel is an instance of a `ReactiveStreamsSubscribableChannel`, the `AbstractMessageSplitter` produces a `Flux` result instead of an `Iterator` and the output channel is _subscribed_ to this `Flux` for back-pressure based splitting on downstream flow demand.
|
||||
In addition, if the splitter's output channel is an instance of a `ReactiveStreamsSubscribableChannel`, the `AbstractMessageSplitter` produces a `Flux` result instead of an `Iterator`, and the output channel is subscribed to this `Flux` for back-pressure-based splitting on downstream flow demand.
|
||||
|
||||
[[splitter-config]]
|
||||
==== Configuring Splitter
|
||||
|
||||
===== Configuring a Splitter using XML
|
||||
==== Configuring a Splitter with XML
|
||||
|
||||
A splitter can be configured through XML as follows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:channel id="inputChannel"/>
|
||||
|
||||
<int:splitter id="splitter" <1>
|
||||
ref="splitterBean" <2>
|
||||
method="split" <3>
|
||||
input-channel="inputChannel" <4>
|
||||
<int:splitter id="splitter" <1>
|
||||
ref="splitterBean" <2>
|
||||
method="split" <3>
|
||||
input-channel="inputChannel" <4>
|
||||
output-channel="outputChannel" /> <5>
|
||||
|
||||
<int:channel id="outputChannel"/>
|
||||
|
||||
<beans:bean id="splitterBean" class="sample.PojoSplitter"/>
|
||||
----
|
||||
|
||||
|
||||
|
||||
<1> The id of the splitter is _optional_.
|
||||
|
||||
|
||||
|
||||
<1> The ID of the splitter is optional.
|
||||
<2> A reference to a bean defined in the application context.
|
||||
The bean must implement the splitting logic as described in the section above ._Optional_.
|
||||
If reference to a bean is not provided, then it is assumed that the _payload_ of the Message that arrived on the `input-channel` is an implementation of `java.util.Collection` and the default splitting logic will be applied to the Collection, incorporating each individual element into a Message and sending it to the `output-channel`.
|
||||
|
||||
|
||||
|
||||
<3> The method (defined on the bean specified above) that implements the splitting logic._Optional_.
|
||||
|
||||
|
||||
|
||||
The bean must implement the splitting logic, as described in the earlier section.
|
||||
Optional.
|
||||
If a reference to a bean is not provided, it is assumed that the payload of the message that arrived on the `input-channel` is an implementation of `java.util.Collection` and the default splitting logic is applied to the collection, incorporating each individual element into a message and sending it to the `output-channel`.
|
||||
<3> The method (defined on the bean) that implements the splitting logic.
|
||||
Optional.
|
||||
<4> The input channel of the splitter.
|
||||
_Required_.
|
||||
Required.
|
||||
<5> The channel to which the splitter sends the results of splitting the incoming message.
|
||||
Optional (because incoming messages can specify a reply channel themselves).
|
||||
====
|
||||
|
||||
We recommend using a `ref` attribute if the custom splitter implementation can be referenced in other `<splitter>` definitions.
|
||||
However if the custom splitter handler implementation should be scoped to a single definition of the `<splitter>`, you can configure an inner bean definition, as the following example follows:
|
||||
|
||||
|
||||
<5> The channel to which the splitter will send the results of splitting the incoming message.
|
||||
_Optional (because incoming
|
||||
messages can specify a reply channel themselves)_.
|
||||
|
||||
Using a `ref` attribute is generally recommended if the custom splitter implementation may be referenced in other `<splitter>` definitions.
|
||||
However if the custom splitter handler implementation should be scoped to a single definition of the `<splitter>`, configure an inner bean definition:
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:splitter id="testSplitter" input-channel="inChannel" method="split"
|
||||
@@ -119,19 +107,24 @@ However if the custom splitter handler implementation should be scoped to a sing
|
||||
<beans:bean class="org.foo.TestSplitter"/>
|
||||
</int:splitter>
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: Using both a `ref` attribute and an inner handler definition in the same `<int:splitter>` configuration is not allowed, as it creates an ambiguous condition and will result in an Exception being thrown.
|
||||
NOTE: Using both a `ref` attribute and an inner handler definition in the same `<int:splitter>` configuration is not allowed, as it creates an ambiguous condition and results in an exception being thrown.
|
||||
|
||||
IMPORTANT: If the "ref" attribute references a bean that extends `AbstractMessageProducingHandler` (such as splitters provided by the framework itself), the configuration is optimized by injecting the output channel into the handler directly.
|
||||
In this case, each "ref" must be to a separate bean instance (or a `prototype`-scoped bean), or use the inner `<bean/>` configuration type.
|
||||
However, this optimization only applies if you don't provide any splitter-specific attributes in the splitter XML definition.
|
||||
If you inadvertently reference the same message handler from multiple beans, you will get a configuration exception.
|
||||
IMPORTANT: If the `ref` attribute references a bean that extends `AbstractMessageProducingHandler` (such as splitters provided by the framework itself), the configuration is optimized by injecting the output channel into the handler directly.
|
||||
In this case, each `ref` must be a separate bean instance (or a `prototype`-scoped bean) or use the inner `<bean/>` configuration type.
|
||||
However, this optimization applies only if you do not provide any splitter-specific attributes in the splitter XML definition.
|
||||
If you inadvertently reference the same message handler from multiple beans, you get a configuration exception.
|
||||
|
||||
===== Configuring a Splitter with Annotations
|
||||
==== Configuring a Splitter with Annotations
|
||||
|
||||
The `@Splitter` annotation is applicable to methods that expect either the `Message` type or the message payload type, and the return values of the method should be a `Collection` of any type.
|
||||
If the returned values are not actual `Message` objects, then each item will be wrapped in a Message as its payload.
|
||||
Each message will be sent to the designated output channel for the endpoint on which the `@Splitter` is defined.
|
||||
If the returned values are not actual `Message` objects, each item is wrapped in a `Message` as the payload of the `Message`.
|
||||
Each resulting `Message` is sent to the designated output channel for the endpoint on which the `@Splitter` is defined.
|
||||
|
||||
The following example shows how to configure a splitter by using the `@Splitter` annotation:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Splitter
|
||||
@@ -139,7 +132,8 @@ List<LineItem> extractItems(Order order) {
|
||||
return order.getItems()
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Also see <<advising-with-annotations>>.
|
||||
See also "`<<advising-with-annotations>>`".
|
||||
|
||||
Also see <<java-dsl-splitters>> in Java DSL chapter.
|
||||
See also "`<<java-dsl-splitters>>`" in the Java DSL chapter.
|
||||
|
||||
@@ -1,29 +1,23 @@
|
||||
[[stomp]]
|
||||
== STOMP Support
|
||||
|
||||
[[stomp-introduction]]
|
||||
=== Introduction
|
||||
|
||||
Spring Integration _version 4.2_ introduced _STOMP Client_ support.
|
||||
It is based on the architecture, infrastructure and API from the Spring Framework's _messaging_ module, _stomp_ package.
|
||||
Many of Spring STOMP components (e.g. `StompSession` or `StompClientSupport`)
|
||||
are used within Spring Integration.
|
||||
For more information, please, refer to the http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html#websocket-stomp-client[Spring Framework STOMP Support]
|
||||
chapter in the Spring Framework reference manual.
|
||||
Spring Integration version 4.2 introduced STOMP (Simple Text Orientated Messaging Protocol) client support.
|
||||
It is based on the architecture, infrastructure, and API from the Spring Framework's messaging module, stomp package.
|
||||
Spring Integration uses many of Spring STOMP components (such as `StompSession` and `StompClientSupport`).
|
||||
For more information, see the http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html#websocket-stomp-client[Spring Framework STOMP Support] chapter in the Spring Framework reference manual.
|
||||
|
||||
[[stomp-overview]]
|
||||
=== Overview
|
||||
|
||||
To configure STOMP (Simple [or Streaming] Text Orientated Messaging Protocol) let's start with the _STOMP Client_ object.
|
||||
The Spring Framework provides these implementations:
|
||||
To configure STOMP, you should start with the STOMP client object.
|
||||
The Spring Framework provides the following implementations:
|
||||
|
||||
* `WebSocketStompClient` - built on the Spring WebSocket API with support for standard JSR-356 WebSocket, Jetty 9,
|
||||
as well as SockJS for HTTP-based WebSocket emulation with SockJS Client.
|
||||
* `WebSocketStompClient`: Built on the Spring WebSocket API with support for standard JSR-356 WebSocket, Jetty 9, and SockJS for HTTP-based WebSocket emulation with SockJS Client.
|
||||
|
||||
* `ReactorNettyTcpStompClient` - built on `ReactorNettyTcpClient` from the `reactor-netty` project.
|
||||
* `ReactorNettyTcpStompClient`: Built on `ReactorNettyTcpClient` from the `reactor-netty` project.
|
||||
|
||||
Any other `StompClientSupport` implementation can be provided.
|
||||
See the JavaDocs of those classes for more information.
|
||||
You can provide any other `StompClientSupport` implementation.
|
||||
See the https://docs.spring.io/spring-integration/api/[Javadoc] of those classes.
|
||||
|
||||
The `StompClientSupport` class is designed as a _factory_ to produce a `StompSession` for the provided
|
||||
`StompSessionHandler` and all the remaining work is done through the _callbacks_ to that `StompSessionHandler`
|
||||
@@ -38,28 +32,27 @@ See `StompSessionManager` (and its implementations) JavaDocs for more informatio
|
||||
[[stomp-inbound-adapter]]
|
||||
=== STOMP Inbound Channel Adapter
|
||||
|
||||
The `StompInboundChannelAdapter` is a one-stop `MessageProducer` component to subscribe our Spring Integration
|
||||
application to the provided STOMP destinations and receive messages from them, converted from the STOMP
|
||||
frames using the provided `MessageConverter` on the connected `StompSession`.
|
||||
The destinations (and therefore STOMP subscriptions) can be changed at runtime using appropriate `@ManagedOperation` s
|
||||
on the `StompInboundChannelAdapter`.
|
||||
The `StompInboundChannelAdapter` is a one-stop `MessageProducer` component that subscribes your Spring Integration application to the provided STOMP destinations and receives messages from them (converted from the STOMP frames by using the provided `MessageConverter` on the connected `StompSession`).
|
||||
You can change the destinations (and therefore STOMP subscriptions) at runtime by using appropriate `@ManagedOperation` annotations on the `StompInboundChannelAdapter`.
|
||||
|
||||
For more configuration options see <<stomp-namespace>> and the `StompInboundChannelAdapter` JavaDocs.
|
||||
For more configuration options, see "`<<stomp-namespace>>`" and the `StompInboundChannelAdapter` https://docs.spring.io/spring-integration/api/org/springframework/integration/stomp/inbound/StompInboundChannelAdapter.html[Javadoc].
|
||||
|
||||
[[stomp-outbound-adapter]]
|
||||
=== STOMP Outbound Channel Adapter
|
||||
|
||||
The `StompMessageHandler` is the `MessageHandler` for the `<int-stomp:outbound-channel-adapter>`
|
||||
to send the outgoing `Message<?>` s to the STOMP `destination` (pre-configured or determined at runtime via a SpEL expression) STOMP through the `StompSession`, provided by the shared `StompSessionManager`.
|
||||
The `StompMessageHandler` is the `MessageHandler` for the `<int-stomp:outbound-channel-adapter>` and is used
|
||||
to send the outgoing `Message<?>` instances to the STOMP `destination` (pre-configured or determined at runtime with a SpEL expression) through the `StompSession` (which is provided by the shared `StompSessionManager`).
|
||||
|
||||
For more configuration option see <<stomp-namespace>> and the `StompMessageHandler` JavaDocs.
|
||||
For more configuration options see "`<<stomp-namespace>>`" and the `StompMessageHandler` https://docs.spring.io/spring-integration/api/org/springframework/integration/stomp/outbound/StompMessageHandler.html[Javadoc].
|
||||
|
||||
[[stomp-headers]]
|
||||
=== STOMP Headers Mapping
|
||||
|
||||
The STOMP protocol provides _headers_ as part of frame; the entire structure of the STOMP frame
|
||||
has this format:
|
||||
The STOMP protocol provides headers as part of its frame.
|
||||
The entire structure of the STOMP frame has the following format:
|
||||
|
||||
====
|
||||
----
|
||||
....
|
||||
COMMAND
|
||||
header1:value1
|
||||
@@ -67,53 +60,50 @@ header2:value2
|
||||
|
||||
Body^@
|
||||
....
|
||||
----
|
||||
====
|
||||
|
||||
Spring Framework provides `StompHeaders`, to represent these headers.
|
||||
See the JavaDocs for more details.
|
||||
STOMP frames are converted to/from `Message<?>` and these headers are mapped to/from `MessageHeaders`.
|
||||
Spring Framework provides `StompHeaders` to represent these headers.
|
||||
See the https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/messaging/simp/stomp/StompHeaders.html[Javadoc] for more details.
|
||||
STOMP frames are converted to and from `Message<?>` instances and these headers are mapped to and from `MessageHeaders` instances.
|
||||
Spring Integration provides a default `HeaderMapper` implementation for the STOMP adapters.
|
||||
The implementation is `StompHeaderMapper` which provides `fromHeaders()` and `toHeaders()` operations for the
|
||||
_inbound_ and _outbound_ adapters respectively.
|
||||
The implementation is `StompHeaderMapper`.
|
||||
It provides `fromHeaders()` and `toHeaders()` operations for the inbound and outbound adapters, respectively.
|
||||
|
||||
As with many other Spring Integration modules, the `IntegrationStompHeaders` class has been
|
||||
introduced to map standard STOMP headers to `MessageHeaders` with `stomp_` as the header name prefix.
|
||||
In addition, all `MessageHeaders` with that prefix are mapped to the `StompHeaders` when sending to a destination.
|
||||
As with many other Spring Integration modules, the `IntegrationStompHeaders` class has been introduced to map standard STOMP headers to `MessageHeaders`, with `stomp_` as the header name prefix.
|
||||
In addition, all `MessageHeaders` instances with that prefix are mapped to the `StompHeaders` when sending to a destination.
|
||||
|
||||
For more information, see the JavaDocs of those classes and the `mapped-headers` attribute description in the
|
||||
<<stomp-namespace>>.
|
||||
For more information, see the https://docs.spring.io/spring-integration/api/[Javadoc] for those classes and the `mapped-headers` attribute description in the
|
||||
"`<<stomp-namespace>>`".
|
||||
|
||||
[[stomp-events]]
|
||||
=== STOMP Integration Events
|
||||
|
||||
Many STOMP operations are asynchronous, including error handling.
|
||||
For example, STOMP has a `RECEIPT` server frame that is returned when a client frame has requested one by adding
|
||||
For example, STOMP has a `RECEIPT` server frame that it returns when a client frame has requested one by adding
|
||||
the `RECEIPT` header.
|
||||
To provide access to these asynchronous events, Spring Integration emits `StompIntegrationEvent` s which can be
|
||||
obtained by implementing an `ApplicationListener` or using an `<int-event:inbound-channel-adapter>` (see <<appevent-inbound>>).
|
||||
To provide access to these asynchronous events, Spring Integration emits `StompIntegrationEvent` instances, which you can obtain by implementing an `ApplicationListener` or by using an `<int-event:inbound-channel-adapter>` (see "`<<appevent-inbound>>`").
|
||||
|
||||
Specifically, a `StompExceptionEvent` is emitted from the `AbstractStompSessionManager`, when a
|
||||
`stompSessionListenableFuture` receives `onFailure()` in case of failure to connect to STOMP Broker.
|
||||
Another example is the `StompMessageHandler` which processes
|
||||
`ERROR` STOMP frames, which are server responses to improper, unaccepted, messages sent by this `StompMessageHandler`.
|
||||
Specifically, a `StompExceptionEvent` is emitted from the `AbstractStompSessionManager` when a
|
||||
`stompSessionListenableFuture` receives `onFailure()` due to failure to connect to STOMP broker.
|
||||
Another example is the `StompMessageHandler`.
|
||||
It processes `ERROR` STOMP frames, which are server responses to improper (unaccepted) messages sent by this `StompMessageHandler`.
|
||||
|
||||
The `StompReceiptEvent` s are emitted from the `StompMessageHandler` as a part of `StompSession.Receiptable`
|
||||
callbacks in the asynchronous answers for the sent messages to the `StompSession`.
|
||||
The `StompReceiptEvent` can be positive and negative depending on whether or not the `RECEIPT` frame was received
|
||||
from the server within the `receiptTimeLimit` period, which can be configured on the `StompClientSupport` instance.
|
||||
Defaults to `15 * 1000`.
|
||||
The `StompMessageHandler` emits `StompReceiptEvent` as a part of `StompSession.Receiptable` callbacks in the asynchronous answers for the messages sent to the `StompSession`.
|
||||
The `StompReceiptEvent` can be positive or negative, depending on whether or not the `RECEIPT` frame was received from the server within the `receiptTimeLimit` period, which you can configure on the `StompClientSupport` instance.
|
||||
It defaults to `15 * 1000` (in milliseconds, so 15 seconds).
|
||||
|
||||
NOTE: The `StompSession.Receiptable` callbacks are added only if the `RECEIPT` STOMP header of the message to send
|
||||
is not `null`.
|
||||
Automatic `RECEIPT` header generation can be enabled on the `StompSession` through its `autoReceipt` option and
|
||||
on the `StompSessionManager` respectively.
|
||||
NOTE: The `StompSession.Receiptable` callbacks are added only if the `RECEIPT` STOMP header of the message to send is not `null`.
|
||||
You can enable automatic `RECEIPT` header generation on the `StompSession` through its `autoReceipt` option and on the `StompSessionManager` respectively.
|
||||
|
||||
See the next paragraph for more information how to configure Spring Integration to accept those `ApplicationEvent` s.
|
||||
See "`<<stomp-java-config>>`" for more information how to configure Spring Integration to accept those `ApplicationEvent` instances.
|
||||
|
||||
[[stomp-java-config]]
|
||||
=== STOMP Adapters Java Configuration
|
||||
|
||||
A comprehensive Java & Annotation Configuration for STOMP Adapters may look like this:
|
||||
The following example shows a comprehensive Java configuration for STOMP adapters:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
@@ -174,13 +164,13 @@ public class StompConfiguration {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[[stomp-namespace]]
|
||||
=== STOMP Namespace Support
|
||||
|
||||
Spring Integration _STOMP_ namespace implements the _inbound_ and _outbound_ channel adapter components described below.
|
||||
To include it in your configuration, simply provide the following namespace declaration in your application context
|
||||
configuration file:
|
||||
The Spring Integration STOMP namespace implements the inbound and outbound channel adapter components.
|
||||
To include it in your configuration, provide the following namespace declaration in your application context configuration file:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
@@ -200,134 +190,97 @@ configuration file:
|
||||
</beans>
|
||||
----
|
||||
|
||||
*<int-stomp:outbound-channel-adapter>*
|
||||
[[stomp-outbound-channel-adapter]]
|
||||
==== Understanding the <int-stomp:outbound-channel-adapter> Element
|
||||
|
||||
The following listing shows the available attributes for the STOMP outbound channel adapter:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<int-stomp:outbound-channel-adapter
|
||||
id="" <1>
|
||||
channel="" <2>
|
||||
stomp-session-manager="" <3>
|
||||
header-mapper="" <4>
|
||||
mapped-headers="" <5>
|
||||
destination="" <6>
|
||||
id="" <1>
|
||||
channel="" <2>
|
||||
stomp-session-manager="" <3>
|
||||
header-mapper="" <4>
|
||||
mapped-headers="" <5>
|
||||
destination="" <6>
|
||||
destination-expression="" <7>
|
||||
auto-startup="" <8>
|
||||
phase=""/> <9>
|
||||
auto-startup="" <8>
|
||||
phase=""/> <9>
|
||||
----
|
||||
|
||||
|
||||
|
||||
<1> The component bean name.
|
||||
The `MessageHandler` is registered with the bean alias `id + '.handler'`.
|
||||
If the `channel` attribute isn't provided, a `DirectChannel` is created and registered with the application context
|
||||
with this `id` attribute as the bean name.
|
||||
In this case, the endpoint is registered with the bean name `id + '.adapter'`.
|
||||
|
||||
|
||||
<2> Identifies the channel attached to this adapter.
|
||||
_Optional_ - if `id` is present - see `id`.
|
||||
|
||||
|
||||
<3> Reference to a `StompSessionManager` bean, which encapsulates the low-level connection and `StompSession`
|
||||
handling operations.
|
||||
_Required_.
|
||||
|
||||
|
||||
<4> Reference to a bean implementing `HeaderMapper<StompHeaders>` that maps Spring Integration MessageHeaders to/from
|
||||
The `MessageHandler` is registered with a bean alias of `id` plus `.handler`.
|
||||
If you do not set the `channel` attribute, a `DirectChannel` is created and registered in the application context with the value of this `id` attribute as the bean name.
|
||||
In this case, the endpoint is registered with a bean name `id` plus `.adapter`.
|
||||
<2> Identifies the channel attached to this adapter if `id` is present.
|
||||
See `id`.
|
||||
Optional.
|
||||
<3> Reference to a `StompSessionManager` bean, which encapsulates the low-level connection and `StompSession` handling operations.
|
||||
Required.
|
||||
<4> Reference to a bean that implements `HeaderMapper<StompHeaders>`, which maps Spring Integration `MessageHeaders` to and from
|
||||
STOMP frame headers.
|
||||
This is mutually exclusive with `mapped-headers`.
|
||||
Defaults to `StompHeaderMapper`.
|
||||
|
||||
|
||||
It is mutually exclusive with `mapped-headers`.
|
||||
It defaults to `StompHeaderMapper`.
|
||||
<5> Comma-separated list of names of STOMP Headers to be mapped to the STOMP frame headers.
|
||||
This can only be provided if the `header-mapper` reference is not set.
|
||||
The values in this list can also be simple patterns to be matched against the header names (e.g. "foo*" or "*foo").
|
||||
A special token `STOMP_OUTBOUND_HEADERS` represents all the standard STOMP headers
|
||||
(content-length, receipt, heart-beat etc); they are included by default.
|
||||
If you wish to add your own headers, you must also include this token if you wish the standard headers to also be
|
||||
mapped or provide your own `HeaderMapper` implementation using `header-mapper`.
|
||||
|
||||
|
||||
<6> Name of the destination to which STOMP Messages will be sent.
|
||||
Mutually exclusive with the `destination-expression`.
|
||||
|
||||
|
||||
It can be provided only if the `header-mapper` reference is not set.
|
||||
The values in this list can also be simple patterns to be matched against the header names (such as `myheader*` or `*myheader`).
|
||||
A special token (`STOMP_OUTBOUND_HEADERS`) represents all the standard STOMP headers (content-length, receipt, heart-beat, and so on).
|
||||
They are included by default.
|
||||
If you want to add your own headers and want the standard headers to also be mapped, you must include this token or provide your own `HeaderMapper` implementation by using `header-mapper`.
|
||||
<6> Name of the destination to which STOMP Messages are sent.
|
||||
It is mutually exclusive with the `destination-expression`.
|
||||
<7> A SpEL expression to be evaluated at runtime against each Spring Integration `Message` as the root object.
|
||||
Mutually exclusive with the `destination`.
|
||||
|
||||
|
||||
It is mutually exclusive with the `destination`.
|
||||
<8> Boolean value indicating whether this endpoint should start automatically.
|
||||
Default to `true`.
|
||||
|
||||
|
||||
It defaults to `true`.
|
||||
<9> The lifecycle phase within which this endpoint should start and stop.
|
||||
The lower the value the earlier this endpoint will start and the later it will stop.
|
||||
The lower the value, the earlier this endpoint starts and the later it stops.
|
||||
The default is `Integer.MIN_VALUE`.
|
||||
Values can be negative.
|
||||
See `SmartLifeCycle`.
|
||||
See https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/SmartLifecycle.html[`SmartLifeCycle`].
|
||||
====
|
||||
|
||||
*<int-stomp:inbound-channel-adapter>*
|
||||
==== Understanding the `<int-stomp:inbound-channel-adapter>` Element
|
||||
|
||||
The following listing shows the available attributes for the STOMP inbound channel adapter:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-stomp:inbound-channel-adapter
|
||||
id="" <1>
|
||||
channel="" <2>
|
||||
error-channel="" <3>
|
||||
id="" <1>
|
||||
channel="" <2>
|
||||
error-channel="" <3>
|
||||
stomp-session-manager="" <4>
|
||||
header-mapper="" <5>
|
||||
mapped-headers="" <6>
|
||||
destinations="" <7>
|
||||
send-timeout="" <8>
|
||||
payload-type="" <9>
|
||||
auto-startup="" <10>
|
||||
phase=""/> <11>
|
||||
header-mapper="" <5>
|
||||
mapped-headers="" <6>
|
||||
destinations="" <7>
|
||||
send-timeout="" <8>
|
||||
payload-type="" <9>
|
||||
auto-startup="" <10>
|
||||
phase=""/> <11>
|
||||
----
|
||||
|
||||
|
||||
|
||||
<1> The component bean name.
|
||||
If the `channel` attribute isn't provided, a `DirectChannel` is created and registered with the application context
|
||||
with this `id` attribute as the bean name.
|
||||
In this case, the endpoint is registered with the bean name `id + '.adapter'`.
|
||||
|
||||
|
||||
If you do not set the `channel` attribute, a `DirectChannel` is created and registered in the application context with the value of this `id` attribute as the bean name.
|
||||
In this case, the endpoint is registered with the bean name `id` plus `.adapter`.
|
||||
<2> Identifies the channel attached to this adapter.
|
||||
|
||||
|
||||
<3> The `MessageChannel` bean reference to which the `ErrorMessages` should be sent.
|
||||
|
||||
|
||||
<4> See the same option on the `<int-stomp:outbound-channel-adapter>`.
|
||||
|
||||
|
||||
<3> The `MessageChannel` bean reference to which `ErrorMessage` instances should be sent.
|
||||
<4> See the same option on the <<stomp-outbound-channel-adapter,`<int-stomp:outbound-channel-adapter>`>>.
|
||||
<5> Comma-separated list of names of STOMP Headers to be mapped from the STOMP frame headers.
|
||||
This can only be provided if the `header-mapper` reference is not set.
|
||||
The values in this list can also be simple patterns to be matched against the header names (e.g. "foo*" or "*foo").
|
||||
A special token `STOMP_INBOUND_HEADERS` represents all the standard STOMP headers
|
||||
(content-length, receipt, heart-beat etc); they are included by default.
|
||||
If you wish to add your own headers, you must also include this token if you wish the standard headers to also be
|
||||
mapped or provide your own `HeaderMapper` implementation using `header-mapper`.
|
||||
|
||||
|
||||
<6> See the same option on the `<int-stomp:outbound-channel-adapter>`.
|
||||
|
||||
|
||||
You can only provide this if the `header-mapper` reference is not set.
|
||||
The values in this list can also be simple patterns to be matched against the header names (for example, `myheader*` or `*myheader`).
|
||||
A special token (`STOMP_INBOUND_HEADERS`) represents all the standard STOMP headers (content-length, receipt, heart-beat, and so on).
|
||||
They are included by default.
|
||||
If you want to add your own headers and want the standard headers to also be mapped, you must also include this token or provide your own `HeaderMapper` implementation using `header-mapper`.
|
||||
<6> See the same option on the <<stomp-outbound-channel-adapter,`<int-stomp:outbound-channel-adapter>`>>.
|
||||
<7> Comma-separated list of STOMP destination names to subscribe.
|
||||
The list of destinations (and therefore subscriptions) can be modified at runtime
|
||||
through the `addDestination()` and `removeDestination()` `@ManagedOperation` s.
|
||||
|
||||
|
||||
<8> Maximum amount of time in milliseconds to wait when sending a message to the channel if the channel may block.
|
||||
The list of destinations (and therefore subscriptions) can be modified at runtime through the `addDestination()` and `removeDestination()` `@ManagedOperation` annotations.
|
||||
<8> Maximum amount of time (in milliseconds) to wait when sending a message to the channel if the channel can block.
|
||||
For example, a `QueueChannel` can block until space is available if its maximum capacity has been reached.
|
||||
|
||||
|
||||
<9> Fully qualified name of the java type for the target `payload` to convert from the incoming STOMP Frame.
|
||||
Default to `String.class`.
|
||||
|
||||
|
||||
<10> See the same option on the `<int-stomp:outbound-channel-adapter>`.
|
||||
|
||||
|
||||
<11> See the same option on the `<int-stomp:outbound-channel-adapter>`.
|
||||
<9> Fully qualified name of the Java type for the target `payload` to convert from the incoming STOMP frame.
|
||||
It defaults to `String.class`.
|
||||
<10> See the same option on the <<stomp-outbound-channel-adapter,`<int-stomp:outbound-channel-adapter>`>>.
|
||||
<11> See the same option on the <<stomp-outbound-channel-adapter,`<int-stomp:outbound-channel-adapter>`>>.
|
||||
====
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
[[stream]]
|
||||
== Stream Support
|
||||
|
||||
[[stream-intro]]
|
||||
=== Introduction
|
||||
|
||||
In many cases application data is obtained from a stream.
|
||||
It is _not_ recommended to send a reference to a Stream as a message payload to a consumer.
|
||||
Instead messages are created from data that is read from an input stream and message payloads are written to an output stream one by one.
|
||||
In many cases, application data is obtained from a stream.
|
||||
It is not recommended to send a reference to a stream as a message payload to a consumer.
|
||||
Instead, messages are created from data that is read from an input stream, and message payloads are written to an output stream one by one.
|
||||
|
||||
[[stream-reading]]
|
||||
=== Reading from streams
|
||||
=== Reading from Streams
|
||||
|
||||
Spring Integration provides two adapters for streams.
|
||||
Both `ByteStreamReadingMessageSource` and `CharacterStreamReadingMessageSource` implement `MessageSource`.
|
||||
By configuring one of these within a channel-adapter element, the polling period can be configured, and the Message Bus can automatically detect and schedule them.
|
||||
By configuring one of these within a channel-adapter element, the polling period can be configured and the message bus can automatically detect and schedule them.
|
||||
The byte stream version requires an `InputStream`, and the character stream version requires a `Reader` as the single constructor argument.
|
||||
The `ByteStreamReadingMessageSource` also accepts the 'bytesPerMessage' property to determine how many bytes it will attempt to read into each `Message`.
|
||||
The `ByteStreamReadingMessageSource` also accepts the 'bytesPerMessage' property to determine how many bytes it tries to read into each `Message`.
|
||||
The default value is 1024.
|
||||
The following example creates an input stream that creates messages that each contain 2048 bytes:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<bean class="org.springframework.integration.stream.ByteStreamReadingMessageSource">
|
||||
@@ -29,40 +28,45 @@ The default value is 1024.
|
||||
<constructor-arg ref="someReader"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The `CharacterStreamReadingMessageSource` wraps the reader in a `BufferedReader` (if it's not one already).
|
||||
The `CharacterStreamReadingMessageSource` wraps the reader in a `BufferedReader` (if it is not one already).
|
||||
You can set the buffer size used by the buffered reader in the second constructor argument.
|
||||
Starting with _version 5.0_, a third constructor argument (`blockToDetectEOF`) controls the behavior of the `CharacterStreamReadingMessageSource`.
|
||||
When `false` (default), the `receive()` method checks if the reader is `ready()` and returns null if not.
|
||||
EOF is not detected in this case.
|
||||
When `true`, the `receive()` method blocks until data is available, or EOF is detected on the underlying stream.
|
||||
When EOF is detected, a `StreamClosedEvent` (application event) is published; you can consume this event with a bean implementing `ApplicationListener<StreamClosedEvent>`.
|
||||
Starting with version 5.0, a third constructor argument (`blockToDetectEOF`) controls the behavior of the `CharacterStreamReadingMessageSource`.
|
||||
When `false` (the default), the `receive()` method checks whether the reader is `ready()` and returns null if not.
|
||||
EOF (end of file) is not detected in this case.
|
||||
When `true`, the `receive()` method blocks until data is available or EOF is detected on the underlying stream.
|
||||
When EOF is detected, a `StreamClosedEvent` (application event) is published.
|
||||
You can consume this event with a bean that implements `ApplicationListener<StreamClosedEvent>`.
|
||||
|
||||
NOTE: To facilitate EOF detection, the poller thread will block in the `receive()` method until either data arrives or EOF is detected.
|
||||
NOTE: To facilitate EOF detection, the poller thread blocks in the `receive()` method until either data arrives or EOF is detected.
|
||||
|
||||
IMPORTANT: The poller will continue to publish an event on each poll once EOF has been detected; the application listener can stop the adapter to prevent this.
|
||||
The event is published on the poller thread and stopping the adapter will cause the thread to be interrupted.
|
||||
If you intend to perform some interruptible task after stopping the adapter, you must either perform the `stop()` on a different thread, or use a different thread for those downstream activities.
|
||||
Note that sending to a `QueueChannel` is interruptible so if you wish to send a message from the listener, do it before stopping the adapter.
|
||||
IMPORTANT: The poller continues to publish an event on each poll once EOF has been detected.
|
||||
The application listener can stop the adapter to prevent this.
|
||||
The event is published on the poller thread.
|
||||
Stopping the adapter causes the thread to be interrupted.
|
||||
If you intend to perform some interruptible task after stopping the adapter, you must either perform the `stop()` on a different thread or use a different thread for those downstream activities.
|
||||
Note that sending to a `QueueChannel` is interruptible, so, if you wish to send a message from the listener, do it before stopping the adapter.
|
||||
|
||||
This facilitates "piping" or redirecting data to `stdin`, such as...
|
||||
This facilitates "`piping`" or redirecting data to `stdin`, as the following two examples shows:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
cat foo.txt | java -jar my.jar
|
||||
cat myfile.txt | java -jar my.jar
|
||||
----
|
||||
|
||||
or
|
||||
|
||||
[source]
|
||||
----
|
||||
java -jar my.jar < foo.txt
|
||||
----
|
||||
====
|
||||
|
||||
allowing the application to terminate when the pipe is closed.
|
||||
This approach lets the application terminate when the pipe is closed.
|
||||
|
||||
Four convenient factory methods are available:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
public static final CharacterStreamReadingMessageSource stdin() { ... }
|
||||
@@ -73,14 +77,16 @@ public static final CharacterStreamReadingMessageSource stdinPipe() { ... }
|
||||
|
||||
public static final CharacterStreamReadingMessageSource stdinPipe(String charsetName) { ... }
|
||||
----
|
||||
====
|
||||
|
||||
[[stream-writing]]
|
||||
=== Writing to streams
|
||||
=== Writing to Streams
|
||||
|
||||
For target streams, there are also two implementations: `ByteStreamWritingMessageHandler` and `CharacterStreamWritingMessageHandler`.
|
||||
Each requires a single constructor argument - `OutputStream` for byte streams or `Writer` for character streams, and each provides a second constructor that adds the optional 'bufferSize'.
|
||||
Since both of these ultimately implement the `MessageHandler` interface, they can be referenced from a _channel-adapter_ configuration as described in more detail in <<channel-adapter>>.
|
||||
For target streams, you can use either of two implementations: `ByteStreamWritingMessageHandler` or `CharacterStreamWritingMessageHandler`.
|
||||
Each requires a single constructor argument (`OutputStream` for byte streams or `Writer` for character streams), and each provides a second constructor that adds the optional 'bufferSize'.
|
||||
Since both of these ultimately implement the `MessageHandler` interface, you can reference them from a `channel-adapter` configuration, as described in "`<<channel-adapter>>`".
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<bean class="org.springframework.integration.stream.ByteStreamWritingMessageHandler">
|
||||
@@ -91,15 +97,16 @@ Since both of these ultimately implement the `MessageHandler` interface, they ca
|
||||
<bean class="org.springframework.integration.stream.CharacterStreamWritingMessageHandler">
|
||||
<constructor-arg ref="someWriter"/>
|
||||
</bean>
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
[[stream-namespace]]
|
||||
=== Stream namespace support
|
||||
=== Stream Namespace Support
|
||||
|
||||
To reduce the configuration needed for stream related channel adapters there is a namespace defined.
|
||||
The following schema locations are needed to use it.
|
||||
Spring Integration defines a namespace to reduce the configuration needed for stream-related channel adapters.
|
||||
The following schema locations are needed to use it:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
@@ -111,21 +118,26 @@ The following schema locations are needed to use it.
|
||||
http://www.springframework.org/schema/integration/stream
|
||||
http://www.springframework.org/schema/integration/stream/spring-integration-stream.xsd">
|
||||
----
|
||||
====
|
||||
|
||||
To configure the inbound channel adapter the following code snippet shows the different configuration options that are supported.
|
||||
The following code snippet shows the different configuration options that are supported to configure the inbound channel adapter:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-stream:stdin-channel-adapter id="adapterWithDefaultCharset"/>
|
||||
|
||||
<int-stream:stdin-channel-adapter id="adapterWithProvidedCharset" charset="UTF-8"/>
|
||||
----
|
||||
====
|
||||
|
||||
Starting with _version 5.0_ you can set the `detect-eof` attribute which sets the `blockToDetectEOF` property - see <<stream-reading>> for more information.
|
||||
Starting with version 5.0, you can set the `detect-eof` attribute, which sets the `blockToDetectEOF` property.
|
||||
See "`<<stream-reading>>`" for more information.
|
||||
|
||||
To configure the outbound channel adapter you can use the namespace support as well.
|
||||
The following code snippet shows the different configuration for an outbound channel adapters.
|
||||
To configure the outbound channel adapter, you can use the namespace support as well.
|
||||
The following example shows the different configuration for an outbound channel adapters:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-stream:stdout-channel-adapter id="stdoutAdapterWithDefaultCharset"
|
||||
@@ -139,3 +151,4 @@ The following code snippet shows the different configuration for an outbound cha
|
||||
<int-stream:stdout-channel-adapter id="newlineAdapter" append-newline="true"
|
||||
channel="testChannel"/>
|
||||
----
|
||||
====
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
[[syslog]]
|
||||
== Syslog Support
|
||||
|
||||
[[syslog-intro]]
|
||||
=== Introduction
|
||||
Spring Integration 2.2 introduced the syslog transformer: `SyslogToMapTransformer`.
|
||||
This transformer, together with a `UDP` or `TCP` inbound adapter, could be used to receive and analyze syslog records from other hosts.
|
||||
The transformer creates a message payload that contains a map of the elements from the syslog message.
|
||||
|
||||
Spring Integration 2.2 introduced the Syslog transformer `SyslogToMapTransformer`.
|
||||
This transformer, together with a `UDP` or `TCP` inbound adapter could be used to receive and analyze syslog records from other hosts.
|
||||
The transformer creates a message payload containing a map of the elements from the syslog message.
|
||||
Spring Integration 3.0 introduced convenient namespace support for configuring a syslog inbound adapter in a single element.
|
||||
|
||||
Spring Integration 3.0 introduced convenient namespace support for configuring a Syslog inbound adapter in a single element.
|
||||
|
||||
Starting with _version 4.1.1_, the framework now supports the extended Syslog format, as specified in https://tools.ietf.org/html/rfc5424[RFC 5424>].
|
||||
Starting with version 4.1.1, the framework now supports the extended syslog format, as specified in https://tools.ietf.org/html/rfc5424[RFC 5424>].
|
||||
In addition, when using TCP and RFC5424, both `octet counting` and `non-transparent framing` described in https://tools.ietf.org/html/rfc6587[RFC 6587] are supported.
|
||||
|
||||
[[syslog-inbound-adapter]]
|
||||
=== Syslog <inbound-channel-adapter>
|
||||
=== Syslog Inbound Channel Adapter
|
||||
|
||||
This element encompasses a `UDP` or `TCP` inbound channel adapter and a `MessageConverter` to convert the Syslog message to a Spring Integration message.
|
||||
The `DefaultMessageConverter` delegates to the `SyslogToMapTransformer`, creating a message with its payload being the `Map` of Syslog fields.
|
||||
In addition, all fields except the message are also made available as headers in the message, prefixed with `syslog_`.
|
||||
This element encompasses a `UDP` or `TCP` inbound channel adapter and a `MessageConverter` to convert the syslog message to a Spring Integration message.
|
||||
The `DefaultMessageConverter` delegates to the `SyslogToMapTransformer`, creating a message with its payload being the `Map` of syslog fields.
|
||||
In addition, all fields except the message are also made available as headers in the message and are prefixed with `syslog_`.
|
||||
In this mode, only https://tools.ietf.org/html/rfc3164[RFC 3164] (BSD) syslogs are supported.
|
||||
|
||||
Since _version 4.1_, the `DefaultMessageConverter` has a property `asMap` (default `true`); when it is `false`, the converter will leave the message payload as the original complete syslog message, in a `byte[]`, while still setting the headers.
|
||||
Since version 4.1, the `DefaultMessageConverter` has a property called `asMap` (the default is `true`).
|
||||
When it is `false`, the converter leaves the message payload as the original complete syslog message (in a `byte[]`) while still setting the headers.
|
||||
|
||||
Since _version 4.1.1_, RFC 5424 is also supported, using the `RFC5424MessageConverter`; in this case the fields are not copied as headers, unless `asMap` is set to `false`, in which case the original message is the payload and the decoded fields are headers.
|
||||
Since version 4.1.1, RFC 5424 is also supported, by using the `RFC5424MessageConverter`.
|
||||
In this case, the fields are not copied as headers, unless `asMap` is set to `false`, in which case the original message is the payload and the decoded fields are headers.
|
||||
|
||||
IMPORTANT: To use RFC 5424 with a TCP transport, additional configuration is required, to enable the different framing techniques described in RFC 6587.
|
||||
The adapter needs a TCP connection factory configured with a `RFC6587SyslogDeserializer`.
|
||||
By default, this deserializer will handle `octet counting` and `non-transparent framing`, using a linefeed (LF) to delimit syslog messages; it uses a `ByteArrayLfSerializer` when `octet counting` is not detected.
|
||||
IMPORTANT: To use RFC 5424 with a TCP transport, you must provide additional configuration to enable the different framing techniques described in RFC 6587.
|
||||
The adapter needs a TCP connection factory that is configured with a `RFC6587SyslogDeserializer`.
|
||||
By default, this deserializer handles `octet counting` and `non-transparent framing` by using a linefeed (LF) to delimit syslog messages.
|
||||
It uses a `ByteArrayLfSerializer` when `octet counting` is not detected.
|
||||
To use different `non-transparent` framing, you can provide it with some other deserializer.
|
||||
While the deserializer can support both `octet counting` and `non-transparent framing`, only one form of the latter is supported.
|
||||
If `asMap` is `false` on the converter, you must set the `retainOriginal` constructor argument in the `RFC6587SyslogDeserializer`.
|
||||
@@ -35,34 +35,47 @@ If `asMap` is `false` on the converter, you must set the `retainOriginal` constr
|
||||
[[syslog-inbound-examplers]]
|
||||
==== Example Configuration
|
||||
|
||||
The following example defines a `UDP` adapter that sends messages to the `syslogIn` channel (the adapter bean name is `syslogIn.adapter`):
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-syslog:inbound-channel-adapter id="syslogIn" port="1514" />
|
||||
----
|
||||
====
|
||||
|
||||
A `UDP` adapter that sends messages to channel `syslogIn` (the adapter bean name is `syslogIn.adapter`).
|
||||
The adapter listens on port `1514`.
|
||||
|
||||
The following example defines a `UDP` adapter that sends messages to the `fromSyslog` channel (the adapter bean name is `syslogIn`):
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-syslog:inbound-channel-adapter id="syslogIn"
|
||||
channel="fromSyslog" port="1514" />
|
||||
----
|
||||
====
|
||||
|
||||
A `UDP` adapter that sends message to channel `fromSyslog` (the adapter bean name is `syslogIn`).
|
||||
The adapter listens on port `1514`.
|
||||
|
||||
The following example defines a `TCP` adapter that sends messages to channel `syslogIn` (the adapter bean name is `syslogIn.adapter`):
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-syslog:inbound-channel-adapter id="bar" protocol="tcp" port="1514" />
|
||||
----
|
||||
====
|
||||
|
||||
A `TCP` adapter that sends messages to channel `syslogIn` (the adapter bean name is `syslogIn.adapter`).
|
||||
The adapter listens on port `1514`.
|
||||
|
||||
Note the addition of the `protocol` attribute.
|
||||
This attribute can contain `udp` or `tcp`; it defaults to `udp`.
|
||||
This attribute can contain `udp` or `tcp`.
|
||||
It defaults to `udp`.
|
||||
|
||||
The following example shows a `UDP` adapter that sends messages to channel `fromSyslog`:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-syslog:inbound-channel-adapter id="udpSyslog"
|
||||
@@ -75,15 +88,18 @@ This attribute can contain `udp` or `tcp`; it defaults to `udp`.
|
||||
<int-syslog:udp-attributes port="1514" lookup-host="false" />
|
||||
</int-syslog:inbound-channel-adapter>
|
||||
----
|
||||
====
|
||||
|
||||
A `UDP` adapter that sends messages to channel `fromSyslog`.
|
||||
It also shows the `SmartLifecycle` attributes `auto-startup` and `phase`.
|
||||
It has a reference to a custom `org.springframework.integration.syslog.MessageConverter` with id `converter` and an `error-channel`.
|
||||
The preceding example also shows two `SmartLifecycle` attributes: `auto-startup` and `phase`.
|
||||
It has a reference to a custom `org.springframework.integration.syslog.MessageConverter` with an ID of `converter` and an `error-channel`.
|
||||
Also notice the `udp-attributes` child element.
|
||||
You can set various UDP attributes here, as defined in <<ip-udp-ib-atts>>.
|
||||
You can set various UDP attributes here, as defined in "`<<ip-udp-ib-atts>>`".
|
||||
|
||||
NOTE: When using the `udp-attributes` element, the `port` attribute must be provided there rather than on the `inbound-channel-adapter` element itself.
|
||||
NOTE: When you use the `udp-attributes` element, you must provide the `port` attribute there rather than on the `inbound-channel-adapter` element itself.
|
||||
|
||||
The following example shows a `TCP` adapter that sends messages to channel `fromSyslog`:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-syslog:inbound-channel-adapter id="TcpSyslog"
|
||||
@@ -93,13 +109,16 @@ NOTE: When using the `udp-attributes` element, the `port` attribute must be prov
|
||||
|
||||
<int-ip:tcp-connection-factory id="cf" type="server" port="1514" />
|
||||
----
|
||||
====
|
||||
|
||||
A `TCP` adapter that sends messages to channel `fromSyslog`.
|
||||
It also shows how to reference an externally defined connection factory, which can be used for advanced configuration (socket keep alive etc).
|
||||
For more information, see <<tcp-connection-factories>>.
|
||||
It also shows how to reference an externally defined connection factory, which can be used for advanced configuration (socket keep-alive and other uses).
|
||||
For more information, see "`<<tcp-connection-factories>>`".
|
||||
|
||||
NOTE: The externally configured `connection-factory` must be of type `server` and, the port is defined there rather than on the `inbound-channel-adapter` element itself.
|
||||
NOTE: The externally configured `connection-factory` must be of type `server`, and the port is defined there rather than on the `inbound-channel-adapter` element itself.
|
||||
|
||||
The following example shows a `TCP` adapter that sends messages to channel `fromSyslog`:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-syslog:inbound-channel-adapter id="rfc5424Tcp"
|
||||
@@ -118,6 +137,6 @@ NOTE: The externally configured `connection-factory` must be of type `server` an
|
||||
|
||||
<bean id="rfc6587" class="org.springframework.integration.syslog.inbound.RFC6587SyslogDeserializer" />
|
||||
----
|
||||
====
|
||||
|
||||
A `TCP` adapter that sends messages to channel `fromSyslog`.
|
||||
It is configured to use the `RFC 5424` converter and is configured with a reference to an externally defined connection factory with the `RFC 6587` deserializer (required for RFC 5424).
|
||||
The preceding example is configured to use the `RFC 5424` converter and is configured with a reference to an externally defined connection factory with the `RFC 6587` deserializer (required for RFC 5424).
|
||||
|
||||
@@ -1,38 +1,42 @@
|
||||
[[testing]]
|
||||
== Testing support
|
||||
|
||||
Spring Integration provides a number of utilities and annotations to help when testing your application.
|
||||
Test support is presented by two modules: `spring-integration-test-support` which contains core items and shared utilities, and `spring-integration-test` which provides mocking and application context configuration components for integration tests.
|
||||
Spring Integration provides a number of utilities and annotations to help you test your application.
|
||||
Testing support is presented by two modules:
|
||||
|
||||
* `spring-integration-test-support` contains core items and shared utilities
|
||||
* `spring-integration-test` provides mocking and application context configuration components for integration tests
|
||||
|
||||
`spring-integration-test-support` (`spring-integration-test` in versions before 5.0) provides basic, standalone utilities, rules, and matchers for unit testing.
|
||||
(it also has no dependencies on Spring Integration itself and is used internally in Framework tests).
|
||||
`spring-integration-test` aims to help with integration testing and provides a comprehensive high-level API to mock integration components and verify the behavior of individual components, including whole integration flows or only parts of them.
|
||||
|
||||
`spring-integration-test-support` (`spring-integration-test` in versions before _5.0_) provides basic, standalone utilities, rules and matchers for unit testing (it also has no dependencies on Spring Integration itself, and is used internally in Framework tests). `spring-integration-test` is aimed to help with integration testing and provides a comprehensive high level API to mock integration components and verify behavior of individual components, including whole integration flows or just parts thereof.
|
||||
A thorough treatment of testing in the enterprise is beyond the scope of this reference manual.
|
||||
See the http://www.enterpriseintegrationpatterns.com/docs/TestDrivenEAI.pdf[Test-Driven Development in Enterprise Integration Projects] paper, by Gregor Hohpe and Wendy Istvanick, for a source of ideas and principles for testing your target integration solution.
|
||||
See the http://www.enterpriseintegrationpatterns.com/docs/TestDrivenEAI.pdf["`Test-Driven Development in Enterprise Integration Projects`"] paper, by Gregor Hohpe and Wendy Istvanick, for a source of ideas and principles for testing your target integration solution.
|
||||
|
||||
[[testing-intro]]
|
||||
=== Introduction
|
||||
The Spring Integration Test Framework and test utilities are fully based on existing JUnit, Hamcrest, and Mockito libraries.
|
||||
The application context interaction is based on the http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/htmlsingle/#testing[Spring test framework].
|
||||
See the documentation for those projects for further information.
|
||||
|
||||
The Spring Integration Test Framework and test utilities are fully based on existing JUnit, Hamcrest and Mockito libraries.
|
||||
The Application Context interaction is based on the http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/htmlsingle/#testing[Spring Test Framework].
|
||||
Please, refer to the documentation for those projects for further information.
|
||||
Thanks to the canonical implementation of the EIP in Spring Integration Framework and its first-class citizens (such as `MessageChannel`, `Endpoint` and `MessageHandler`), abstractions, and loose coupling principles, you can implement integration solutions of any complexity.
|
||||
With the Spring Integration API for the flow definitions, you can improve, modify or even replace some part of the flow without impacting (mostly) other components in the integration solution.
|
||||
Testing such an integration solution is still a challenge, both from an end-to-end approach and from an in-isolation approach.
|
||||
Several existing tools can help to test or mock some integration protocols, and they work well with Spring Integration channel adapters. Examples of such tools include the following:
|
||||
|
||||
Thanks to the canonical implementation of the EIP in Spring Integration Framework and its first class citizens like `MessageChannel`, `Endpoint` and `MessageHandler` abstractions and supported out-of-the-box loose coupling principles, we can implement integration solutions of any complexity.
|
||||
With the Spring Integration API for the flow definitions, we can improve, modify or even replace some part of the flow without impacting (mostly) other components in the integration solution.
|
||||
Testing such an integration solution is still a challenge, from an __end-to-end__ perspective, as well as with an __in-isolation__ approach.
|
||||
There are several existing tools which help to test or mock some integration protocols and they work very well with Spring Integration Channel Adapters; examples include:
|
||||
* Spring `MockMVC` and its `MockRestServiceServer` can be used for testing HTTP.
|
||||
* Some RDBMS vendors provide embedded data bases for JDBC or JPA support.
|
||||
* ActiveMQ can be embedded for testing JMS or STOMP protocols.
|
||||
* There are tools for embedded MongoDB and Redis.
|
||||
* Tomcat and Jetty have embedded libraries to test real HTTP, Web Services, or WebSockets.
|
||||
* The `FtpServer` and `SshServer` from the Apache Mina project can be used for testing the FTP and SFTP protocols.
|
||||
* Gemfire and Hazelcast can be run as real-data grid nodes in the tests.
|
||||
* The Curator Framework provides a `TestingServer` for Zookeeper interaction.
|
||||
* Apache Kafka provides admin tools to embed a Kafka Broker in the tests.
|
||||
|
||||
- Spring `MockMVC` and its `MockRestServiceServer` for HTTP;
|
||||
- Some RDBMS vendors provide embedded data bases for JDBC or JPA support;
|
||||
- ActiveMQ can be embedded for testing JMS or STOMP protocols;
|
||||
- There are tools for embedded MongoDB and Redis;
|
||||
- Tomcat and Jetty have embedded libraries to test real HTTP, Web Services or WebSockets;
|
||||
- The `FtpServer` and `SshServer` from the Apache Mina project can be used for testing (S)FTP protocols;
|
||||
- Gemfire and Hazelcast can be run as real data grid nodes in the tests;
|
||||
- The Curator Framework provides a `TestingServer` for Zookeeper interaction;
|
||||
- Apache Kafka provides admin tools to embed a Kafka Broker in the tests.
|
||||
Most of these tools and libraries are used in Spring Integration tests.
|
||||
Also, from the GitHub https://github.com/spring-projects/spring-integration[repository] (in the `test` directory of each module), you can discover ideas for how to build your own tests for integration solutions.
|
||||
|
||||
Most of these tools and libraries are used in Spring Integration tests and from the GitHub https://github.com/spring-projects/spring-integration[repository], in the `test` directory of each module, you can discover ideas how to build your own tests for integration solutions.
|
||||
|
||||
The rest of this chapter describes the testing tools and utilities provided by the Spring Integration Framework.
|
||||
The rest of this chapter describes the testing tools and utilities provided by Spring Integration.
|
||||
|
||||
[[testing-utilities]]
|
||||
=== Testing Utilities
|
||||
@@ -41,7 +45,9 @@ The `spring-integration-test-support` module provides utilities and helpers for
|
||||
|
||||
==== TestUtils
|
||||
|
||||
The `TestUtils` class is mostly used for properties assertions in JUnit tests:
|
||||
The `TestUtils` class is mostly used for properties assertions in JUnit tests, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Test
|
||||
@@ -52,18 +58,20 @@ public void loadBalancerRef() {
|
||||
assertTrue(lbStrategy instanceof SampleLoadBalancingStrategy);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
`TestUtils.getPropertyValue()` is based on Spring's `DirectFieldAccessor` and provides the ability to get a value from the target private property.
|
||||
As you see by the example above it also supports nested properties access, using dotted notation.
|
||||
As shown in the preceding example, it also supports nested properties access by using dotted notation.
|
||||
|
||||
The `createTestApplicationContext()` factory method produce a `TestApplicationContext` instance with the supplied Spring Integration environment.
|
||||
The `createTestApplicationContext()` factory method produces a `TestApplicationContext` instance with the supplied Spring Integration environment.
|
||||
|
||||
See the JavaDocs of other `TestUtils` methods for more information about this class.
|
||||
See the https://docs.spring.io/spring-integration/api/org/springframework/integration/test/util/TestUtils.html[Javadoc] of other `TestUtils` methods for more information about this class.
|
||||
|
||||
==== SocketUtils
|
||||
==== Using the `SocketUtils` Class
|
||||
|
||||
The `SocketUtils` provides several methods to select a random port(s) for exposing server-side components without conflicts:
|
||||
The https://docs.spring.io/spring-integration/api/org/springframework/integration/test/util/SocketUtils.html[`SocketUtils` class] provides several methods that select one or more random ports for exposing server-side components without conflicts, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="socketUtils" class="org.springframework.integration.test.util.SocketUtils" />
|
||||
@@ -76,9 +84,11 @@ The `SocketUtils` provides several methods to select a random port(s) for exposi
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
----
|
||||
====
|
||||
|
||||
Which is used from the unit test as:
|
||||
The following example shows how the preceding configuration is used from the unit test:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Autowired @Qualifier("syslog.adapter")
|
||||
@@ -100,15 +110,19 @@ public void testSimplestUdp() throws Exception {
|
||||
assertNotNull(message);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: This tecnique is not foolproof; some other process could be allocated the "free" port before your test opens it.
|
||||
It is generally more preferable to use a server port `0` and let the operating system select the port for you, then discover the selected port in your test.
|
||||
NOTE: This technique is not foolproof.
|
||||
Some other process could be allocated the "`free`" port before your test opens it.
|
||||
It is generally more preferable to use server port `0`, let the operating system select the port for you, and then discover the selected port in your test.
|
||||
We have converted most framework tests to use this preferred technique.
|
||||
|
||||
==== OnlyOnceTrigger
|
||||
==== Using `OnlyOnceTrigger`
|
||||
|
||||
The `OnlyOnceTrigger` is useful for polling endpoints when it is good to produce only one test message and verify the behavior without impacting of unexpected other period messages:
|
||||
https://docs.spring.io/spring-integration/api/org/springframework/integration/test/util/OnlyOnceTrigger.html[`OnlyOnceTrigger`] is useful for polling endpoints when you need to produce only one test message and verify the behavior without impacting other period messages.
|
||||
The following example shows how to configure `OnlyOnceTrigger`:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="testTrigger" class="org.springframework.integration.test.util.OnlyOnceTrigger" />
|
||||
@@ -117,7 +131,11 @@ The `OnlyOnceTrigger` is useful for polling endpoints when it is good to produce
|
||||
<int:transactional transaction-manager="transactionManager" />
|
||||
</int:poller>
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows how to use the preceding configuration of `OnlyOnceTrigger` for testing:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Autowired
|
||||
@@ -141,15 +159,27 @@ public void testWithEntityClass() throws Exception {
|
||||
...
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
==== Support Components
|
||||
|
||||
The `org.springframework.integration.test.support` package contains various abstract classes which should be implemented in target tests.
|
||||
See their JavaDocs for more information.
|
||||
The `org.springframework.integration.test.support` package contains various abstract classes that you should implement in target tests
|
||||
|
||||
* https://docs.spring.io/spring-integration/api/org/springframework/integration/test/support/AbstractRequestResponseScenarioTests.html[`AbstractRequestResponseScenarioTests`]
|
||||
* https://docs.spring.io/spring-integration/api/org/springframework/integration/test/support/AbstractResponseValidator.html[`AbstractResponseValidator`]
|
||||
* https://docs.spring.io/spring-integration/api/org/springframework/integration/test/support/LogAdjustingTestSupport.html[`LogAdjustingTestSupport`] (Deprecated)
|
||||
* https://docs.spring.io/spring-integration/api/org/springframework/integration/test/support/LongRunningIntegrationTest.html[`LongRunningIntegrationTest`]
|
||||
* https://docs.spring.io/spring-integration/api/org/springframework/integration/test/support/MessageValidator.html[`MessageValidator`]
|
||||
* https://docs.spring.io/spring-integration/api/org/springframework/integration/test/support/PayloadValidator.html[`PayloadValidator`]
|
||||
* https://docs.spring.io/spring-integration/api/org/springframework/integration/test/support/RequestResponseScenario.html[`RequestResponseScenario`]
|
||||
* https://docs.spring.io/spring-integration/api/org/springframework/integration/test/support/SingleRequestResponseScenarioTests.html[`SingleRequestResponseScenarioTests`]
|
||||
|
||||
==== Hamcrest and Mockito Matchers
|
||||
|
||||
The `org.springframework.integration.test.matcher` package contains several `Matcher` implementations to assert `Message` and its properties in unit tests:
|
||||
The `org.springframework.integration.test.matcher` package contains several `Matcher` implementations to assert `Message` and its properties in unit tests.
|
||||
The following example shows how to use one such matcher (`PayloadMatcher`):
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
import static org.springframework.integration.test.matcher.PayloadMatcher.hasPayload;
|
||||
@@ -162,9 +192,11 @@ public void transform_withFilePayload_convertedToByteArray() throws Exception {
|
||||
assertThat(result, hasPayload(SAMPLE_CONTENT.getBytes(DEFAULT_ENCODING)));
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The `MockitoMessageMatchers` factory can be used for mocks stubbing and verifications:
|
||||
The `MockitoMessageMatchers` factory can be used for mocks for stubbing and verifications, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
static final Date SOME_PAYLOAD = new Date();
|
||||
@@ -186,17 +218,17 @@ when(channel.send(messageWithHeaderEntry(SOME_HEADER_KEY, is(instanceOf(Short.cl
|
||||
.thenReturn(true);
|
||||
assertThat(channel.send(message), is(false));
|
||||
----
|
||||
|
||||
Additional utilities will eventually be added or migrated.
|
||||
For example `RemoteFileTestSupport` implementations for the (S)FTP tests can be moved from the `test` directory of those particular modules to this `spring-integration-test-support` artifact.
|
||||
====
|
||||
|
||||
[[test-context]]
|
||||
=== Spring Integration and test context
|
||||
=== Spring Integration and the Test Context
|
||||
|
||||
Typically, tests for Spring applications use the Spring Test Framework and since Spring Integration is based on the Spring Framework foundation, everything we can do with the Spring Test Framework is applied as well when testing integration flows.
|
||||
Typically, tests for Spring applications use the Spring Test Framework.
|
||||
Since Spring Integration is based on the Spring Framework foundation, everything we can do with the Spring Test Framework also applies when testing integration flows.
|
||||
The `org.springframework.integration.test.context` package provides some components for enhancing the test context for integration needs.
|
||||
First of all we configure our test class with a `@SpringIntegrationTest` annotation to enable the Spring Integration Test Framework:
|
||||
First of all, we configure our test class with a `@SpringIntegrationTest` annotation to enable the Spring Integration Test Framework, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@RunWith(SpringRunner.class)
|
||||
@@ -208,14 +240,17 @@ public class MyIntegrationTests {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The `@SpringIntegrationTest` annotation populates a `MockIntegrationContext` bean which can be autowired to the test class to access its methods.
|
||||
With the provided `noAutoStartup` option, the Spring Integration Test Framework prevents endpoints that are normally `autoStartup=true` from starting. The endpoints are matched to the provided patterns, which support the following simple pattern styles: `xxx*`, `*xxx`, `*xxx*` and `xxx*yyy`.
|
||||
The `@SpringIntegrationTest` annotation populates a `MockIntegrationContext` bean, which you can autowire to the test class to access its methods.
|
||||
With the `noAutoStartup` option, the Spring Integration Test Framework prevents endpoints that are normally `autoStartup=true` from starting. The endpoints are matched to the provided patterns, which support the following simple pattern styles: `xxx*`, `*xxx`, `*xxx*`, and `xxx*yyy`.
|
||||
|
||||
This is useful, when we would like to not have real connections to the target systems from Inbound Channel Adapters, for example an AMQP Inbound Gateway, JDBC Polling Channel Adapter, WebSocket Message Producer in client mode etc.
|
||||
This is useful when we would like to not have real connections to the target systems from inbound channel adapters (for example an AMQP Inbound Gateway, JDBC Polling Channel Adapter, WebSocket Message Producer in client mode, and so on).
|
||||
|
||||
The `MockIntegrationContext` is aimed to be used in the target test-cases for modifications to beans in the real application context, for example those endpoints that have `autoStartup` overridden to false can be replaced with mocks:
|
||||
The `MockIntegrationContext` is meant to be used in the target test cases for modifications to beans in the real application context.
|
||||
For example, endpoints that have `autoStartup` overridden to `false` can be replaced with mocks, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Test
|
||||
@@ -228,21 +263,23 @@ public void testMockMessageSource() {
|
||||
assertNotNull(receive);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
See their JavaDocs for more information.
|
||||
See the https://docs.spring.io/spring-integration/api/org/springframework/integration/test/context/MockIntegrationContext.html[Javadoc] for more information.
|
||||
|
||||
[[testing-mocks]]
|
||||
=== Integration Mocks
|
||||
|
||||
The `org.springframework.integration.test.mock` package offers tools and utilities for mocking, stubbing and verification of activity on Spring Integration components.
|
||||
The mocking functionality is fully based and compatible with the well known Mockito Framework.
|
||||
(The current Mockito transitive dependency is of _version 2.5.x_.)
|
||||
The `org.springframework.integration.test.mock` package offers tools and utilities for mocking, stubbing, and verification of activity on Spring Integration components.
|
||||
The mocking functionality is fully based on and compatible with the well known Mockito Framework.
|
||||
(The current Mockito transitive dependency is on version 2.5.x.)
|
||||
|
||||
==== MockIntegration
|
||||
|
||||
The `MockIntegration` factory provides an API to build mocks for Spring Integration beans which are parts of the integration flow - `MessageSource`, `MessageProducer`, `MessageHandler`, `MessageChannel`.
|
||||
The target mocks can be used during configuration phase:
|
||||
The `MockIntegration` factory provides an API to build mocks for Spring Integration beans that are parts of the integration flow (`MessageSource`, `MessageProducer`, `MessageHandler`, and `MessageChannel`).
|
||||
You can use the target mocks during the configuration phase as well as in the target test method to replace the real endpoints before performing verifications and assertions, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:inbound-channel-adapter id="inboundChannelAdapter" channel="results">
|
||||
@@ -257,7 +294,11 @@ The target mocks can be used during configuration phase:
|
||||
</bean>
|
||||
</int:inbound-channel-adapter>
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows how to use Java Configuration to achieve the same configuration as the preceding example:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@InboundChannelAdapter(channel = "results")
|
||||
@@ -274,10 +315,11 @@ StandardIntegrationFlow flow = IntegrationFlows
|
||||
IntegrationFlowRegistration registration = this.integrationFlowContext.registration(flow)
|
||||
.register();
|
||||
----
|
||||
====
|
||||
|
||||
as well as in the target test method to replace the real endpoints before performing verifications and assertions.
|
||||
For this purpose, the aforementioned `MockIntegrationContext` should be used from the test:
|
||||
For this purpose, the aforementioned `MockIntegrationContext` should be used from the test, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
this.mockIntegrationContext.substituteMessageSourceFor("mySourceEndpoint",
|
||||
@@ -286,22 +328,25 @@ Message<?> receive = this.results.receive(10_000);
|
||||
assertNotNull(receive);
|
||||
assertEquals("FOO", receive.getPayload());
|
||||
----
|
||||
====
|
||||
|
||||
Unlike the Mockito `MessageSource` mock object, the `MockMessageHandler` is just a regular `AbstractMessageProducingHandler` extension with a chain API to stub handling for incoming messages.
|
||||
The `MockMessageHandler` provides `handleNext(Consumer<Message<?>>)` to specify a one-way stub for the next request message; used to mock message handlers that don't produce replies.
|
||||
The `handleNextAndReply(Function<Message<?>, ?>)` is provided for performing the same stub logic for the next request message and producing a reply for it.
|
||||
Unlike the Mockito `MessageSource` mock object, the `MockMessageHandler` is a regular `AbstractMessageProducingHandler` extension with a chain API to stub handling for incoming messages.
|
||||
The `MockMessageHandler` provides `handleNext(Consumer<Message<?>>)` to specify a one-way stub for the next request message.
|
||||
It is used to mock message handlers that do not produce replies.
|
||||
`handleNextAndReply(Function<Message<?>, ?>)` is provided for performing the same stub logic for the next request message and producing a reply for it.
|
||||
They can be chained to simulate any arbitrary request-reply scenarios for all expected request messages variants.
|
||||
These consumers and functions are applied to the incoming messages, one at a time from the stack, until the last, which is then used for all remaining messages.
|
||||
The behavior is similar to the Mockito `Answer` or `doReturn()` API.
|
||||
|
||||
In addition, a Mockito `ArgumentCaptor<Message<?>>` can be supplied to the `MockMessageHandler` in a constructor argument.
|
||||
In addition, you can supply a Mockito `ArgumentCaptor<Message<?>>` to the `MockMessageHandler` in a constructor argument.
|
||||
Each request message for the `MockMessageHandler` is captured by that `ArgumentCaptor`.
|
||||
During the test, its `getValue()/getAllValues()` can be used to verify and assert those request messages.
|
||||
During the test, you can use its `getValue()` and `getAllValues()` methods to verify and assert those request messages.
|
||||
|
||||
The `MockIntegrationContext` provides an `substituteMessageHandlerFor()` API for replacing the actual configured `MessageHandler` with a `MockMessageHandler`, in the particular endpoint in the application context under test.
|
||||
The `MockIntegrationContext` provides a `substituteMessageHandlerFor()` API that lets you replace the actual configured `MessageHandler` with a `MockMessageHandler` in the endpoint under test.
|
||||
|
||||
A typical usage might be:
|
||||
The following example shows a typical usage scenario:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
ArgumentCaptor<Message<?>> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
|
||||
@@ -319,11 +364,12 @@ assertNotNull(received);
|
||||
assertEquals("FOO", received.getPayload());
|
||||
assertSame(message, messageArgumentCaptor.getValue());
|
||||
----
|
||||
====
|
||||
|
||||
See `MockIntegration` and `MockMessageHandler` JavaDocs for more information.
|
||||
See the https://docs.spring.io/spring-integration/api/org/springframework/integration/test/mock/MockIntegration.html[`MockIntegration`] and https://docs.spring.io/spring-integration/api/org/springframework/integration/test/mock/MockMessageHandler.html[`MockMessageHandler`] Javadoc for more information.
|
||||
|
||||
[[testing-other-resources]]
|
||||
=== Other Resources
|
||||
|
||||
As well as exploring the test cases in the framework itself, the https://github.com/spring-projects/spring-integration-samples[spring-integration-samples repository] has some sample apps specifically around testing, such as `testing-examples` and `advanced-testing-examples`.
|
||||
As well as exploring the test cases in the framework itself, the https://github.com/spring-projects/spring-integration-samples[Spring Integration Samples repository] has some sample applications specifically made to show testing, such as `testing-examples` and `advanced-testing-examples`.
|
||||
In some cases, the samples themselves have comprehensive end-to-end tests, such as the `file-split-ftp` sample.
|
||||
|
||||
@@ -1,70 +1,80 @@
|
||||
[[transactions]]
|
||||
== Transaction Support
|
||||
|
||||
This chapter covers Spring Integration's support for transactions. It covers the following topics:
|
||||
|
||||
* <<transaction-support>>
|
||||
* <<transaction-boundaries>>
|
||||
* <<transaction-synchronization>>
|
||||
* <<pseudo-transactions>>
|
||||
|
||||
[[transaction-support]]
|
||||
=== Understanding Transactions in Message flows
|
||||
|
||||
Spring Integration exposes several hooks to address transactional needs of you message flows.
|
||||
But to better understand these hooks and how you can benefit from them we must first revisit the 6 mechanisms that could be used to initiate Message flows and see how transactional needs of these flows could be addressed within each of these mechanisms.
|
||||
Spring Integration exposes several hooks to address the transactional needs of your message flows.
|
||||
To better understand these hooks and how you can benefit from them, we must first revisit the six mechanisms that you can use to initiate message flows and see how you can address the transactional needs of these flows within each of these mechanisms.
|
||||
|
||||
Here are the 6 mechanisms to initiate a Message flow and their short summary (details for each are provided throughout this manual):
|
||||
The following six mechanisms initiate a message flow (details for each are provided throughout this manual):
|
||||
|
||||
* _Gateway Proxy_ - Your basic Messaging Gateway
|
||||
* Gateway proxy: A basic messaging gateway.
|
||||
|
||||
* _MessageChannel_ - Direct interactions with MessageChannel methods (e.g., channel.send(message))
|
||||
* Message channel: Direct interactions with `MessageChannel` methods (for example, `channel.send(message)`).
|
||||
|
||||
* _Message Publisher_ - the way to initiate message flow as the by-product of method invocations on Spring beans
|
||||
* Message publisher: The way to initiate a message flow as the by-product of method invocations on Spring beans.
|
||||
|
||||
* _Inbound Channel Adapters/Gateways_ - the way to initiate message flow based on connecting third-party system with Spring Integration messaging system(e.g., [JmsMessage] -> Jms Inbound Adapter[SI Message] -> SI Channel)
|
||||
* Inbound channel adapters and gateways: The way to initiate a message flow based on connecting third-party system with the Spring Integration messaging system (for example, `[JmsMessage] -> Jms Inbound Adapter[SI Message] -> SI Channel`).
|
||||
|
||||
* _Scheduler_ - the way to initiate message flow based on scheduling events distributed by a pre-configured Scheduler
|
||||
* Scheduler: The way to initiate a message flow based on scheduling events distributed by a pre-configured scheduler.
|
||||
|
||||
* _Poller_ - similar to the Scheduler and is the way to initiate message flow based on scheduling or interval-based events distributed by a pre-configured Poller
|
||||
* Poller: Similar to the scheduler, this is the way to initiate message flow based on scheduling or interval-based events distributed by a pre-configured poller.
|
||||
|
||||
These 6 could be split in 2 general categories:
|
||||
We can split these six mechanisms into two general categories:
|
||||
|
||||
* _Message flows initiated by a USER process_ - Example scenarios in this category would be invoking a Gateway method or explicitly sending a Message to a MessageChannel.
|
||||
In other words, these message flows depend on a third party process (e.g., some code that we wrote) to be initiated.
|
||||
* Message flows initiated by a user process: Example scenarios in this category would be invoking a gateway method or explicitly sending a `Message` to a `MessageChannel`.
|
||||
In other words, these message flows depend on a third party process (such as some code that you wrote) to be initiated.
|
||||
|
||||
* _Message flows initiated by a DAEMON process_ - Example scenarios in this category would be a Poller polling a Message queue to initiate a new Message flow with the polled Message or a Scheduler scheduling the process by creating a new Message and initiating a message flow at a predefined time.
|
||||
* Message flows initiated by a daemon process: Example scenarios in this category include a Poller polling a message queue to initiate a new message flow with the polled message or a scheduler scheduling the process by creating a new message and initiating a message flow at a predefined time.
|
||||
|
||||
Clearly the _Gateway Proxy_, _MessageChannel.send(..)_ and _MessagePublisher_ all belong to the 1st category and _Inbound Adapters/Gateways_, _Scheduler_ and _Poller_ belong to the 2nd.
|
||||
Clearly the gateway proxy, `MessageChannel.send(...)` and `MessagePublisher` all belong to the first category, and inbound adapters and gateways, scheduler, and poller belong to the second category.
|
||||
|
||||
So, how do we address transactional needs in various scenarios within each category and is there a need for Spring Integration to provide something explicitly with regard to transactions for a particular scenario? Or, can Spring's Transaction Support be leveraged instead?.
|
||||
So, how can you address transactional needs in various scenarios within each category, and is there a need for Spring Integration to provide something explicit with regard to transactions for a particular scenario?
|
||||
Or can you use Spring's transaction support instead?
|
||||
|
||||
The first and most obvious goal is NOT to re-invent something that has already been invented unless you can provide a better solution.
|
||||
In our case Spring itself provides first class support for transaction management.
|
||||
So our goal here is not to provide something new but rather delegate/use Spring to benefit from the existing support for transactions.
|
||||
In other words as a framework we must expose hooks to the Transaction management functionality provided by Spring.
|
||||
But since Spring Integration configuration is based on Spring Configuration it is not always necessary to expose these hooks as they are already exposed via Spring natively.
|
||||
Remember every Spring Integration component is a Spring Bean after all.
|
||||
Spring itself provides first class support for transaction management.
|
||||
So our goal here is not to provide something new but rather use Spring to benefit from its existing support for transactions.
|
||||
In other words, as a framework, we must expose hooks to Spring's transaction management functionality.
|
||||
However, since Spring Integration configuration is based on Spring configuration, we need not always expose these hooks, because Spring already exposes them .
|
||||
After all, every Spring Integration component is a Spring Bean.
|
||||
|
||||
With this goal in mind let's look at the two scenarios.
|
||||
With this goal in mind, we can again consider the two scenarios: messgae flows initiated by a user process and message flows initiated by a daemon.
|
||||
|
||||
If you think about it, Message flows that are initiated by the _USER process_ (Category 1) and obviously configured in a Spring Application Context, are subject to transactional configuration of such processes and therefore don't need to be explicitly configured by Spring Integration to support transactions.
|
||||
The transaction could and should be initiated through standard Transaction support provided by Spring.
|
||||
The Spring Integration message flow will honor the transactional semantics of the components naturally because it is Spring configured.
|
||||
For example, a Gateway or ServiceActivator method could be annotated with `@Transactional` or `TransactionInterceptor` could be defined in an XML configuration with a point-cut expression pointing to specific methods that should be transactional.
|
||||
Message flows that are initiated by a user process and configured in a Spring application context are subject to the usual transactional configuration of such processes.
|
||||
Therefore they need not be explicitly configured by Spring Integration to support transactions.
|
||||
The transaction could and should be initiated through Spring's standard transaction support.
|
||||
The Spring Integration message flow naturally honors the transactional semantics of the components, because it is itself configured by Spring.
|
||||
For example, a gateway or service activator method could be annotated with `@Transactional`, or a `TransactionInterceptor` could be defined in an XML configuration with a pointcut expression that pointa to specific methods that should be transactional.
|
||||
The bottom line is that you have full control over transaction configuration and boundaries in these scenarios.
|
||||
|
||||
However, things are a bit different when it comes to Message flows initiated by the _DAEMON process_ (Category 2).
|
||||
Although configured by the developer these flows do not directly involve a human or some other process to be initiated.
|
||||
These are trigger-based flows that are initiated by a trigger process (DAEMON process) based on the configuration of such process.
|
||||
For example, we could have a Scheduler initiating a message flow every Friday night of every week.
|
||||
We can also configure a trigger that initiates a Message flow every second, etc.
|
||||
So, we obviously need a way to let these trigger-based processes know of our intention to make the resulting Message flows transactional so that a Transaction context could be created whenever a new Message flow is initiated.
|
||||
In other words we need to expose some Transaction configuration, but ONLY enough to delegate to Transaction support already provided by Spring (as we do in other scenarios).
|
||||
|
||||
Spring Integration provides transactional support for Pollers.
|
||||
Pollers are a special type of component because we can call receive() within that poller task against a resource that is itself transactional thus including _receive()_ call in the the boundaries of the Transaction allowing it to be rolled back in case of a task failure.
|
||||
If we were to add the same support for channels, the added transactions would affect all downstream components starting with that _send()_ call.
|
||||
That is providing a rather wide scope for transaction demarcation without any strong reason especially when Spring already provides several ways to address the transactional needs of any component downstream.
|
||||
However the _receive()_ method being included in a transaction boundary is the "strong reason" for pollers.
|
||||
However, things are a bit different when it comes to message flows initiated by a daemon process.
|
||||
Although configured by the developer, these flows do not directly involve a human or some other process to be initiated.
|
||||
These are trigger-based flows that are initiated by a trigger process (a daemon process) based on the configuration of the process.
|
||||
For example, we could have a scheduler initiate a message flow every Friday night.
|
||||
We can also configure a trigger that initiates a message flow every second and so on.
|
||||
As a result, we need a way to let these trigger-based processes know of our intention to make the resulting message flows be transactional, so that a Transaction context can be created whenever a new message flow is initiated.
|
||||
In other words, we need to expose some transaction configuration, but only enough to delegate to the transaction support already provided by Spring (as we do in other scenarios).
|
||||
|
||||
[[transaction-poller]]
|
||||
==== Poller Transaction Support
|
||||
|
||||
Any time you configure a Poller you can provide transactional configuration via the _transactional_ sub-element and its attributes:
|
||||
Spring Integration provides transactional support for pollers.
|
||||
Pollers are a special type of component because, within a poller task, we can call `receive()` against a resource that is itself transactional, thus including the `receive()` call in the the boundaries of the transaction, which lets it be rolled back in case of a task failure.
|
||||
If we were to add the same support for channels, the added transactions would affect all downstream components starting with the `send()` call.
|
||||
That provides a rather wide scope for transaction demarcation without any strong reason, especially when Spring already provides several ways to address the transactional needs of any component downstream.
|
||||
However the `receive()` method being included in a transaction boundary is the "`strong reason`" for pollers.
|
||||
|
||||
Any time you configure a Poller, you can provide transactional configuration by using the `transactional` child element and its attributes,as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:poller max-messages-per-poll="1" fixed-rate="1000">
|
||||
@@ -75,21 +85,25 @@ Any time you configure a Poller you can provide transactional configuration via
|
||||
timeout="1000"/>
|
||||
</poller>
|
||||
----
|
||||
====
|
||||
|
||||
As you can see this configuration looks very similar to native Spring transaction configuration.
|
||||
You must still provide a reference to a Transaction manager and specify transaction attributes or rely on defaults (e.g., if the 'transaction-manager' attribute is not specified, it will default to the bean with the name 'transactionManager').
|
||||
Internally the process would be wrapped in Spring's native Transaction where `TransactionInterceptor` is responsible for handling transactions.
|
||||
For more information on how to configure a Transaction Manager, the types of Transaction Managers (e.g., JTA, Datasource etc.) and other details related to transaction configuration please refer to Spring's Reference manual (Chapter 10 - Transaction Management).
|
||||
The preceding configuration looks similar to a native Spring transaction configuration.
|
||||
You must still provide a reference to a transaction manager and either specify transaction attributes or rely on defaults (for example, if the 'transaction-manager' attribute is not specified, it defaults to the bean named 'transactionManager').
|
||||
Internally, the process is wrapped in Spring's native transaction, where `TransactionInterceptor` is responsible for handling transactions.
|
||||
For more information on how to configure a transaction manager, the types of transaction managers (such as JTA, Datasource, and others), and other details related to transaction configuration, see the https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#transaction[Spring Framework Reference Guide].
|
||||
|
||||
With the above configuration all Message flows initiated by this poller will be transactional.
|
||||
For more information and details on a Poller's transactional configuration please refer to section - _21.1.1.
|
||||
Polling and Transactions_.
|
||||
With the preceding configuration, all message flows initiated by this poller are transactional.
|
||||
For more information and details on a poller's transactional configuration, see "`<<jdbc-polling-transactions>>`".
|
||||
|
||||
Along with transactions, several more cross cutting concerns might need to be addressed when running a Poller.
|
||||
To help with that, the Poller element accepts an _<advice-chain> _ sub-element which allows you to define a custom chain of Advice instances to be applied on the Poller.
|
||||
(see section 4.4 for more details) In Spring Integration 2.0, the Poller went through the a refactoring effort and is now using a proxy mechanism to address transactional concerns as well as other cross cutting concerns.
|
||||
One of the significant changes evolving from this effort is that we made _<transactional>_ and _<advice-chain>_ elements mutually exclusive.
|
||||
The rationale behind this is that if you need more than one advice, and one of them is Transaction advice, then you can simply include it in the _<advice-chain>_ with the same convenience as before but with much more control since you now have an option to position any advice in the desired order.
|
||||
Along with transactions, you might need to address several more cross-cutting concerns when you run a poller.
|
||||
To help with that, the poller element accepts an `<advice-chain>` child element, which lets you define a custom chain of advice instances to be applied on the Poller.
|
||||
(See "`<<pollable-message-source>>`" for more details.)
|
||||
In Spring Integration 2.0, the Poller went through a refactoring effort and now uses a proxy mechanism to address transactional concerns as well as other cross-cutting concerns.
|
||||
One of the significant changes evolving from this effort is that we made the `<transactional>` and `<advice-chain>` elements be mutually exclusive.
|
||||
The rationale behind this is that, if you need more than one advice and one of them is Transaction advice, you can include it in the `<advice-chain>` with the same convenience as before but with much more control, since you now have an option to position the advice in the desired order.
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:poller max-messages-per-poll="1" fixed-rate="10000">
|
||||
@@ -106,11 +120,11 @@ The rationale behind this is that if you need more than one advice, and one of t
|
||||
<tx:method name="*"/>
|
||||
</tx:attributes>
|
||||
</tx:advice>
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
As you can see from the example above, we have provided a very basic XML-based configuration of Spring Transaction advice - "txAdvice" and included it within the _<advice-chain>_ defined by the Poller.
|
||||
If you only need to address transactional concerns of the Poller, then you can still use the _<transactional>_ element as a convenience.
|
||||
The preceding example shows a basic XML-based configuration of Spring Transaction advice (`txAdvice`) and included it within the `<advice-chain>` defined by the Poller.
|
||||
If you need to address only the transactional concerns of the poller, you can still use the `<transactional>` element as a convenience.
|
||||
|
||||
[[transaction-boundaries]]
|
||||
=== Transaction Boundaries
|
||||
@@ -127,15 +141,17 @@ For example, you can use a Queue-backed Channel that delegates to a transactiona
|
||||
[[transaction-synchronization]]
|
||||
=== Transaction Synchronization
|
||||
|
||||
In some environments, it is advantageous to synchronize operations with a transaction that encompasses the entire flow.
|
||||
For example, consider a <file:inbound-channel-adapter/> at the start of a flow, that performs a number of database updates.
|
||||
If the transaction commits, we might want to move the file to a _success_ directory, while we might want to move it to a _failures_ directory if the transaction rolls back.
|
||||
In some environments, it help to synchronize operations with a transaction that encompasses the entire flow.
|
||||
For example, consider a `<file:inbound-channel-adapter/>` at the start of a flow that performs a number of database updates.
|
||||
If the transaction commits, we might want to move the file to a `success` directory, while we might want to move it to a `failure` directory if the transaction rolls back.
|
||||
|
||||
Spring Integration 2.2 introduces the capability of synchronizing these operations with a transaction.
|
||||
In addition, you can configure a `PseudoTransactionManager` if you don't have a 'real' transaction, but still want to perform different actions on success, or failure.
|
||||
For more information, see <<pseudo-transactions>>.
|
||||
Spring Integration 2.2 introduced the capability of synchronizing these operations with a transaction.
|
||||
In addition, you can configure a `PseudoTransactionManager` if you do not have a 'real' transaction but still want to perform different actions on success or failure.
|
||||
For more information, see "`<<pseudo-transactions>>`".
|
||||
|
||||
Key strategy interfaces for this feature are
|
||||
The following listing shows the key strategy interfaces for this feature:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public interface TransactionSynchronizationFactory {
|
||||
@@ -153,27 +169,31 @@ public interface TransactionSynchronizationProcessor {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The factory is responsible for creating a http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/support/TransactionSynchronization.html[TransactionSynchronization] object.
|
||||
You can implement your own, or use the one provided by the framework: `DefaultTransactionSynchronizationFactory`.
|
||||
This implementation returns a `TransactionSynchronization` that delegates to a default implementation of `TransactionSynchronizationProcessor`, the `ExpressionEvaluatingTransactionSynchronizationProcessor`.
|
||||
This processor supports three SpEL expressions, _beforeCommitExpression_, _afterCommitExpression_, and _afterRollbackExpression_.
|
||||
The factory is responsible for creating a http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/support/TransactionSynchronization.html[`TransactionSynchronization`] object.
|
||||
You can implement your own or use the one provided by the framework: `DefaultTransactionSynchronizationFactory`.
|
||||
This implementation returns a `TransactionSynchronization` that delegates to a default implementation of `TransactionSynchronizationProcessor`: `ExpressionEvaluatingTransactionSynchronizationProcessor`.
|
||||
This processor supports three SpEL expressions: `beforeCommitExpression`, `afterCommitExpression`, and `afterRollbackExpression`.
|
||||
|
||||
These actions should be self-explanatory to those familiar with transactions.
|
||||
In each case, the _#root_ variable is the original `Message`; in some cases, other SpEL variables are made available, depending on the `MessageSource` being polled by the poller.
|
||||
For example, the `MongoDbMessageSource` provides the _#mongoTemplate_ variable which references the message source's `MongoTemplate`; the `RedisStoreMessageSource` provides the _#store_ variable which references the `RedisStore` created by the poll.
|
||||
In each case, the `#root` variable is the original `Message`.
|
||||
In some cases, other SpEL variables are made available, depending on the `MessageSource` being polled by the poller.
|
||||
For example, the `MongoDbMessageSource` provides the `#mongoTemplate` variable, which references the message source's `MongoTemplate`.
|
||||
Similarly, the `RedisStoreMessageSource` provides the `#store` variable, which references the `RedisStore` created by the poll.
|
||||
|
||||
To enable the feature for a particular poller, you provide a reference to the `TransactionSynchronizationFactory` on the poller's <transactional/> element using the _synchronization-factory_ attribute.
|
||||
To enable the feature for a particular poller, you can provide a reference to the `TransactionSynchronizationFactory` on the poller's `<transactional/>` element by using the `synchronization-factory` attribute.
|
||||
|
||||
Starting with _version 5.0_, a new `PassThroughTransactionSynchronizationFactory` is provided which is applied by default to polling endpoints when no `TransactionSynchronizationFactory` is configured but an advice of type TransactionInterceptor exists in the advice chain.
|
||||
When using any out-of-the-box `TransactionSynchronizationFactory` implementation, polling endpoints bind a polled message to the current transactional context and provide it as a `failedMessage` in a `MessagingException` if an exception is thrown after the TX advice.
|
||||
When using a custom TX advice that does not implement `TransactionInterceptor`, a `PassThroughTransactionSynchronizationFactory` can be configured explicitly to achieve this behavior.
|
||||
In either case, the `MessagingException` becomes the payload of the `ErrorMessage` that is sent to the `errorChannel` and the cause is the raw exception thrown by the advice.
|
||||
Starting with version 5.0, Spring Integration provides `PassThroughTransactionSynchronizationFactory`, which is applied by default to polling endpoints when no `TransactionSynchronizationFactory` is configured but an advice of type `TransactionInterceptor` exists in the advice chain.
|
||||
When using any out-of-the-box `TransactionSynchronizationFactory` implementation, polling endpoints bind a polled message to the current transactional context and provide it as a `failedMessage` in a `MessagingException` if an exception is thrown after the transaction advice.
|
||||
When using a custom transaction advice that does not implement `TransactionInterceptor`, you can explicitly configure a `PassThroughTransactionSynchronizationFactory` to achieve this behavior.
|
||||
In either case, the `MessagingException` becomes the payload of the `ErrorMessage` that is sent to the `errorChannel`, and the cause is the raw exception thrown by the advice.
|
||||
Previously, the `ErrorMessage` had a payload that was the raw exception thrown by the advice and did not provide a reference to the `failedMessage` information, making it difficult to determine the reasons for the transaction commit problem.
|
||||
|
||||
To simplify configuration of these components, namespace support for the default factory has been provided.
|
||||
Configuration is best described using an example:
|
||||
To simplify configuration of these components, Spring Integration provides namespace support for the default factory.
|
||||
The following example shows how to use the namespace to configure a file inbound channel adapter:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-file:inbound-channel-adapter id="inputDirPoller"
|
||||
@@ -191,38 +211,44 @@ Configuration is best described using an example:
|
||||
<int:after-rollback expression="payload.renameTo('/failed/' + payload.name)" channel="rolledBackChannel" />
|
||||
</int:transaction-synchronization-factory>
|
||||
----
|
||||
====
|
||||
|
||||
The result of the SpEL evaluation is sent as the payload to either the _committedChannel_ or _rolledBackChannel_ (in this case, this would be `Boolean.TRUE` or `Boolean.FALSE` - the result of the `java.io.File.renameTo()` method call).
|
||||
The result of the SpEL evaluation is sent as the payload to either `committedChannel` or `rolledBackChannel` (in this case, this would be `Boolean.TRUE` or `Boolean.FALSE` -- the result of the `java.io.File.renameTo()` method call).
|
||||
|
||||
If you wish to send the entire payload for further Spring Integration processing, simply use the expression 'payload'.
|
||||
If you wish to send the entire payload for further Spring Integration processing, use the 'payload' expression.
|
||||
|
||||
[IMPORTANT]
|
||||
=====
|
||||
It is important to understand that this is simply synchronizing the actions with a transaction, it does not make a resource that is not inherently transactional actually transactional.
|
||||
Instead, the transaction (be it JDBC or otherwise) is started before the poll, and committed/rolled back when the flow completes, followed by the synchronized action.
|
||||
It is important to understand that this synchronizes the actions with a transaction.
|
||||
It does not make a resource that is not inherently transactional actually be transactional.
|
||||
Instead, the transaction (be it JDBC or otherwise) is started before the poll and either committed or rolled back when the flow completes, followed by the synchronized action.
|
||||
|
||||
It is also important to understand that if you provide a custom `TransactionSynchronizationFactory`, it is responsible for creating a resource synchronization that will cause the bound resource to be unbound automatically, when the transaction completes.
|
||||
The default `TransactionSynchronizationFactory` does this by returning a subclass of `ResourceHolderSynchronization`, with the default _shouldUnbindAtCompletion()_ returning `true`.
|
||||
If you provide a custom `TransactionSynchronizationFactory`, it is responsible for creating a resource synchronization that causes the bound resource to be unbound automatically when the transaction completes.
|
||||
The default `TransactionSynchronizationFactory` does so by returning a subclass of `ResourceHolderSynchronization`, with the default `shouldUnbindAtCompletion()` returning `true`.
|
||||
=====
|
||||
|
||||
In addition to the _after-commit_ and _after-rollback_ expressions, _before-commit_ is also supported.
|
||||
In that case, if the evaluation (or downstream processing) throws an exception, the transaction will be rolled back instead of being committed.
|
||||
In addition to the `after-commit` and `after-rollback` expressions, `before-commit` is also supported.
|
||||
In that case, if the evaluation (or downstream processing) throws an exception, the transaction is rolled back instead of being committed.
|
||||
|
||||
[[pseudo-transactions]]
|
||||
=== Pseudo Transactions
|
||||
|
||||
Referring to the above section, you may be thinking it would be useful to take these 'success' or 'failure' actions when a flow completes, even if there is no 'real' transactional resources (such as JDBC) downstream of the poller.
|
||||
For example, consider a <file:inbound-channel-adapter/> followed by an <ftp:outbout-channel-adapter/>.
|
||||
Neither of these components is transactional but we might want to move the input file to different directories, based on the success or failure of the ftp transfer.
|
||||
After reading the "`<<transaction-synchronization>>`" section, you might think it would be useful to take these 'success' or 'failure' actions when a flow completes, even if there is no "`real`" transactional resources (such as JDBC) downstream of the poller.
|
||||
For example, consider a "`<file:inbound-channel-adapter/>`" followed by an "`<ftp:outbout-channel-adapter/>`".
|
||||
Neither of these components is transactional, but we might want to move the input file to different directories, based on the success or failure of the FTP transfer.
|
||||
|
||||
To provide this functionality, the framework provides a `PseudoTransactionManager`, enabling the above configuration even when there is no real transactional resource involved.
|
||||
If the flow completes normally, the _beforeCommit_ and _afterCommit_ synchronizations will be called, on failure the _afterRollback_ will be called.
|
||||
Of course, because it is not a real transaction there will be no actual commit or rollback.
|
||||
The pseudo transaction is simply a vehicle used to enable the synchronization features.
|
||||
If the flow completes normally, the `beforeCommit` and `afterCommit` synchronizations are called.
|
||||
On failure, the `afterRollback` synchronization is called.
|
||||
Because it is not a real transaction, no actual commit or rollback occurs.
|
||||
The pseudo transaction is a vehicle used to enable the synchronization features.
|
||||
|
||||
To use a `PseudoTransactionManager`, simply define it as a <bean/>, in the same way you would configure a real transaction manager:
|
||||
To use a `PseudoTransactionManager`, you can define it as a <bean/>, in the same way you would configure a real transaction manager.
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="transactionManager" class="o.s.i.transaction.PseudoTransactionManager" />
|
||||
----
|
||||
====
|
||||
|
||||
@@ -1,40 +1,39 @@
|
||||
[[transformer]]
|
||||
=== Transformer
|
||||
|
||||
[[transformer-introduction]]
|
||||
==== Introduction
|
||||
Message transformers play a very important role in enabling the loose-coupling of message producers and message consumers.
|
||||
Rather than requiring every message-producing component to know what type is expected by the next consumer, you can add transformers between those components.
|
||||
Generic transformers, such as one that converts a `String` to an XML Document, are also highly reusable.
|
||||
|
||||
Message Transformers play a very important role in enabling the loose-coupling of Message Producers and Message Consumers.
|
||||
Rather than requiring every Message-producing component to know what type is expected by the next consumer, Transformers can be added between those components.
|
||||
Generic transformers, such as one that converts a String to an XML Document, are also highly reusable.
|
||||
|
||||
For some systems, it may be best to provide a http://www.eaipatterns.com/CanonicalDataModel.html[Canonical Data Model], but Spring Integration's general philosophy is not to require any particular format.
|
||||
For some systems, it may be best to provide a http://www.eaipatterns.com/CanonicalDataModel.html[canonical data model], but Spring Integration's general philosophy is not to require any particular format.
|
||||
Rather, for maximum flexibility, Spring Integration aims to provide the simplest possible model for extension.
|
||||
As with the other endpoint types, the use of declarative configuration in XML and/or Annotations enables simple POJOs to be adapted for the role of Message Transformers.
|
||||
These configuration options will be described below.
|
||||
As with the other endpoint types, the use of declarative configuration in XML or Java annotations enables simple POJOs to be adapted for the role of message transformers.
|
||||
The rest of this chapter describes these configuration options.
|
||||
|
||||
NOTE: For the same reason of maximizing flexibility, Spring does not require XML-based Message payloads.
|
||||
Nevertheless, the framework does provide some convenient Transformers for dealing with XML-based payloads if that is indeed the right choice for your application.
|
||||
For more information on those transformers, see <<xml>>.
|
||||
|
||||
[[transformer-config]]
|
||||
==== Configuring Transformer
|
||||
NOTE: For the sake of maximizing flexibility, Spring does not require XML-based message payloads.
|
||||
Nevertheless, the framework does provide some convenient transformers for dealing with XML-based payloads if that is indeed the right choice for your application.
|
||||
For more information on those transformers, see "`<<xml>>`".
|
||||
|
||||
[[transformer-namespace]]
|
||||
===== Configuring Transformer with XML
|
||||
==== Configuring a Transformer with XML
|
||||
|
||||
The <transformer> element is used to create a Message-transforming endpoint.
|
||||
In addition to "input-channel" and "output-channel" attributes, it requires a "ref".
|
||||
The "ref" may either point to an Object that contains the @Transformer annotation on a single method (see below) or it may be combined with an explicit method name value provided via the "method" attribute.
|
||||
The `<transformer>` element is used to create a message-transforming endpoint.
|
||||
In addition to `input-channel` and `output-channel` attributes, it requires a ` attribute`.
|
||||
The `ref` may either point to an object that contains the `@Transformer` annotation on a single method (see "`<<transformer-annotation>>`"), or it may be combined with an explicit method name value provided in the `method` attribute.
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:transformer id="testTransformer" ref="testTransformerBean" input-channel="inChannel"
|
||||
method="transform" output-channel="outChannel"/>
|
||||
<beans:bean id="testTransformerBean" class="org.foo.TestTransformer" />
|
||||
----
|
||||
====
|
||||
|
||||
Using a `ref` attribute is generally recommended if the custom transformer handler implementation can be reused in other `<transformer>` definitions.
|
||||
However if the custom transformer handler implementation should be scoped to a single definition of the `<transformer>`, you can define an inner bean definition:
|
||||
However, if the custom transformer handler implementation should be scoped to a single definition of the `<transformer>`, you can define an inner bean definition, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:transformer id="testTransformer" input-channel="inChannel" method="transform"
|
||||
@@ -42,76 +41,90 @@ However if the custom transformer handler implementation should be scoped to a s
|
||||
<beans:bean class="org.foo.TestTransformer"/>
|
||||
</transformer>
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: Using both the "ref" attribute and an inner handler definition in the same `<transformer>` configuration is not allowed, as it creates an ambiguous condition and will result in an Exception being thrown.
|
||||
NOTE: Using both the `ref` attribute and an inner handler definition in the same `<transformer>` configuration is not allowed, as it creates an ambiguous condition and results in an exception being thrown.
|
||||
|
||||
IMPORTANT: If the "ref" attribute references a bean that extends `AbstractMessageProducingHandler` (such as transformers provided by the framework itself), the configuration is optimized by injecting the output channel into the handler directly.
|
||||
In this case, each "ref" must be to a separate bean instance (or a `prototype`-scoped bean), or use the inner `<bean/>` configuration type.
|
||||
If you inadvertently reference the same message handler from multiple beans, you will get a configuration exception.
|
||||
IMPORTANT: If the `ref` attribute references a bean that extends `AbstractMessageProducingHandler` (such as transformers provided by the framework itself), the configuration is optimized by injecting the output channel into the handler directly.
|
||||
In this case, each `ref` must be to a separate bean instance (or a `prototype`-scoped bean) or use the inner `<bean/>` configuration type.
|
||||
If you inadvertently reference the same message handler from multiple beans, you get a configuration exception.
|
||||
|
||||
When using a POJO, the method that is used for transformation may expect either the `Message` type or the payload type of inbound Messages.
|
||||
It may also accept Message header values either individually or as a full map by using the `@Header` and `@Headers` parameter annotations respectively.
|
||||
When using a POJO, the method that is used for transformation may expect either the `Message` type or the payload type of inbound messages.
|
||||
It may also accept message header values either individually or as a full map by using the `@Header` and `@Headers` parameter annotations, respectively.
|
||||
The return value of the method can be any type.
|
||||
If the return value is itself a `Message`, that will be passed along to the transformer's output channel.
|
||||
If the return value is itself a `Message`, that is passed along to the transformer's output channel.
|
||||
|
||||
As of Spring Integration 2.0, a Message Transformer's transformation method can no longer return `null`.
|
||||
Returning `null` will result in an exception since a Message Transformer should always be expected to transform each source Message into a valid target Message.
|
||||
In other words, a Message Transformer should not be used as a Message Filter since there is a dedicated `<filter>` option for that.
|
||||
However, if you do need this type of behavior (where a component might return NULL and that should not be considered an error), a _service-activator_ could be used.
|
||||
Its `requires-reply` value is FALSE by default, but that can be set to TRUE in order to have Exceptions thrown for NULL return values as with the transformer.
|
||||
As of Spring Integration 2.0, a message transformer's transformation method can no longer return `null`.
|
||||
Returning `null` results in an exception, because a message transformer should always be expected to transform each source message into a valid target message.
|
||||
In other words, a message transformer should not be used as a message filter, because there is a dedicated `<filter>` option for that.
|
||||
However, if you do need this type of behavior (where a component might return `null` and that should not be considered an error), you could use a service activator.
|
||||
Its `requires-reply` value is `false` by default, but that can be set to `true` in order to have exceptions thrown for `null` return values, as with the transformer.
|
||||
|
||||
_Transformers and Spring Expression Language (SpEL)_
|
||||
==== Transformers and Spring Expression Language (SpEL)
|
||||
|
||||
Just like Routers, Aggregators and other components, as of Spring Integration 2.0 Transformers can also benefit from SpEL support (http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html) whenever transformation logic is relatively simple.
|
||||
Like routers, aggregators, and other components, as of Spring Integration 2.0, transformers can also benefit from http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html[SpEL support] whenever transformation logic is relatively simple. The following example shows how to use a SpEL expression:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:transformer input-channel="inChannel"
|
||||
output-channel="outChannel"
|
||||
expression="payload.toUpperCase() + '- [' + T(java.lang.System).currentTimeMillis() + ']'"/>
|
||||
----
|
||||
====
|
||||
|
||||
In the above configuration we are achieving a simple transformation of the _payload_ with a simple SpEL expression and without writing a custom transformer.
|
||||
Our _payload_ (assuming String) will be upper-cased and concatenated with the current timestamp with some simple formatting.
|
||||
The preceding example transforms the payload without writing a custom transformer.
|
||||
Our payload (assumed to be a `String`) is upper-cased, concatenated with the current timestamp, and has some formatting applied.
|
||||
|
||||
===== Common Transformers
|
||||
==== Common Transformers
|
||||
|
||||
There are also a few Transformer implementations available out of the box.
|
||||
Spring Integration provides a few transformer implementations.
|
||||
|
||||
====== Object-to-String Transformer
|
||||
===== Object-to-String Transformer
|
||||
|
||||
Because, it is fairly common to use the `toString()` representation of an Object, Spring Integration provides an `ObjectToStringTransformer` whose output is a `Message` with a String `payload`.
|
||||
That String is the result of invoking the `toString()` operation on the inbound Message's payload.
|
||||
Because it is fairly common to use the `toString()` representation of an `Object`, Spring Integration provides an `ObjectToStringTransformer` whose output is a `Message` with a String `payload`.
|
||||
That `String` is the result of invoking the `toString()` operation on the inbound Message's payload.
|
||||
The following example shows how to declare an instance of the object-to-string transformer:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:object-to-string-transformer input-channel="in" output-channel="out"/>
|
||||
----
|
||||
====
|
||||
|
||||
A potential example for this would be sending some arbitrary object to the 'outbound-channel-adapter' in the _file_ namespace.
|
||||
Whereas that Channel Adapter only supports String, byte-array, or `java.io.File` payloads by default, adding this transformer immediately before the adapter will handle the necessary conversion.
|
||||
Of course, that works fine as long as the result of the `toString()` call is what you want to be written to the File.
|
||||
Otherwise, you can just provide a custom POJO-based Transformer via the generic 'transformer' element shown previously.
|
||||
A potential use for this transformer would be sending some arbitrary object to the 'outbound-channel-adapter' in the `file` namespace.
|
||||
Whereas that channel adapter only supports `String`, byte-array, or `java.io.File` payloads by default, adding this transformer immediately before the adapter handles the necessary conversion.
|
||||
That works fine as long as the result of the `toString()` call is what you want to be written to the file.
|
||||
Otherwise, you can provide a custom POJO-based transformer by using the generic 'transformer' element shown previously.
|
||||
|
||||
TIP: When debugging, this transformer is not typically necessary since the 'logging-channel-adapter' is capable of logging the Message payload.
|
||||
Refer to <<channel-wiretap>> for more detail.
|
||||
TIP: When debugging, this transformer is not typically necessary, since the 'logging-channel-adapter' is capable of logging the message payload.
|
||||
See "`<<channel-wiretap>>`" for more detail.
|
||||
|
||||
[NOTE]
|
||||
=====
|
||||
The _object-to-string-transformer_ is very simple; it invokes `toString()` on the inbound payload.
|
||||
There are two exceptions to this (since 3.0): if the payload is a `char[]`, it invokes `new String(payload)`; if the payload is a `byte[]`, it invokes `new String(payload, charset)`, where `charset` is "UTF-8" by default.
|
||||
The `charset` can be modified by supplying the _charset_ attribute on the transformer.
|
||||
====
|
||||
The object-to-string transformer is very simple.
|
||||
It invokes `toString()` on the inbound payload.
|
||||
Since Spring Integration 3.0, there are two exceptions to this rule:
|
||||
|
||||
For more sophistication (such as selection of the charset dynamically, at runtime), you can use a SpEL expression-based transformer instead; for example:
|
||||
* If the payload is a `char[]`, it invokes `new String(payload)`.
|
||||
* If the payload is a `byte[]`, it invokes `new String(payload, charset)`, where `charset` is UTF-8 by default.
|
||||
The `charset` can be modified by supplying the charset attribute on the transformer.
|
||||
|
||||
For more sophistication (such as selection of the charset dynamically, at runtime), you can use a SpEL expression-based transformer instead, as the following example shows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<int:transformer input-channel="in" output-channel="out"
|
||||
expression="new java.lang.String(payload, headers['myCharset']" />
|
||||
----
|
||||
=====
|
||||
====
|
||||
|
||||
If you need to serialize an Object to a byte array or deserialize a byte array back into an Object, Spring Integration provides symmetrical serialization transformers.
|
||||
These will use standard Java serialization by default, but you can provide an implementation of Spring 3.0's Serializer or Deserializer strategies via the 'serializer' and 'deserializer' attributes, respectively.
|
||||
If you need to serialize an `Object` to a byte array or deserialize a byte array back into an `Object`, Spring Integration provides symmetrical serialization transformers.
|
||||
These use standard Java serialization by default, but you can provide an implementation of Spring 3.0's serializer or seserializer strategies by using the 'serializer' and 'deserializer' attributes, respectively.
|
||||
The following example shows to use Spring's serializer and deserializer:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:payload-serializing-transformer input-channel="objectsIn" output-channel="bytesOut"/>
|
||||
@@ -119,18 +132,21 @@ These will use standard Java serialization by default, but you can provide an im
|
||||
<int:payload-deserializing-transformer input-channel="bytesIn" output-channel="objectsOut"
|
||||
white-list="com.mycom.*,com.yourcom.*"/>
|
||||
----
|
||||
====
|
||||
|
||||
IMPORTANT: When deserializing data from untrusted sources, you should consider adding a `white-list` of package/class patterns.
|
||||
By default, all classes will be deserialized.
|
||||
IMPORTANT: When deserializing data from untrusted sources, you should consider adding a `white-list` of package and class patterns.
|
||||
By default, all classes are deserialized.
|
||||
|
||||
====== Object-to-Map and Map-to-Object Transformers
|
||||
===== `Object`-to-`Map` and `Map`-to-`Object` Transformers
|
||||
|
||||
Spring Integration also provides _Object-to-Map_ and _Map-to-Object_ transformers which utilize the JSON to serialize and de-serialize the object graphs.
|
||||
The object hierarchy is introspected to the most primitive types (String, int, etc.).
|
||||
The path to this type is described via SpEL, which becomes the _key_ in the transformed Map.
|
||||
Spring Integration also provides `Object`-to-`Map` and `Map`-to-`Object` transformers, which use the JSON to serialize and de-serialize the object graphs.
|
||||
The object hierarchy is introspected to the most primitive types (`String`, `int`, and so on).
|
||||
The path to this type is described with SpEL, which becomes the `key` in the transformed `Map`.
|
||||
The primitive type becomes the value.
|
||||
|
||||
For example:
|
||||
Consider the following example:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class Parent{
|
||||
@@ -145,12 +161,22 @@ public class Child{
|
||||
// setters and getters are omitted
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
\...will be transformed to a Map which looks like this: `{person.name=George, person.child.name=Jenna, person.child.nickNames[0]=Bimbo ... etc}`
|
||||
The two classes in the preceding example are transformed to the following `Map`:
|
||||
|
||||
The JSON-based Map allows you to describe the object structure without sharing the actual types allowing you to restore/rebuild the object graph into a differently typed Object graph as long as you maintain the structure.
|
||||
====
|
||||
[source]
|
||||
----
|
||||
{person.name=George, person.child.name=Jenna, person.child.nickNames[0]=Jen ...}
|
||||
----
|
||||
====
|
||||
|
||||
For example: The above structure could be easily restored back to the following Object graph via the Map-to-Object transformer:
|
||||
The JSON-based `Map` lets you describe the object structure without sharing the actual types, which lets you restore and rebuild the object graph into a differently typed object graph, as long as you maintain the structure.
|
||||
|
||||
For example, the preceding structure could be restored back to the following object graph by using the `Map`-to-`Object` transformer:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class Father {
|
||||
@@ -165,11 +191,15 @@ public class Kid {
|
||||
// setters and getters are omitted
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
If you need to create a "structured" map, you can provide the 'flatten' attribute.
|
||||
The default value for this attribute is 'true' meaning the default behavior; if you provide a 'false' value, then the structure will be a map of maps.
|
||||
If you need to create a "`structured`" map, you can provide the 'flatten' attribute.
|
||||
The default is 'true'.
|
||||
If you set it to 'false', the structure is a `Map` of `Map` objects.
|
||||
|
||||
For example:
|
||||
Consider the following example:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class Parent {
|
||||
@@ -184,51 +214,71 @@ public class Child {
|
||||
// setters and getters are omitted
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
\...will be transformed to a Map which looks like this: `{name=George, child={name=Jenna, nickNames=[Bimbo, ...]}}`
|
||||
The two classes in the preceding example are transformed to the following `Map`:
|
||||
|
||||
To configure these transformers, Spring Integration provides namespace support Object-to-Map:
|
||||
====
|
||||
[source]
|
||||
----
|
||||
{name=George, child={name=Jenna, nickNames=[Bimbo, ...]}}
|
||||
----
|
||||
====
|
||||
|
||||
To configure these transformers, Spring Integration provides namespace support for Object-to-Map, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:object-to-map-transformer input-channel="directInput" output-channel="output"/>
|
||||
----
|
||||
====
|
||||
|
||||
or
|
||||
You can also set the `flatten` attribute to false, as follows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:object-to-map-transformer input-channel="directInput" output-channel="output" flatten="false"/>
|
||||
----
|
||||
====
|
||||
|
||||
Map-to-Object
|
||||
Spring Integration provides namespace support for Map-to-Object, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:map-to-object-transformer input-channel="input"
|
||||
output-channel="output"
|
||||
type="org.foo.Person"/>
|
||||
type="org.something.Person"/>
|
||||
----
|
||||
====
|
||||
|
||||
or
|
||||
Alterately, you could use a `ref` attribute and a prototype-scoped bean, as the following example shows:
|
||||
[source,xml]
|
||||
----
|
||||
<int:map-to-object-transformer input-channel="inputA"
|
||||
output-channel="outputA"
|
||||
ref="person"/>
|
||||
<bean id="person" class="org.foo.Person" scope="prototype"/>
|
||||
<bean id="person" class="org.something.Person" scope="prototype"/>
|
||||
|
||||
----
|
||||
|
||||
NOTE: NOTE: 'ref' and 'type' attributes are mutually exclusive.
|
||||
You can only use one.
|
||||
Also, if using the 'ref' attribute, you must point to a 'prototype' scoped bean, otherwise a `BeanCreationException` will be thrown.
|
||||
NOTE: The 'ref' and 'type' attributes are mutually exclusive.
|
||||
Also, if you use the 'ref' attribute, you must point to a 'prototype' scoped bean.
|
||||
Otherwise, a `BeanCreationException` is thrown.
|
||||
|
||||
Starting with _version 5.0_, the `ObjectToMapTransformer` can be supplied with the customized `JsonObjectMapper`, for example in use-cases when we need special formats for dates or nulls for empty collections.
|
||||
See <<json-transformers>> for more information about `JsonObjectMapper` implementations.
|
||||
Starting with version 5.0, you can supply the `ObjectToMapTransformer` with a customized `JsonObjectMapper` -- for when you need special formats for dates or nulls for empty collections (and other uses).
|
||||
See "`<<json-transformers>>`" for more information about `JsonObjectMapper` implementations.
|
||||
|
||||
[[stream-transformer]]
|
||||
====== Stream Transformer
|
||||
===== Stream Transformer
|
||||
|
||||
The `StreamTransformer` transforms `InputStream` payloads to a `byte[]` or a `String` if a `charset` is provided.
|
||||
The `StreamTransformer` transforms `InputStream` payloads to a `byte[]`( or a `String` if a `charset` is provided).
|
||||
|
||||
The following example shows how to use the `stream-tansformer` element in XML:
|
||||
|
||||
====
|
||||
[source, xml]
|
||||
----
|
||||
<int:stream-transformer input-channel="directInput" output-channel="output"/> <!-- byte[] -->
|
||||
@@ -236,7 +286,11 @@ The `StreamTransformer` transforms `InputStream` payloads to a `byte[]` or a `St
|
||||
<int:stream-transformer id="withCharset" charset="UTF-8"
|
||||
input-channel="charsetChannel" output-channel="output"/> <!-- String -->
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows how to use the `StreamTransformer` class and the `@Transformer` annotation to configure a stream transformer in Java:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
@@ -251,68 +305,75 @@ public StreamTransformer streamToString() {
|
||||
return new StreamTransformer("UTF-8"); // transforms to String
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[[json-transformers]]
|
||||
====== JSON Transformers
|
||||
===== JSON Transformers
|
||||
|
||||
_Object to JSON_ and _JSON to Object_ transformers are provided.
|
||||
Spring Integration provides Object-to-JSON and JSON-to-Object transformers.
|
||||
The following pair of examples show how to declare them in XML:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:object-to-json-transformer input-channel="objectMapperInput"/>
|
||||
----
|
||||
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<int:json-to-object-transformer input-channel="objectMapperInput"
|
||||
type="foo.MyDomainObject"/>
|
||||
----
|
||||
====
|
||||
|
||||
These use a vanilla `JsonObjectMapper` by default based on implementation from classpath.
|
||||
You can provide your own custom `JsonObjectMapper` implementation with appropriate options or based on required library (e.g.
|
||||
GSON).
|
||||
By default, the transformers in the preceding listing use a vanilla `JsonObjectMapper`.
|
||||
It is based on an implementation from the classpath.
|
||||
You can provide your own custom `JsonObjectMapper` implementation with appropriate options or based on a required library (such as GSON), as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:json-to-object-transformer input-channel="objectMapperInput"
|
||||
type="foo.MyDomainObject" object-mapper="customObjectMapper"/>
|
||||
type="something.MyDomainObject" object-mapper="customObjectMapper"/>
|
||||
----
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
=====
|
||||
Beginning with version 3.0, the `object-mapper` attribute references an instance of a new strategy interface `JsonObjectMapper`.
|
||||
This abstraction allows multiple implementations of json mappers to be used.
|
||||
Implementations that wraphttps://github.com/RichardHightower/boon[Boon] and https://github.com/FasterXML[Jackson 2] are provided, with the version being detected on the classpath.
|
||||
These classes are `BoonJsonObjectMapper` and `Jackson2JsonObjectMapper`.
|
||||
====
|
||||
Beginning with version 3.0, the `object-mapper` attribute references an instance of a new strategy interface: `JsonObjectMapper`.
|
||||
This abstraction lets multiple implementations of JSON mappers be used.
|
||||
Implementations that wrap https://github.com/RichardHightower/boon[Boon] and https://github.com/FasterXML[Jackson 2] are provided, with the version being detected on the classpath.
|
||||
These classes are `BoonJsonObjectMapper` and `Jackson2JsonObjectMapper`, respectively.
|
||||
|
||||
Note, `BoonJsonObjectMapper` is provided since _version 4.1_.
|
||||
=====
|
||||
Note, `BoonJsonObjectMapper` was added in version 4.1.
|
||||
====
|
||||
|
||||
[IMPORTANT]
|
||||
=====
|
||||
If there are requirements to use both Jackson libraries and/or Boon in the same application, keep in mind that before version 3.0, the JSON transformers used only Jackson 1.x.
|
||||
From _4.1_ on, the framework will select Jackson 2 by default ahead of the Boon implementation if both are on the classpath.
|
||||
Jackson 1.x is no longer supported by the framework internally but, of course, you can still use it within your code.
|
||||
To avoid unexpected issues with JSON mapping features, when using annotations, there may be a need to apply annotations from both Jacksons and/or Boon on domain classes:
|
||||
====
|
||||
If you have requirements to use both Jackson and Boon in the same application, keep in mind that, before version 3.0, the JSON transformers used only Jackson 1.x.
|
||||
From 4.1 on, the framework selects Jackson 2 by default, preferring it to the Boon implementation if both are on the classpath.
|
||||
Jackson 1.x is no longer supported by the framework internally. However, you can still use it within your code by including the necessary library.
|
||||
To avoid unexpected issues with JSON mapping features when you use annotations, you may need to apply annotations from both Jackson and Boon on domain classes, as the following example shows:
|
||||
[source,java]
|
||||
----
|
||||
@org.codehaus.jackson.annotate.JsonIgnoreProperties(ignoreUnknown=true)
|
||||
@com.fasterxml.jackson.annotation.JsonIgnoreProperties(ignoreUnknown=true)
|
||||
@org.boon.json.annotations.JsonIgnoreProperties("foo")
|
||||
public class Foo {
|
||||
@org.boon.json.annotations.JsonIgnoreProperties("thing1")
|
||||
public class Thing1 {
|
||||
|
||||
@org.codehaus.jackson.annotate.JsonProperty("fooBar")
|
||||
@com.fasterxml.jackson.annotation.JsonProperty("fooBar")
|
||||
@org.boon.json.annotations.JsonProperty("fooBar")
|
||||
public Object bar;
|
||||
@org.codehaus.jackson.annotate.JsonProperty("thing1Thing2")
|
||||
@com.fasterxml.jackson.annotation.JsonProperty("thing1Thing2")
|
||||
@org.boon.json.annotations.JsonProperty("thing1Thing2")
|
||||
public Object thing2;
|
||||
|
||||
}
|
||||
----
|
||||
=====
|
||||
====
|
||||
|
||||
You may wish to consider using a `FactoryBean` or simple factory method to create the `JsonObjectMapper` with the required characteristics.
|
||||
You may wish to consider using a `FactoryBean` or a factory method to create the `JsonObjectMapper` with the required characteristics.
|
||||
The following example shows how to use such a factory:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class ObjectMapperFactory {
|
||||
@@ -324,72 +385,74 @@ public class ObjectMapperFactory {
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows how to do the same thing in XML
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="customObjectMapper" class="foo.ObjectMapperFactory"
|
||||
<bean id="customObjectMapper" class="something.ObjectMapperFactory"
|
||||
factory-method="getMapper"/>
|
||||
----
|
||||
====
|
||||
|
||||
[IMPORTANT]
|
||||
=====
|
||||
Beginning with _version 2.2_, the `object-to-json-transformer` sets the _content-type_ header to `application/json`, by default, if the input message does not already have that header present.
|
||||
====
|
||||
Beginning with version 2.2, the `object-to-json-transformer` sets the `content-type` header to `application/json`, by default, if the input message does not already have that header.
|
||||
|
||||
It you wish to set the _content type_ header to some other value, or explicitly overwrite any existing header with some value (including `application/json`), use the `content-type` attribute.
|
||||
It you wish to set the `content-type` header to some other value or explicitly overwrite any existing header with some value (including `application/json`), use the `content-type` attribute.
|
||||
If you wish to suppress the setting of the header, set the `content-type` attribute to an empty string (`""`).
|
||||
This will result in a message with no `content-type` header, unless such a header was present on the input message.
|
||||
=====
|
||||
Doing so results in a message with no `content-type` header, unless such a header was present on the input message.
|
||||
====
|
||||
|
||||
Beginning with _version 3.0_, the `ObjectToJsonTransformer` adds headers, reflecting the source type, to the message.
|
||||
Beginning with version 3.0, the `ObjectToJsonTransformer` adds headers, reflecting the source type, to the message.
|
||||
Similarly, the `JsonToObjectTransformer` can use those type headers when converting the JSON to an object.
|
||||
These headers are mapped in the AMQP adapters so that they are entirely compatible with the Spring-AMQP http://docs.spring.io/spring-amqp/api/[JsonMessageConverter].
|
||||
These headers are mapped in the AMQP adapters so that they are entirely compatible with the Spring-AMQP http://docs.spring.io/spring-amqp/api/[`JsonMessageConverter`].
|
||||
|
||||
This enables the following flows to work without any special configuration...
|
||||
|
||||
`...->amqp-outbound-adapter---->`
|
||||
|
||||
`---->amqp-inbound-adapter->json-to-object-transformer->...`
|
||||
This enables the following flows to work without any special configuration:
|
||||
|
||||
* `...->amqp-outbound-adapter---->`
|
||||
* `---->amqp-inbound-adapter->json-to-object-transformer->...`
|
||||
+
|
||||
Where the outbound adapter is configured with a `JsonMessageConverter` and the inbound adapter uses the default `SimpleMessageConverter`.
|
||||
|
||||
`...->object-to-json-transformer->amqp-outbound-adapter---->`
|
||||
|
||||
`---->amqp-inbound-adapter->...`
|
||||
|
||||
* `...->object-to-json-transformer->amqp-outbound-adapter---->`
|
||||
* `---->amqp-inbound-adapter->...`
|
||||
+
|
||||
Where the outbound adapter is configured with a `SimpleMessageConverter` and the inbound adapter uses the default `JsonMessageConverter`.
|
||||
|
||||
`...->object-to-json-transformer->amqp-outbound-adapter---->`
|
||||
|
||||
`---->amqp-inbound-adapter->json-to-object-transformer->`
|
||||
|
||||
* `...->object-to-json-transformer->amqp-outbound-adapter---->`
|
||||
* `---->amqp-inbound-adapter->json-to-object-transformer->`
|
||||
+
|
||||
Where both adapters are configured with a `SimpleMessageConverter`.
|
||||
|
||||
NOTE: When using the headers to determine the type, you should *not* provide a `class` attribute, because it takes precedence over the headers.
|
||||
NOTE: When using the headers to determine the type, you should not provide a `class` attribute, because it takes precedence over the headers.
|
||||
|
||||
In addition to JSON Transformers, Spring Integration provides a built-in _#jsonPath_ SpEL function for use in expressions.
|
||||
For more information see <<spel>>.
|
||||
In addition to JSON Transformers, Spring Integration provides a built-in `#jsonPath` SpEL function for use in expressions.
|
||||
For more information see "`<<spel>>`".
|
||||
|
||||
[[transformer-xpath-spel-function]]
|
||||
*#xpath SpEL Function*
|
||||
Since version 3.0, Spring Integration also provides a built-in `#xpath` SpEL function for use in expressions.
|
||||
For more information see "`<<xpath-spel-function>>`".
|
||||
|
||||
Since version _3.0_, Spring Integration also provides a built-in _#xpath_ SpEL function for use in expressions.
|
||||
For more information see <<xpath-spel-function>>.
|
||||
|
||||
Beginning with _version 4.0_, the `ObjectToJsonTransformer` supports the `resultType` property, to specify the _node_ JSON representation.
|
||||
Beginning with version 4.0, the `ObjectToJsonTransformer` supports the `resultType` property, to specify the node JSON representation.
|
||||
The result node tree representation depends on the implementation of the provided `JsonObjectMapper`.
|
||||
By default, the `ObjectToJsonTransformer` uses a `Jackson2JsonObjectMapper` and delegates the conversion of the object to the node tree to the `ObjectMapper#valueToTree` method.
|
||||
The node JSON representation provides efficiency for using the `JsonPropertyAccessor`, when the downstream message flow uses SpEL expressions with access to the properties of the JSON data.
|
||||
See <<spel-property-accessors>>.
|
||||
The node JSON representation provides efficiency for using the `JsonPropertyAccessor` when the downstream message flow uses SpEL expressions with access to the properties of the JSON data.
|
||||
See "`<<spel-property-accessors>>`" for more information.
|
||||
When using Boon, the `NODE` representation is a `Map<String, Object>`
|
||||
|
||||
Beginning with _version 5.1_, the `resultType` can be configured as `BYTES` to produce a message with the `byte[]` payload for convenience in downstream handlers which operate with this data type.
|
||||
Beginning with version 5.1, the `resultType` can be configured as `BYTES` to produce a message with the `byte[]` payload for convenience when working with downstream handlers which operate with this data type.
|
||||
|
||||
[[transformer-annotation]]
|
||||
===== Configuring a Transformer with Annotations
|
||||
==== Configuring a Transformer with Annotations
|
||||
|
||||
The `@Transformer` annotation can also be added to methods that expect either the `Message` type or the message payload type.
|
||||
The return value will be handled in the exact same way as described above in the section describing the <transformer> element.
|
||||
You can add the `@Transformer` annotation to methods that expect either the `Message` type or the message payload type.
|
||||
The return value is handled in the exact same way as described earlier <<transformer-namespace,in the section describing the `<transformer>` element>>.
|
||||
The following example shows how to use the `@Transformer` annotation to transform a `String` into an `Order`:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Transformer
|
||||
@@ -397,8 +460,12 @@ Order generateOrder(String productId) {
|
||||
return new Order(productId);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Transformer methods may also accept the @Header and @Headers annotations that is documented in <<annotations>>
|
||||
Transformer methods can also accept the `@Header` and `@Headers` annotations, as documented in `<<annotations>>`.
|
||||
The following examples shows how to use the `@Header` annotation:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Transformer
|
||||
@@ -406,31 +473,33 @@ Order generateOrder(String productId, @Header("customerName") String customer) {
|
||||
return new Order(productId, customer);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Also see <<advising-with-annotations>>.
|
||||
See also "`<<advising-with-annotations>>`".
|
||||
|
||||
[[header-filter]]
|
||||
==== Header Filter
|
||||
|
||||
Some times your transformation use case might be as simple as removing a few headers.
|
||||
For such a use case, Spring Integration provides a _Header Filter_ which allows you to specify certain header names
|
||||
that should be removed from the output Message (e.g. for security reasons or a value that was only needed temporarily).
|
||||
Basically, the _Header Filter_ is the opposite of the _Header Enricher_.
|
||||
The latter is discussed in <<header-enricher>>.
|
||||
Sometimes, your transformation use case might be as simple as removing a few headers.
|
||||
For such a use case, Spring Integration provides a header filter that lets you specify certain header names that should be removed from the output message (for example, removing headers for security reasons or a value that was needed only temporarily).
|
||||
Basically, the header filter is the opposite of the header enricher.
|
||||
The latter is discussed in "`<<header-enricher>>`".
|
||||
The following example defines a header filter:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:header-filter input-channel="inputChannel"
|
||||
output-channel="outputChannel" header-names="lastName, state"/>
|
||||
----
|
||||
====
|
||||
|
||||
As you can see, configuration of a _Header Filter_ is quite simple.
|
||||
It is a typical endpoint with input/output channels and a `header-names` attribute.
|
||||
That attribute accepts the names of the header(s) (delimited by commas if there are multiple)
|
||||
that need to be removed.
|
||||
So, in the above example the headers named 'lastName' and 'state' will not be present on the outbound Message.
|
||||
As you can see, configuration of a header filter is quite simple.
|
||||
It is a typical endpoint with input and output channels and a `header-names` attribute.
|
||||
That attribute accepts the names of the headers (delimited by commas if there are multiple) that need to be removed.
|
||||
So, in the preceding example, the headers named 'lastName' and 'state' are not present on the outbound message.
|
||||
|
||||
|
||||
==== Codec-Based Transformers
|
||||
|
||||
See <<codec>>.
|
||||
See "`<<codec>>`".
|
||||
|
||||
@@ -2,54 +2,60 @@
|
||||
== Twitter Support
|
||||
|
||||
Spring Integration provides support for interacting with Twitter.
|
||||
With the Twitter adapters you can both receive and send Twitter messages.
|
||||
You can also perform a Twitter search based on a schedule and publish the search results within Messages.
|
||||
Since _version 4.0_, a search outbound gateway is provided to perform dynamic searches.
|
||||
|
||||
[[twitter-intro]]
|
||||
=== Introduction
|
||||
With the Twitter adapters, you can both receive and send Twitter messages.
|
||||
You can also perform a Twitter search based on a schedule and publish the search results within messages.
|
||||
Since version 4.0, a search outbound gateway is provided to perform dynamic searches.
|
||||
|
||||
Twitter is a social networking and micro-blogging service that enables its users to send and read messages known as tweets.
|
||||
Tweets are text-based posts of up to 140 characters displayed on the author's profile page and delivered to the author's subscribers who are known as followers.
|
||||
Tweets are text-based posts of up to 280 characters (up from 140 in 2018) displayed on the author's profile page and delivered to the author's subscribers, who are known as followers.
|
||||
|
||||
IMPORTANT: Versions of Spring Integration prior to 2.1 were dependent upon the http://twitter4j.org[Twitter4J API], but with the release of http://projects.spring.io/spring-social[Spring Social 1.0 GA], Spring Integration, as of version 2.1, now builds directly upon Spring Social's Twitter support, instead of Twitter4J.
|
||||
All Twitter endpoints require the configuration of a `TwitterTemplate` because even search operations require an authenticated template.
|
||||
IMPORTANT: Versions of Spring Integration prior to 2.1 were dependent upon the http://twitter4j.org[Twitter4J API].
|
||||
However, with the release of http://projects.spring.io/spring-social[Spring Social 1.0 GA], Spring Integration (as of version 2.1) now builds directly upon Spring Social's Twitter support, instead of Twitter4J.
|
||||
All Twitter endpoints require the configuration of a `TwitterTemplate`, because even search operations require an authenticated template.
|
||||
|
||||
Spring Integration provides a convenient namespace configuration to define Twitter artifacts.
|
||||
You can enable it by adding the following within your XML header.
|
||||
You can enable it by adding the following within your XML header:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
xmlns:int-twitter="http://www.springframework.org/schema/integration/twitter"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration/twitter
|
||||
http://www.springframework.org/schema/integration/twitter/spring-integration-twitter.xsd"
|
||||
----
|
||||
====
|
||||
|
||||
[[twitter-oauth]]
|
||||
=== Twitter OAuth Configuration
|
||||
|
||||
For authenticated operations, Twitter uses OAuth - an authentication protocol that allows users to approve an application to act on their behalf without sharing their password.
|
||||
More information can be found at http://oauth.net[http://oauth.net] or in this article http://hueniverse.com/oauth[http://hueniverse.com/oauth] from Hueniverse.
|
||||
Please also see http://dev.twitter.com/pages/oauth_faq[OAuth FAQ] for more information about OAuth and Twitter.
|
||||
For authenticated operations, Twitter uses OAuth, an authentication protocol that lets users approve an application to act on their behalf without sharing their password.
|
||||
More information can be found at http://oauth.net[http://oauth.net] or in http://hueniverse.com/oauth[this article] from Hueniverse.
|
||||
See the http://dev.twitter.com/pages/oauth_faq[OAuth FAQ] for more information about OAuth and Twitter.
|
||||
|
||||
In order to use OAuth authentication/authorization with Twitter you must create a new Application on the Twitter Developers site.
|
||||
Follow the directions below to create a new application and obtain consumer keys and an access token:
|
||||
In order to use OAuth authentication and authorization with Twitter, you must create a new application on the Twitter Developers site.
|
||||
The following directions describe how to create a new application and obtain consumer keys and an access token:
|
||||
|
||||
* Go to http://dev.twitter.com[http://dev.twitter.com]
|
||||
. Go to http://dev.twitter.com[http://dev.twitter.com].
|
||||
|
||||
* Click on the `Register an app` link and fill out all required fields on the form provided; set `Application Type` to `Client` and depending on the nature of your application select `Default Access Type` as _Read & Write_ or _Read-only_ and Submit the form.
|
||||
If everything is successful you'll be presented with the `Consumer Key` and `Consumer Secret`.
|
||||
. Click on the `Register an app` link and fill out all required fields on the form provided.
|
||||
Set `Application Type` to `Client` and, depending on the nature of your application, set `Default Access Type` to `Read & Write` or `Read-only`.
|
||||
Submit the form.
|
||||
If everything is successful, you see the `Consumer Key` and `Consumer Secret`.
|
||||
Copy both values in a safe place.
|
||||
|
||||
* On the same page you should see a `My Access Token` button on the side bar (right).
|
||||
Click on it and you'll be presented with two more values: `Access Token` and `Access Token Secret`.
|
||||
. On the same page, you should see a `My Access Token` button on the side bar (right).
|
||||
Click on it and you should see two more values: `Access Token` and `Access Token Secret`.
|
||||
Copy these values in a safe place as well.
|
||||
|
||||
=== Twitter Template
|
||||
|
||||
As mentioned above, Spring Integration relies upon Spring Social, and that library provides an implementation of the template pattern, `o.s.social.twitter.api.impl.TwitterTemplate` to interact with Twitter.
|
||||
For anonymous operations (e.g., search), you don't have to define an instance of `TwitterTemplate` explicitly, since a default instance will be created and injected into the endpoint.
|
||||
However, for authenticated operations (update status, send direct message, etc.), you must configure a `TwitterTemplate` as a bean and inject it explicitly into the endpoint, because the authentication configuration is required.
|
||||
Below is a sample configuration of TwitterTemplate:
|
||||
As <<twitter,mentioned earlier>>, Spring Integration relies upon Spring Social.
|
||||
That library provides an implementation of the template pattern( `o.s.social.twitter.api.impl.TwitterTemplate`) to let you interact with Twitter.
|
||||
For anonymous operations (such as search), you need not explicitly define an instance of `TwitterTemplate`, since a default instance is created and injected into the endpoint.
|
||||
However, for authenticated operations (update status, send direct message, asd others), you must configure a `TwitterTemplate` as a bean and inject it explicitly into the endpoint, because the authentication configuration is required.
|
||||
The following example configures a TwitterTemplate:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="twitterTemplate" class="o.s.social.twitter.api.impl.TwitterTemplate">
|
||||
@@ -59,15 +65,21 @@ Below is a sample configuration of TwitterTemplate:
|
||||
<constructor-arg value="AbRxUAvyNCtqQtxFK8w5ZMtMj20KFhB6o"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: The values above are not real.
|
||||
|
||||
As you can see from the configuration above, all we need to do is to provide OAuth `attributes` as constructor arguments.
|
||||
The values would be those you obtained in the previous step.
|
||||
The order of constructor arguments is: 1) `consumerKey`, 2) `consumerSecret`, 3) `accessToken`, and 4) `accessTokenSecret`.
|
||||
As the preceding configuration shows, all you need to do is to provide OAuth `attributes` as constructor arguments.
|
||||
The values should be those you obtained in the previous step.
|
||||
The order of constructor arguments is:
|
||||
. `consumerKey`
|
||||
. `consumerSecret`
|
||||
. `accessToken`
|
||||
. `accessTokenSecret`.
|
||||
|
||||
A more practical way to manage OAuth connection attributes would be via Spring's property placeholder support by simply creating a property file (e.g., oauth.properties):
|
||||
A more practical way to manage OAuth connection attributes is to use Spring's property placeholder support by creating a property file (for example, oauth.properties), as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
twitter.oauth.consumerKey=4XzBPacJQxyBzzzH
|
||||
@@ -75,9 +87,11 @@ twitter.oauth.consumerSecret=AbRxUAvyCtqQtvxFK8w5ZMtMj20KFhB6o
|
||||
twitter.oauth.accessToken=21691649-4YZY5iJEOfz2A9qCFd9SjBRGb3HLmIm4HNE
|
||||
twitter.oauth.accessTokenSecret=AbRxUAvyNCtqQtxFK8w5ZMtMj20KFhB6o
|
||||
----
|
||||
====
|
||||
|
||||
Then, you can configure a `property-placeholder` to point to the above property file:
|
||||
Then you can configure a `property-placeholder` to point to the above property file, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<context:property-placeholder location="classpath:oauth.properties"/>
|
||||
@@ -89,48 +103,49 @@ Then, you can configure a `property-placeholder` to point to the above property
|
||||
<constructor-arg value="${twitter.oauth.accessTokenSecret}"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
[[twitter-inbound]]
|
||||
=== Twitter Inbound Adapters
|
||||
|
||||
Twitter inbound adapters allow you to receive Twitter Messages.
|
||||
There are several types of http://support.twitter.com/articles/119138-types-of-tweets-and-where-they-appear[twitter messages, or tweets]
|
||||
Twitter inbound adapters let you receive Twitter Messages.
|
||||
There are several types of http://support.twitter.com/articles/119138-types-of-tweets-and-where-they-appear[twitter messages, or tweets].
|
||||
|
||||
_Spring Integration version 2.0 and above_ provides support for receiving tweets as _Timeline Updates_, _Direct Messages_, _Mention Messages_ as well as Search Results.
|
||||
As of version 2.0, Spring Integration provides support for receiving tweets as timeline updates, direct messages, and mention messages (as well as search results).
|
||||
|
||||
[IMPORTANT]
|
||||
=====
|
||||
Every Inbound Twitter Channel Adapter is a _Polling Consumer_ which means you have to provide a poller configuration.
|
||||
Twitter defines a concept of Rate Limiting.
|
||||
You can read more about it here: https://dev.twitter.com/docs/rate-limiting/1.1[Rate Limiting].
|
||||
In a nutshell, Rate Limiting is a mechanism that Twitter uses to manage how often an application can poll for updates.
|
||||
You should consider this when setting your poller intervals so that the adapter polls in compliance with the Twitter policies.
|
||||
Every inbound Twitter channel adapter is a polling consumer, which means you have to provide a poller configuration.
|
||||
Twitter uses a concept called https://dev.twitter.com/docs/rate-limiting/1.1[Rate Limiting].
|
||||
In a nutshell, Twitter uses rate limiting to manage how often an application can poll for updates.
|
||||
You should consider this when setting your poller intervals so that the adapter polls in compliance with Twitter policies.
|
||||
|
||||
With Spring Integration prior to _version 3.0_, a hard-coded limit within the adapters was used to ensure the polling interval could not be less than 15 seconds.
|
||||
This is no longer the case and the poller configuration is applied directly.
|
||||
With Spring Integration prior to version 3.0, a hard-coded limit within the adapters was used to ensure the polling interval could not be less than 15 seconds.
|
||||
This is no longer the case, and the poller configuration is applied directly.
|
||||
=====
|
||||
|
||||
Another issue that we need to worry about is handling duplicate Tweets.
|
||||
The same adapter (e.g., Search or Timeline Update) while polling on Twitter may receive the same values more than once.
|
||||
For example if you keep searching on Twitter with the same search criteria you'll end up with the same set of tweets unless some other new tweet that matches your search criteria was posted in between your searches.
|
||||
In that situation you'll get all the tweets you had before plus the new one.
|
||||
But what you really want is only the new tweet(s).
|
||||
Another issue that you need to consider is handling duplicate Tweets.
|
||||
The same adapter (for example, search or timeline update), while polling on Twitter, may receive the same values more than once.
|
||||
For example, if you keep searching on Twitter with the same search criteria, you end up with the same set of tweets unless some new tweet that matches your search criteria was posted in between your searches.
|
||||
In that situation, you get all the tweets you had before plus the new one.
|
||||
However, you really want only the new tweet.
|
||||
Spring Integration provides an elegant mechanism for handling these situations.
|
||||
The latest Tweet id will be stored in an instance of the `org.springframework.integration.metadata.MetadataStore` strategy (e.g.
|
||||
last retrieved tweet in this case).
|
||||
For more information see <<metadata-store>>.
|
||||
The latest Tweet ID (the last retrieved tweet in this case) is stored in an instance of the `org.springframework.integration.metadata.MetadataStore` strategy .
|
||||
For more information, see "`<<metadata-store>>`".
|
||||
|
||||
NOTE: The key used to persist the latest _twitter id_ is the value of the (required) `id` attribute of the Twitter Inbound Channel Adapter component plus the `profileId` of the Twitter user.
|
||||
NOTE: The key used to persist the latest Twitter ID is the value of the (required) `id` attribute of the Twitter inbound channel adapter component plus the `profileId` of the Twitter user.
|
||||
|
||||
Prior to _version 4.0_, the page size was hard-coded to 20.
|
||||
This is now configurable using the `page-size` attribute (defaults to 20).
|
||||
Prior to version 4.0, the page size was hard-coded to 20.
|
||||
You can now configure it by using the `page-size` attribute (which defaults to 20).
|
||||
|
||||
[[inbound-twitter-update]]
|
||||
==== Inbound Message Channel Adapter
|
||||
|
||||
This adapter allows you to receive updates from everyone you follow.
|
||||
It's essentially the "Timeline Update" adapter.
|
||||
This adapter lets you receive updates from everyone you follow.
|
||||
It is essentially the "`timeline update`" adapter.
|
||||
The following example configures a Twitter inbound channel adapter to poll for at most three messages every five seconds:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-twitter:inbound-channel-adapter
|
||||
@@ -139,12 +154,15 @@ It's essentially the "Timeline Update" adapter.
|
||||
<int:poller fixed-rate="5000" max-messages-per-poll="3"/>
|
||||
</int-twitter:inbound-channel-adapter>
|
||||
----
|
||||
====
|
||||
|
||||
[[inbound-twitter-direct]]
|
||||
==== Direct Inbound Message Channel Adapter
|
||||
|
||||
This adapter allows you to receive Direct Messages that were sent to you from other Twitter users.
|
||||
This adapter lets you receive direct messages that were sent to you from other Twitter users.
|
||||
The following example configures a Twitter direct message inbound channel adapter to poll for at most three direct messages every five seconds:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-twitter:dm-inbound-channel-adapter
|
||||
@@ -153,12 +171,15 @@ This adapter allows you to receive Direct Messages that were sent to you from ot
|
||||
<int-poller fixed-rate="5000" max-messages-per-poll="3"/>
|
||||
</int-twitter:dm-inbound-channel-adapter>
|
||||
----
|
||||
====
|
||||
|
||||
[[inbound-twitter-mention]]
|
||||
==== Mentions Inbound Message Channel Adapter
|
||||
|
||||
This adapter allows you to receive Twitter Messages that Mention you via @user syntax.
|
||||
This adapter lets you receive Twitter messages that mention you when someone uses the `@user` syntax.
|
||||
The following example configures a Twitter mention inbound channel adapter to poll for at most three mentions every five seconds:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-twitter:mentions-inbound-channel-adapter
|
||||
@@ -167,12 +188,17 @@ This adapter allows you to receive Twitter Messages that Mention you via @user s
|
||||
<int:poller fixed-rate="5000" max-messages-per-poll="3"/>
|
||||
</int-twitter:mentions-inbound-channel-adapter>
|
||||
----
|
||||
====
|
||||
|
||||
[[inbound-twitter-search]]
|
||||
==== Search Inbound Message Channel Adapter
|
||||
|
||||
This adapter allows you to perform searches.
|
||||
As you can see it is not necessary to define twitter-template since a search can be performed anonymously, however you must define a search query.
|
||||
This adapter lets you perform searches.
|
||||
You need not define a `twitter-template`, because you can search anonymously.
|
||||
However you must define a search query.
|
||||
The following example configures a Twitter search inbound channel adapter that searchs for the `#springintegration` hashtag and returns at most three results every five seconds:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-twitter:search-inbound-channel-adapter
|
||||
@@ -181,43 +207,56 @@ As you can see it is not necessary to define twitter-template since a search can
|
||||
<int:poller fixed-rate="5000" max-messages-per-poll="3"/>
|
||||
</int-twitter:search-inbound-channel-adapter>
|
||||
----
|
||||
====
|
||||
|
||||
Refer to https://dev.twitter.com/docs/using-search to learn more about Twitter queries.
|
||||
See https://dev.twitter.com/docs/using-search to learn more about Twitter queries.
|
||||
|
||||
As you can see the configuration of all of these adapters is very similar to other inbound adapters with one exception.
|
||||
Some may need to be injected with the `twitter-template`.
|
||||
Once received each Twitter Message would be encapsulated in a Spring Integration Message and sent to the channel specified by the `channel` attribute.
|
||||
Currently the Payload type of any Message is `org.springframework.integration.twitter.core.Tweet` which is very similar to the object with the same name in Spring Social.
|
||||
As we migrate to Spring Social we'll be depending on their API and some of the artifacts that are currently in use will be obsolete, however we've already made sure that the impact of such migration is minimal by aligning our API with the current state (at the time of writing) of Spring Social.
|
||||
The configuration of all of these adapters is similar to other inbound adapters, with one exception: Some may need to have the `twitter-template` injected.
|
||||
Once received, each Twitter message is encapsulated in a Spring Integration `Message` and sent to the channel specified by the `channel` attribute.
|
||||
|
||||
To get the text from the `org.springframework.social.twitter.api.Tweet` simply invoke the `getText()` method.
|
||||
NOTE: Currently, the payload type of any Twitter `Message` is `org.springframework.integration.twitter.core.Tweet`, which is very similar to the object with the same name in Spring Social.
|
||||
As we migrate to Spring Social, we plan to depend on its API.
|
||||
Some of the artifacts that we currently use are about to be obsolete.
|
||||
However, we have already made sure that the impact of such migration is minimal, by aligning our API with the current state (at the time of this writing) of Spring Social.
|
||||
|
||||
To get the text from the `org.springframework.social.twitter.api.Tweet`, invoke the `getText()` method.
|
||||
|
||||
[[twitter-outbound]]
|
||||
=== Twitter Outbound Adapter
|
||||
|
||||
Twitter outbound channel adapters allow you to send Twitter Messages, or tweets.
|
||||
Twitter outbound channel adapters let you send Twitter Messages (called tweets).
|
||||
|
||||
_Spring Integration version 2.0 and above_ supports sending _Status Update Messages_ and _Direct Messages_.
|
||||
Twitter outbound channel adapters will take the Message payload and send it as a Twitter message.
|
||||
Currently the only supported payload type is`String`, so consider adding a _transformer_ if the payload of the incoming message is not a String.
|
||||
As of version 2.0, Spring Integration supports sending status update messages and direct messages.
|
||||
Twitter outbound channel adapters take the `Message` payload and send it as a Twitter message.
|
||||
Currently, the only supported payload type is `String`, so you should consider adding a transformer if the payload of the incoming message is not a `String`.
|
||||
|
||||
[[outbound-twitter-update]]
|
||||
==== Twitter Outbound Update Channel Adapter
|
||||
|
||||
This adapter allows you to send regular status updates by simply sending a Message to the channel identified by the `channel` attribute.
|
||||
This adapter lets you send regular status updates by sending a `Message` to the channel identified by the `channel` attribute.
|
||||
The following example configures a basic Twitter outbound channel adapter:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-twitter:outbound-channel-adapter
|
||||
twitter-template="twitterTemplate"
|
||||
channel="twitterChannel"/>
|
||||
----
|
||||
====
|
||||
|
||||
The only extra configuration that is required for this adapter is the `twitter-template` reference.
|
||||
The only extra configuration adapter requires is the `twitter-template` reference.
|
||||
|
||||
Starting with _version 4.0_ the `<int-twitter:outbound-channel-adapter>` supports a `tweet-data-expression` to populate the `TweetData` argument (http://projects.spring.io/spring-social-twitter/[Spring Social Twitter]) using the message as the root object of the expression evaluation context.
|
||||
The result can be a `String`, which will be used for the `TweetData` message; a `Tweet` object, the `text` of which will be used for the `TweetData` message; or an entire `TweetData` object.
|
||||
For convenience, the `TweetData` can be built from the expression directly without needing a fully qualified class name:
|
||||
Starting with version 4.0, the `<int-twitter:outbound-channel-adapter>` element supports a `tweet-data-expression` attribute to populate the `TweetData` argument (see http://projects.spring.io/spring-social-twitter/[Spring Social Twitter]) by using the message as the root object of the expression evaluation context.
|
||||
The result can be one of the following:
|
||||
|
||||
* A `String`, which is used for the `TweetData` message
|
||||
* A `Tweet` object, the `text` of which is used for the `TweetData` message
|
||||
* An entire `TweetData` object.
|
||||
|
||||
For convenience, the `TweetData` object can be built from the expression directly without needing a fully qualified class name, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-twitter:outbound-channel-adapter
|
||||
@@ -225,46 +264,56 @@ For convenience, the `TweetData` can be built from the expression directly witho
|
||||
channel="twitterChannel"
|
||||
tweet-data-expression="new TweetData(payload).withMedia(headers.media).displayCoordinates(true)/>
|
||||
----
|
||||
====
|
||||
|
||||
This allows, for example, attaching an image to the tweet.
|
||||
This allows, among other things, attaching an image to the tweet.
|
||||
|
||||
[[outbound-twitter-direct]]
|
||||
==== Twitter Outbound Direct Message Channel Adapter
|
||||
|
||||
This adapter allows you to send Direct Twitter Messages (i.e., @user) by simply sending a Message to the channel identified by the `channel` attribute.
|
||||
This adapter lets you send Twitter direct messages (in other words, `@user`) by simply sending a `Message` to the channel identified by the `channel` attribute.
|
||||
The following example configures a basic Twitter outbound direct message channel adapter:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-twitter:dm-outbound-channel-adapter
|
||||
twitter-template="twitterTemplate"
|
||||
channel="twitterChannel"/>
|
||||
----
|
||||
====
|
||||
|
||||
The only extra configuration that is required for this adapter is the `twitter-template` reference.
|
||||
The only extra configuration this adapter requires is the `twitter-template` reference.
|
||||
|
||||
When it comes to Twitter Direct Messages, you must specify who you are sending the message to - the _target userid_.
|
||||
The Twitter Outbound Direct Message Channel Adapter will look for a target userid in the Message headers under the name `twitter_dmTargetUserId` which is also identified by the following constant: `TwitterHeaders.DM_TARGET_USER_ID`.
|
||||
So when creating a Message all you need to do is add a value for that header.
|
||||
When it comes to Twitter direct messages, you must specify to whom you are sending the message (that is, the target user ID).
|
||||
The Twitter outbound direct message channel adapter looks for a target user ID in the message headers under the name of `twitter_dmTargetUserId`, which is also identified by the following constant: `TwitterHeaders.DM_TARGET_USER_ID`.
|
||||
So, when creating a `Message`, you need only add a value for that header, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Message message = MessageBuilder.withPayload("hello")
|
||||
.setHeader(TwitterHeaders.DM_TARGET_USER_ID, "z_oleg").build();
|
||||
----
|
||||
====
|
||||
|
||||
The above approach works well if you are creating the Message programmatically.
|
||||
However it's more common to provide the header value within a messaging flow.
|
||||
The value can be provided by an upstream <header-enricher>.
|
||||
The preceding approach works well if you create the `Message` programmatically.
|
||||
However, it is more common to provide the header value within a messaging flow.
|
||||
The value can be provided by an upstream `<header-enricher>`, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:header-enricher input-channel="in" output-channel="out">
|
||||
<int:header name="twitter_dmTargetUserId" value="z_oleg"/>
|
||||
</int:header-enricher>
|
||||
----
|
||||
====
|
||||
|
||||
It's quite common that the value must be determined dynamically.
|
||||
For those cases you can take advantage of SpEL support within the <header-enricher>.
|
||||
It is quite common that the value must be determined dynamically.
|
||||
For those cases, you can take advantage of SpEL support within the `<header-enricher>` by using the `expression` attribute, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int:header-enricher input-channel="in" output-channel="out">
|
||||
@@ -272,59 +321,67 @@ For those cases you can take advantage of SpEL support within the <header-enrich
|
||||
expression="@twitterIdService.lookup(headers.username)"/>
|
||||
</int:header-enricher>
|
||||
----
|
||||
====
|
||||
|
||||
IMPORTANT: Twitter does not allow you to post duplicate Messages.
|
||||
This is a common problem during testing when the same code works the first time but does not work the second time.
|
||||
So, make sure to change the content of the Message each time.
|
||||
Another thing that works well for testing is to append a timestamp to the end of each message.
|
||||
IMPORTANT: Twitter does not let you post duplicate messages.
|
||||
This is a common problem during testing, when the same code works the first time but does not work the second time.
|
||||
Consequently, you need to change the content of the message each time.
|
||||
Appending a timestamp to the end of each message works well for testing.
|
||||
|
||||
[[twitter-sog]]
|
||||
=== Twitter Search Outbound Gateway
|
||||
|
||||
In Spring Integration, an outbound gateway is used for two-way request/response communication with an external service.
|
||||
The Twitter Search Outbound Gateway allows you to issue dynamic twitter searches.
|
||||
In Spring Integration, an outbound gateway is used for two-way request-response communication with an external service.
|
||||
The Twitter search outbound gateway lets you issue dynamic Twitter searches.
|
||||
The reply message payload is a collection of `Tweet` objects.
|
||||
If the search returns no results, the payload is an empty collection.
|
||||
You can limit the number of tweets and you can page through a larger set of tweets by making multiple calls.
|
||||
To facilitate this, search reply messages contain a header `twitter_searchMetadata` with its value being a `SearchMetadata` object.
|
||||
For more information on the `Tweet`, `SearchParameters` and `SearchMetadata` classes, refer to the http://projects.spring.io/spring-social-twitter/[Spring Social Twitter] documentation.
|
||||
You can limit the number of tweets, and you can page through a larger set of tweets by making multiple calls.
|
||||
To facilitate this, search reply messages contain a header called `twitter_searchMetadata`.
|
||||
Its value is a `SearchMetadata` object.
|
||||
For more information on the `Tweet`, `SearchParameters`, and `SearchMetadata` classes, see the http://projects.spring.io/spring-social-twitter/[Spring Social Twitter] documentation.
|
||||
|
||||
*Configuring the Outbound Gateway*
|
||||
==== Configuring the Twitter Search Outbound Gateway
|
||||
|
||||
The following listing shows the available attributes for a Twitter search outbound gateway:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-twitter:search-outbound-gateway id="twitter"
|
||||
request-channel="in" <1>
|
||||
request-channel="in" <1>
|
||||
twitter-template="twitterTemplate" <2>
|
||||
search-args-expression="payload" <3>
|
||||
reply-channel="out" <4>
|
||||
reply-timeout="123" <5>
|
||||
order="1" <6>
|
||||
auto-startup="false" <7>
|
||||
phase="100" /> <8>
|
||||
|
||||
search-args-expression="payload" <3>
|
||||
reply-channel="out" <4>
|
||||
reply-timeout="123" <5>
|
||||
order="1" <6>
|
||||
auto-startup="false" <7>
|
||||
phase="100" /> <8>
|
||||
----
|
||||
|
||||
<1> The channel used to send search requests to this gateway.
|
||||
|
||||
<2> A reference to a `TwitterTemplate` with authentication configuration.
|
||||
|
||||
<3> A SpEL expression that evaluates to argument(s) for the search.
|
||||
Default: *"payload"* - in which case the payload can be a `String` (e.g "#springintegration") and the gateway limits the query to 20 tweets, or the payload can be a `SearchParameters` object. +
|
||||
The expression can also be specified as a http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html#expressions-inline-lists[SpEL List].
|
||||
The first element (String) is the query, the remaining elements (Numbers) are `pageSize, sinceId, maxId` respectively - refer to the Spring Social Twitter documentation for more information about these parameters.
|
||||
<2> A reference to a `TwitterTemplate` that has authentication configuration.
|
||||
<3> A SpEL expression that evaluates to the arguments for the search.
|
||||
Default: *"payload"* - in which case the payload can be a `String` (such as "#springintegration"), and the gateway limits the query to 20 tweets.
|
||||
Alternatively, the payload can be a `SearchParameters` object.
|
||||
You can also specify the expression as a http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html#expressions-inline-lists[SpEL List].
|
||||
The first element (a `String`) is the query, the remaining elements (`Number` objects) are `pageSize`, `sinceId`, and `maxId`, respectively. See the http://projects.spring.io/spring-social-twitter/[Spring Social Twitter] documentation for more information about these parameters.
|
||||
When specifying a `SearchParameters` object directly in the SpEL expression, you do not have to fully qualify the class name.
|
||||
Some examples: +
|
||||
`new SearchParameters(payload).count(5).sinceId(headers.sinceId)` +
|
||||
`{payload, 30}` +
|
||||
The following examples all work:
|
||||
+
|
||||
`new SearchParameters(payload).count(5).sinceId(headers.sinceId)`
|
||||
+
|
||||
`{payload, 30}`
|
||||
+
|
||||
`{payload, headers.pageSize, headers.sinceId, headers.maxId}`
|
||||
<4> The channel to which to send the reply.
|
||||
If omitted, the `replyChannel` header is used.
|
||||
|
||||
<4> The channel to which to send the reply; if omitted, the `replyChannel` header is used.
|
||||
<5> The timeout when sending the reply message to the reply channel.
|
||||
It applies only if the reply channel can block (for example, a bounded queue channel that is full).
|
||||
|
||||
<5> The timeout when sending the reply message to the reply channel; only applies if the reply channel can block, for example a bounded queue channel that is full.
|
||||
|
||||
<6> When subscribed to a publish/subscribe channel, the order in which this endpoint will be invoked.
|
||||
<6> When subscribed to a publish-subscribe channel, the order in which this endpoint is invoked.
|
||||
|
||||
<7> `SmartLifecycle` method.
|
||||
|
||||
<8> `SmartLifecycle` method.
|
||||
====
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
[[web-sockets]]
|
||||
== WebSockets Support
|
||||
|
||||
[[web-socket-introduction]]
|
||||
=== Introduction
|
||||
Starting with version 4.1, Spring Integration has WebSocket support.
|
||||
It is based on the architecture, infrastructure, and API from the Spring Framework's `web-socket` module.
|
||||
Therefore, many of Spring WebSocket's components (such as `SubProtocolHandler` or `WebSocketClient`) and configuration options (such as `@EnableWebSocketMessageBroker`) can be reused within Spring Integration.
|
||||
For more information, see the http://docs.spring.io/spring/docs/current/spring-framework-reference/html/#websocket[Spring Framework WebSocket Support] chapter in the Spring Framework reference manual.
|
||||
|
||||
Starting with _version 4.1_ Spring Integration has introduced _WebSocket_ support.
|
||||
It is based on architecture, infrastructure and API from the Spring Framework's _web-socket_ module.
|
||||
Therefore, many of Spring WebSocket's components (e.g.
|
||||
`SubProtocolHandler` or `WebSocketClient`) and configuration options (e.g.
|
||||
`@EnableWebSocketMessageBroker`) can be reused within Spring Integration.
|
||||
For more information, please, refer to the http://docs.spring.io/spring/docs/current/spring-framework-reference/html/#websocket[Spring Framework WebSocket Support] chapter in the Spring Framework reference manual.
|
||||
|
||||
NOTE: Since the Spring Framework WebSocket infrastructure is based on the _Spring Messaging_ foundation and provides a basic Messaging framework based on the same `MessageChannel` s, `MessageHandler` s that Spring Integration uses, and some POJO-method annotation mappings, Spring Integration can be directly involved in a WebSocket flow, even without WebSocket adapters.
|
||||
For this purpose you can simply configure a Spring Integration `@MessagingGateway` with appropriate annotations:
|
||||
The Spring Framework WebSocket infrastructure is based on the Spring messaging foundation and provides a basic messaging framework based on the same `MessageChannel` implementations and `MessageHandler` implementations that Spring Integration uses (and some POJO-method annotation mappings).
|
||||
Consequently, Spring Integration can be directly involved in a WebSocket flow, even without WebSocket adapters.
|
||||
For this purpose, you can configure a Spring Integration `@MessagingGateway` with appropriate annotations, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@MessagingGateway
|
||||
@@ -27,15 +24,19 @@ public interface WebSocketGateway {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[[web-socket-overview]]
|
||||
=== Overview
|
||||
|
||||
Since the WebSocket protocol is _streaming_ by definition and we can _send_ and _receive_ messages to/from a WebSocket at the same time, we can simply deal with an appropriate `WebSocketSession`, regardless of being on the client or server side.
|
||||
Since the WebSocket protocol is streaming by definition and we can send and receive messages to and from a WebSocket at the same time, we can deal with an appropriate `WebSocketSession`, regardless of being on the client or server side.
|
||||
To encapsulate the connection management and `WebSocketSession` registry, the `IntegrationWebSocketContainer` is provided with `ClientWebSocketContainer` and `ServerWebSocketContainer` implementations.
|
||||
Thanks to the https://www.jcp.org/en/jsr/detail?id=356[WebSocket API] and its implementation in the Spring Framework, with many extensions, the same classes are used on the server side as well as the client side (from a Java perspective, of course).
|
||||
Hence most connection and `WebSocketSession` registry options are the same on both sides.
|
||||
That allows us to reuse many configuration items and infrastructure hooks to build WebSocket applications on the server side as well as on the client side:
|
||||
Thanks to the https://www.jcp.org/en/jsr/detail?id=356[WebSocket API] and its implementation in the Spring Framework (with many extensions), the same classes are used on the server side as well as the client side (from a Java perspective, of course).
|
||||
Consequently, most connection and `WebSocketSession` registry options are the same on both sides.
|
||||
That lets us reuse many configuration items and infrastructure hooks to build WebSocket applications on the server side as well as on the client side.
|
||||
The following example shows how components can serve both purposes:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
//Client side
|
||||
@@ -55,59 +56,69 @@ public IntegrationWebSocketContainer serverWebSocketContainer() {
|
||||
return new ServerWebSocketContainer("/endpoint").withSockJs();
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The `IntegrationWebSocketContainer` is designed to achieve _bidirectional_ messaging and can be shared between Inbound and Outbound Channel Adapters (see below), can be referenced only from one of them (when using one-way - sending or receiving - WebSocket messaging).
|
||||
It can be used without any Channel Adapter, but in this case, `IntegrationWebSocketContainer` only plays a role as the `WebSocketSession` registry.
|
||||
The `IntegrationWebSocketContainer` is designed to achieve bidirectional messaging and can be shared between inbound and outbound channel adapters (see below), can be referenced from only one of them when using one-way (sending or receiving) WebSocket messaging.
|
||||
It can be used without any channel adapter, but, in this case, `IntegrationWebSocketContainer` only plays a role as the `WebSocketSession` registry.
|
||||
|
||||
NOTE: The `ServerWebSocketContainer` implements `WebSocketConfigurer` to register an internal `IntegrationWebSocketContainer.IntegrationWebSocketHandler` as an `Endpoint` under the provided `paths` and other server WebSocket options (such as `HandshakeHandler` or `SockJS fallback`) within the `ServletWebSocketHandlerRegistry` for the target vendor WebSocket Container.
|
||||
NOTE: The `ServerWebSocketContainer` implements `WebSocketConfigurer` to register an internal `IntegrationWebSocketContainer.IntegrationWebSocketHandler` as an `Endpoint`.
|
||||
It does so under the provided `paths` and other server WebSocket options (such as `HandshakeHandler` or `SockJS fallback`) within the `ServletWebSocketHandlerRegistry` for the target vendor WebSocket Container.
|
||||
This registration is achieved with an infrastructural `WebSocketIntegrationConfigurationInitializer` component, which does the same as the `@EnableWebSocket` annotation.
|
||||
This means that using just `@EnableIntegration` (or any Spring Integration Namespace in the application context) you can omit the `@EnableWebSocket` declaration, because all WebSocket Endpoints are detected by the Spring Integration infrastructure.
|
||||
This means that, by using `@EnableIntegration` (or any Spring Integration namespace in the application context), you can omit the `@EnableWebSocket` declaration, because the Spring Integration infrastructure detects all WebSocket endpoints.
|
||||
|
||||
[[web-socket-inbound-adapter]]
|
||||
=== WebSocket Inbound Channel Adapter
|
||||
|
||||
The `WebSocketInboundChannelAdapter` implements the receiving part of `WebSocketSession` interaction.
|
||||
It must be supplied with a `IntegrationWebSocketContainer`, and the adapter registers itself as a `WebSocketListener` to handle incoming messages and `WebSocketSession` events.
|
||||
You must supply it with a `IntegrationWebSocketContainer`, and the adapter registers itself as a `WebSocketListener` to handle incoming messages and `WebSocketSession` events.
|
||||
|
||||
NOTE: Only one `WebSocketListener` can be registered in the `IntegrationWebSocketContainer`.
|
||||
|
||||
For WebSocket _sub-protocol_s, the `WebSocketInboundChannelAdapter` can be configured with `SubProtocolHandlerRegistry` as the second constructor argument.
|
||||
The adapter delegates to the `SubProtocolHandlerRegistry` to determine the appropriate `SubProtocolHandler` for the accepted `WebSocketSession` and to convert `WebSocketMessage` to a `Message` according to the sub-protocol implementation.
|
||||
For WebSocket subprotocols, the `WebSocketInboundChannelAdapter` can be configured with `SubProtocolHandlerRegistry` as the second constructor argument.
|
||||
The adapter delegates to the `SubProtocolHandlerRegistry` to determine the appropriate `SubProtocolHandler` for the accepted `WebSocketSession` and to convert a `WebSocketMessage` to a `Message` according to the sub-protocol implementation.
|
||||
|
||||
NOTE: By default, the `WebSocketInboundChannelAdapter` relies just only on the raw `PassThruSubProtocolHandler` implementation, which simply converts the `WebSocketMessage` to a `Message`.
|
||||
NOTE: By default, the `WebSocketInboundChannelAdapter` relies only on the raw `PassThruSubProtocolHandler` implementation, which converts the `WebSocketMessage` to a `Message`.
|
||||
|
||||
The `WebSocketInboundChannelAdapter` accepts and sends to the underlying integration flow only `Message` s with `SimpMessageType.MESSAGE` or an empty `simpMessageType` header.
|
||||
All other `Message` types are handled through the `ApplicationEvent` s emitted from a `SubProtocolHandler` implementation (e.g.
|
||||
The `WebSocketInboundChannelAdapter` accepts and sends to the underlying integration flow only `Message` instances that have `SimpMessageType.MESSAGE` or an empty `simpMessageType` header.
|
||||
All other `Message` types are handled through the `ApplicationEvent` instances emitted from a `SubProtocolHandler` implementation (such as
|
||||
`StompSubProtocolHandler`).
|
||||
|
||||
On the server side `WebSocketInboundChannelAdapter` can be configured with the `useBroker = true` option, if the `@EnableWebSocketMessageBroker` configuration is present.
|
||||
In this case all `non-MESSAGE` `Message` types are delegated to the provided `AbstractBrokerMessageHandler`.
|
||||
In addition, if the Broker Relay is configured with destination prefixes, those Messages, which match to the Broker destinations, are routed to the `AbstractBrokerMessageHandler`, instead of to the `outputChannel` of the `WebSocketInboundChannelAdapter`.
|
||||
On the server side, if the `@EnableWebSocketMessageBroker` configuration is present, you can configure `WebSocketInboundChannelAdapter` with the `useBroker = true` option.
|
||||
In this case, all `non-MESSAGE` `Message` types are delegated to the provided `AbstractBrokerMessageHandler`.
|
||||
In addition, if the broker relay is configured with destination prefixes, those messages that match the Broker destinations are routed to the `AbstractBrokerMessageHandler` instead of to the `outputChannel` of the `WebSocketInboundChannelAdapter`.
|
||||
|
||||
If `useBroker = false` and received message is of `SimpMessageType.CONNECT` type, the `WebSocketInboundChannelAdapter` sends `SimpMessageType.CONNECT_ACK` message to the `WebSocketSession` immediately without sending it to the channel.
|
||||
If `useBroker = false` and the received message is of the `SimpMessageType.CONNECT` type, the `WebSocketInboundChannelAdapter` immediately sends a `SimpMessageType.CONNECT_ACK` message to the `WebSocketSession` without sending it to the channel.
|
||||
|
||||
NOTE: Spring's WebSocket Support allows the configuration of only one Broker Relay, hence we don't require an `AbstractBrokerMessageHandler` reference, it is detected in the Application Context.
|
||||
NOTE: Spring's WebSocket Support allows the configuration of only one broker relay.
|
||||
Consequently, we do not require an `AbstractBrokerMessageHandler` reference.
|
||||
It is detected in the Application Context.
|
||||
|
||||
For more configuration options see <<web-sockets-namespace>>.
|
||||
For more configuration options, see "`<<web-sockets-namespace>>`".
|
||||
|
||||
[[web-socket-outbound-adapter]]
|
||||
=== WebSocket Outbound Channel Adapter
|
||||
|
||||
The `WebSocketOutboundChannelAdapter` accepts Spring Integration messages from its `MessageChannel`, determines the `WebSocketSession` `id` from the `MessageHeaders`, retrieves the `WebSocketSession` from the provided `IntegrationWebSocketContainer` and delegates the conversion and sending `WebSocketMessage` work to the appropriate `SubProtocolHandler` from the provided `SubProtocolHandlerRegistry`.
|
||||
The `WebSocketOutboundChannelAdapter`:
|
||||
|
||||
On the client side, the `WebSocketSession` `id` message header isn't required, because `ClientWebSocketContainer` deals only with a single connection and its `WebSocketSession` respectively.
|
||||
. Accepts Spring Integration messages from its `MessageChannel`
|
||||
. Determines the `WebSocketSession` `id` from the `MessageHeaders`
|
||||
. Retrieves the `WebSocketSession` from the provided `IntegrationWebSocketContainer`
|
||||
. Delegates the conversion and sending of `WebSocketMessage` work to the appropriate `SubProtocolHandler` from the provided `SubProtocolHandlerRegistry`.
|
||||
|
||||
To use the STOMP sub-protocol, this adapter should be configured with a `StompSubProtocolHandler`.
|
||||
On the client side, the `WebSocketSession` `id` message header is not required, because `ClientWebSocketContainer` deals only with a single connection and its `WebSocketSession` respectively.
|
||||
|
||||
To use the STOMP subprotocol, you should configure this adapter with a `StompSubProtocolHandler`.
|
||||
Then you can send any STOMP message type to this adapter, using `StompHeaderAccessor.create(StompCommand...)` and a `MessageBuilder`, or just using a `HeaderEnricher` (see <<header-enricher>>).
|
||||
|
||||
For more configuration options see below.
|
||||
The rest of this chapter covers largely additional configuration options.
|
||||
|
||||
[[web-sockets-namespace]]
|
||||
=== WebSockets Namespace Support
|
||||
|
||||
Spring Integration _WebSocket_ namespace includes several components described below.
|
||||
To include it in your configuration, simply provide the following namespace declaration in your application context configuration file:
|
||||
The Spring Integration WebSocket namespace includes several components described in the remainder of this chapter.
|
||||
To include it in your configuration, use the following namespace declaration in your application context configuration file:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
@@ -125,249 +136,188 @@ To include it in your configuration, simply provide the following namespace decl
|
||||
...
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
*<int-websocket:client-container>*
|
||||
[[websocket-client-container-attributes]]
|
||||
==== `<int-websocket:client-container>` Attributes
|
||||
|
||||
The following listing shows the attributes available for the `<int-websocket:client-container>` element:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-websocket:client-container
|
||||
id="" <1>
|
||||
client="" <2>
|
||||
uri="" <3>
|
||||
uri-variables="" <4>
|
||||
origin="" <5>
|
||||
send-time-limit="" <6>
|
||||
send-buffer-size-limit="" <7>
|
||||
auto-startup="" <8>
|
||||
phase=""> <9>
|
||||
id="" <1>
|
||||
client="" <2>
|
||||
uri="" <3>
|
||||
uri-variables="" <4>
|
||||
origin="" <5>
|
||||
send-time-limit="" <6>
|
||||
send-buffer-size-limit="" <7>
|
||||
auto-startup="" <8>
|
||||
phase=""> <9>
|
||||
<int-websocket:http-headers>
|
||||
<entry key="" value=""/>
|
||||
<entry key="" value=""/>
|
||||
</int-websocket:http-headers> <10>
|
||||
</int-websocket:client-container>
|
||||
----
|
||||
|
||||
<1> The component bean name.
|
||||
|
||||
|
||||
<2> The `WebSocketClient` bean reference.
|
||||
|
||||
|
||||
<3> The `uri` or `uriTemplate` to the target WebSocket service.
|
||||
If it is used as a `uriTemplate` with URI variable placeholders, the `uri-variables` attribute is required.
|
||||
|
||||
|
||||
If you use it as a `uriTemplate` with URI variable placeholders, the `uri-variables` attribute is required.
|
||||
<4> Comma-separated values for the URI variable placeholders within the `uri` attribute value.
|
||||
The values are replaced into the placeholders according to the order in the `uri`.
|
||||
See `UriComponents.expand(Object...
|
||||
uriVariableValues)`.
|
||||
|
||||
|
||||
The values are replaced into the placeholders according to their order in the `uri`.
|
||||
See https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/util/UriComponents.html#expand-java.lang.Object[`UriComponents.expand(Object...uriVariableValues)`].
|
||||
<5> The `Origin` Handshake HTTP header value.
|
||||
|
||||
|
||||
<6> The WebSocket session 'send' timeout limit.
|
||||
Defaults to `10000`.
|
||||
|
||||
|
||||
<7> The WebSocket session 'send' message size limit.
|
||||
Defaults to `524288`.
|
||||
|
||||
|
||||
<8> Boolean value indicating whether this endpoint should start automatically.
|
||||
Defaults to `false`, assuming that this container will be started from the <<web-socket-inbound-adapter>>.
|
||||
|
||||
|
||||
Defaults to `false`, assuming that this container is started from the <<web-socket-inbound-adapter, WebSocket inbound adapter>>.
|
||||
<9> The lifecycle phase within which this endpoint should start and stop.
|
||||
The lower the value the earlier this endpoint will start and the later it will stop.
|
||||
The lower the value, the earlier this endpoint starts and the later it stops.
|
||||
The default is `Integer.MAX_VALUE`.
|
||||
Values can be negative.
|
||||
See `SmartLifeCycle`.
|
||||
|
||||
|
||||
See https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/SmartLifecycle.html[`SmartLifeCycle`].
|
||||
<10> A `Map` of `HttpHeaders` to be used with the Handshake request.
|
||||
====
|
||||
|
||||
*<int-websocket:server-container>*
|
||||
==== `<int-websocket:server-container>` Attributes
|
||||
|
||||
The following listing shows the attributes available for the `<int-websocket:server-container>` element:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-websocket:server-container
|
||||
id="" <1>
|
||||
path="" <2>
|
||||
handshake-handler="" <3>
|
||||
handshake-interceptors="" <4>
|
||||
decorator-factories="" <5>
|
||||
send-time-limit="" <6>
|
||||
send-buffer-size-limit="" <7>
|
||||
allowed-origins=""> <8>
|
||||
<int-websocket:sockjs
|
||||
client-library-url="" <9>
|
||||
stream-bytes-limit="" <10>
|
||||
session-cookie-needed="" <11>
|
||||
heartbeat-time="" <12>
|
||||
disconnect-delay="" <13>
|
||||
message-cache-size="" <14>
|
||||
websocket-enabled="" <15>
|
||||
scheduler="" <16>
|
||||
message-codec="" <17>
|
||||
transport-handlers="" <18>
|
||||
suppress-cors="true"="" /> <19>
|
||||
id="" <1>
|
||||
path="" <2>
|
||||
handshake-handler="" <3>
|
||||
handshake-interceptors="" <4>
|
||||
decorator-factories="" <5>
|
||||
send-time-limit="" <6>
|
||||
send-buffer-size-limit="" <7>
|
||||
allowed-origins=""> <8>
|
||||
<int-websocket:sockjs
|
||||
client-library-url="" <9>
|
||||
stream-bytes-limit="" <10>
|
||||
session-cookie-needed="" <11>
|
||||
heartbeat-time="" <12>
|
||||
disconnect-delay="" <13>
|
||||
message-cache-size="" <14>
|
||||
websocket-enabled="" <15>
|
||||
scheduler="" <16>
|
||||
message-codec="" <17>
|
||||
transport-handlers="" <18>
|
||||
suppress-cors="true"="" /> <19>
|
||||
</int-websocket:server-container>
|
||||
----
|
||||
|
||||
<1> The component bean name.
|
||||
|
||||
|
||||
<2> A path (or comma-separated paths) that maps a particular request to a `WebSocketHandler`.
|
||||
Exact path mapping URIs (such as `"/myPath"`) are supported as well as ant-style path patterns (such as `/myPath/**`).
|
||||
|
||||
|
||||
Supports exact path mapping URIs (such as `/myPath`) and ant-style path patterns (such as `/myPath/**`).
|
||||
<3> The `HandshakeHandler` bean reference.
|
||||
Default to `DefaultHandshakeHandler`.
|
||||
|
||||
|
||||
Defaults to `DefaultHandshakeHandler`.
|
||||
<4> List of `HandshakeInterceptor` bean references.
|
||||
|
||||
|
||||
<5> Configure one or more factories (`WebSocketHandlerDecoratorFactory`) to decorate the handler
|
||||
used to process WebSocket messages.
|
||||
This may be useful for some advanced use cases, for example to allow Spring Security to forcibly close
|
||||
the WebSocket session when the corresponding HTTP session expires.
|
||||
See http://docs.spring.io/spring-session/docs/current/reference/html5/#websocket[Spring Session Project]
|
||||
for more information.
|
||||
|
||||
<6> See the same option on the `<int-websocket:client-container>`.
|
||||
|
||||
|
||||
<7> See the same option on the `<int-websocket:client-container>`.
|
||||
|
||||
|
||||
<8> Configure allowed Origin header values. Multiple origins may be specified as a comma-separated list.
|
||||
<5> List of one or more factories (`WebSocketHandlerDecoratorFactory`) that decorate the handler used to process WebSocket messages.
|
||||
This may be useful for some advanced use cases (for example, to allow Spring Security to forcibly close
|
||||
the WebSocket session when the corresponding HTTP session expires).
|
||||
See the http://docs.spring.io/spring-session/docs/current/reference/html5/#websocket[Spring Session Project] for more information.
|
||||
<6> See the same option on the <<websocket-client-container-attributes,`<int-websocket:client-container>`>>.
|
||||
<7> See the same option on the <<websocket-client-container-attributes,`<int-websocket:client-container>`>>.
|
||||
<8> The allowed origin header values.
|
||||
You can specify multiple origins as a comma-separated list.
|
||||
This check is mostly designed for browser clients.
|
||||
There is noting preventing other types of client to modify the Origin header value.
|
||||
When SockJS is enabled and allowed origins are restricted, transport types that do not use Origin headers for cross origin requests (jsonp-polling, iframe-xhr-polling, iframe-eventsource and iframe-htmlfile) are disabled.
|
||||
As a consequence, IE6/IE7 are not supported and IE8/IE9 will only be supported without cookies.
|
||||
There is nothing preventing other types of client from modifying the origin header value.
|
||||
When SockJS is enabled and allowed origins are restricted, transport types that do not use origin headers for cross-origin requests (`jsonp-polling`, `iframe-xhr-polling`, `iframe-eventsource`, and `iframe-htmlfile`) are disabled.
|
||||
As a consequence, IE6 and IE7 are not supported, and IE8 and IE9 are supported only without cookies.
|
||||
By default, all origins are allowed.
|
||||
|
||||
|
||||
<9> Transports with no native cross-domain communication (e.g.
|
||||
"eventsource", "htmlfile") must get a simple page from the "foreign" domain in an invisible iframe so that code in the iframe can run from a domain local to the SockJS server.
|
||||
Since the iframe needs to load the SockJS javascript client library, this property allows specifying where to load it from.
|
||||
By default this is set to point to `https://d1fxtkz8shb9d2.cloudfront.net/sockjs-0.3.4.min.js`.
|
||||
However it can also be set to point to a URL served by the application.
|
||||
Note that it's possible to specify a relative URL in which case the URL must be relative to the iframe URL.
|
||||
For example assuming a SockJS endpoint mapped to "/sockjs", and resulting iframe URL "/sockjs/iframe.html", then the The relative URL must start with "../../" to traverse up to the location above the SockJS mapping.
|
||||
In case of a prefix-based Servlet mapping one more traversal may be needed.
|
||||
|
||||
|
||||
<10> Minimum number of bytes that can be send over a single HTTP streaming request before it will be closed.
|
||||
Defaults to `128K` (i.e.
|
||||
128*1024 bytes).
|
||||
|
||||
|
||||
<11> The "cookie_needed" value in the response from the SockJs `"/info"` endpoint.
|
||||
This property indicates whether the use of a JSESSIONID cookie is required for the application to function correctly, e.g.
|
||||
for load balancing or in Java Servlet containers for the use of an HTTP session.
|
||||
|
||||
|
||||
<12> The amount of time in milliseconds when the server has not sent any messages and after which the server should
|
||||
<9> Transports with no native cross-domain communication (such as `eventsource` and `htmlfile`) must get a simple page from the "`foreign`" domain in an invisible iframe so that code in the iframe can run from a domain local to the SockJS server.
|
||||
Since the iframe needs to load the SockJS javascript client library, this property lets you specify the location from which to load it.
|
||||
By default, it points to `https://d1fxtkz8shb9d2.cloudfront.net/sockjs-0.3.4.min.js`.
|
||||
However, you can also set it to point to a URL served by the application.
|
||||
Note that it is possible to specify a relative URL, in which case the URL must be relative to the iframe URL.
|
||||
For example, assuming a SockJS endpoint mapped to `/sockjs` and the resulting iframe URL is `/sockjs/iframe.html`, the relative URL must start with "../../" to traverse up to the location above the SockJS mapping.
|
||||
For prefix-based servlet mapping, you may need one more traversal.
|
||||
<10> Minimum number of bytes that can be sent over a single HTTP streaming request before it is closed.
|
||||
Defaults to `128K` (that is, 128*1024 or 131072 bytes).
|
||||
<11> The `cookie_needed` value in the response from the SockJs `/info` endpoint.
|
||||
This property indicates whether a `JSESSIONID` cookie is required for the application to function correctly (for example, for load balancing or in Java Servlet containers for the use of an HTTP session).
|
||||
<12> The amount of time (in milliseconds) when the server has not sent any messages and after which the server should
|
||||
send a heartbeat frame to the client in order to keep the connection from breaking.
|
||||
The default value is `25,000` (25 seconds).
|
||||
|
||||
|
||||
<13> The amount of time in milliseconds before a client is considered disconnected after not having a receiving
|
||||
connection, i.e.
|
||||
an active connection over which the server can send data to the client.
|
||||
<13> The amount of time (in milliseconds) before a client is considered disconnected after not having a receiving connection (that is, an active connection over which the server can send data to the client).
|
||||
The default value is `5000`.
|
||||
|
||||
|
||||
<14> The number of server-to-client messages that a session can cache while waiting for the next HTTP polling request
|
||||
from the client.
|
||||
<14> The number of server-to-client messages that a session can cache while waiting for the next HTTP polling request from the client.
|
||||
The default size is `100`.
|
||||
|
||||
|
||||
<15> Some load balancers don't support websockets.
|
||||
<15> Some load balancers do not support WebSockets.
|
||||
Set this option to `false` to disable the WebSocket transport on the server side.
|
||||
The default value is `true`.
|
||||
|
||||
|
||||
<16> The `TaskScheduler` bean reference; a new `ThreadPoolTaskScheduler` instance will be created if no value is
|
||||
provided.
|
||||
This scheduler instance will be used for scheduling heart-beat messages.
|
||||
|
||||
|
||||
<16> The `TaskScheduler` bean reference.
|
||||
A new `ThreadPoolTaskScheduler` instance is created if no value is provided.
|
||||
This scheduler instance is used for scheduling heart-beat messages.
|
||||
<17> The `SockJsMessageCodec` bean reference to use for encoding and decoding SockJS messages.
|
||||
By default `Jackson2SockJsMessageCodec` is used requiring the Jackson library to be present on the classpath.
|
||||
|
||||
|
||||
By default, `Jackson2SockJsMessageCodec` is used, which requires the Jackson library to be present on the classpath.
|
||||
<18> List of `TransportHandler` bean references.
|
||||
|
||||
|
||||
<19> The option to disable automatic addition of CORS headers for SockJS requests.
|
||||
<19> Whether to disable automatic addition of CORS headers for SockJS requests.
|
||||
The default value is `false`.
|
||||
====
|
||||
|
||||
*<int-websocket:outbound-channel-adapter>*
|
||||
[[websocket-outbound-channel-adapter-attributes]]
|
||||
==== `<int-websocket:outbound-channel-adapter>` Attributes
|
||||
|
||||
The following listing shows the attributes available for the `<int-websocket:outbound-channel-adapter>` element:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-websocket:outbound-channel-adapter
|
||||
id="" <1>
|
||||
channel="" <2>
|
||||
container="" <3>
|
||||
default-protocol-handler="" <4>
|
||||
protocol-handlers="" <5>
|
||||
message-converters="" <6>
|
||||
id="" <1>
|
||||
channel="" <2>
|
||||
container="" <3>
|
||||
default-protocol-handler="" <4>
|
||||
protocol-handlers="" <5>
|
||||
message-converters="" <6>
|
||||
merge-with-default-converters="" <7>
|
||||
auto-startup="" <8>
|
||||
phase=""/> <9>
|
||||
auto-startup="" <8>
|
||||
phase=""/> <9>
|
||||
----
|
||||
|
||||
|
||||
|
||||
<1> The component bean name.
|
||||
If the `channel` attribute isn't provided, a `DirectChannel` is created and registered with the application context
|
||||
with this `id` attribute as the bean name.
|
||||
In this case, the endpoint is registered with the bean name `id + '.adapter'`.
|
||||
And the `MessageHandler` is registered with the bean alias `id + '.handler'`.
|
||||
|
||||
|
||||
If you do not provide the `channel` attribute, a `DirectChannel` is created and registered in the application context with this `id` attribute as the bean name.
|
||||
In this case, the endpoint is registered with the bean name `id` plus `.adapter`.
|
||||
And the `MessageHandler` is registered with the bean alias `id` plus `.handler`.
|
||||
<2> Identifies the channel attached to this adapter.
|
||||
|
||||
|
||||
<3> The reference to the `IntegrationWebSocketContainer` bean, which encapsulates the low-level connection and WebSocketSession handling operations.
|
||||
<3> The reference to the `IntegrationWebSocketContainer` bean, which encapsulates the low-level connection and `WebSocketSession` handling operations.
|
||||
Required.
|
||||
|
||||
|
||||
<4> Optional reference to a `SubProtocolHandler` instance.
|
||||
It is used when the client did not request a sub-protocol or it is a single protocol-handler.
|
||||
If this reference or `protocol-handlers` list aren't provided the `PassThruSubProtocolHandler` is used by default.
|
||||
|
||||
|
||||
<5> List of `SubProtocolHandler` bean references for this Channel Adapter.
|
||||
If only a single bean reference is provided and a `default-protocol-handler` isn't provided, that single `SubProtocolHandler` will be used as the `default-protocol-handler`.
|
||||
If this attribute or `default-protocol-handler` aren't provided, the `PassThruSubProtocolHandler` is used by default.
|
||||
|
||||
|
||||
<6> List of `MessageConverter` bean references for this Channel Adapter.
|
||||
|
||||
|
||||
<7> Flag to indicate if the default converters should be registered after any custom converters.
|
||||
This flag is used only if `message-converters` are provided, otherwise all default converters will be registered.
|
||||
If this reference or a `protocol-handlers` list is not provided, the `PassThruSubProtocolHandler` is used by default.
|
||||
<5> List of `SubProtocolHandler` bean references for this channel adapter.
|
||||
If you provide only a single bean reference and do not provide a `default-protocol-handler`, that single `SubProtocolHandler` is used as the `default-protocol-handler`.
|
||||
If you do not set this attribute or `default-protocol-handler`, the `PassThruSubProtocolHandler` is used by default.
|
||||
<6> List of `MessageConverter` bean references for this channel adapter.
|
||||
<7> Boolean value indicating whether the default converters should be registered after any custom converters.
|
||||
This flag is used only if `message-converters` is provided.
|
||||
Otherwise, all default converters are registered.
|
||||
Defaults to `false`.
|
||||
The default converters are (in the order): `StringMessageConverter`, `ByteArrayMessageConverter` and `MappingJackson2MessageConverter` if the Jackson library is present on the classpath.
|
||||
|
||||
|
||||
The default converters are (in order): `StringMessageConverter`, `ByteArrayMessageConverter`, and `MappingJackson2MessageConverter` (if the Jackson library is present on the classpath).
|
||||
<8> Boolean value indicating whether this endpoint should start automatically.
|
||||
Default to `true`.
|
||||
|
||||
|
||||
Defaults to `true`.
|
||||
<9> The lifecycle phase within which this endpoint should start and stop.
|
||||
The lower the value the earlier this endpoint will start and the later it will stop.
|
||||
The lower the value, the earlier this endpoint starts and the later it stops.
|
||||
The default is `Integer.MIN_VALUE`.
|
||||
Values can be negative.
|
||||
See `SmartLifeCycle`.
|
||||
See https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/SmartLifecycle.html[`SmartLifeCycle`].
|
||||
====
|
||||
|
||||
*<int-websocket:inbound-channel-adapter>*
|
||||
==== `<int-websocket:inbound-channel-adapter>` Attributes
|
||||
|
||||
The following listing shows the attributes available for the `<int-websocket:outbound-channel-adapter>` element:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-websocket:inbound-channel-adapter
|
||||
@@ -389,56 +339,33 @@ See `SmartLifeCycle`.
|
||||
|
||||
|
||||
<1> The component bean name.
|
||||
If the `channel` attribute isn't provided, a `DirectChannel` is created and registered with the application context with this `id` attribute as the bean name.
|
||||
In this case, the endpoint is registered with the bean name `id + '.adapter'`.
|
||||
|
||||
|
||||
If you do not set the `channel` attribute, a `DirectChannel` is created and registered in the application context with this `id` attribute as the bean name.
|
||||
In this case, the endpoint is registered with the bean name `id` plus `.adapter`.
|
||||
<2> Identifies the channel attached to this adapter.
|
||||
|
||||
|
||||
<3> The `MessageChannel` bean reference to which the `ErrorMessages` should be sent.
|
||||
|
||||
|
||||
<4> See the same option on the `<int-websocket:outbound-channel-adapter>`.
|
||||
|
||||
|
||||
<5> See the same option on the `<int-websocket:outbound-channel-adapter>`.
|
||||
|
||||
|
||||
<6> See the same option on the `<int-websocket:outbound-channel-adapter>`.
|
||||
|
||||
|
||||
<7> See the same option on the `<int-websocket:outbound-channel-adapter>`.
|
||||
|
||||
|
||||
<8> See the same option on the `<int-websocket:outbound-channel-adapter>`.
|
||||
|
||||
|
||||
<9> Maximum amount of time in milliseconds to wait when sending a message to the channel if the channel may block.
|
||||
<3> The `MessageChannel` bean reference to which the `ErrorMessage` instances should be sent.
|
||||
<4> See the same option on the <<websocket-outbound-channel-adapter-attributes,`<int-websocket:outbound-channel-adapter>`>>.
|
||||
<5> See the same option on the <<websocket-outbound-channel-adapter-attributes,`<int-websocket:outbound-channel-adapter>`>>.
|
||||
<6> See the same option on the <<websocket-outbound-channel-adapter-attributes,`<int-websocket:outbound-channel-adapter>`>>.
|
||||
<7> See the same option on the <<websocket-outbound-channel-adapter-attributes,`<int-websocket:outbound-channel-adapter>`>>.
|
||||
<8> See the same option on the <<websocket-outbound-channel-adapter-attributes,`<int-websocket:outbound-channel-adapter>`>>.
|
||||
<9> Maximum amount of time (in milliseconds) to wait when sending a message to the channel if the channel can block.
|
||||
For example, a `QueueChannel` can block until space is available if its maximum capacity has been reached.
|
||||
|
||||
|
||||
<10> Fully qualified name of the java type for the target `payload` to convert from the incoming `WebSocketMessage`.
|
||||
Default to `String`.
|
||||
|
||||
|
||||
<11> Flag to indicate if this adapter will send `non-MESSAGE` `WebSocketMessage` s and messages with broker destinations to the `AbstractBrokerMessageHandler` from the application context.
|
||||
The `Broker Relay` configuration is required when this attribute is `true`.
|
||||
<10> Fully qualified name of the Java type for the target `payload` to convert from the incoming `WebSocketMessage`.
|
||||
Defaults to `java.lang.String`.
|
||||
<11> Indicates whether this adapter sends `non-MESSAGE` `WebSocketMessage` instances and messages with broker destinations to the `AbstractBrokerMessageHandler` from the application context.
|
||||
When this attribute is `true`, the `Broker Relay` configuration is required.
|
||||
This attribute is used only on the server side.
|
||||
On the client side, it is ignored.
|
||||
Defaults to `false`.
|
||||
|
||||
|
||||
<12> See the same option on the `<int-websocket:outbound-channel-adapter>`.
|
||||
|
||||
|
||||
<13> See the same option on the `<int-websocket:outbound-channel-adapter>`.
|
||||
<12> See the same option on the <<websocket-outbound-channel-adapter-attributes,`<int-websocket:outbound-channel-adapter>`>>.
|
||||
<13> See the same option on the <<websocket-outbound-channel-adapter-attributes,`<int-websocket:outbound-channel-adapter>`>>.
|
||||
====
|
||||
|
||||
[[client-stomp-encoder]]
|
||||
=== ClientStompEncoder
|
||||
=== Using `ClientStompEncoder`
|
||||
|
||||
Starting with _version 4.3.13_, the `ClientStompEncoder` is provided as an extension of standard `StompEncoder` for using on client side of the WebSocket Channel Adapters.
|
||||
An instance of the `ClientStompEncoder` must be injected into the `StompSubProtocolHandler` for proper client side message preparation.
|
||||
One of the problem of the default `StompSubProtocolHandler` that it was designed for the server side, so it updates the `SEND` `stompCommand` header into `MESSAGE` as it must be by the STOMP protocol from server side.
|
||||
If client doesn't send its messages in the proper `SEND` web socket frame, some STOMP brokers won't accept them.
|
||||
The purpose of the `ClientStompEncoder`, in this case, is to override `stompCommand` header to the `SEND` value before encoding the message to the `byte[]`.
|
||||
Starting with version 4.3.13, Spring Integration provides `ClientStompEncoder` (as an extension of the standard `StompEncoder`) for use on the client side of WebSocket channel adapters.
|
||||
For proper client side message preparation, you must inject an instance of the `ClientStompEncoder` into the `StompSubProtocolHandler`.
|
||||
One problem with the default `StompSubProtocolHandler` is that it was designed for the server side, so it updates the `SEND` `stompCommand` header into `MESSAGE` (as required by the STOMP protocol for the server side).
|
||||
If the client does not send its messages in the proper `SEND` web socket frame, some STOMP brokers do not accept them.
|
||||
The purpose of the `ClientStompEncoder`, in this case, is to override the `stompCommand` header and set it to the `SEND` value before encoding the message to the `byte[]`.
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
[[webflux]]
|
||||
== WebFlux Support
|
||||
|
||||
[[webflux-intro]]
|
||||
=== Introduction
|
||||
|
||||
The WebFlux Spring Integration module (`spring-integration-webflux`) allows for the execution of HTTP requests and the processing of inbound HTTP requests in Reactive manner.
|
||||
The WebFlux support consists of the following gateway implementations: `WebFluxInboundEndpoint`, `WebFluxRequestExecutingMessageHandler`.
|
||||
The implementation is fully based on the Spring https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#spring-webflux[WebFlux] and https://projectreactor.io/[Project Reactor] foundations.
|
||||
Also see <<http>> for more information since many options are shared between reactive and regular HTTP components.
|
||||
The WebFlux Spring Integration module (`spring-integration-webflux`) allows for the execution of HTTP requests and the processing of inbound HTTP requests in a reactive manner.
|
||||
The WebFlux support consists of the following gateway implementations: `WebFluxInboundEndpoint` and `WebFluxRequestExecutingMessageHandler`.
|
||||
The support is fully based on the Spring https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#spring-webflux[WebFlux] and https://projectreactor.io/[Project Reactor] foundations.
|
||||
See "`<<http>>`" for more information, since many options are shared between reactive and regular HTTP components.
|
||||
|
||||
[[webflux-inbound]]
|
||||
=== WebFlux Inbound Components
|
||||
|
||||
Starting with _version 5.0_, the `WebFluxInboundEndpoint`, `WebHandler`, implementation is provided.
|
||||
This component is similar to the MVC-based `HttpRequestHandlingEndpointSupport` with which it shares some common options via the newly extracted `BaseHttpInboundEndpoint`.
|
||||
Instead of MVC, it is used in the Spring WebFlux Reactive environment.
|
||||
A simple sample for explanation:
|
||||
Starting with version 5.0, the `WebFluxInboundEndpoint` implementation of `WebHandler` is provided.
|
||||
This component is similar to the MVC-based `HttpRequestHandlingEndpointSupport`, with which it shares some common options through the newly extracted `BaseHttpInboundEndpoint`.
|
||||
It is used in the Spring WebFlux reactive environment (instead of MVC).
|
||||
The following example shows a simple implementation of a WebFlux endpoint:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
@@ -41,16 +39,18 @@ public class ReactiveHttpConfiguration {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
As can be seen, the configuration is similar to the `HttpRequestHandlingEndpointSupport` mentioned above, except that we use `@EnableWebFlux` to add the WebFlux infrastructure to our integration application.
|
||||
Also, the `WebFluxInboundEndpoint` performs `sendAndReceive` operation to the downstream flow using back-pressure, on demand based capabilities, provided by the reactive HTTP server implementation.
|
||||
The configuration is similar to the `HttpRequestHandlingEndpointSupport` (mentioned prior to the example), except that we use `@EnableWebFlux` to add the WebFlux infrastructure to our integration application.
|
||||
Also, the `WebFluxInboundEndpoint` performs `sendAndReceive` operations to the downstream flow by using back-pressure, on-demand based capabilities, provided by the reactive HTTP server implementation.
|
||||
|
||||
NOTE: The reply part is non-blocking as well and based on the internal `FutureReplyChannel` which is flat-mapped to a reply `Mono` for on demand resolution.
|
||||
NOTE: The reply part is non-blocking as well and is based on the internal `FutureReplyChannel`, which is flat-mapped to a reply `Mono` for on-demand resolution.
|
||||
|
||||
The `WebFluxInboundEndpoint` can be configured with a custom `ServerCodecConfigurer`, `RequestedContentTypeResolver` and even a `ReactiveAdapterRegistry`.
|
||||
The latter provides a mechanism where we can return a reply as any reactive type - Reactor `Flux`, RxJava `Observable`, `Flowable` etc.
|
||||
This way, we can simply implement https://en.wikipedia.org/wiki/Server-sent_events[Server Sent Events] scenarios with Spring Integration components:
|
||||
You can configure the `WebFluxInboundEndpoint` with a custom `ServerCodecConfigurer`, a `RequestedContentTypeResolver`, and even a `ReactiveAdapterRegistry`.
|
||||
The latter provides a mechanism you can use to return a reply as any reactive type: Reactor `Flux`, RxJava `Observable`, `Flowable`, and others.
|
||||
This way, we can implement https://en.wikipedia.org/wiki/Server-sent_events[Server Sent Events] scenarios with Spring Integration components, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
@@ -62,17 +62,20 @@ public IntegrationFlow sseFlow() {
|
||||
.get();
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Also see <<http-request-mapping>> and <<http-cors>> for more possible configuration options.
|
||||
See "`<<http-request-mapping>>`" and "`<<http-cors>>`" for more possible configuration options.
|
||||
|
||||
When the request body is empty, or `payloadExpression` returns `null`, the request params `MultiValueMap<String, String>` is used for a `payload` of the target message to process.
|
||||
When the request body is empty or `payloadExpression` returns `null`, the request params (`MultiValueMap<String, String>`) is used for a `payload` of the target message to process.
|
||||
|
||||
[[webflux-outbound]]
|
||||
=== WebFlux Outbound Components
|
||||
|
||||
The `WebFluxRequestExecutingMessageHandler` (starting with _version 5.0_) implementation is very similar to `HttpRequestExecutingMessageHandler`, using a `WebClient` from the Spring Framework WebFlux module.
|
||||
To configure it, define a bean like this:
|
||||
The `WebFluxRequestExecutingMessageHandler` (starting with version 5.0) implementation is similar to `HttpRequestExecutingMessageHandler`.
|
||||
It uses a `WebClient` from the Spring Framework WebFlux module.
|
||||
To configure it, define a bean similar to the following:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="httpReactiveOutbound"
|
||||
@@ -81,9 +84,11 @@ To configure it, define a bean like this:
|
||||
<property name="outputChannel" ref="responseChannel" />
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
You can configure a `WebClient` instance to use:
|
||||
You can configure a `WebClient` instance to use, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<beans:bean id="webClient" class="org.springframework.web.reactive.function.client.WebClient"
|
||||
@@ -96,29 +101,29 @@ You can configure a `WebClient` instance to use:
|
||||
<property name="outputChannel" ref="responseChannel" />
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The `WebClient` `exchange()` operation returns a `Mono<ClientResponse>` which is mapped (using several `Mono.map()` steps) to an `AbstractIntegrationMessageBuilder` as the output from the `WebFluxRequestExecutingMessageHandler`.
|
||||
The `WebClient` `exchange()` operation returns a `Mono<ClientResponse>`, which is mapped (by using several `Mono.map()` steps) to an `AbstractIntegrationMessageBuilder` as the output from the `WebFluxRequestExecutingMessageHandler`.
|
||||
Together with the `ReactiveChannel` as an `outputChannel`, the `Mono<ClientResponse>` evaluation is deferred until a downstream subscription is made.
|
||||
Otherwise, it is treated as an `async` mode and the `Mono` response is adapted to an `SettableListenableFuture` for an asynchronous reply from the `WebFluxRequestExecutingMessageHandler`.
|
||||
Otherwise, it is treated as an `async` mode, and the `Mono` response is adapted to a `SettableListenableFuture` for an asynchronous reply from the `WebFluxRequestExecutingMessageHandler`.
|
||||
The target payload of the output message depends on the `WebFluxRequestExecutingMessageHandler` configuration.
|
||||
The `setExpectedResponseType(Class<?>)` or `setExpectedResponseTypeExpression(Expression)` identifies the target type of the response body element conversion.
|
||||
If the `replyPayloadToFlux` is set to `true`, the response body is converted to a `Flux` with the provided `expectedResponseType` for each element and this `Flux` is sent as the payload downstream.
|
||||
A <<splitter,splitter>> afterwards can be used to iterate over this `Flux` in a reactive manner.
|
||||
If `replyPayloadToFlux` is set to `true`, the response body is converted to a `Flux` with the provided `expectedResponseType` for each element, and this `Flux` is sent as the payload downstream.
|
||||
Afterwards, you can use a <<splitter,splitter>> to iterate over this `Flux` in a reactive manner.
|
||||
|
||||
In addition a `BodyExtractor<?, ClientHttpResponse>` can be injected into the `WebFluxRequestExecutingMessageHandler` instead of `expectedResponseType` and `replyPayloadToFlux` properties.
|
||||
In addition a `BodyExtractor<?, ClientHttpResponse>` can be injected into the `WebFluxRequestExecutingMessageHandler` instead of the `expectedResponseType` and `replyPayloadToFlux` properties.
|
||||
It can be used for low-level access to the `ClientHttpResponse` and more control over body and HTTP headers conversion.
|
||||
The `ClientHttpResponseBodyExtractor` is provided out-of-the-box as identity function to produce downstream the whole `ClientHttpResponse` and any other possible custom logic.
|
||||
Spring Integration provides `ClientHttpResponseBodyExtractor` as a identity function to produce (downstream) the whole `ClientHttpResponse` and any other possible custom logic.
|
||||
|
||||
Also see <<http-outbound>> for more possible configuration options.
|
||||
See "`<<http-outbound>>`" for more possible configuration options.
|
||||
|
||||
[[webflux-namespace]]
|
||||
=== WebFlux Namespace Support
|
||||
|
||||
==== Introduction
|
||||
|
||||
Spring Integration provides a _webflux_ namespace and the corresponding schema definition.
|
||||
To include it in your configuration, simply provide the following namespace declaration in your application context configuration file:
|
||||
Spring Integration provides a `webflux` namespace and the corresponding schema definition.
|
||||
To include it in your configuration, include the following namespace declaration in your application context configuration file:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
@@ -136,11 +141,14 @@ To include it in your configuration, simply provide the following namespace decl
|
||||
...
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
==== Inbound
|
||||
|
||||
To configure Spring Integration WebFlux via XML you may use appropriate components from the mentioned `int-webflux` namespace - `inbound-channel-adapter` or `inbound-gateway` according request/response requirements respectively:
|
||||
To configure Spring Integration WebFlux with XML, you caus use appropriate components from the `int-webflux` namespace: `inbound-channel-adapter` or `inbound-gateway`, corresponding to request and response requirements, respectively.
|
||||
The following example shows how to configure both an inbound channel adapter and an inbound gateway:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<inbound-channel-adapter id="reactiveFullConfig" channel="requests"
|
||||
@@ -181,11 +189,14 @@ To configure Spring Integration WebFlux via XML you may use appropriate componen
|
||||
<header name="foo" expression="'foo'"/>
|
||||
</inbound-gateway>
|
||||
----
|
||||
====
|
||||
|
||||
==== Outbound
|
||||
|
||||
If you want to execute the http request in a reactive, non-blocking way, you can use the `outbound-gateway` or `outbound-channel-adapter`.
|
||||
If you want to execute the HTTP request in a reactive, non-blocking way, you can use the `outbound-gateway` or `outbound-channel-adapter`.
|
||||
The following example shows how to configure both an outbound gateway and an outbound channel adapter:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-webflux:outbound-gateway id="reactiveExample1"
|
||||
@@ -209,12 +220,14 @@ If you want to execute the http request in a reactive, non-blocking way, you can
|
||||
auto-startup="false"/>
|
||||
|
||||
----
|
||||
|
||||
====
|
||||
|
||||
[[webflux-java-config]]
|
||||
=== Configuring WebFlux Endpoints with Java
|
||||
|
||||
.Inbound Gateway Using Java Configuration
|
||||
The following example shows how to configure a WebFlux inbound endpoint with Java:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
@@ -237,8 +250,11 @@ Flux<Person> getPersons() {
|
||||
return Flux.just(new Person("Jane"), new Person("Jason"), new Person("John"));
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
.Inbound Gateway Using the Java DSL
|
||||
The following example shows how to configure a WebFlux inbound gateway with the Java DSL:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
@@ -252,8 +268,11 @@ public IntegrationFlow inboundChannelAdapterFlow() {
|
||||
.get();
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
.Outbound Gateway Using Java Configuration
|
||||
The following example shows how to configure a WebFlux outbound gateway with Java:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@ServiceActivator(inputChannel = "reactiveHttpOutRequest")
|
||||
@@ -266,8 +285,11 @@ public WebFluxRequestExecutingMessageHandler reactiveOutbound(WebClient client)
|
||||
return handler;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
.Outbound Gateway Using the Java DSL
|
||||
The following example shows how to configure a WebFlux outbound gateway with the Java DSL:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
@@ -282,10 +304,10 @@ public IntegrationFlow outboundReactive() {
|
||||
.expectedResponseType(String.class));
|
||||
}
|
||||
----
|
||||
|
||||
====
|
||||
|
||||
[[webflux-header-mapping]]
|
||||
=== WebFlux Header Mappings
|
||||
|
||||
Since WebFlux components are fully based on the HTTP protocol there is no difference in the HTTP headers mapping.
|
||||
See <<http-header-mapping>> for more possible options and components to use for mapping headers.
|
||||
Since WebFlux components are fully based on the HTTP protocol, there is no difference in the HTTP headers mapping.
|
||||
See "`<<http-header-mapping>>`" for more possible options and components to use for mapping headers.
|
||||
|
||||
@@ -1,91 +1,107 @@
|
||||
[[whats-new]]
|
||||
|
||||
== What's new in Spring Integration 5.1?
|
||||
== What's New in Spring Integration 5.1?
|
||||
|
||||
This chapter provides an overview of the new features and improvements that have been introduced with Spring
|
||||
Integration `5.1`.
|
||||
If you are interested in more details, please see the Issue Tracker tickets that were resolved as part of the 5.1 development process.
|
||||
If you are interested in more details, see the Issue Tracker tickets that were resolved as part of the 5.1 development process.
|
||||
|
||||
[[x5.1-new-components]]
|
||||
=== New Components
|
||||
|
||||
==== AmqpDedicatedChannelAdvice
|
||||
The following components are new in 5.1:
|
||||
|
||||
* <<AmqpDedicatedChannelAdvice>>
|
||||
|
||||
<<AmqpDedicatedChannelAdvice>>
|
||||
==== `AmqpDedicatedChannelAdvice`
|
||||
|
||||
See <<amqp-strict-ordering>>.
|
||||
|
||||
[[x5.1-general]]
|
||||
=== General Changes
|
||||
|
||||
The following changes have been made in version 5.1:
|
||||
|
||||
* <<java-dsl>>
|
||||
* <<dispatcher-exceptions>>
|
||||
* <<global-channel-interceptors>>
|
||||
* <<objecttojsontransformer>>
|
||||
* <<integration-flows-generated-bean-names>>
|
||||
|
||||
[[java-dsl]]
|
||||
==== Java DSL
|
||||
|
||||
The `IntegrationFlowContext` is now an interface and `IntegrationFlowRegistration` is an inner interface of the `IntegrationFlowContext`.
|
||||
The `IntegrationFlowContext` is now an interface and `IntegrationFlowRegistration` is an inner interface of `IntegrationFlowContext`.
|
||||
|
||||
[[dispatcher-exceptions]]
|
||||
==== Dispatcher Exceptions
|
||||
|
||||
Exceptions caught and re-thrown by `AbstractDispatcher` are now more consistent:
|
||||
|
||||
- A `MessagingException` of any kind, with a `failedMessage` property, is re-thrown unchanged
|
||||
- All other exceptions are wrapped in a `MessageDeliveryException` with the `failedMessage` property set
|
||||
* 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, with a `failedMessage` property, was re-thrown unchanged
|
||||
- A `MessagingException`, with no `failedMessage` property, was wrapped in a `MessagingException` with the `failedMessage` property set
|
||||
- Other `RuntimeException` s were re-thrown unchanged
|
||||
- Checked exceptions were wrapped in a `MessageDeliveryException` with the `failedMessage` property set
|
||||
* 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.
|
||||
|
||||
[[global-channel-interceptors]]
|
||||
==== Global Channel Interceptors
|
||||
|
||||
Global channel interceptors are now applied to channels registered dynamically - such as via the `IntegrationFlowContext` when using the Java DSL, or beans that are initialized using `beanFactory.initializeBean()`.
|
||||
Previously, interceptors were not applied when beans were created after the application context was refreshed.
|
||||
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.
|
||||
|
||||
==== ObjectToJsonTransformer
|
||||
[[objecttojsontransformer]]
|
||||
==== `ObjectToJsonTransformer`
|
||||
|
||||
A new `ResultType.BYTES` mode is introduced for the `ObjectToJsonTransformer`.
|
||||
|
||||
See <<json-transformers>> for more information.
|
||||
See "`<<json-transformers>>`" for more information.
|
||||
|
||||
==== Integration Flows: Generated bean names
|
||||
[[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.
|
||||
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 <<java-dsl-flows>> for more information.
|
||||
See "`<<java-dsl-flows>>`" for more information.
|
||||
|
||||
==== Aggregator Changes
|
||||
|
||||
An aggregator now expires the group immediately, if the `groupTimeout` is evaluated to a negative value.
|
||||
Only `null` is considered as a signal do nothing for the current message.
|
||||
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.
|
||||
|
||||
See <<aggregator>> for more information.
|
||||
See "`<<aggregator>>`" for more information.
|
||||
|
||||
==== @Publisher annotation changes
|
||||
|
||||
Starting with _version 5.1, the `@Publisher` AOP functionality has to be turned on explicitly via `@EnablePublisher` or via `<int:enable-publisher>` sub-element on the `<int:annotation-config>`.
|
||||
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>`.
|
||||
|
||||
See <<publisher-annotation>> for more information.
|
||||
See "`<<publisher-annotation>>`" for more information.
|
||||
|
||||
=== AMQP Changes
|
||||
|
||||
`ID` and `Timestamp` header mapping changes in the `DefaultAmqpHeaderMapper`.
|
||||
See the note near the bottom of <<amqp-message-headers>> for more information.
|
||||
We have made `ID` and `Timestamp` header mapping changes in the `DefaultAmqpHeaderMapper`.
|
||||
See the note near the bottom of "`<<amqp-message-headers>>`" for more information.
|
||||
|
||||
The `contentType` header is no longer incorrectly mapped as an entry in the general headers map.
|
||||
See <<amqp-content-type>> for more information.
|
||||
The `contentType` header is now correctly mapped as an entry in the general headers map.
|
||||
See "`<<amqp-content-type>>`" for more information.
|
||||
|
||||
=== JDBC Changes
|
||||
|
||||
A confusing `max-rows-per-poll` property on the JDBC Inbound Channel Adapter and JDBC Outbound Gateway has been deprecated in favor newly introduced `max-rows` property.
|
||||
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.
|
||||
|
||||
See <<jdbc>> for more information.
|
||||
See "`<<jdbc>>`" for more information.
|
||||
|
||||
=== (S)FTP Changes
|
||||
=== FTP and SFTP Changes
|
||||
|
||||
A `RotatingServerAdvice` is now available to poll multiple servers and/or directories with the inbound channel adapters.
|
||||
|
||||
See <<ftp-rotating-server-advice>> and <<sftp-rotating-server-advice>> for more information.
|
||||
|
||||
Also inbound adapter `localFilenameExpression` s can contain the variable `#remoteDirectory` which contains the remote directory being polled.
|
||||
A `RotatingServerAdvice` is now available to poll multiple servers and directories with the inbound channel adapters.
|
||||
See "`<<ftp-rotating-server-advice>>`" and "`<<sftp-rotating-server-advice>>`" 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 simply `Comparator<F>`.
|
||||
See <<ftp-streaming>> and <<sftp-streaming>> for more information.
|
||||
|
||||
|
||||
@@ -1,38 +1,52 @@
|
||||
[[ws]]
|
||||
== Web Services Support
|
||||
|
||||
This chapter describes Spring Integration's support for web services, including:
|
||||
|
||||
* <<webservices-outbound>>
|
||||
* <<webservices-inbound>>
|
||||
* <<webservices-namespace>>
|
||||
* <<outbound-uri>>
|
||||
* <<ws-message-headers>>
|
||||
* <<mtom-support>>
|
||||
|
||||
[[webservices-outbound]]
|
||||
=== Outbound Web Service Gateways
|
||||
|
||||
To invoke a Web Service upon sending a message to a channel, there are two options - both of which build upon the http://projects.spring.io/spring-ws/[Spring Web Services] project: `SimpleWebServiceOutboundGateway` and `MarshallingWebServiceOutboundGateway`.
|
||||
The former will accept either a `String` or `javax.xml.transform.Source` as the message payload.
|
||||
The latter provides support for any implementation of the `Marshaller` and `Unmarshaller` interfaces.
|
||||
Both require a Spring Web Services `DestinationProvider` for determining the URI of the Web Service to be called.
|
||||
To invoke a web service when you send a message to a channel, you have two options, both of which build upon the http://projects.spring.io/spring-ws/[Spring Web Services] project: `SimpleWebServiceOutboundGateway` and `MarshallingWebServiceOutboundGateway`.
|
||||
The former accepts either a `String` or `javax.xml.transform.Source` as the message payload.
|
||||
The latter supports any implementation of the `Marshaller` and `Unmarshaller` interfaces.
|
||||
Both require a Spring Web Services `DestinationProvider`, to determine the URI of the web service to be called.
|
||||
The following example shows both options for invoking a web service:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
simpleGateway = new SimpleWebServiceOutboundGateway(destinationProvider);
|
||||
|
||||
marshallingGateway = new MarshallingWebServiceOutboundGateway(destinationProvider, marshaller);
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: When using the namespace support described below, you will only need to set a URI.
|
||||
Internally, the parser will configure a fixed URI `DestinationProvider` implementation.
|
||||
If you do need dynamic resolution of the URI at runtime, however, then the `DestinationProvider` can provide such behavior as looking up the URI from a registry.
|
||||
See the Spring Web Services http://docs.spring.io/spring-ws/docs/current/api/org/springframework/ws/client/support/destination/DestinationProvider.html[DestinationProvider] JavaDoc for more information about this strategy.
|
||||
NOTE: When using the namespace support (<<webservices-namespace,described later>>), you need only set a URI.
|
||||
Internally, the parser configures a fixed URI `DestinationProvider` implementation.
|
||||
If you need dynamic resolution of the URI at runtime, however, then the `DestinationProvider` can provide such behavior as looking up the URI from a registry.
|
||||
See the Spring Web Services http://docs.spring.io/spring-ws/docs/current/api/org/springframework/ws/client/support/destination/DestinationProvider.html[`DestinationProvider`] Javadoc for more information about this strategy.
|
||||
|
||||
Starting with _version 5.0_ the `SimpleWebServiceOutboundGateway` and `MarshallingWebServiceOutboundGateway` can be supplied with an external `WebServiceTemplate` instance, which may be configured for any custom properties, including `checkConnectionForFault` allowing your application to deal with non-conforming services.
|
||||
Starting with version 5.0, you can supply the `SimpleWebServiceOutboundGateway` and `MarshallingWebServiceOutboundGateway` with an external `WebServiceTemplate` instance, which you can configure for any custom properties, including `checkConnectionForFault` (which allows your application to deal with non-conforming services).
|
||||
|
||||
For more detail on the inner workings, see the Spring Web Services reference guide's chapter covering http://docs.spring.io/spring-ws/docs/current/reference/html/client.html[client access] as well as the chapter covering http://docs.spring.io/spring/docs/current/spring-framework-reference/html/oxm.html[Object/XML mapping].
|
||||
For more detail on the inner workings, see the Spring Web Services reference guide's chapter covering http://docs.spring.io/spring-ws/docs/current/reference/html/client.html[client access] and the chapter covering http://docs.spring.io/spring/docs/current/spring-framework-reference/html/oxm.html[Object/XML mapping].
|
||||
|
||||
[[webservices-inbound]]
|
||||
=== Inbound Web Service Gateways
|
||||
|
||||
To send a message to a channel upon receiving a Web Service invocation, there are two options again: `SimpleWebServiceInboundGateway` and `MarshallingWebServiceInboundGateway`.
|
||||
The former will extract a `javax.xml.transform.Source` from the `WebServiceMessage` and set it as the message payload.
|
||||
The latter provides support for implementation of the `Marshaller` and `Unmarshaller` interfaces.
|
||||
If the incoming web service message is a SOAP message the SOAP Action header will be added to the headers of the`Message` that is forwarded onto the request channel.
|
||||
To send a message to a channel upon receiving a web service invocation, you again have two options: `SimpleWebServiceInboundGateway` and `MarshallingWebServiceInboundGateway`.
|
||||
The former extracts a `javax.xml.transform.Source` from the `WebServiceMessage` and sets it as the message payload.
|
||||
The latter supports implementation of the `Marshaller` and `Unmarshaller` interfaces.
|
||||
If the incoming web service message is a SOAP message, the SOAP action header is added to the headers of the `Message` that is forwarded onto the request channel.
|
||||
The following example shows both options:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
simpleGateway = new SimpleWebServiceInboundGateway();
|
||||
@@ -41,18 +55,18 @@ If the incoming web service message is a SOAP message the SOAP Action header wil
|
||||
|
||||
marshallingGateway = new MarshallingWebServiceInboundGateway(marshaller);
|
||||
//set request and optionally reply channel
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
Both gateways implement the Spring Web Services `MessageEndpoint` interface, so they can be configured with a `MessageDispatcherServlet` as per standard Spring Web Services configuration.
|
||||
|
||||
For more detail on how to use these components, see the Spring Web Services reference guide's chapter covering http://docs.spring.io/spring-ws/docs/current/reference/html/server.html[creating a Web Service].
|
||||
For more detail on how to use these components, see the Spring Web Services reference guide's chapter covering http://docs.spring.io/spring-ws/docs/current/reference/html/server.html[creating a web service].
|
||||
The chapter covering http://docs.spring.io/spring/docs/current/spring-framework-reference/html/oxm.html[Object/XML mapping] is also applicable again.
|
||||
|
||||
To include the `SimpleWebServiceInboundGateway` and `MarshallingWebServiceInboundGateway` configurations to the Spring WS
|
||||
infrastructure you should add the `EndpointMapping` definition between `MessageDispatcherServlet` and the target
|
||||
`MessageEndpoint` implementations like you do that with normal Spring WS application.
|
||||
For this purpose (from Spring Integration perspective), the Spring WS provides these convenient `EndpointMapping`
|
||||
To add the `SimpleWebServiceInboundGateway` and `MarshallingWebServiceInboundGateway` configurations to the Spring WS
|
||||
infrastructure, you should add the `EndpointMapping` definition between `MessageDispatcherServlet` and the target
|
||||
`MessageEndpoint` implementations, as you would for a normal Spring WS application.
|
||||
For this purpose (from the Spring Integration perspective), Spring WS provides the following convenient `EndpointMapping`
|
||||
implementations:
|
||||
|
||||
* `o.s.ws.server.endpoint.mapping.UriEndpointMapping`
|
||||
@@ -60,42 +74,50 @@ implementations:
|
||||
* `o.s.ws.soap.server.endpoint.mapping.SoapActionEndpointMapping`
|
||||
* `o.s.ws.server.endpoint.mapping.XPathPayloadEndpointMapping`
|
||||
|
||||
The beans for these classes must be specified in the application context referencing to the
|
||||
You must specify the beans for these classes in the application context and reference the
|
||||
`SimpleWebServiceInboundGateway` and/or `MarshallingWebServiceInboundGateway` bean definitions according to the WS
|
||||
mapping algorithm.
|
||||
|
||||
Please, refer to the http://docs.spring.io/spring-ws/docs/current/reference/html/server.html#server-endpoint-mapping[Endpoint mappings]
|
||||
for the more information.
|
||||
See the http://docs.spring.io/spring-ws/docs/current/reference/html/server.html#server-endpoint-mapping[endpoint mappings] for more information.
|
||||
|
||||
[[webservices-namespace]]
|
||||
=== Web Service Namespace Support
|
||||
|
||||
To configure an outbound Web Service Gateway, use the "outbound-gateway" element from the "ws" namespace:
|
||||
To configure an outbound web service gateway, use the `outbound-gateway` element from the `ws` namespace, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-ws:outbound-gateway id="simpleGateway"
|
||||
request-channel="inputChannel"
|
||||
uri="http://example.org"/>
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: Notice that this example does not provide a 'reply-channel'.
|
||||
If the Web Service were to return a non-empty response, the Message containing that response would be sent to the reply
|
||||
channel provided in the request Message's `REPLY_CHANNEL` header, and if that were not available a channel resolution Exception would be thrown.
|
||||
If you want to send the reply to another channel instead, then provide a 'reply-channel' attribute on the 'outbound-gateway' element.
|
||||
NOTE: This example does not provide a 'reply-channel'.
|
||||
If the web service returns a non-empty response, the `Message` containing that response is sent to the reply channel defined in the request message's `REPLY_CHANNEL` header.
|
||||
If that is not available, a channel resolution exception is thrown.
|
||||
If you want to send the reply to another channel instead, provide a 'reply-channel' attribute on the 'outbound-gateway' element.
|
||||
|
||||
TIP: When invoking a Web Service that returns an empty response after using a String payload for the request Message, _no reply Message will be sent by default_.
|
||||
Therefore you don't need to set a 'reply-channel' or have a REPLY_CHANNEL header in the request Message.
|
||||
If for any reason you actually _do_ want to receive the empty response as a Message, then provide the 'ignore-empty-responses' attribute with a value of _false_ (this only applies for Strings, because using a Source or Document object simply leads to a NULL response and will therefore _never_ generate a reply Message).
|
||||
TIP: By default, when you invoke a web service that returns an empty response after using a String payload for the request `Message`, no reply `Message` is sent.
|
||||
Therefore, you need not set a 'reply-channel' or have a `REPLY_CHANNEL` header in the request `Message`.
|
||||
If you actually do want to receive the empty response as a `Message`, you can set the 'ignore-empty-responses' attribute to `false`.
|
||||
Doing so works only for `String` objects, because using a `Source` or a `Document` object leads to a null response and consequently never generates a reply `Message`.
|
||||
|
||||
To set up an inbound Web Service Gateway, use the "inbound-gateway":
|
||||
To set up an inbound Web Service Gateway, use the `inbound-gateway` element, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-ws:inbound-gateway id="simpleGateway"
|
||||
request-channel="inputChannel"/>
|
||||
----
|
||||
====
|
||||
|
||||
To use Spring OXM Marshallers and/or Unmarshallers, provide bean references.
|
||||
For outbound:
|
||||
To use Spring OXM marshallers or unmarshallers, you must provide bean references.
|
||||
The following example shows how to provide a bean reference for an outbound marshalling gateway:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-ws:outbound-gateway id="marshallingGateway"
|
||||
@@ -104,8 +126,11 @@ For outbound:
|
||||
marshaller="someMarshaller"
|
||||
unmarshaller="someUnmarshaller"/>
|
||||
----
|
||||
====
|
||||
|
||||
And for inbound:
|
||||
The following example shows how to provide a bean reference for an inbound marshalling gateway:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-ws:inbound-gateway id="marshallingGateway"
|
||||
@@ -113,33 +138,34 @@ And for inbound:
|
||||
marshaller="someMarshaller"
|
||||
unmarshaller="someUnmarshaller"/>
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: Most `Marshaller` implementations also implement the `Unmarshaller` interface.
|
||||
When using such a `Marshaller`, only the "marshaller" attribute is necessary.
|
||||
Even when using a `Marshaller`, you may also provide a reference for the "request-callback" on the outbound gateways.
|
||||
When using such a `Marshaller`, only the `marshaller` attribute is necessary.
|
||||
Even when using a `Marshaller`, you may also provide a reference for the `request-callback` on the outbound gateways.
|
||||
|
||||
For either outbound gateway type, a "destination-provider" attribute can be specified instead of the "uri" (exactly one of them is required).
|
||||
You can then reference any Spring Web Services DestinationProvider implementation (e.g.
|
||||
to lookup the URI at runtime from a registry).
|
||||
For either outbound gateway type, you can specify a `destination-provider` attribute instead of the `uri` (exactly one of them is required).
|
||||
You can then reference any Spring Web Services `DestinationProvider` implementation (for example, to lookup the URI from a registry at runtime).
|
||||
|
||||
For either outbound gateway type, the "message-factory" attribute can also be configured with a reference to any Spring Web Services `WebServiceMessageFactory` implementation.
|
||||
For either outbound gateway type, the `message-factory` attribute can also be configured with a reference to any Spring Web Services `WebServiceMessageFactory` implementation.
|
||||
|
||||
For the simple inbound gateway type, the "extract-payload" attribute can be set to false to forward the entire `WebServiceMessage` instead of just its payload as a `Message` to the request channel.
|
||||
This might be useful, for example, when a custom Transformer works against the `WebServiceMessage` directly.
|
||||
For the simple inbound gateway type, you can set the `extract-payload` attribute to `false` to forward the entire `WebServiceMessage` instead of just its payload as a `Message` to the request channel.
|
||||
Doing so might be useful, for example, when a custom transformer works against the `WebServiceMessage` directly.
|
||||
|
||||
Starting with _version 5.0_ the `web-service-template` reference attribute is presented for the injection of a `WebServiceTemplate` with any possible custom properties.
|
||||
Starting with version 5.0, the `web-service-template` reference attribute lets you inject a `WebServiceTemplate` with any possible custom properties.
|
||||
|
||||
[[outbound-uri]]
|
||||
=== Outbound URI Configuration
|
||||
|
||||
For all URI-schemes supported by Spring Web Services (http://docs.spring.io/spring-ws/docs/current/reference/html/client.html#client-transports[URIs and Transports]) `<uri-variable/>` substitution is provided:
|
||||
For all URI schemes supported by Spring Web Services (see http://docs.spring.io/spring-ws/docs/current/reference/html/client.html#client-transports[URIs and Transports]) `<uri-variable/>` substitution is provided.
|
||||
The following example shows how to define it:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<ws:outbound-gateway id="gateway" request-channel="input"
|
||||
uri="http://springsource.org/{foo}-{bar}">
|
||||
<ws:uri-variable name="foo" expression="payload.substring(1,7)"/>
|
||||
<ws:uri-variable name="bar" expression="headers.x"/>
|
||||
uri="http://springsource.org/{thing1}-{thing2}">
|
||||
<ws:uri-variable name="thing1" expression="payload.substring(1,7)"/>
|
||||
<ws:uri-variable name="thing2" expression="headers.x"/>
|
||||
</ws:outbound-gateway>
|
||||
|
||||
<ws:outbound-gateway request-channel="inputJms"
|
||||
@@ -151,15 +177,17 @@ For all URI-schemes supported by Spring Web Services (http://docs.spring.io/spri
|
||||
</ws:outbound-gateway>
|
||||
----
|
||||
|
||||
If a `DestinationProvider` is supplied, variable substitution is not supported and a configuration error will result if variables are provided.
|
||||
If you supply a `DestinationProvider`, variable substitution is not supported and a configuration error occurs if you provide variables.
|
||||
|
||||
_Controlling URI Encoding_
|
||||
==== Controlling URI Encoding
|
||||
|
||||
By default, the URL string is encoded (see http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/util/UriComponentsBuilder.html[UriComponentsBuilder]) to the URI object before sending the request.
|
||||
In some scenarios with a non-standard URI it is undesirable to perform the encoding.
|
||||
Since _version 4.1_ the `<ws:outbound-gateway/>` provides an `encode-uri` attribute.
|
||||
To disable encoding the URL, this attribute should be set to `false` (by default it is `true`).
|
||||
If you wish to partially encode some of the URL, this can be achieved using an `expression` within a `<uri-variable/>`:
|
||||
By default, the URL string is encoded (see http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/util/UriComponentsBuilder.html[`UriComponentsBuilder`]) to the URI object before sending the request.
|
||||
In some scenarios with a non-standard URI, it is undesirable to perform the encoding.
|
||||
Since version 4.1, the `<ws:outbound-gateway/>` element provides an `encode-uri` attribute.
|
||||
To disable encoding the URL, set this attribute `false` (it defaults to `true`).
|
||||
If you wish to partially encode some of the URL, you can do so by using an `expression` within a `<uri-variable/>`, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<ws:outbound-gateway url="http://somehost/%2f/fooApps?bar={param}" encode-uri="false">
|
||||
@@ -168,48 +196,45 @@ If you wish to partially encode some of the URL, this can be achieved using an `
|
||||
.encodeWithinQuery('Hello World!')"/>
|
||||
</ws:outbound-gateway>
|
||||
----
|
||||
====
|
||||
|
||||
Note, `encode-uri` is ignored, if `DestinationProvider` is supplied.
|
||||
NOTE: If you set `DestinationProvider`, `encode-uri` is ignored.
|
||||
|
||||
[[ws-message-headers]]
|
||||
=== WS Message Headers
|
||||
|
||||
The Spring Integration WebService Gateways will map the SOAP Action header automatically.
|
||||
It will be copied by default to and from Spring Integration `MessageHeaders` using the
|
||||
http://docs.spring.io/spring-integration/api/org/springframework/integration/ws/DefaultSoapHeaderMapper.html[DefaultSoapHeaderMapper].
|
||||
The Spring Integration web service gateways automatically map the SOAP action header.
|
||||
By default, it is copied to and from Spring Integration `MessageHeaders` by using the
|
||||
http://docs.spring.io/spring-integration/api/org/springframework/integration/ws/DefaultSoapHeaderMapper.html[`DefaultSoapHeaderMapper`].
|
||||
|
||||
Of course, you can pass in your own implementation of SOAP specific header mappers, as the gateways have respective
|
||||
properties to support that.
|
||||
You can pass in your own implementation of SOAP-specific header mappers, as the gateways have properties to support doing so.
|
||||
|
||||
Any user-defined SOAP headers will NOT
|
||||
be copied to or from a SOAP Message, unless explicitly specified by the _requestHeaderNames_ and/or
|
||||
_replyHeaderNames_ properties of the `DefaultSoapHeaderMapper`.
|
||||
Unless explicitly specified by the `requestHeaderNames` or `replyHeaderNames` properties of the `DefaultSoapHeaderMapper`, any user-defined SOAP headers are not copied to or from a SOAP Message.
|
||||
|
||||
When using the XML namespace for configuration, these properties can be set using the `mapped-request-headers` and
|
||||
`mapped-reply-headers`, or a custom mapper can be provided using the `header-mapper` attribute.
|
||||
When you use the XML namespace for configuration, you can set these properties by using the `mapped-request-headers` and
|
||||
`mapped-reply-headers` attributes, you can provide a custom mapper by setting the `header-mapper` attribute.
|
||||
|
||||
TIP: When mapping user-defined headers, the values can also contain simple wildcard patterns (e.g. "foo*" or "*foo") to be matched.
|
||||
For example, if you need to copy all user-defined headers simply use the wildcard character `*`.
|
||||
TIP: When mapping user-defined headers, the values can also contain simple wildcard patterns (such `myheader*` or `*myheader`).
|
||||
For example, if you need to copy all user-defined headers, you can use the wildcard character: `*`.
|
||||
|
||||
Starting with _version 4.1_, the `AbstractHeaderMapper` (a `DefaultSoapHeaderMapper` superclass) allows the
|
||||
`NON_STANDARD_HEADERS` token to be configured for the _requestHeaderNames_ and/or _replyHeaderNames_
|
||||
properties (in addition to existing `STANDARD_REQUEST_HEADERS` and `STANDARD_REPLY_HEADERS`) to map all
|
||||
user-defined headers.
|
||||
Note, it is recommended to use the combination like this `STANDARD_REPLY_HEADERS, NON_STANDARD_HEADERS` instead of a
|
||||
`*`, to avoid mapping of _request_ headers to the reply.
|
||||
Starting with version 4.1, the `AbstractHeaderMapper` (a `DefaultSoapHeaderMapper` superclass) lets the `NON_STANDARD_HEADERS` token be configured for the `requestHeaderNames` and `replyHeaderNames` properties (in addition to existing `STANDARD_REQUEST_HEADERS` and `STANDARD_REPLY_HEADERS`) to map all user-defined headers.
|
||||
|
||||
Starting with _version 4.3_, patterns in the header mappings can be negated by preceding the pattern with `!`.
|
||||
NOTE: Rather than using the wildcard (`*`), we recommend using the following combination : `STANDARD_REPLY_HEADERS, NON_STANDARD_HEADERS`.
|
||||
Doing so avoids mapping `request` headers to the reply.
|
||||
|
||||
Starting with version 4.3, you can negate patterns in the header mappings by preceding the pattern with `!`.
|
||||
Negated patterns get priority, so a list such as
|
||||
`STANDARD_REQUEST_HEADERS,foo,ba*,!bar,!baz,qux,!foo` will *NOT* map `foo`
|
||||
(nor `bar` nor `baz`); the standard headers plus `bad`, `qux` will be mapped.
|
||||
`STANDARD_REQUEST_HEADERS,thing1,thing*,!thing2,!thing3,qux,!thing1` does not map `thing1`, `thing2`, or `thing3`. It does map the standard headers, `thing4`, and `qux`.
|
||||
(Note that `thing1` is included in both non-negated and negated forms. Because negated values take precedence, `thing1` is not mapped.)
|
||||
|
||||
IMPORTANT: If you have a user defined header that begins with `!` that you *do* wish to map, you need to escape it with
|
||||
`\` thus: `STANDARD_REQUEST_HEADERS,\!myBangHeader` and it *WILL* be mapped.
|
||||
IMPORTANT: If you have a user-defined header that begins with `!` that you do wish to map, you can escape it with
|
||||
`\`, as follows: `STANDARD_REQUEST_HEADERS,\!myBangHeader`. `!myBangHeader` is then mapped.
|
||||
|
||||
Inbound SOAP headers (request headers for the inbound gateway, reply-headers for the outbound gateway) are mapped as
|
||||
Inbound SOAP headers (request headers for the inbound gateway and reply headers for the outbound gateway) are mapped as
|
||||
`SoapHeaderElement` objects.
|
||||
The contents can be explored by accessing the `Source`:
|
||||
You can explore the contents by accessing the `Source`:
|
||||
|
||||
====
|
||||
[source, xml]
|
||||
----
|
||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
@@ -227,9 +252,13 @@ The contents can be explored by accessing the `Source`:
|
||||
</soapenv:Body>
|
||||
</soapenv:Envelope>
|
||||
----
|
||||
====
|
||||
|
||||
If `mapped-request-headers` is `"auth, ba*"`, the `auth`, `bar` and `baz` headers are mapped but `qux` is not.
|
||||
If `mapped-request-headers` is `auth, ca*`, the `auth`, `cat`, and `can` headers are mapped, but `qux` is not mapped.
|
||||
|
||||
The following example shows how to get a value named `user` from a header named `auth`:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
...
|
||||
@@ -240,8 +269,12 @@ assertEquals("username", nodeList.item(0).getNodeName());
|
||||
assertEquals("user", nodeList.item(0).getFirstChild().getNodeValue());
|
||||
...
|
||||
----
|
||||
====
|
||||
|
||||
Starting with _version 5.0_, the `DefaultSoapHeaderMapper` supports user-defined headers of type `javax.xml.transform.Source` and populates them as child nodes of the `<soapenv:Header>`:
|
||||
Starting with version 5.0, the `DefaultSoapHeaderMapper` supports user-defined headers of type `javax.xml.transform.Source` and populates them as child nodes of the `<soapenv:Header>`.
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
Map<String, Object> headers = new HashMap<>();
|
||||
@@ -256,7 +289,11 @@ headers.put("auth", new StringSource(authXml));
|
||||
DefaultSoapHeaderMapper mapper = new DefaultSoapHeaderMapper();
|
||||
mapper.setRequestHeaderNames("auth");
|
||||
----
|
||||
And in the end we have SOAP envelope as:
|
||||
====
|
||||
|
||||
The result of the preceding examples is the following SOAP envelope:
|
||||
|
||||
====
|
||||
[source, xml]
|
||||
----
|
||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
@@ -271,13 +308,17 @@ And in the end we have SOAP envelope as:
|
||||
</soapenv:Body>
|
||||
</soapenv:Envelope>
|
||||
----
|
||||
====
|
||||
|
||||
[[mtom-support]]
|
||||
=== MTOM Support
|
||||
|
||||
The Marshalling Inbound and Outbound WebService Gateways support attachments directly via built-in functionality of the marshaller, e.g. `Jaxb2Marshaller` provides the `mtomEnabled` option.
|
||||
Starting with _version 5.0_, the Simple WebService Gateways can operate with inbound and outbound `MimeMessage` s directly, which have an API to manipulate attachments.
|
||||
When you need to send WebService message with attachments (either a reply from a server, or a client request) you should use the `WebServiceMessageFactory` directly and send a `WebServiceMessage` with attachments as a `payload` to the request or reply channel of the gateway:
|
||||
The marshalling inbound and outbound web service gateways support attachments directly through built-in functionality of the marshaller (for example, `Jaxb2Marshaller` provides the `mtomEnabled` option).
|
||||
Starting with version 5.0, the simple web service gateways can directly operate with inbound and outbound `MimeMessage` instances, which have an API to manipulate attachments.
|
||||
When you need to send web service message with attachments (either a reply from a server or a client request) you should use the `WebServiceMessageFactory` directly and send a `WebServiceMessage` with attachments as a `payload` to the request or reply channel of the gateway.
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
WebServiceMessageFactory messageFactory = new SaajSoapMessageFactory(MessageFactory.newInstance());
|
||||
@@ -293,3 +334,4 @@ webServiceMessage.addAttachment("myAttachment", new ByteArrayResource("my_data".
|
||||
|
||||
this.webServiceChannel.send(new GenericMessage<>(webServiceMessage));
|
||||
----
|
||||
====
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,39 +1,41 @@
|
||||
[[xmpp]]
|
||||
== XMPP Support
|
||||
|
||||
Spring Integration provides Channel Adapters for http://www.xmpp.org[XMPP].
|
||||
|
||||
[[xmpp-intro]]
|
||||
=== Introduction
|
||||
Spring Integration provides channel adapters for http://www.xmpp.org[XMPP].
|
||||
XMPP stands for "`Extensible Messaging and Presence Protocol`".
|
||||
|
||||
XMPP describes a way for multiple agents to communicate with each other in a distributed system.
|
||||
The canonical use case is to send and receive chat messages, though XMPP can be, and is, used for far more applications.
|
||||
XMPP is used to describe a network of actors.
|
||||
Within that network, actors may address each other directly, as well as broadcast status changes (e.g.
|
||||
"presence").
|
||||
The canonical use case is to send and receive chat messages, though XMPP can be (and is) used for other kinds of applications.
|
||||
XMPP describes a network of actors.
|
||||
Within that network, actors may address each other directly and broadcast status changes (such as
|
||||
"`presence`").
|
||||
|
||||
XMPP provides the messaging fabric that underlies some of the biggest Instant Messaging networks in the world, including Google Talk (GTalk) - which is also available from within GMail - and Facebook Chat.
|
||||
There are many good open-source XMPP servers available.
|
||||
Two popular implementations are http://www.igniterealtime.org/projects/openfire/[_Openfire_] and http://www.ejabberd.im[_ejabberd_]
|
||||
XMPP provides the messaging fabric that underlies some of the biggest instant messaging networks in the world, including Google Talk (GTalk, which is also available from within GMail) and Facebook Chat.
|
||||
Many good open-source XMPP servers are available.
|
||||
Two popular implementations are http://www.igniterealtime.org/projects/openfire/[Openfire] and http://www.ejabberd.im[ejabberd].
|
||||
|
||||
Spring integration provides support for XMPP via XMPP adapters which support sending and receiving both XMPP chat messages and presence changes from other entries in your roster.
|
||||
Spring integration provides support for XMPP by providing XMPP adapters, which support sending and receiving both XMPP chat messages and presence changes from other entries in a client's roster.
|
||||
As with other adapters, the XMPP adapters come with support for a convenient namespace-based configuration.
|
||||
To configure the XMPP namespace, include the following elements in the headers of your XML configuration file:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
xmlns:int-xmpp="http://www.springframework.org/schema/integration/xmpp"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration/xmpp
|
||||
http://www.springframework.org/schema/integration/xmpp/spring-integration-xmpp.xsd"
|
||||
----
|
||||
====
|
||||
|
||||
[[xmpp-connection]]
|
||||
=== XMPP Connection
|
||||
|
||||
Before using inbound or outbound XMPP adapters to participate in the XMPP network, an actor must establish its XMPP connection.
|
||||
This connection object could be shared by all XMPP adapters connected to a particular account.
|
||||
Typically this requires - at a minimum -`user`, `password`, and `host`.
|
||||
To create a basic XMPP connection, you can utilize the convenience of the namespace.
|
||||
All XMPP adapters connected to a particular account can share this connection object.
|
||||
Typically this requires (at a minimum) `user`, `password`, and `host`.
|
||||
To create a basic XMPP connection, you can use the convenience of the namespace, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-xmpp:xmpp-connection
|
||||
@@ -45,30 +47,38 @@ To create a basic XMPP connection, you can utilize the convenience of the namesp
|
||||
resource="theNameOfTheResource"
|
||||
subscription-mode="accept_all"/>
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: For added convenience you can rely on the default naming convention and omit the `id` attribute.
|
||||
The default name _xmppConnection_ will be used for this connection bean.
|
||||
NOTE: For added convenience, you can rely on the default naming convention and omit the `id` attribute.
|
||||
The default name (`xmppConnection`) is used for this connection bean.
|
||||
|
||||
If the XMPP Connection goes stale, reconnection attempts will be made with an automatic login as long as the previous connection state was logged (authenticated).
|
||||
We also register a `ConnectionListener` which will log connection events if the DEBUG logging level is enabled.
|
||||
If the XMPP connection goes stale, reconnection attempts are made with an automatic login as long as the previous connection state was logged (authenticated).
|
||||
We also register a `ConnectionListener`, which logs connection events if the `DEBUG` logging level is enabled.
|
||||
|
||||
The `subscription-mode` initiates the Roster listener to deal with incoming subscriptions from other users.
|
||||
This functionality isn't always available for the target XMPP servers.
|
||||
For example GCM/FCM fully disables it.
|
||||
To switch off the Roster listener for subscriptions you should configure it with an empty string when using XML configuration: `subscription-mode=""`, or with `XmppConnectionFactoryBean.setSubscriptionMode(null)` when using Java Configuration. Doing so will disable Roster at the login phase as well.
|
||||
See `Roster.setRosterLoadedAtLogin(Boolean)` for more information.
|
||||
The `subscription-mode` attribute initiates the roster listener to deal with incoming subscriptions from other users.
|
||||
This functionality is not always available for the target XMPP servers.
|
||||
For example, Google Cloud Messaging (GCM) and Firebase Cloud Messaging (FCM) fully disable it.
|
||||
To switch off the roster listener for subscriptions, you can configure it with an empty string when using XML configuration (`subscription-mode=""`) or with `XmppConnectionFactoryBean.setSubscriptionMode(null)` when using Java Configuration.
|
||||
Doing so disables the roster at the login phase as well.
|
||||
See http://download.igniterealtime.org/smack/docs/latest/javadoc/org/jivesoftware/smack/roster/Roster.html#setRosterLoadedAtLogin-boolean-[`Roster.setRosterLoadedAtLogin(boolean)`] for more information.
|
||||
|
||||
[[xmpp-messages]]
|
||||
=== XMPP Messages
|
||||
|
||||
Spring Integration provides support for sending and receiving XMPP messages.
|
||||
For receiving them, it offers an inbound message channel adapter.
|
||||
For sending them, it offers an outbound message channel adapter.
|
||||
|
||||
[[xmpp-message-inbound-channel-adapter]]
|
||||
==== Inbound Message Channel Adapter
|
||||
|
||||
The Spring Integration adapters support receiving chat messages from other users in the system.
|
||||
To do this, the _Inbound Message Channel Adapter_ "logs in" as a user on your behalf and receives the messages sent to that user.
|
||||
To do so, the inbound message channel adapter "`logs in`" as a user on your behalf and receives the messages sent to that user.
|
||||
Those messages are then forwarded to your Spring Integration client.
|
||||
Configuration support for the XMPP _Inbound Message Channel Adapter_ is provided via the `inbound-channel-adapter` element.
|
||||
The `inbound-channel-adapter` element provides Configuration support for the XMPP inbound message channel adapter.
|
||||
The following example shows how to configure it:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-xmpp:inbound-channel-adapter id="xmppInboundAdapter"
|
||||
@@ -78,132 +88,150 @@ Configuration support for the XMPP _Inbound Message Channel Adapter_ is provided
|
||||
stanza-filter="stanzaFilter"
|
||||
auto-startup="true"/>
|
||||
----
|
||||
====
|
||||
|
||||
As you can see amongst the usual attributes this adapter also requires a reference to an XMPP Connection.
|
||||
Along with the usual attributes (for a message channel adapter), this adapter also requires a reference to an XMPP Connection.
|
||||
|
||||
It is also important to mention that the XMPP inbound adapter is an _event driven adapter_ and a `Lifecycle` implementation.
|
||||
When started it will register a `PacketListener` that will listen for incoming XMPP Chat Messages.
|
||||
It forwards any received messages to the underlying adapter which will convert them to Spring Integration Messages and send them to the specified `channel`.
|
||||
It will unregister the `PacketListener` when it is stopped.
|
||||
The XMPP inbound adapter is event-driven and a `Lifecycle` implementation.
|
||||
When started, it registers a `PacketListener` that listens for incoming XMPP chat messages.
|
||||
It forwards any received messages to the underlying adapter, which converts them to Spring Integration messages and sends them to the specified `channel`.
|
||||
When stopped, it unregisters the `PacketListener`.
|
||||
|
||||
Starting with _version 4.3_ the `ChatMessageListeningEndpoint` (and its `<int-xmpp:inbound-channel-adapter>`)
|
||||
supports a `org.jivesoftware.smack.filter.StanzaFilter` injection to be registered on the provided `XMPPConnection`
|
||||
Starting with version 4.3, the `ChatMessageListeningEndpoint` (and its `<int-xmpp:inbound-channel-adapter>`)
|
||||
supports the injection of a `org.jivesoftware.smack.filter.StanzaFilter` to be registered on the provided `XMPPConnection`,
|
||||
together with an internal `StanzaListener` implementation.
|
||||
See their https://www.igniterealtime.org/builds/smack/docs/latest/javadoc/org/jivesoftware/smack/XMPPConnection.html#addAsyncStanzaListener%28org.jivesoftware.smack.StanzaListener,%20org.jivesoftware.smack.filter.StanzaFilter%29[JavaDocs] for more information.
|
||||
See the https://www.igniterealtime.org/builds/smack/docs/latest/javadoc/org/jivesoftware/smack/XMPPConnection.html#addAsyncStanzaListener%28org.jivesoftware.smack.StanzaListener,%20org.jivesoftware.smack.filter.StanzaFilter%29[Javadoc] for more information.
|
||||
|
||||
Also with the _version 4.3_ the `payload-expression` has been introduced for the `ChatMessageListeningEndpoint`.
|
||||
The incoming `org.jivesoftware.smack.packet.Message` represents a root object of evaluation context.
|
||||
This option is useful in case of <<xmpp-extensions>>.
|
||||
For example, for the GCM protocol we can extract the body using expression:
|
||||
Version 4.3 introduced the `payload-expression` attribute for the `ChatMessageListeningEndpoint`.
|
||||
The incoming `org.jivesoftware.smack.packet.Message` represents a root object for the evaluation context.
|
||||
This option is useful when you use <<xmpp-extensions,XMPP extensions>>.
|
||||
For example, for the GCM protocol we can extract the body by using the following expression:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
payload-expression="getExtension('google:mobile:data').json"
|
||||
----
|
||||
====
|
||||
|
||||
for the XHTML protocol:
|
||||
The following example extracts the body for the XHTML protocol:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
payload-expression="getExtension(T(org.jivesoftware.smackx.xhtmlim.packet.XHTMLExtension).NAMESPACE).bodies[0]"
|
||||
----
|
||||
====
|
||||
|
||||
To simplify the access to the Extension in the XMPP Message, the `extension` variable is added into the
|
||||
`EvaluationContext`.
|
||||
Note, it is done only when one and only one Extension is present in the Message.
|
||||
The samples above with the `namespace` manipulations can be simplified to something like:
|
||||
To simplify access to the extension in the XMPP Message, the `extension` variable is added into the `EvaluationContext`.
|
||||
Note that it is added when only one extension is present in the message.
|
||||
The preceding examples that show the `namespace` manipulations can be simplified to the following example:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
payload-expression="#extension.json"
|
||||
payload-expression="#extension.bodies[0]"
|
||||
----
|
||||
====
|
||||
|
||||
[[xmpp-message-outbound-channel-adapter]]
|
||||
==== Outbound Message Channel Adapter
|
||||
|
||||
You may also send chat messages to other users on XMPP using the _Outbound Message Channel Adapter_.
|
||||
Configuration support for the XMPP _Outbound Message Channel Adapter_ is provided via the `outbound-channel-adapter` element.
|
||||
You can also send chat messages to other users on XMPP by using the outbound message channel adapter.
|
||||
The `outbound-channel-adapter` element provides configuration support for the XMPP outbound message channel adapter.
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-xmpp:outbound-channel-adapter id="outboundEventAdapter"
|
||||
channel="outboundEventChannel"
|
||||
xmpp-connection="testConnection"/>
|
||||
----
|
||||
====
|
||||
|
||||
The adapter expects as its input - at a minimum - a payload of type `java.lang.String`, and a header value for
|
||||
`XmppHeaders.CHAT_TO` that specifies to which user the Message should be sent.
|
||||
To create a message you might use the following Java code:
|
||||
The adapter expects its input to be (at a minimum) a payload of type `java.lang.String` and a header value for
|
||||
`XmppHeaders.CHAT_TO` that specifies to which user the message should be sent.
|
||||
To create a message, you can use Java code similar to the following:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Message<String> xmppOutboundMsg = MessageBuilder.withPayload("Hello, XMPP!" )
|
||||
.setHeader(XmppHeaders.CHAT_TO, "userhandle")
|
||||
.build();
|
||||
----
|
||||
====
|
||||
|
||||
Another mechanism of setting the header is by using the XMPP header-enricher support.
|
||||
Here is an example.
|
||||
You can also set the header by using the XMPP header-enricher support, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-xmpp:header-enricher input-channel="input" output-channel="output">
|
||||
<int-xmpp:chat-to value="test1@example.org"/>
|
||||
</int-xmpp:header-enricher>
|
||||
----
|
||||
====
|
||||
|
||||
Starting with _version 4.3_ the packet extension support has been added to the `ChatMessageSendingMessageHandler`
|
||||
(`<int-xmpp:outbound-channel-adapter>`).
|
||||
Alongside with the regular `String` and `org.jivesoftware.smack.packet.Message` `payload`, now you can send a message
|
||||
with a `payload` as a `org.jivesoftware.smack.packet.ExtensionElement` which is populated to the
|
||||
`org.jivesoftware.smack.packet.Message.addExtension()` instead of `setBody()`.
|
||||
For the convenience an `extension-provider` option has been added for the `ChatMessageSendingMessageHandler`
|
||||
to allow to inject `org.jivesoftware.smack.provider.ExtensionElementProvider`, which builds an `ExtensionElement`
|
||||
against the `payload` at runtime.
|
||||
For this case the payload must be `String` in JSON or XML format depending of the XEP protocol.
|
||||
Starting with version 4.3, the packet extension support has been added to the `ChatMessageSendingMessageHandler` (the `<int-xmpp:outbound-channel-adapter>` in XML configuration).
|
||||
Along with the regular `String` and `org.jivesoftware.smack.packet.Message` payload, now you can send a message with a payload of `org.jivesoftware.smack.packet.ExtensionElement` (which is populated to the `org.jivesoftware.smack.packet.Message.addExtension()`) instead of `setBody()`.
|
||||
For convenience, we added an `extension-provider` option for the `ChatMessageSendingMessageHandler`.
|
||||
It lets you inject `org.jivesoftware.smack.provider.ExtensionElementProvider`, which builds an `ExtensionElement` against the payload at runtime.
|
||||
For this case, the payload must be a string in JSON or XML format, depending of the XEP protocol.
|
||||
|
||||
[[xmpp-presence]]
|
||||
=== XMPP Presence
|
||||
|
||||
XMPP also supports broadcasting state.
|
||||
You can use this capability to let people who have you on their roster see your state changes.
|
||||
This happens all the time with your IM clients; you change your away status, and then set an away message, and everybody who has you on their roster sees your icon or username change to reflect this new state, and additionally might see your new "away" message.
|
||||
If you would like to receive notification, or notify others, of state changes, you can use Spring Integration's "presence" adapters.
|
||||
You can use this ability to let people who have you on their roster see your state changes.
|
||||
This happens all the time with your IM clients.
|
||||
You change your away status and set an away message, and everybody who has you on their roster sees your icon or username change to reflect this new state and might see your new "`away`" message.
|
||||
If you would like to receive notifications or notify others of state changes, you can use Spring Integration's "`presence`" adapters.
|
||||
|
||||
[[xmpp-roster-inbound-channel-adapter]]
|
||||
==== Inbound Presence Message Channel Adapter
|
||||
|
||||
Spring Integration provides an _Inbound Presence Message Channel Adapter_ which supports receiving Presence events from other users in the system who happen to be on your Roster.
|
||||
To do this, the adapter "logs in" as a user on your behalf, registers a `RosterListener` and forwards received Presence update events as Messages to the channel identified by the `channel` attribute.
|
||||
The payload of the Message will be a `org.jivesoftware.smack.packet.Presence` object (see https://www.igniterealtime.org/builds/smack/docs/latest/javadoc/org/jivesoftware/smack/packet/Presence.html).
|
||||
Spring Integration provides an inbound presence message channel adapter, which supports receiving presence events from other users in the system who are on your roster.
|
||||
To do this, the adapter "`logs in`" as a user on your behalf, registers a `RosterListener`, and forwards received presence update events as messages to the channel identified by the `channel` attribute.
|
||||
The payload of the message is a `org.jivesoftware.smack.packet.Presence` object (see https://www.igniterealtime.org/builds/smack/docs/latest/javadoc/org/jivesoftware/smack/packet/Presence.html).
|
||||
|
||||
Configuration support for the XMPP _Inbound Presence Message Channel Adapter_ is provided via the `presence-inbound-channel-adapter` element.
|
||||
The `presence-inbound-channel-adapter` element provides configuration support for the XMPP inbound presence message channel adapter.
|
||||
The following example configures an inbound presence message channel adapter:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-xmpp:presence-inbound-channel-adapter channel="outChannel"
|
||||
xmpp-connection="testConnection" auto-startup="false"/>
|
||||
----
|
||||
====
|
||||
|
||||
As you can see amongst the usual attributes this adapter also requires a reference to an XMPP Connection.
|
||||
It is also important to mention that this adapter is an event driven adapter and a `Lifecycle` implementation.
|
||||
It will register a `RosterListener` when started and will unregister that `RosterListener` when stopped.
|
||||
Along with the usual attributes, this adapter requires a reference to an XMPP Connection.
|
||||
This adapter is event-driven and a `Lifecycle` implementation.
|
||||
It registers a `RosterListener` when started and unregisters that `RosterListener` when stopped.
|
||||
|
||||
[[xmpp-roster-outbound-channel-adapter]]
|
||||
==== Outbound Presence Message Channel Adapter
|
||||
|
||||
Spring Integration also supports sending Presence events to be seen by other users in the network who happen to have you on their Roster.
|
||||
When you send a Message to the _Outbound Presence Message Channel Adapter_ it extracts the payload, which is expected to be of type `org.jivesoftware.smack.packet.Presence` and sends it to the XMPP Connection, thus advertising your presence events to the rest of the network.
|
||||
Spring Integration also supports sending presence events to be seen by other users in the network who happen to have you on their roster.
|
||||
When you send a message to the outbound presence message channel adapter, it extracts the payload (which is expected to be of type `org.jivesoftware.smack.packet.Presence`) and sends it to the XMPP Connection, thus advertising your presence events to the rest of the network.
|
||||
|
||||
Configuration support for the XMPP _Outbound Presence Message Channel Adapter_ is provided via the `presence-outbound-channel-adapter` element.
|
||||
The `presence-outbound-channel-adapter` element provides configuration support for the XMPP outbound presence message channel adapter.
|
||||
The following example shows how to configure an outbound presence message channel adapter:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-xmpp:presence-outbound-channel-adapter id="eventOutboundPresenceChannel"
|
||||
xmpp-connection="testConnection"/>
|
||||
----
|
||||
====
|
||||
|
||||
It can also be a _Polling Consumer_ (if it receives Messages from a Pollable Channel) in which case you would need to register a Poller.
|
||||
It can also be a polling consumer (if it receives messages from a pollable channel) in which case you would need to register a poller.
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-xmpp:presence-outbound-channel-adapter id="pollingOutboundPresenceAdapter"
|
||||
@@ -212,23 +240,27 @@ It can also be a _Polling Consumer_ (if it receives Messages from a Pollable Cha
|
||||
<int:poller fixed-rate="1000" max-messages-per-poll="1"/>
|
||||
</int-xmpp:presence-outbound-channel-adapter>
|
||||
----
|
||||
====
|
||||
|
||||
Like its inbound counterpart, it requires a reference to an XMPP Connection.
|
||||
|
||||
NOTE: If you are relying on the default naming convention for an XMPP Connection bean (described earlier), and you have only one XMPP Connection bean configured in your Application Context, you may omit the `xmpp-connection` attribute.
|
||||
In that case, the bean with the name _xmppConnection_ will be located and injected into the adapter.
|
||||
NOTE: If you rely on the default naming convention for an XMPP Connection bean (<<xmpp-connection,described earlier>>) and you have only one XMPP Connection bean configured in your application context, you can omit the `xmpp-connection` attribute.
|
||||
In that case, the bean with named `xmppConnection` is located and injected into the adapter.
|
||||
|
||||
[[xmpp-advanced]]
|
||||
=== Advanced Configuration
|
||||
|
||||
Since Spring Integration XMPP support is based on the Smack 4.0 API (http://www.igniterealtime.org/projects/smack/), it is important to know a few details related to more complex configuration of the XMPP Connection object.
|
||||
Spring Integration's XMPP support is based on the Smack 4.0 API (http://www.igniterealtime.org/projects/smack/), which allows more complex configuration of the XMPP Connection object.
|
||||
|
||||
As stated earlier the `xmpp-connection` namespace support is designed to simplify basic connection configuration and only supports a few common configuration attributes.
|
||||
However, the `org.jivesoftware.smack.ConnectionConfiguration` object defines about 20 attributes, and there is no real value of adding namespace support for all of them.
|
||||
So, for more complex connection configurations, simply configure an instance of our `XmppConnectionFactoryBean` as a regular bean, and inject a `org.jivesoftware.smack.ConnectionConfiguration` as a constructor argument to that FactoryBean.
|
||||
Every property you need, can be specified directly on that ConnectionConfiguration instance (a bean definition with the 'p' namespace would work well).
|
||||
This way SSL, or any other attributes, could be set directly.
|
||||
Here's an example:
|
||||
As <<xmpp-connection,stated earlier>>, the `xmpp-connection` namespace support is designed to simplify basic connection configuration and supports only a few common configuration attributes.
|
||||
However, the `org.jivesoftware.smack.ConnectionConfiguration` object defines about 20 attributes, and adding namespace support for all of them offers no real value.
|
||||
So, for more complex connection configurations, you can configure an instance of our `XmppConnectionFactoryBean` as a regular bean and inject a `org.jivesoftware.smack.ConnectionConfiguration` as a constructor argument to that `FactoryBean`.
|
||||
You can specify every property you need directly on that `ConnectionConfiguration` instance.
|
||||
(A bean definition with the 'p' namespace would work well.)
|
||||
This way, you can directly set SSL (or any other attributes).
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="xmppConnection" class="o.s.i.xmpp.XmppConnectionFactoryBean">
|
||||
@@ -246,12 +278,16 @@ Here's an example:
|
||||
channel="outboundEventChannel"
|
||||
xmpp-connection="xmppConnection"/>
|
||||
----
|
||||
====
|
||||
|
||||
Another important aspect of the Smack API is static initializers.
|
||||
For more complex cases (e.g., registering a SASL Mechanism), you may need to execute certain static initializers.
|
||||
One of those static initializers is `SASLAuthentication`, which allows you to register supported SASL mechanisms.
|
||||
For that level of complexity, we would recommend Spring JavaConfig-style of the XMPP Connection configuration.
|
||||
Then, you can configure the entire component through Java code and execute all other necessary Java code including static initializers at the appropriate time.
|
||||
The Smack API also offers static initializers, which can be helpful.
|
||||
For more complex cases (such as registering a SASL mechanism), you may need to execute certain static initializers.
|
||||
One of those static initializers is `SASLAuthentication`, which lets you register supported SASL mechanisms.
|
||||
For that level of complexity, we recommend using Spring Java configuration for the XMPP connection configuration.
|
||||
That way, you can configure the entire component through Java code and execute all other necessary Java code, including static initializers, at the appropriate time.
|
||||
The following exampe shows how to configure an XMPP connection with an SASL (Simple Authentication and Security Layer) in Java:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
@@ -268,99 +304,88 @@ public class CustomConnectionConfiguration {
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
For more information on the JavaConfig style of Application Context configuration, refer to the following section
|
||||
in the http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-java[Spring Reference Manual].
|
||||
For more information on using Java for application context configuration, see the following section in the http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-java[Spring Reference Manual].
|
||||
|
||||
[[xmpp-message-headers]]
|
||||
=== XMPP Message Headers
|
||||
|
||||
The Spring Integration XMPP Adapters will map standard XMPP properties automatically.
|
||||
These properties will be copied by default to and from Spring Integration `MessageHeaders` using the
|
||||
http://docs.spring.io/spring-integration/api/org/springframework/integration/xmpp/support/DefaultXmppHeaderMapper.html[DefaultXmppHeaderMapper].
|
||||
The Spring Integration XMPP Adapters automatically map standard XMPP properties.
|
||||
By default, these properties are copied to and from Spring Integration `MessageHeaders` by using
|
||||
http://docs.spring.io/spring-integration/api/org/springframework/integration/xmpp/support/DefaultXmppHeaderMapper.html[`DefaultXmppHeaderMapper`].
|
||||
|
||||
Any user-defined headers will NOT be copied to or from an XMPP Message, unless explicitly specified by the
|
||||
_requestHeaderNames_ and/or _replyHeaderNames_ properties of the `DefaultXmppHeaderMapper`.
|
||||
Any user-defined headers are not copied to or from an XMPP Message, unless explicitly specified by the `requestHeaderNames` or `replyHeaderNames` properties of the `DefaultXmppHeaderMapper`.
|
||||
|
||||
TIP: When mapping user-defined headers, the values can also contain simple wildcard patterns (e.g. "foo*" or "*foo") to
|
||||
be matched.
|
||||
TIP: When mapping user-defined headers, the values can also contain simple wildcard patterns (such "thing*" or "*thing").
|
||||
|
||||
Starting with _version 4.1_, the `AbstractHeaderMapper` (a `DefaultXmppHeaderMapper` superclass) allows the
|
||||
`NON_STANDARD_HEADERS` token to be configured for the _requestHeaderNames_ property (in addition to existing
|
||||
`STANDARD_REQUEST_HEADERS`) to map all user-defined headers.
|
||||
Starting with version 4.1, `AbstractHeaderMapper` (a superclass of `DefaultXmppHeaderMapper`) lets you configure the `NON_STANDARD_HEADERS` token for the `requestHeaderNames` property (in addition to `STANDARD_REQUEST_HEADERS`), to map all user-defined headers.
|
||||
|
||||
Class `org.springframework.xmpp.XmppHeaders` identifies the default headers that will be used by the `DefaultXmppHeaderMapper`:
|
||||
The `org.springframework.xmpp.XmppHeaders` class identifies the default headers to be used by the `DefaultXmppHeaderMapper`:
|
||||
|
||||
* xmpp_from
|
||||
* `xmpp_from`
|
||||
* `xmpp_subject`
|
||||
* `xmpp_thread`
|
||||
* `xmpp_to`
|
||||
* `xmpp_type`
|
||||
|
||||
* xmpp_subject
|
||||
Starting with version 4.3, you can negate patterns in the header mappings by preceding the pattern with `!`.
|
||||
Negated patterns get priority, so a list such as `STANDARD_REQUEST_HEADERS,thing1,thing*,!thing2,!thing3,qux,!thing1` does not map `thing1`, `thing2`,or `thing3`.
|
||||
That list does map the standard headers plus `thing4` and `qux`.
|
||||
|
||||
* xmpp_thread
|
||||
|
||||
* xmpp_to
|
||||
|
||||
* xmpp_type
|
||||
|
||||
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,foo,ba*,!bar,!baz,qux,!foo` will *NOT* map `foo`
|
||||
(nor `bar` nor `baz`); the standard headers plus `bad`, `qux` will be mapped.
|
||||
|
||||
IMPORTANT: If you have a user defined header that begins with `!` that you *do* wish to map, you need to escape it with
|
||||
`\` thus: `STANDARD_REQUEST_HEADERS,\!myBangHeader` and it *WILL* be mapped.
|
||||
IMPORTANT: If you have a user-defined header that begins with `!` that you do wish to map, can escape it with `\` thus: `STANDARD_REQUEST_HEADERS,\!myBangHeader`.
|
||||
In that example, the standard request headers and `!myBangHeader` are mapped.
|
||||
|
||||
[[xmpp-extensions]]
|
||||
=== XMPP Extensions
|
||||
|
||||
The XMPP protocol stands for **eXstensible Messaging and Presence Protocol**.
|
||||
The "extensible" part is important.
|
||||
XMPP is based around XML, a data format that supports a concept known as _namespacing_.
|
||||
Extensions put the "`Extensible`" in the "`Extensible Messaging and Presence Protocol`".
|
||||
|
||||
XMPP is based around XML, a data format that supports a concept known as namespacing.
|
||||
Through namespacing, you can add bits to XMPP that are not defined in the original specifications.
|
||||
This is important because the XMPP specification deliberately describes only a set of core things like:
|
||||
The XMPP specification deliberately describes only a set of core features:
|
||||
|
||||
- How a client connects to a server
|
||||
- Encryption (SSL/TLS)
|
||||
- Authentication
|
||||
- How servers can communicate with each other to relay messages
|
||||
- and a few other basic building blocks.
|
||||
* How a client connects to a server
|
||||
* Encryption (SSL/TLS)
|
||||
* Authentication
|
||||
* How servers can communicate with each other to relay messages
|
||||
* A few other basic building blocks
|
||||
|
||||
Once you have implemented this, you have an XMPP client and can send any kind of data you like.
|
||||
But that's not the end.
|
||||
However, you may need to do more than the basics.
|
||||
For example, you might need to include formatting (bold, italic, and so on) in a message, which is not defined in the core XMPP specification.
|
||||
Well, you can make up a way to do that, but, unless everyone else does it the same way you do, no other software can interpret it (they ignore namespaces they cannot understand).
|
||||
|
||||
For example, perhaps you decide that you want to include formatting in a message (bold, italic, etc.) which is not
|
||||
defined in the core XMPP specification.
|
||||
Well, you can make up a way to do that, but unless everyone else does it the same way as you,
|
||||
no other software will be able interpret it (they will just ignore namespaces they don't understand).
|
||||
To solve that problem, the XMPP Standards Foundation (XSF) publishes a series of extra documents, known as http://xmpp.org/extensions/xep-0001.html[XMPP Enhancement Proposals] (XEPs).
|
||||
In general, each XEP describes a particular activity (from message formatting to file transfers, multi-user chats, and many more).
|
||||
They also provide a standard format for everyone to use for that activity.
|
||||
|
||||
So the XMPP Standards Foundation (XSF) publishes a series of extra documents, known as
|
||||
http://xmpp.org/extensions/xep-0001.html[XMPP Enhancement Proposals] (XEPs).
|
||||
In general each XEP describes a particular activity (from message formatting, to file transfers, multi-user
|
||||
chats and many more), and they provide a standard format for everyone to use for that activity.
|
||||
The Smack API provides many XEP implementations with its `extensions` and `experimental` http://www.igniterealtime.org/builds/smack/docs/latest/documentation/extensions/index.html[projects].
|
||||
Starting with Spring Integration version 4.3, you can use any XEP with the existing XMPP channel adapters.
|
||||
|
||||
The Smack API provides many XEP implementations with its `extensions` and `experimental`
|
||||
http://www.igniterealtime.org/builds/smack/docs/latest/documentation/extensions/index.html[projects].
|
||||
And starting with Spring Integration _version 4.3_ any XEP can be use with the existing XMPP channel adapters.
|
||||
|
||||
To be able to process XEPs or any other custom XMPP extensions, the Smack's `ProviderManager` pre-configuration
|
||||
must be provided.
|
||||
It can be done via direct usage from the `static` Java code:
|
||||
To be able to process XEPs or any other custom XMPP extensions, you must provide the Smack's `ProviderManager` pre-configuration.
|
||||
You can do so with `static` Java code, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
ProviderManager.addIQProvider("element", "namespace", new MyIQProvider());
|
||||
ProviderManager.addExtensionProvider("element", "namespace", new MyExtProvider());
|
||||
----
|
||||
====
|
||||
|
||||
or via `.providers` configuration file in the specific instance and JVM argument:
|
||||
You can also use a `.providers` configuration file in the specific instance and access it with a JVM argument, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
-Dsmack.provider.file=file:///c:/my/provider/mycustom.providers
|
||||
----
|
||||
====
|
||||
|
||||
where `mycustom.providers` might be like this:
|
||||
The `mycustom.providers` file might be as follows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0"?>
|
||||
@@ -384,12 +409,13 @@ where `mycustom.providers` might be like this:
|
||||
</extensionProvider>
|
||||
</smackProviders>
|
||||
----
|
||||
====
|
||||
|
||||
For example the most popular XMPP messaging extension is
|
||||
https://developers.google.com/cloud-messaging/[Google Cloud Messaging] (GCM).
|
||||
The Smack provides the particular `org.jivesoftware.smackx.gcm.provider.GcmExtensionProvider` for that and
|
||||
registers that by default with the `smack-experimental` jar in the classpath using `experimental.providers` resource:
|
||||
For example, the most popular XMPP messaging extension is https://developers.google.com/cloud-messaging/[Google Cloud Messaging] (GCM).
|
||||
The Smack library provides `org.jivesoftware.smackx.gcm.provider.GcmExtensionProvider` for that purposes.
|
||||
By default, it registers that class with the `smack-experimental` jar in the classpath by using the `experimental.providers` resource, as the following Maven example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<!-- GCM JSON payload -->
|
||||
@@ -399,8 +425,11 @@ registers that by default with the `smack-experimental` jar in the classpath usi
|
||||
<className>org.jivesoftware.smackx.gcm.provider.GcmExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
----
|
||||
====
|
||||
|
||||
Also the `GcmPacketExtension` is present for the target messaging protocol to parse incoming packets and build outgoing:
|
||||
Also, the `GcmPacketExtension` lets the target messaging protocol parse incoming packets and build outgoing packets, as the following examples show:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
GcmPacketExtension gcmExtension = (GcmPacketExtension) xmppMessage.getExtension(GcmPacketExtension.NAMESPACE);
|
||||
@@ -413,5 +442,6 @@ GcmPacketExtension packetExtension = new GcmPacketExtension(gcmJson);
|
||||
Message smackMessage = new Message();
|
||||
smackMessage.addExtension(packetExtension);
|
||||
----
|
||||
====
|
||||
|
||||
See <<xmpp-message-inbound-channel-adapter>> and <<xmpp-message-outbound-channel-adapter>> above for more information.
|
||||
See "`<<xmpp-message-inbound-channel-adapter>>`" and "`<<xmpp-message-outbound-channel-adapter>>`" earlier in this chapter for more information.
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
[[zookeeper]]
|
||||
== Zookeeper Support
|
||||
|
||||
=== Introduction
|
||||
Version 4.2 added https://zookeeper.apache.org/[Zookeeper] support to the framework in version 4.2, which consists of:
|
||||
|
||||
https://zookeeper.apache.org/[Zookeeper] support was added to the framework in _version 4.2_, comprised of:
|
||||
|
||||
* MetadataStore
|
||||
* LockRegistry
|
||||
* Leadership Event Handling
|
||||
* <<zk-metadata-store,A metadata store>>
|
||||
* <<zk-lock-registry,A lock registry>>
|
||||
* <<zk-leadership,Leadership event handling>>
|
||||
|
||||
[[zk-metadata-store]]
|
||||
=== Zookeeper Metadata Store
|
||||
|
||||
The `ZookeeperMetadataStore` can be used where any `MetadataStore` is needed, such as peristent file list filters,
|
||||
etc.
|
||||
See <<metadata-store>> for more information.
|
||||
You ca use the `ZookeeperMetadataStore` where any `MetadataStore` is needed, such as for persistent file list filters.
|
||||
See "`<<metadata-store>>`" for more information.
|
||||
The following example configures a Zookeeper metadata store with XML:
|
||||
|
||||
====
|
||||
[source, xml]
|
||||
----
|
||||
<bean id="client" class="org.springframework.integration.zookeeper.config.CuratorFrameworkFactoryBean">
|
||||
@@ -26,6 +25,9 @@ See <<metadata-store>> for more information.
|
||||
<constructor-arg ref="client" />
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows how to configure a Zookeeper metadata store with Java:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@@ -38,14 +40,13 @@ public MetadataStore zkStore(CuratorFramework client) {
|
||||
[[zk-lock-registry]]
|
||||
=== Zookeeper Lock Registry
|
||||
|
||||
The `ZookeeperLockRegistry` can be used where any `LockRegistry` is needed, such as when using an `Aggregator` in a
|
||||
clustered environment, with a shared `MessageStore`.
|
||||
The `ZookeeperLockRegistry` can be used where any `LockRegistry` is needed, such as when using an aggregator in a clustered environment with a shared `MessageStore`.
|
||||
|
||||
A `LocRegistry` is used to "look up" a lock based on a key (the aggregator uses the `correlationId`).
|
||||
By default, locks in the `ZookeeperLockRegistry` are maintained in zookeeper under the path
|
||||
`/SpringIntegration-LockRegistry/`.
|
||||
You can customize the path by providing an implementation of `ZookeeperLockRegistry.KeyToPathStrategy`.
|
||||
A `LockRegistry` is used to "`look up`" a lock based on a key (the aggregator uses `correlationId`).
|
||||
By default, locks in the `ZookeeperLockRegistry` are maintained in zookeeper under the following path: `/SpringIntegration-LockRegistry/`.
|
||||
You can customize the path by providing an implementation of `ZookeeperLockRegistry.KeyToPathStrategy`, as the following example shows:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
public interface KeyToPathStrategy {
|
||||
@@ -56,30 +57,34 @@ public interface KeyToPathStrategy {
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
If the strategy returns `true` from `isBounded`, unused locks do not need to be harvested.
|
||||
For unbounded strategies (such as the default) you will need to invoke `expireUnusedOlderThan(long age)` from time
|
||||
to time, to remove old unused locks from memory.
|
||||
For unbounded strategies (such as the default), you need to periodicallyinvoke `expireUnusedOlderThan(long age)` to remove old unused locks from memory.
|
||||
|
||||
[[zk-leadership]]
|
||||
=== Zookeeper Leadership Event Handling
|
||||
|
||||
To configure an application for leader election using Zookeeper in XML:
|
||||
The following example uses XML to configure an application for leader election in Zookeeper:
|
||||
|
||||
====
|
||||
[source, xml]
|
||||
----
|
||||
<int-zk:leader-listener client="client" path="/siNamespace" role="cluster" />
|
||||
----
|
||||
====
|
||||
|
||||
`client` is a reference to a `CuratorFramework` bean; a `CuratorFrameworkFactoryBean` is available.
|
||||
When a leader is elected, an `OnGrantedEvent` will be published for the role `cluster`; any endpoints in that role
|
||||
will be started.
|
||||
When leadership is revoked, an `OnRevokedEvent` will be published for the role `cluster`; any endpoints in that role
|
||||
will be stopped.
|
||||
See <<endpoint-roles>> for more information.
|
||||
`client` is a reference to a `CuratorFramework` bean.
|
||||
A `CuratorFrameworkFactoryBean` is available.
|
||||
When a leader is elected, an `OnGrantedEvent` is published for the role `cluster`.
|
||||
Any endpoints in that role are started.
|
||||
When leadership is revoked, an `OnRevokedEvent` is published for the role `cluster`.
|
||||
Any endpoints in that role are stopped.
|
||||
See "`<<endpoint-roles>>`" for more information.
|
||||
|
||||
In Java configuration you can create an instance of the leader initiator like this:
|
||||
You can use Java configuration to create an instance of the leader initiator, as the following example shows:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
@@ -90,3 +95,4 @@ public LeaderInitiatorFactoryBean leaderInitiator(CuratorFramework client) {
|
||||
.setRole("cluster");
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Reference in New Issue
Block a user