INT-2882 Upgrade DocBook Reference Plugin to 0.2.6
For reference see: https://jira.springsource.org/browse/INT-2882 * Verify spacing * Ensure all source code samples are typed: e.g. <programlisting language="xml"> * Ensure source code fits space in PDF format
This commit is contained in:
committed by
Gary Russell
parent
35365990f9
commit
06831b9e22
@@ -8,7 +8,7 @@ buildscript {
|
||||
maven { url 'http://repo.springsource.org/plugins-release' }
|
||||
}
|
||||
dependencies {
|
||||
classpath 'org.springframework.build.gradle:docbook-reference-plugin:0.1.6'
|
||||
classpath 'org.springframework.build.gradle:docbook-reference-plugin:0.2.6'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
// default implementation exists
|
||||
}
|
||||
|
||||
protected abstract Object aggregatePayloads(MessageGroup group, Map<String, Object> defaultHeaders);
|
||||
protected abstract Object aggregatePayloads(MessageGroup group, Map<String, Object> defaultHeaders);
|
||||
|
||||
}]]></programlisting>
|
||||
|
||||
@@ -127,16 +127,14 @@
|
||||
|
||||
and it has a default value based on the <code>MessageHeaders.CORRELATION_ID</code> message header:
|
||||
|
||||
<programlisting language="java"><![CDATA[
|
||||
public AbstractCorrelatingMessageHandler(MessageGroupProcessor processor, MessageGroupStore store,
|
||||
CorrelationStrategy correlationStrategy, ReleaseStrategy releaseStrategy) {
|
||||
...
|
||||
this.correlationStrategy = correlationStrategy == null ?
|
||||
<programlisting language="java"><![CDATA[public AbstractCorrelatingMessageHandler(MessageGroupProcessor processor, MessageGroupStore store,
|
||||
CorrelationStrategy correlationStrategy, ReleaseStrategy releaseStrategy) {
|
||||
...
|
||||
this.correlationStrategy = correlationStrategy == null ?
|
||||
new HeaderAttributeCorrelationStrategy(MessageHeaders.CORRELATION_ID) : correlationStrategy;
|
||||
this.releaseStrategy = releaseStrategy == null ? new SequenceSizeReleaseStrategy() : releaseStrategy;
|
||||
...
|
||||
}
|
||||
]]></programlisting>
|
||||
this.releaseStrategy = releaseStrategy == null ? new SequenceSizeReleaseStrategy() : releaseStrategy;
|
||||
...
|
||||
}]]></programlisting>
|
||||
|
||||
<para>As for actual processing of the message group, the default implementation is the
|
||||
<classname>DefaultAggregatingMessageGroupProcessor</classname>. It creates a
|
||||
@@ -331,8 +329,7 @@ then you should simply provide an implementation of the <classname>ReleaseStrate
|
||||
XML through the <code><aggregator/></code> element. Below you can see an example
|
||||
of an aggregator.</para>
|
||||
|
||||
<programlisting lang="xml"><![CDATA[<channel id="inputChannel"/>
|
||||
|
||||
<programlisting language="xml"><![CDATA[<channel id="inputChannel"/>
|
||||
|
||||
<int:aggregator id="myAggregator" ]]><co id="aggxml01" /><![CDATA[
|
||||
auto-startup="true" ]]><co id="aggxml02" /><![CDATA[
|
||||
@@ -358,7 +355,6 @@ then you should simply provide an implementation of the <classname>ReleaseStrate
|
||||
expire-groups-upon-completion="false" ]]><co id="aggxml18" /><![CDATA[
|
||||
empty-group-min-timeout="60000" /> ]]><co id="aggxml19" /><![CDATA[
|
||||
|
||||
|
||||
<int:channel id="outputChannel"/>
|
||||
|
||||
<int:channel id="throwAwayChannel"/>
|
||||
@@ -598,22 +594,22 @@ then you should simply provide an implementation of the <classname>ReleaseStrate
|
||||
strategy will assemble all aggregated messages in the List. So now we have two problems. First we need to extract
|
||||
individual messages from the list, and then we need to extract the payload of each message and assemble
|
||||
the array of objects (see code below).
|
||||
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[public String[] processRelease(List<Message<String>> messages){
|
||||
List<String> stringList = new ArrayList<String>();
|
||||
for (Message<String> message : messages) {
|
||||
stringList.add(message.getPayload());
|
||||
}
|
||||
return stringList.toArray(new String[]{});
|
||||
List<String> stringList = new ArrayList<String>();
|
||||
for (Message<String> message : messages) {
|
||||
stringList.add(message.getPayload());
|
||||
}
|
||||
return stringList.toArray(new String[]{});
|
||||
}]]></programlisting>
|
||||
|
||||
<para>
|
||||
However, with SpEL such a requirement could actually be handled relatively easily with a
|
||||
one-line expression, thus sparing you from writing a custom class and configuring it as a bean.
|
||||
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<int:aggregator input-channel="aggChannel"
|
||||
output-channel="replyChannel"
|
||||
expression="#this.![payload].toArray()"/>]]></programlisting>
|
||||
|
||||
output-channel="replyChannel"
|
||||
expression="#this.![payload].toArray()"/>]]></programlisting>
|
||||
<para>
|
||||
In the above configuration we are using a <ulink
|
||||
url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html#d0e12113">Collection Projection</ulink> expression
|
||||
to assemble a new collection from the payloads of all messages in the list and then transforming it to an Array, thus
|
||||
@@ -726,28 +722,28 @@ then you should simply provide an implementation of the <classname>ReleaseStrate
|
||||
<interfacename>MessageGroupStore</interfacename>.
|
||||
|
||||
|
||||
<programlisting><![CDATA[public interface MessageGroupStore {
|
||||
int getMessageCountForAllMessageGroups();
|
||||
<programlisting language="java"><![CDATA[public interface MessageGroupStore {
|
||||
int getMessageCountForAllMessageGroups();
|
||||
|
||||
int getMarkedMessageCountForAllMessageGroups();
|
||||
int getMarkedMessageCountForAllMessageGroups();
|
||||
|
||||
int getMessageGroupCount();
|
||||
int getMessageGroupCount();
|
||||
|
||||
MessageGroup getMessageGroup(Object groupId);
|
||||
MessageGroup getMessageGroup(Object groupId);
|
||||
|
||||
MessageGroup addMessageToGroup(Object groupId, Message<?> message);
|
||||
MessageGroup addMessageToGroup(Object groupId, Message<?> message);
|
||||
|
||||
MessageGroup markMessageGroup(MessageGroup group);
|
||||
MessageGroup markMessageGroup(MessageGroup group);
|
||||
|
||||
MessageGroup removeMessageFromGroup(Object key, Message<?> messageToRemove);
|
||||
MessageGroup removeMessageFromGroup(Object key, Message<?> messageToRemove);
|
||||
|
||||
MessageGroup markMessageFromGroup(Object key, Message<?> messageToMark);
|
||||
MessageGroup markMessageFromGroup(Object key, Message<?> messageToMark);
|
||||
|
||||
void removeMessageGroup(Object groupId);
|
||||
void removeMessageGroup(Object groupId);
|
||||
|
||||
void registerMessageGroupExpiryCallback(MessageGroupCallback callback);
|
||||
void registerMessageGroupExpiryCallback(MessageGroupCallback callback);
|
||||
|
||||
int expireMessageGroups(long timeout);
|
||||
int expireMessageGroups(long timeout);
|
||||
}]]></programlisting>
|
||||
|
||||
For more information please refer to the
|
||||
@@ -763,9 +759,9 @@ then you should simply provide an implementation of the <classname>ReleaseStrate
|
||||
callbacks to apply to its <interfacename>MessageGroups</interfacename> when they
|
||||
expire. The interface is very straightforward:</para>
|
||||
|
||||
<programlisting><![CDATA[public interface MessageGroupCallback {
|
||||
<programlisting language="java"><![CDATA[public interface MessageGroupCallback {
|
||||
|
||||
void execute(MessageGroupStore messageGroupStore, MessageGroup group);
|
||||
void execute(MessageGroupStore messageGroupStore, MessageGroup group);
|
||||
|
||||
}]]></programlisting>
|
||||
|
||||
@@ -787,13 +783,13 @@ then you should simply provide an implementation of the <classname>ReleaseStrate
|
||||
for the message expiry in the form of a
|
||||
<classname>MessageGroupStoreReaper</classname>:</para>
|
||||
|
||||
<programlisting><![CDATA[<bean id="reaper" class="org...MessageGroupStoreReaper">
|
||||
<property name="messageGroupStore" ref="messageStore"/>
|
||||
<property name="timeout" value="30000"/>
|
||||
<programlisting language="xml"><![CDATA[<bean id="reaper" class="org...MessageGroupStoreReaper">
|
||||
<property name="messageGroupStore" ref="messageStore"/>
|
||||
<property name="timeout" value="30000"/>
|
||||
</bean>
|
||||
|
||||
<task:scheduled-tasks scheduler="scheduler">
|
||||
<task:scheduled ref="reaper" method="run" fixed-rate="10000"/>
|
||||
<task:scheduled ref="reaper" method="run" fixed-rate="10000"/>
|
||||
</task:scheduled-tasks>]]></programlisting>
|
||||
|
||||
<para>The reaper is a <interfacename>Runnable</interfacename>, and all that is happening
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
poller's "maxMessagesPerPoll" property will enforce a limit on the throughput.
|
||||
</para>
|
||||
<para>
|
||||
Another valid use for a Messaging Bridge is to connect two different systems. In such a scenario, Spring
|
||||
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 <emphasis>Transformer</emphasis> between the
|
||||
two systems to translate between their formats, and in that case, the channels would be provided as the
|
||||
@@ -31,18 +31,18 @@
|
||||
<para>
|
||||
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:
|
||||
<programlisting language="xml"><![CDATA[ <int:bridge input-channel="input" output-channel="output"/>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int:bridge input-channel="input" output-channel="output"/>]]></programlisting>
|
||||
As mentioned above, a common use case for the Messaging Bridge is to connect a
|
||||
<interfacename>PollableChannel</interfacename> to a <interfacename>SubscribableChannel</interfacename>, and when
|
||||
performing this role, the Messaging Bridge may also serve as a throttler:
|
||||
<programlisting language="xml"><![CDATA[ <int:bridge input-channel="pollable" output-channel="subscribable">
|
||||
<programlisting language="xml"><![CDATA[<int:bridge input-channel="pollable" output-channel="subscribable">
|
||||
<int:poller max-messages-per-poll="10" fixed-rate="5000"/>
|
||||
</int:bridge>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
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.
|
||||
<programlisting language="xml"><![CDATA[ <int-stream:stdin-channel-adapter id="stdin"/>
|
||||
<programlisting language="xml"><![CDATA[<int-stream:stdin-channel-adapter id="stdin"/>
|
||||
|
||||
<int-stream:stdout-channel-adapter id="stdout"/>
|
||||
|
||||
|
||||
@@ -58,13 +58,13 @@
|
||||
The <chain> element provides an <code>input-channel</code> attribute, and if the last element in the chain is capable
|
||||
of producing reply messages (optional), it also supports an <code>output-channel</code> 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.
|
||||
<programlisting language="xml"><![CDATA[ <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-enricher>
|
||||
<int:service-activator ref="someService" method="someMethod"/>
|
||||
</int:chain>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<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-enricher>
|
||||
<int:service-activator ref="someService" method="someMethod"/>
|
||||
</int:chain>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
The <header-enricher> element used in the above example will set a message header named "foo" with a value
|
||||
@@ -75,14 +75,14 @@
|
||||
<para>
|
||||
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>:
|
||||
<programlisting language="xml"><![CDATA[ <int:chain input-channel="input">
|
||||
<si-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-enricher>
|
||||
<int:logging-channel-adapter level="INFO" log-full-message="true"/>
|
||||
</int:chain>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int:chain input-channel="input">
|
||||
<si-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-enricher>
|
||||
<int:logging-channel-adapter level="INFO" log-full-message="true"/>
|
||||
</int:chain>]]></programlisting>
|
||||
</para>
|
||||
<para><emphasis>Disallowed Attributes and Elements</emphasis></para>
|
||||
<para>
|
||||
@@ -130,7 +130,7 @@
|
||||
To accomplish this you can utilize a Messaging Gateway by including a <gateway> element.
|
||||
For example:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[ <int:chain id="main-chain" input-channel="in" output-channel="out">
|
||||
<programlisting language="xml"><![CDATA[<int:chain id="main-chain" input-channel="in" output-channel="out">
|
||||
<int:header-enricher>
|
||||
<int:header name="name" value="Many" />
|
||||
</int:header-enricher>
|
||||
@@ -138,26 +138,26 @@
|
||||
<bean class="org.foo.SampleService" />
|
||||
</int:service-activator>
|
||||
<int:gateway request-channel="inputA"/>
|
||||
</int:chain>
|
||||
</int:chain>
|
||||
|
||||
<int:chain id="nested-chain-a" input-channel="inputA">
|
||||
<int:chain id="nested-chain-a" input-channel="inputA">
|
||||
<int:header-enricher>
|
||||
<int:header name="name" value="Moe" />
|
||||
<int:header name="name" value="Moe" />
|
||||
</int:header-enricher>
|
||||
<int:gateway request-channel="inputB"/>
|
||||
<int:service-activator>
|
||||
<bean class="org.foo.SampleService" />
|
||||
<bean class="org.foo.SampleService" />
|
||||
</int:service-activator>
|
||||
</int:chain>
|
||||
</int:chain>
|
||||
|
||||
<int:chain id="nested-chain-b" input-channel="inputB">
|
||||
<int:chain id="nested-chain-b" input-channel="inputB">
|
||||
<int:header-enricher>
|
||||
<int:header name="name" value="Jack" />
|
||||
<int:header name="name" value="Jack" />
|
||||
</int:header-enricher>
|
||||
<int:service-activator>
|
||||
<bean class="org.foo.SampleService" />
|
||||
<bean class="org.foo.SampleService" />
|
||||
</int:service-activator>
|
||||
</int:chain>]]></programlisting>
|
||||
</int:chain>]]></programlisting>
|
||||
<para>
|
||||
In the above example the <emphasis>nested-chain-a</emphasis> will be called at the end of
|
||||
<emphasis>main-chain</emphasis> processing by the 'gateway' element configured there. While in
|
||||
|
||||
@@ -92,8 +92,7 @@
|
||||
|
||||
If the channel being adapted is a <interfacename>PollableChannel</interfacename>, provide a poller sub-element:
|
||||
<programlisting language="xml"><![CDATA[<int:outbound-channel-adapter channel="channel2" ref="target" method="handle">
|
||||
]]><emphasis><![CDATA[<int:poller fixed-rate="3000"/>
|
||||
]]></emphasis><![CDATA[
|
||||
]]><emphasis><![CDATA[<int:poller fixed-rate="3000"/>]]></emphasis><![CDATA[
|
||||
</int:outbound-channel-adapter>
|
||||
|
||||
<beans:bean id="target" class="org.Foo"/>
|
||||
@@ -105,8 +104,7 @@
|
||||
is only referenced by a single definition of the <code><outbound-channel-adapter></code>, you
|
||||
can define it as inner bean:
|
||||
<programlisting language="xml"><![CDATA[<int:outbound-channel-adapter channel="channel" method="handle">
|
||||
<beans:bean class="org.Foo"/>
|
||||
]]><![CDATA[
|
||||
<beans:bean class="org.Foo"/>
|
||||
</int:outbound-channel-adapter>
|
||||
]]></programlisting>
|
||||
</para>
|
||||
|
||||
@@ -241,18 +241,17 @@
|
||||
<title>Scoped Channel</title>
|
||||
<para>
|
||||
Spring Integration 1.0 provided a <classname>ThreadLocalChannel</classname> implementation, but that has been removed as of 2.0. Now, there is a more general way for handling the same requirement by simply adding a "scope" attribute to a channel. The value of the attribute can be any name of a Scope that is available within the context. For example, in a web environment, certain Scopes are available, and any custom Scope implementations can be registered with the context. Here's an example of a ThreadLocal-based scope being applied to a channel, including the registration of the Scope itself.</para>
|
||||
<programlisting language="xml"><![CDATA[ <int:channel id="threadScopedChannel" scope="thread">
|
||||
<programlisting language="xml"><![CDATA[<int:channel id="threadScopedChannel" scope="thread">
|
||||
<int:queue />
|
||||
</int:channel>
|
||||
</int:channel>
|
||||
|
||||
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
|
||||
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
|
||||
<property name="scopes">
|
||||
<map>
|
||||
<entry key="thread" value="org.springframework.context.support.SimpleThreadScope" />
|
||||
</map>
|
||||
</property>
|
||||
</bean>
|
||||
]]></programlisting>
|
||||
<map>
|
||||
<entry key="thread" value="org.springframework.context.support.SimpleThreadScope" />
|
||||
</map>
|
||||
</property>
|
||||
</bean>]]></programlisting>
|
||||
<para>
|
||||
The channel above also delegates to a queue internally, but the channel is bound
|
||||
to the current thread, so the contents of the queue are as well. That way the thread that
|
||||
@@ -707,13 +706,13 @@ payload to an Integer.
|
||||
As mentioned above, Spring Integration provides a simple <emphasis>Wire Tap</emphasis> interceptor out of
|
||||
the box. You can configure a <emphasis>Wire Tap</emphasis> on any channel within an <interceptors/> element.
|
||||
This is especially useful for debugging, and can be used in conjunction with Spring Integration's logging
|
||||
Channel Adapter as follows: <programlisting language="xml"><![CDATA[ <int:channel id="in">
|
||||
<int:interceptors>
|
||||
<int:wire-tap channel="logger"/>
|
||||
</int:interceptors>
|
||||
</int:channel>
|
||||
Channel Adapter as follows: <programlisting language="xml"><![CDATA[<int:channel id="in">
|
||||
<int:interceptors>
|
||||
<int:wire-tap channel="logger"/>
|
||||
</int:interceptors>
|
||||
</int:channel>
|
||||
|
||||
<int:logging-channel-adapter id="logger" level="DEBUG"/>]]></programlisting>
|
||||
<int:logging-channel-adapter id="logger" level="DEBUG"/>]]></programlisting>
|
||||
<tip>
|
||||
The 'logging-channel-adapter' also accepts an 'expression' attribute so that you can evaluate
|
||||
a SpEL expression against 'payload' and/or 'headers' variables. Alternatively, to simply log
|
||||
|
||||
@@ -61,31 +61,33 @@
|
||||
File, etc.) that provides namespace support defines its elements within a separate schema. In order to use these
|
||||
elements, simply add the necessary namespaces with an "xmlns" entry and the corresponding "schemaLocation" mapping.
|
||||
For example, the following root element shows several of these namespace declarations:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-file="http://www.springframework.org/schema/integration/file"
|
||||
xmlns:int-jms="http://www.springframework.org/schema/integration/jms"
|
||||
xmlns:int-mail="http://www.springframework.org/schema/integration/mail"
|
||||
xmlns:int-rmi="http://www.springframework.org/schema/integration/rmi"
|
||||
xmlns:int-ws="http://www.springframework.org/schema/integration/ws"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration
|
||||
http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/file
|
||||
http://www.springframework.org/schema/integration/file/spring-integration-file.xsd
|
||||
http://www.springframework.org/schema/integration/jms
|
||||
http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd
|
||||
http://www.springframework.org/schema/integration/mail
|
||||
http://www.springframework.org/schema/integration/mail/spring-integration-mail.xsd
|
||||
http://www.springframework.org/schema/integration/rmi
|
||||
http://www.springframework.org/schema/integration/rmi/spring-integration-rmi.xsd
|
||||
http://www.springframework.org/schema/integration/ws
|
||||
http://www.springframework.org/schema/integration/ws/spring-integration-ws.xsd">
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-file="http://www.springframework.org/schema/integration/file"
|
||||
xmlns:int-jms="http://www.springframework.org/schema/integration/jms"
|
||||
xmlns:int-mail="http://www.springframework.org/schema/integration/mail"
|
||||
xmlns:int-rmi="http://www.springframework.org/schema/integration/rmi"
|
||||
xmlns:int-ws="http://www.springframework.org/schema/integration/ws"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration
|
||||
http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/file
|
||||
http://www.springframework.org/schema/integration/file/spring-integration-file.xsd
|
||||
http://www.springframework.org/schema/integration/jms
|
||||
http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd
|
||||
http://www.springframework.org/schema/integration/mail
|
||||
http://www.springframework.org/schema/integration/mail/spring-integration-mail.xsd
|
||||
http://www.springframework.org/schema/integration/rmi
|
||||
http://www.springframework.org/schema/integration/rmi/spring-integration-rmi.xsd
|
||||
http://www.springframework.org/schema/integration/ws
|
||||
http://www.springframework.org/schema/integration/ws/spring-integration-ws.xsd">
|
||||
...
|
||||
</beans>]]></programlisting>
|
||||
<para>
|
||||
The reference manual provides specific examples of the various elements in their corresponding chapters. Here, the
|
||||
main thing to recognize is the consistency of the naming for each namespace URI and schema location.
|
||||
</para>
|
||||
@@ -94,11 +96,11 @@
|
||||
<section id="namespace-taskscheduler">
|
||||
<title>Configuring the Task Scheduler</title>
|
||||
<para>
|
||||
|
||||
|
||||
In Spring Integration, the ApplicationContext plays the central role of a Message Bus, and there are only a
|
||||
couple configuration options to consider. First, you may want to control the central TaskScheduler instance.
|
||||
You can do so by providing a single bean with the name "taskScheduler". This is also defined as a constant:
|
||||
<programlisting><![CDATA[ IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME ]]></programlisting>
|
||||
<programlisting language="java"><![CDATA[IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME ]]></programlisting>
|
||||
By default Spring Integration relies on an instance of ThreadPoolTaskScheduler as described in the <ulink url="http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/scheduling.html">Task Execution and Scheduling</ulink>
|
||||
section of the Spring Framework reference manual. That default TaskScheduler will startup automatically with a
|
||||
pool of 10 threads. If you provide your own TaskScheduler instance instead, you can set the 'autoStartup' property
|
||||
@@ -120,9 +122,9 @@
|
||||
as described <ulink url="http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/scheduling.html#scheduling-task-scheduler-implementations">
|
||||
here</ulink>, instead of the default <emphasis>taskScheduler</emphasis>. To do that, simply define a bean
|
||||
with the appropriate JNDI name for your environment, for example:
|
||||
<programlisting><![CDATA[<bean id="taskScheduler" class="org.springframework.scheduling.commonj.TimerManagerTaskScheduler">
|
||||
<property name="timerManagerName" value="tm/MyTimerManager" />
|
||||
<property name="resourceRef" value="true" />
|
||||
<programlisting language="xml"><![CDATA[<bean id="taskScheduler" class="o.s.scheduling.commonj.TimerManagerTaskScheduler">
|
||||
<property name="timerManagerName" value="tm/MyTimerManager" />
|
||||
<property name="resourceRef" value="true" />
|
||||
</bean>]]></programlisting>
|
||||
</important>
|
||||
<para>
|
||||
@@ -169,9 +171,9 @@
|
||||
Whenever relying on Spring Integration's XML namespace support, a default "errorChannel" bean will be
|
||||
created behind the scenes. However, you can just as easily define your own if you want to control the
|
||||
settings.
|
||||
<programlisting language="xml"><![CDATA[ <int:channel id="errorChannel">
|
||||
<int:queue capacity="500"/>
|
||||
</int:channel>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int:channel id="errorChannel">
|
||||
<int:queue capacity="500"/>
|
||||
</int:channel>]]></programlisting>
|
||||
<note>
|
||||
The default "errorChannel" is a PublishSubscribeChannel.
|
||||
</note>
|
||||
@@ -197,17 +199,17 @@
|
||||
<section id="annotations">
|
||||
<title>Annotation Support</title>
|
||||
<para>
|
||||
In addition to the XML namespace support for configuring Message Endpoints,
|
||||
it is also possible to use annotations. First, Spring Integration provides
|
||||
the class-level <interfacename>@MessageEndpoint</interfacename> as a
|
||||
<emphasis>stereotype</emphasis> annotation, meaning that it is itself
|
||||
annotated with Spring's <interfacename>@Component</interfacename> annotation
|
||||
and is therefore recognized automatically as a bean definition when using
|
||||
In addition to the XML namespace support for configuring Message Endpoints,
|
||||
it is also possible to use annotations. First, Spring Integration provides
|
||||
the class-level <interfacename>@MessageEndpoint</interfacename> as a
|
||||
<emphasis>stereotype</emphasis> annotation, meaning that it is itself
|
||||
annotated with Spring's <interfacename>@Component</interfacename> annotation
|
||||
and is therefore recognized automatically as a bean definition when using
|
||||
Spring component-scanning.
|
||||
</para>
|
||||
<para>
|
||||
Even more important are the various method-level annotations that indicate
|
||||
the annotated method is capable of handling a message. The following example
|
||||
Even more important are the various method-level annotations that indicate
|
||||
the annotated method is capable of handling a message. The following example
|
||||
demonstrates both:
|
||||
<programlisting language="java">@MessageEndpoint
|
||||
public class FooService {
|
||||
@@ -219,7 +221,7 @@ public class FooService {
|
||||
}</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Exactly what it means for the method to "handle" the Message depends on the
|
||||
Exactly what it means for the method to "handle" the Message depends on the
|
||||
particular annotation. Annotations available in Spring Integration include:
|
||||
<itemizedlist>
|
||||
<listitem>@Aggregator</listitem>
|
||||
@@ -233,16 +235,16 @@ public class FooService {
|
||||
<para>The behavior of each is described in its own chapter or section within
|
||||
this reference.</para>
|
||||
<note>
|
||||
If you are using XML configuration in combination with annotations, the
|
||||
<interfacename>@MessageEndpoint</interfacename> annotation is not required.
|
||||
If you want to configure a POJO reference from the "ref" attribute of a
|
||||
<service-activator/> element, it is sufficient to provide the
|
||||
method-level annotations. In that case, the annotation prevents ambiguity
|
||||
If you are using XML configuration in combination with annotations, the
|
||||
<interfacename>@MessageEndpoint</interfacename> annotation is not required.
|
||||
If you want to configure a POJO reference from the "ref" attribute of a
|
||||
<service-activator/> element, it is sufficient to provide the
|
||||
method-level annotations. In that case, the annotation prevents ambiguity
|
||||
even when no "method" attribute exists on the <service-activator/> element.
|
||||
</note>
|
||||
<para>
|
||||
In most cases, the annotated handler method should not require the
|
||||
<classname>Message</classname> type as its parameter. Instead, the method
|
||||
In most cases, the annotated handler method should not require the
|
||||
<classname>Message</classname> type as its parameter. Instead, the method
|
||||
parameter type can match the message's payload type.
|
||||
<programlisting language="java">public class FooService {
|
||||
|
||||
@@ -311,7 +313,7 @@ public class FooService {
|
||||
to use XML for the endpoints, since it is easier to keep track of the overall configuration in a single, external
|
||||
location (and besides the namespace-based XML configuration is not very verbose). If you do prefer to provide
|
||||
channels with the annotations however, you just need to enable a SI Annotations BeanPostProcessor. The following element should
|
||||
be added: <programlisting language="xml"><![CDATA[ <int:annotation-config/> ]]></programlisting>
|
||||
be added: <programlisting language="xml"><![CDATA[<int:annotation-config/>]]></programlisting>
|
||||
<note>
|
||||
When configuring the "inputChannel" and "outputChannel" with annotations, the "inputChannel"
|
||||
<emphasis>must</emphasis> be a reference to a <interfacename>SubscribableChannel</interfacename> instance.
|
||||
|
||||
@@ -37,8 +37,8 @@
|
||||
For example, to execute a method on a Spring Bean a client
|
||||
could send a message to the operation channel as follows:</para>
|
||||
|
||||
<programlisting>Message operation = MessageBuilder.withPayload("@myServiceBean.shutdown()").build();
|
||||
operationChannel.send(operation)</programlisting>
|
||||
<programlisting language="java"><![CDATA[Message operation = MessageBuilder.withPayload("@myServiceBean.shutdown()").build();
|
||||
operationChannel.send(operation)]]></programlisting>
|
||||
|
||||
<para>The root of the context for the expression is the
|
||||
<classname>Message</classname> itself, so you also have access to the 'payload'
|
||||
|
||||
@@ -24,11 +24,12 @@
|
||||
but the delayer also has 'default-delay' and 'delay-header-name' attributes that are used to
|
||||
determine the number of milliseconds
|
||||
that each Message should be delayed. The following delays all messages by 3 seconds:
|
||||
<programlisting language="xml"><![CDATA[ <int:delayer id="delayer" input-channel="input" default-delay="3000" output-channel="output"/>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int:delayer id="delayer" input-channel="input"
|
||||
default-delay="3000" output-channel="output"/>]]></programlisting>
|
||||
If you need per-Message determination of the delay, then you can also provide the name of a header
|
||||
using the 'delay-header-name' attribute:
|
||||
<programlisting language="xml"><![CDATA[ <int:delayer id="delayer" input-channel="input" output-channel="output"
|
||||
default-delay="3000" delay-header-name="delay"/>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int:delayer id="delayer" input-channel="input" output-channel="output"
|
||||
default-delay="3000" delay-header-name="delay"/>]]></programlisting>
|
||||
In the example above the 3 second delay would only apply in the case that the header value is
|
||||
not present for a given inbound Message. If you only want to apply a delay to Messages that have
|
||||
an explicit header value, then you can set the 'default-delay' to 0 or don't use it at all (by default it is 0).
|
||||
@@ -53,11 +54,11 @@
|
||||
provided by Spring Integration on startup: <xref linkend="namespace-taskscheduler"/>.
|
||||
If you want to delegate to a different scheduler, you can provide a reference through the delayer element's
|
||||
'scheduler' attribute:
|
||||
<programlisting language="xml"><![CDATA[ <int:delayer id="delayer" input-channel="input" output-channel="output"
|
||||
delay-header-name="delay"
|
||||
scheduler="exampleTaskScheduler"/>
|
||||
<programlisting language="xml"><![CDATA[<int:delayer id="delayer" input-channel="input" output-channel="output"
|
||||
delay-header-name="delay"
|
||||
scheduler="exampleTaskScheduler"/>
|
||||
|
||||
<task:scheduler id="exampleTaskScheduler" pool-size="3"/>]]></programlisting>
|
||||
<task:scheduler id="exampleTaskScheduler" pool-size="3"/>]]></programlisting>
|
||||
<tip>
|
||||
If you configure an external <classname>ThreadPoolTaskScheduler</classname>
|
||||
you can set on this scheduler property <code>waitForTasksToCompleteOnShutdown = true</code>.
|
||||
@@ -104,18 +105,18 @@
|
||||
Message will remain in the persistent <interfacename>MessageStore</interfacename>. You can use any custom
|
||||
<interfacename>org.aopalliance.aop.Advice</interfacename> implementation within the <code><advice-chain></code>.
|
||||
A sample configuration of the <code><delayer></code> may look like this:
|
||||
<programlisting language="xml"><![CDATA[ <int:delayer id="delayer" input-channel="input" output-channel="output"
|
||||
delay-header-name="delay"
|
||||
message-store="jdbcMessageStore">
|
||||
<int:advice-chain>
|
||||
<beans:ref bean="customAdviceBean"/>
|
||||
<tx:advice>
|
||||
<tx:attributes>
|
||||
<tx:method name="*" read-only="true"/>
|
||||
</tx:attributes>
|
||||
</tx:advice>
|
||||
</int:advice-chain>
|
||||
</int:delayer>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int:delayer id="delayer" input-channel="input" output-channel="output"
|
||||
delay-header-name="delay"
|
||||
message-store="jdbcMessageStore">
|
||||
<int:advice-chain>
|
||||
<beans:ref bean="customAdviceBean"/>
|
||||
<tx:advice>
|
||||
<tx:attributes>
|
||||
<tx:method name="*" read-only="true"/>
|
||||
</tx:attributes>
|
||||
</tx:advice>
|
||||
</int:advice-chain>
|
||||
</int:delayer>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
The <classname>DelayHandler</classname> can be exported as a JMX <code>MBean</code>
|
||||
@@ -123,9 +124,9 @@
|
||||
which allows the rescheduling of delayed persisted Messages at runtime, for example, if the
|
||||
<interfacename>TaskScheduler</interfacename> has previously been stopped.
|
||||
These operations can be invoked via a <code>Control Bus</code> command:
|
||||
<programlisting language="java"><![CDATA[ Message<String> delayerReschedulingMessage =
|
||||
MessageBuilder.withPayload("@'delayer.handler'.reschedulePersistedMessages()").build();
|
||||
controlBusChannel.send(delayerReschedulingMessage);]]></programlisting>
|
||||
<programlisting language="java"><![CDATA[Message<String> delayerReschedulingMessage =
|
||||
MessageBuilder.withPayload("@'delayer.handler'.reschedulePersistedMessages()").build();
|
||||
controlBusChannel.send(delayerReschedulingMessage);]]></programlisting>
|
||||
</para>
|
||||
<note>
|
||||
For more information regarding the Message Store, JMX and the Control Bus, please read <xref linkend="system-management-chapter"/>.
|
||||
|
||||
@@ -372,20 +372,20 @@ consumer.setTaskExecutor(taskExecutor);</programlisting>
|
||||
|
||||
<para>
|
||||
For example, a simple interval-based poller with a 1-second interval would be
|
||||
configured like this: <programlisting language="xml"><![CDATA[ <int:transformer input-channel="pollable"
|
||||
ref="transformer"
|
||||
output-channel="output">
|
||||
<int:poller fixed-rate="1000"/>
|
||||
configured like this: <programlisting language="xml"><![CDATA[<int:transformer input-channel="pollable"
|
||||
ref="transformer"
|
||||
output-channel="output">
|
||||
<int:poller fixed-rate="1000"/>
|
||||
</int:transformer>]]></programlisting>
|
||||
As an alternative to <emphasis>fixed-rate</emphasis> you can also use the <emphasis>fixed-delay</emphasis> attribute.
|
||||
</para>
|
||||
<para>
|
||||
For a poller based on a Cron expression, use the <emphasis>cron</emphasis> attribute instead:
|
||||
<programlisting language="xml"><![CDATA[ <int:transformer input-channel="pollable"
|
||||
ref="transformer"
|
||||
output-channel="output">
|
||||
<int:poller cron="*/10 * * * * MON-FRI"/>
|
||||
</int:transformer>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int:transformer input-channel="pollable"
|
||||
ref="transformer"
|
||||
output-channel="output">
|
||||
<int:poller cron="*/10 * * * * MON-FRI"/>
|
||||
</int:transformer>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
If the input channel is a <interfacename>PollableChannel</interfacename>, then the poller configuration is
|
||||
@@ -398,13 +398,13 @@ consumer.setTaskExecutor(taskExecutor);</programlisting>
|
||||
It is also possible to create top-level pollers in which case only a <emphasis>ref</emphasis> is required:
|
||||
</para>
|
||||
|
||||
<programlisting language="xml"><![CDATA[ <int:poller id="weekdayPoller" cron="*/10 * * * * MON-FRI"/>
|
||||
<programlisting language="xml"><![CDATA[<int:poller id="weekdayPoller" cron="*/10 * * * * MON-FRI"/>
|
||||
|
||||
<int:transformer input-channel="pollable"
|
||||
ref="transformer"
|
||||
output-channel="output">
|
||||
<int:transformer input-channel="pollable"
|
||||
ref="transformer"
|
||||
output-channel="output">
|
||||
<int:poller ref="weekdayPoller"/>
|
||||
</int:transformer>]]></programlisting>
|
||||
</int:transformer>]]></programlisting>
|
||||
|
||||
<note>
|
||||
<para>
|
||||
@@ -423,12 +423,12 @@ consumer.setTaskExecutor(taskExecutor);</programlisting>
|
||||
endpoint with a PollableChannel for its input-channel that is defined within the same ApplicationContext and has
|
||||
no explicitly configured <emphasis>poller</emphasis> sub-element will use that default.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[ <int:poller id="defaultPoller" default="true" max-messages-per-poll="5" fixed-rate="3000"/>
|
||||
<programlisting language="xml"><![CDATA[<int:poller id="defaultPoller" default="true" max-messages-per-poll="5" fixed-rate="3000"/>
|
||||
|
||||
<!-- No <poller/> sub-element is necessary since there is a default -->
|
||||
<int:transformer input-channel="pollable"
|
||||
ref="transformer"
|
||||
output-channel="output"/>]]></programlisting>
|
||||
<!-- No <poller/> sub-element is necessary since there is a default -->
|
||||
<int:transformer input-channel="pollable"
|
||||
ref="transformer"
|
||||
output-channel="output"/>]]></programlisting>
|
||||
|
||||
<para>
|
||||
<emphasis>Transaction Support</emphasis>
|
||||
@@ -491,12 +491,12 @@ any transaction configuration essentially allowing you to enhance the behavior o
|
||||
(the other major factor being the expected volume on the channel to which the endpoint subscribes). To enable
|
||||
concurrency for a polling endpoint that is configured with the XML namespace support, provide the <emphasis>task-executor</emphasis>
|
||||
reference on its <poller/> element and then provide one or more of the properties shown below:
|
||||
<programlisting language="xml"><![CDATA[ <int:poller task-executor="pool" fixed-rate="1000"/>
|
||||
<programlisting language="xml"><![CDATA[<int:poller task-executor="pool" fixed-rate="1000"/>
|
||||
|
||||
<task:executor id="pool"
|
||||
pool-size="5-25"
|
||||
queue-capacity="20"
|
||||
keep-alive="120"/>]]></programlisting>
|
||||
<task:executor id="pool"
|
||||
pool-size="5-25"
|
||||
queue-capacity="20"
|
||||
keep-alive="120"/>]]></programlisting>
|
||||
If no <emphasis>task-executor</emphasis> is provided, the consumer's handler will be invoked in the caller's thread. Note that the
|
||||
<emphasis>caller</emphasis> is usually the default <interfacename>TaskScheduler</interfacename>
|
||||
(see <xref linkend="namespace-taskscheduler"/>). Also, keep in mind that the <emphasis>task-executor</emphasis> attribute can
|
||||
@@ -513,11 +513,11 @@ any transaction configuration essentially allowing you to enhance the behavior o
|
||||
hand when using Spring Integration's own queue-based channels, the timeout value does have a chance to
|
||||
participate. The following example demonstrates how a Polling Consumer will receive Messages nearly
|
||||
instantaneously.
|
||||
<programlisting language="xml"><![CDATA[ <int:service-activator input-channel="someQueueChannel"
|
||||
output-channel="output">
|
||||
<int:poller receive-timeout="30000" fixed-rate="10"/>
|
||||
<programlisting language="xml"><![CDATA[<int:service-activator input-channel="someQueueChannel"
|
||||
output-channel="output">
|
||||
<int:poller receive-timeout="30000" fixed-rate="10"/>
|
||||
|
||||
</int:service-activator>]]></programlisting>
|
||||
</int:service-activator>]]></programlisting>
|
||||
Using this approach does not carry much overhead since internally it is nothing more then a timed-wait thread
|
||||
which does not require nearly as much CPU resource usage as a thrashing, infinite while loop for example.
|
||||
</para>
|
||||
@@ -593,13 +593,13 @@ any transaction configuration essentially allowing you to enhance the behavior o
|
||||
To register a Converter all you need is to implement
|
||||
<interfacename>org.springframework.core.convert.converter.Converter</interfacename> and define it via
|
||||
convenient namespace support:
|
||||
<programlisting language="xml"><![CDATA[ <int:converter ref="sampleConverter"/>
|
||||
<programlisting language="xml"><![CDATA[<int:converter ref="sampleConverter"/>
|
||||
|
||||
<bean id="sampleConverter" class="foo.bar.TestConverter"/>]]></programlisting>
|
||||
|
||||
or as an inner bean:
|
||||
<programlisting language="xml"><![CDATA[ <int:converter>
|
||||
<bean class="org.springframework.integration.config.xml.ConverterParserTests$TestConverter3"/>
|
||||
<programlisting language="xml"><![CDATA[<int:converter>
|
||||
<bean class="o.s.i.config.xml.ConverterParserTests$TestConverter3"/>
|
||||
</int:converter>]]></programlisting>
|
||||
</para>
|
||||
<important>
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
A <classname>FileReadingMessageSource</classname> can be used to consume files from the filesystem.
|
||||
This is an implementation of <interfacename>MessageSource</interfacename> that creates messages from
|
||||
a file system directory. <programlisting language="xml"><![CDATA[<bean id="pollableFileSource"
|
||||
class="org.springframework.integration.file.FileReadingMessageSource"
|
||||
p:inputDirectory="${input.directory}"/>]]></programlisting>
|
||||
class="org.springframework.integration.file.FileReadingMessageSource"
|
||||
p:inputDirectory="${input.directory}"/>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
To prevent creating messages for certain files, you may supply a
|
||||
@@ -36,9 +36,9 @@
|
||||
<classname>AcceptOnceFileListFilter</classname> is used. This filter
|
||||
ensures files are picked up only once from the directory.
|
||||
<programlisting language="xml"><![CDATA[<bean id="pollableFileSource"
|
||||
class="org.springframework.integration.file.FileReadingMessageSource"
|
||||
p:inputDirectory="${input.directory}"
|
||||
p:filter-ref="customFilterBean"/>]]></programlisting>
|
||||
class="org.springframework.integration.file.FileReadingMessageSource"
|
||||
p:inputDirectory="${input.directory}"
|
||||
p:filter-ref="customFilterBean"/>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
A common problem with reading files is that a file may be detected before
|
||||
@@ -51,19 +51,19 @@
|
||||
The <classname>CompositeFileListFilter</classname> enables the
|
||||
composition.
|
||||
<programlisting language="xml"><![CDATA[<bean id="pollableFileSource"
|
||||
class="org.springframework.integration.file.FileReadingMessageSource"
|
||||
p:inputDirectory="${input.directory}"
|
||||
p:filter-ref="compositeFilter"/>
|
||||
class="org.springframework.integration.file.FileReadingMessageSource"
|
||||
p:inputDirectory="${input.directory}"
|
||||
p:filter-ref="compositeFilter"/>
|
||||
<bean id="compositeFilter"
|
||||
class="org.springframework.integration.file.filters.CompositeFileListFilter">
|
||||
<constructor-arg>
|
||||
<list>
|
||||
<bean class="org.springframework.integration.file.filters.AcceptOnceFileListFilter"/>
|
||||
<bean class="org.springframework.integration.file.filters.RegexPatternFileListFilter">
|
||||
<constructor-arg value="^test.*$"/>
|
||||
</bean>
|
||||
</list>
|
||||
</constructor-arg>
|
||||
class="org.springframework.integration.file.filters.CompositeFileListFilter">
|
||||
<constructor-arg>
|
||||
<list>
|
||||
<bean class="o.s.i.file.filters.AcceptOnceFileListFilter"/>
|
||||
<bean class="o.s.i.file.filters.RegexPatternFileListFilter">
|
||||
<constructor-arg value="^test.*$"/>
|
||||
</bean>
|
||||
</list>
|
||||
</constructor-arg>
|
||||
</bean>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
@@ -85,20 +85,20 @@
|
||||
<para>
|
||||
Within this namespace you can reduce the FileReadingMessageSource and wrap
|
||||
it in an inbound Channel Adapter like this:
|
||||
<programlisting language="xml"><![CDATA[ <int-file:inbound-channel-adapter id="filesIn1"
|
||||
directory="file:${input.directory}" prevent-duplicates="true"/>
|
||||
<programlisting language="xml"><![CDATA[<int-file:inbound-channel-adapter id="filesIn1"
|
||||
directory="file:${input.directory}" prevent-duplicates="true"/>
|
||||
|
||||
<int-file:inbound-channel-adapter id="filesIn2"
|
||||
directory="file:${input.directory}"
|
||||
filter="customFilterBean" />
|
||||
<int-file:inbound-channel-adapter id="filesIn2"
|
||||
directory="file:${input.directory}"
|
||||
filter="customFilterBean" />
|
||||
|
||||
<int-file:inbound-channel-adapter id="filesIn3"
|
||||
directory="file:${input.directory}"
|
||||
filename-pattern="test*" />
|
||||
<int-file:inbound-channel-adapter id="filesIn3"
|
||||
directory="file:${input.directory}"
|
||||
filename-pattern="test*" />
|
||||
|
||||
<int-file:inbound-channel-adapter id="filesIn4"
|
||||
directory="file:${input.directory}"
|
||||
filename-regex="test[0-9]+\.txt" /> ]]></programlisting>
|
||||
<int-file:inbound-channel-adapter id="filesIn4"
|
||||
directory="file:${input.directory}"
|
||||
filename-regex="test[0-9]+\.txt" />]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
The first channel adapter is relying on the default filter that just prevents
|
||||
@@ -117,17 +117,17 @@
|
||||
them from being picked up concurrently. To do this you can use a <interfacename>FileLocker</interfacename>.
|
||||
There is a java.nio based implementation available out of the box, but it is also possible to implement your
|
||||
own locking scheme. The nio locker can be injected as follows
|
||||
<programlisting language="xml"><![CDATA[ <int-file:inbound-channel-adapter id="filesIn"
|
||||
directory="file:${input.directory}" prevent-duplicates="true">
|
||||
<int-file:nio-locker/>
|
||||
</int-file:inbound-channel-adapter>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int-file:inbound-channel-adapter id="filesIn"
|
||||
directory="file:${input.directory}" prevent-duplicates="true">
|
||||
<int-file:nio-locker/>
|
||||
</int-file:inbound-channel-adapter>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
A custom locker you can configure like this:
|
||||
<programlisting language="xml"><![CDATA[ <int-file:inbound-channel-adapter id="filesIn"
|
||||
directory="file:${input.directory}" prevent-duplicates="true">
|
||||
<int-file:locker ref="customLocker"/>
|
||||
</int-file:inbound-channel-adapter>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int-file:inbound-channel-adapter id="filesIn"
|
||||
directory="file:${input.directory}" prevent-duplicates="true">
|
||||
<int-file:locker ref="customLocker"/>
|
||||
</int-file:inbound-channel-adapter>]]></programlisting>
|
||||
</para>
|
||||
<note>
|
||||
When a file inbound adapter is configured with a locker, it will take the responsibility to acquire a
|
||||
@@ -143,8 +143,8 @@
|
||||
that Spring Integration uses internally to wire FileListFilters FileLocker to the FileReadingMessageSource.
|
||||
A custom DirectoryScanner can be injected into the <int-file:inbound-channel-adapter/> on the <code>scanner</code>
|
||||
attribute.
|
||||
<programlisting language="xml"><![CDATA[ <int-file:inbound-channel-adapter id="filesIn" directory="file:${input.directory}"
|
||||
prevent-duplicates="true" scanner="customDirectoryScanner"/>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int-file:inbound-channel-adapter id="filesIn" directory="file:${input.directory}"
|
||||
prevent-duplicates="true" scanner="customDirectoryScanner"/>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
This gives you full freedom to choose the ordering, listing and locking strategies.
|
||||
@@ -355,13 +355,13 @@
|
||||
</section>
|
||||
<section id="file-outbound-channel-adapter">
|
||||
<title>File Outbound Channel Adapter</title>
|
||||
<programlisting language="xml"><![CDATA[ <int-file:outbound-channel-adapter id="filesOut" directory="${input.directory.property}"/>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int-file:outbound-channel-adapter id="filesOut" directory="${input.directory.property}"/>]]></programlisting>
|
||||
<para>
|
||||
The namespace based configuration also supports a <code>delete-source-files</code> attribute.
|
||||
If set to <code>true</code>, it will trigger the deletion of the original source files after writing
|
||||
to a destination. The default value for that flag is <code>false</code>.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[ <int-file:outbound-channel-adapter id="filesOut"
|
||||
<programlisting language="xml"><![CDATA[<int-file:outbound-channel-adapter id="filesOut"
|
||||
directory="${output.directory}"
|
||||
delete-source-files="true"/>]]></programlisting>
|
||||
<note>
|
||||
@@ -436,11 +436,11 @@
|
||||
</para>
|
||||
<para>
|
||||
To configure File specific transformers you can use the appropriate elements from the file namespace.
|
||||
<programlisting language="xml"><![CDATA[ <int-file:file-to-bytes-transformer input-channel="input" output-channel="output"
|
||||
delete-files="true"/>
|
||||
<programlisting language="xml"><![CDATA[<int-file:file-to-bytes-transformer input-channel="input" output-channel="output"
|
||||
delete-files="true"/>
|
||||
|
||||
<int-file:file-to-string-transformer input-channel="input" output-channel="output"
|
||||
delete-files="true" charset="UTF-8"/>]]></programlisting>
|
||||
<int-file:file-to-string-transformer input-channel="input" output-channel="output"
|
||||
delete-files="true" charset="UTF-8"/>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
The <emphasis>delete-files</emphasis> option signals to the transformer that it should delete
|
||||
|
||||
@@ -19,13 +19,13 @@
|
||||
<para>
|
||||
In Spring Integration, a Message Filter may be configured as a Message Endpoint that delegates to an
|
||||
implementation of the <interfacename>MessageSelector</interfacename> interface. That interface is itself quite
|
||||
simple: <programlisting language="java"><![CDATA[ public interface MessageSelector {
|
||||
simple: <programlisting language="java"><![CDATA[public interface MessageSelector {
|
||||
|
||||
boolean accept(Message<?> message);
|
||||
boolean accept(Message<?> message);
|
||||
|
||||
}]]></programlisting>
|
||||
}]]></programlisting>
|
||||
The <classname>MessageFilter</classname> constructor accepts a selector instance:
|
||||
<programlisting language="java"><![CDATA[ MessageFilter filter = new MessageFilter(someSelector);]]></programlisting>
|
||||
<programlisting language="java"><![CDATA[MessageFilter filter = new MessageFilter(someSelector);]]></programlisting>
|
||||
</para>
|
||||
In combination with the namespace and SpEL, very powerful filters can be configured with very little java code.
|
||||
</section>
|
||||
@@ -37,34 +37,34 @@
|
||||
|
||||
<para>
|
||||
The <filter> element is used to create a Message-selecting endpoint. In addition to "<code>input-channel</code>
|
||||
and <code>output-channel</code> attributes, it requires a <code>ref</code>. The <code>ref</code> may point to a
|
||||
and <code>output-channel</code> attributes, it requires a <code>ref</code>. The <code>ref</code> may point to a
|
||||
<interfacename>MessageSelector</interfacename> implementation:
|
||||
<programlisting language="xml"><![CDATA[ <int:filter input-channel="input" ref="selector" output-channel="output"/>
|
||||
<programlisting language="xml"><![CDATA[<int:filter input-channel="input" ref="selector" output-channel="output"/>
|
||||
|
||||
<bean id="selector" class="example.MessageSelectorImpl"/>]]></programlisting>
|
||||
<bean id="selector" class="example.MessageSelectorImpl"/>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Alternatively, the <code>method</code> attribute can be added at which point the <code>ref</code> may refer to any object.
|
||||
The referenced method may expect either the <interfacename>Message</interfacename> type or the payload type of
|
||||
inbound Messages. The method must return a boolean value. If the method returns 'true',
|
||||
the Message <emphasis>will</emphasis> be sent to the output-channel.
|
||||
<programlisting language="xml"><![CDATA[ <int:filter input-channel="input" output-channel="output"
|
||||
ref="exampleObject" method="someBooleanReturningMethod"/>
|
||||
<programlisting language="xml"><![CDATA[<int:filter input-channel="input" output-channel="output"
|
||||
ref="exampleObject" method="someBooleanReturningMethod"/>
|
||||
|
||||
<bean id="exampleObject" class="example.SomeObject"/>]]></programlisting>
|
||||
<bean id="exampleObject" class="example.SomeObject"/>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
If the selector or adapted POJO method returns <code>false</code>, 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
|
||||
<code>throw-exception-on-rejection</code> attribute to <code>true</code>:
|
||||
<programlisting language="xml"><![CDATA[ <int:filter input-channel="input" ref="selector"
|
||||
output-channel="output" throw-exception-on-rejection="true"/> ]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int:filter input-channel="input" ref="selector"
|
||||
output-channel="output" throw-exception-on-rejection="true"/> ]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
<para>
|
||||
If you want rejected messages to be routed to a specific channel, provide that reference as the <code>discard-channel</code>:
|
||||
<programlisting language="xml"><![CDATA[ <int:filter input-channel="input" ref="selector"
|
||||
output-channel="output" discard-channel="rejectedMessages"/> ]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int:filter input-channel="input" ref="selector"
|
||||
output-channel="output" discard-channel="rejectedMessages"/> ]]></programlisting>
|
||||
</para>
|
||||
<note>
|
||||
Message Filters are commonly used in conjunction with a Publish Subscribe Channel. Many filter endpoints may
|
||||
@@ -96,11 +96,11 @@
|
||||
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
|
||||
#{} notation as defined in the
|
||||
<ulink url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html#expressions-beandef">
|
||||
SpEL reference documentation
|
||||
</ulink>.
|
||||
<programlisting language="xml"><![CDATA[<int:filter input-channel="input"
|
||||
<programlisting language="xml"><![CDATA[<int:filter input-channel="input"
|
||||
expression="payload.matches(#{filterPatterns.nonsensePattern})"/>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
@@ -112,59 +112,57 @@
|
||||
had been modified. If the ExpressionSource bean is named "expressionSource", then it is not necessary to provide the
|
||||
<code>source</code> attribute on the <expression> element, but in this case it's shown for completeness.
|
||||
<programlisting language="xml"><![CDATA[<int:filter input-channel="input" output-channel="output">
|
||||
<int:expression key="filterPatterns.example" source="myExpressions"/>
|
||||
</int:filter>
|
||||
<int:expression key="filterPatterns.example" source="myExpressions"/>
|
||||
</int:filter>
|
||||
|
||||
<beans:bean id="myExpressions" id="myExpressions"
|
||||
class="org.springframework.integration.expression.ReloadableResourceBundleExpressionSource">
|
||||
<beans:property name="basename" value="config/integration/expressions"/>
|
||||
<beans:property name="cacheSeconds" value="60"/>
|
||||
</beans:bean>
|
||||
<beans:bean id="myExpressions" id="myExpressions"
|
||||
class="o.s.i.expression.ReloadableResourceBundleExpressionSource">
|
||||
<beans:property name="basename" value="config/integration/expressions"/>
|
||||
<beans:property name="cacheSeconds" value="60"/>
|
||||
</beans:bean>
|
||||
]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
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:
|
||||
<programlisting language="xml"><![CDATA[ filterPatterns.example=payload > 100]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[filterPatterns.example=payload > 100]]></programlisting>
|
||||
|
||||
<note>All of these examples that use <code>expression</code> as an attribute
|
||||
or sub-element can also be applied within transformer, router, splitter,
|
||||
<note>All of these examples that use <code>expression</code> 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
|
||||
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.</note>
|
||||
</para>
|
||||
</section>
|
||||
<section id="filter-annotations">
|
||||
<title>Configuring a Filter with Annotations</title>
|
||||
<para>A filter configured using annotations would look like this.</para>
|
||||
|
||||
<programlisting language="java"><![CDATA[public class PetFilter {
|
||||
...
|
||||
|
||||
@Filter ]]><co id="filterann" /><![CDATA[
|
||||
public boolean dogsOnly(String input) {
|
||||
...
|
||||
}
|
||||
|
||||
}]]></programlisting>
|
||||
|
||||
<para>A filter configured using annotations would look like this.</para>
|
||||
|
||||
<programlisting language="java"><![CDATA[public class PetFilter {
|
||||
...
|
||||
@Filter ]]><co id="filterann" /><![CDATA[
|
||||
public boolean dogsOnly(String input) {
|
||||
...
|
||||
}
|
||||
}]]></programlisting>
|
||||
|
||||
<calloutlist>
|
||||
<callout arearefs="filterann">
|
||||
<para>An annotation indicating that this method shall be used as a
|
||||
filter. Must be specified if this class will be used as a filter.</para>
|
||||
</callout>
|
||||
</calloutlist>
|
||||
|
||||
|
||||
<para>All of the configuration options provided by the xml element are
|
||||
also available for the <interfacename>@Filter</interfacename> annotation.</para>
|
||||
|
||||
|
||||
<para>The filter can be either referenced explicitly from XML or, if
|
||||
the <interfacename>@MessageEndpoint</interfacename> annotation is defined
|
||||
the <interfacename>@MessageEndpoint</interfacename> annotation is defined
|
||||
on the class, detected automatically through classpath scanning.</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -11,12 +11,12 @@
|
||||
The File Transfer Protocol (FTP) is a simple network protocol which allows you to transfer files between two computers on the Internet.
|
||||
</para>
|
||||
<para>
|
||||
There are two actors when it comes to FTP communication: <emphasis>client</emphasis> and <emphasis>server</emphasis>.
|
||||
To transfer files with FTP/FTPS, you use a <emphasis>client</emphasis> which initiates a connection to a remote computer
|
||||
that is running an FTP <emphasis>server</emphasis>. After the connection is established, the <emphasis>client</emphasis> can choose
|
||||
to send and/or receive copies of files.
|
||||
There are two actors when it comes to FTP communication: <emphasis>client</emphasis> and <emphasis>server</emphasis>.
|
||||
To transfer files with FTP/FTPS, you use a <emphasis>client</emphasis> which initiates a connection to a remote computer
|
||||
that is running an FTP <emphasis>server</emphasis>. After the connection is established, the <emphasis>client</emphasis> can choose
|
||||
to send and/or receive copies of files.
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
Spring Integration supports sending and receiving files over FTP/FTPS by providing three <emphasis>client</emphasis>
|
||||
side endpoints: <emphasis>Inbound Channel Adapter</emphasis>, <emphasis>Outbound Channel Adapter</emphasis>, and
|
||||
@@ -26,153 +26,153 @@
|
||||
<para>
|
||||
To use the <emphasis>FTP</emphasis> namespace, add the following to the header of your XML file:
|
||||
<programlisting language="xml"><![CDATA[xmlns:int-ftp="http://www.springframework.org/schema/integration/ftp"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration/ftp
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration/ftp
|
||||
http://www.springframework.org/schema/integration/ftp/spring-integration-ftp.xsd"
|
||||
]]></programlisting>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
<section id="ftp-session-factory">
|
||||
<title>FTP Session Factory</title>
|
||||
<para>
|
||||
Before configuring FTP adapters you must configure an <emphasis>FTP Session Factory</emphasis>. You can configure
|
||||
Before configuring FTP adapters you must configure an <emphasis>FTP Session Factory</emphasis>. You can configure
|
||||
the <emphasis>FTP Session Factory</emphasis> with a regular bean definition where the implementation class is <classname>org.springframework.integration.ftp.session.DefaultFtpSessionFactory</classname>:
|
||||
Below is a basic configuration:
|
||||
|
||||
<programlisting language="xml"><![CDATA[<bean id="ftpClientFactory"
|
||||
class="org.springframework.integration.ftp.session.DefaultFtpSessionFactory">
|
||||
<property name="host" value="localhost"/>
|
||||
<property name="port" value="22"/>
|
||||
<property name="username" value="kermit"/>
|
||||
<property name="password" value="frog"/>
|
||||
<property name="clientMode" value="0"/>
|
||||
<property name="fileType" value="2"/>
|
||||
<property name="bufferSize" value="100000"/>
|
||||
|
||||
<programlisting language="xml"><![CDATA[<bean id="ftpClientFactory"
|
||||
class="org.springframework.integration.ftp.session.DefaultFtpSessionFactory">
|
||||
<property name="host" value="localhost"/>
|
||||
<property name="port" value="22"/>
|
||||
<property name="username" value="kermit"/>
|
||||
<property name="password" value="frog"/>
|
||||
<property name="clientMode" value="0"/>
|
||||
<property name="fileType" value="2"/>
|
||||
<property name="bufferSize" value="100000"/>
|
||||
</bean>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
For FTPS connections all you need to do is use <classname>org.springframework.integration.ftp.session.DefaultFtpsSessionFactory</classname> instead.
|
||||
Below is the complete configuration sample:
|
||||
|
||||
<programlisting language="xml"><![CDATA[<bean id="ftpClientFactory"
|
||||
class="org.springframework.integration.ftp.client.DefaultFtpsClientFactory">
|
||||
<property name="host" value="localhost"/>
|
||||
<property name="port" value="22"/>
|
||||
<property name="username" value="oleg"/>
|
||||
<property name="password" value="password"/>
|
||||
<property name="clientMode" value="1"/>
|
||||
<property name="fileType" value="2"/>
|
||||
<property name="useClientMode" value="true"/>
|
||||
<property name="cipherSuites" value="a,b.c"/>
|
||||
<property name="keyManager" ref="keyManager"/>
|
||||
<property name="protocol" value="SSL"/>
|
||||
<property name="trustManager" ref="trustManager"/>
|
||||
<property name="prot" value="P"/>
|
||||
<property name="needClientAuth" value="true"/>
|
||||
<property name="authValue" value="oleg"/>
|
||||
<property name="sessionCreation" value="true"/>
|
||||
<property name="protocols" value="SSL, TLS"/>
|
||||
<property name="implicit" value="true"/>
|
||||
|
||||
<programlisting language="xml"><![CDATA[<bean id="ftpClientFactory"
|
||||
class="org.springframework.integration.ftp.client.DefaultFtpsClientFactory">
|
||||
<property name="host" value="localhost"/>
|
||||
<property name="port" value="22"/>
|
||||
<property name="username" value="oleg"/>
|
||||
<property name="password" value="password"/>
|
||||
<property name="clientMode" value="1"/>
|
||||
<property name="fileType" value="2"/>
|
||||
<property name="useClientMode" value="true"/>
|
||||
<property name="cipherSuites" value="a,b.c"/>
|
||||
<property name="keyManager" ref="keyManager"/>
|
||||
<property name="protocol" value="SSL"/>
|
||||
<property name="trustManager" ref="trustManager"/>
|
||||
<property name="prot" value="P"/>
|
||||
<property name="needClientAuth" value="true"/>
|
||||
<property name="authValue" value="oleg"/>
|
||||
<property name="sessionCreation" value="true"/>
|
||||
<property name="protocols" value="SSL, TLS"/>
|
||||
<property name="implicit" value="true"/>
|
||||
</bean>]]></programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Every time an adapter requests a session object from its <interfacename>SessionFactory</interfacename> the session is
|
||||
returned from a session pool maintained by a caching wrapper around the factory. A Session in the session pool might go stale
|
||||
(if it has been disconnected by the server due to inactivity) so the <interfacename>SessionFactory</interfacename>
|
||||
Every time an adapter requests a session object from its <interfacename>SessionFactory</interfacename> the session is
|
||||
returned from a session pool maintained by a caching wrapper around the factory. A Session in the session pool might go stale
|
||||
(if it has been disconnected by the server due to inactivity) so the <interfacename>SessionFactory</interfacename>
|
||||
will perform validation to make sure that it never returns a stale session to the adapter. If a stale session was encountered,
|
||||
it will be removed from the pool, and a new one will be created.
|
||||
<note>
|
||||
If you experience connectivity problems and would like to trace Session creation as well as see which Sessions are
|
||||
If you experience connectivity problems and would like to trace Session creation as well as see which Sessions are
|
||||
polled you may enable it by setting the logger to TRACE level (e.g., log4j.category.org.springframework.integration.file=TRACE)
|
||||
</note>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Now all you need to do is inject these session factories into your adapters. Obviously the protocol (FTP or FTPS) that an adapter will
|
||||
Now all you need to do is inject these session factories into your adapters. Obviously the protocol (FTP or FTPS) that an adapter will
|
||||
use depends on the type of session factory that has been injected into the adapter.
|
||||
</para>
|
||||
<para>
|
||||
<note>
|
||||
A more practical way to provide values for <emphasis>FTP/FTPS Session Factories</emphasis> is by using Spring's property
|
||||
A more practical way to provide values for <emphasis>FTP/FTPS Session Factories</emphasis> is by using Spring's property
|
||||
placeholder support (See: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-factory-placeholderconfigurer).
|
||||
</note>
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
<emphasis>Advanced Configuration</emphasis>
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
<classname>DefaultFtpSessionFactory</classname> provides an abstraction over the underlying client API which, in the current release of
|
||||
Spring Integration, is <ulink url="http://commons.apache.org/net/">Apache Commons Net</ulink>. This spares you from the low level configuration details
|
||||
of the <classname>org.apache.commons.net.ftp.FTPClient</classname>. However there are times when access to lower level <classname>FTPClient</classname> details is
|
||||
necessary to achieve more advanced configuration (e.g., setting data timeout, default timeout etc.). For that purpose, <classname>AbstractFtpSessionFactory</classname>
|
||||
<classname>DefaultFtpSessionFactory</classname> provides an abstraction over the underlying client API which, in the current release of
|
||||
Spring Integration, is <ulink url="http://commons.apache.org/net/">Apache Commons Net</ulink>. This spares you from the low level configuration details
|
||||
of the <classname>org.apache.commons.net.ftp.FTPClient</classname>. However there are times when access to lower level <classname>FTPClient</classname> details is
|
||||
necessary to achieve more advanced configuration (e.g., setting data timeout, default timeout etc.). For that purpose, <classname>AbstractFtpSessionFactory</classname>
|
||||
(the base class for all FTP Session Factories) exposes hooks, in the form of the two post-processing methods below.
|
||||
<programlisting language="java"><![CDATA[/**
|
||||
* Will handle additional initialization after client.connect() method was invoked,
|
||||
* but before any action on the client has been taken
|
||||
*/
|
||||
protected void postProcessClientAfterConnect(T t) throws IOException {
|
||||
// NOOP
|
||||
}
|
||||
/**
|
||||
* Will handle additional initialization before client.connect() method was invoked.
|
||||
*/
|
||||
protected void postProcessClientBeforeConnect(T client) throws IOException {
|
||||
// NOOP
|
||||
}]]></programlisting>
|
||||
As you can see, there is no default implementation for these two methods. However, by extending <classname>DefaultFtpSessionFactory</classname> you can override these methods
|
||||
* Will handle additional initialization after client.connect() method was invoked,
|
||||
* but before any action on the client has been taken
|
||||
*/
|
||||
protected void postProcessClientAfterConnect(T t) throws IOException {
|
||||
// NOOP
|
||||
}
|
||||
/**
|
||||
* Will handle additional initialization before client.connect() method was invoked.
|
||||
*/
|
||||
protected void postProcessClientBeforeConnect(T client) throws IOException {
|
||||
// NOOP
|
||||
}]]></programlisting>
|
||||
As you can see, there is no default implementation for these two methods. However, by extending <classname>DefaultFtpSessionFactory</classname> you can override these methods
|
||||
to provide more advanced configuration of the <classname>FTPClient</classname>. For example:
|
||||
<programlisting language="java"><![CDATA[public class AdvancedFtpSessionFactory extends DefaultFtpSessionFactory {
|
||||
|
||||
protected void postProcessClientBeforeConnect(FTPClient ftpClient) throws IOException {
|
||||
ftpClient.setDataTimeout(5000);
|
||||
ftpClient.setDefaultTimeout(5000);
|
||||
}
|
||||
protected void postProcessClientBeforeConnect(FTPClient ftpClient) throws IOException {
|
||||
ftpClient.setDataTimeout(5000);
|
||||
ftpClient.setDefaultTimeout(5000);
|
||||
}
|
||||
}]]></programlisting>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="ftp-inbound">
|
||||
<title>FTP Inbound Channel Adapter</title>
|
||||
<para>
|
||||
The <emphasis>FTP Inbound Channel Adapter</emphasis> is a special listener that will connect to the FTP server and will listen
|
||||
The <emphasis>FTP Inbound Channel Adapter</emphasis> is a special listener that will connect to the FTP server and will listen
|
||||
for the remote directory events (e.g., new file created) at which point it will initiate a file transfer.
|
||||
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-ftp:inbound-channel-adapter id="ftpInbound"
|
||||
channel="ftpChannel"
|
||||
session-factory="ftpSessionFactory"
|
||||
charset="UTF-8"
|
||||
auto-create-local-directory="true"
|
||||
delete-remote-files="true"
|
||||
filename-pattern="*.txt"
|
||||
remote-directory="some/remote/path"
|
||||
remote-file-separator="/"
|
||||
local-filename-generator-expression="#this.toUpperCase() + '.a'"
|
||||
local-directory=".">
|
||||
<int:poller fixed-rate="1000"/>
|
||||
channel="ftpChannel"
|
||||
session-factory="ftpSessionFactory"
|
||||
charset="UTF-8"
|
||||
auto-create-local-directory="true"
|
||||
delete-remote-files="true"
|
||||
filename-pattern="*.txt"
|
||||
remote-directory="some/remote/path"
|
||||
remote-file-separator="/"
|
||||
local-filename-generator-expression="#this.toUpperCase() + '.a'"
|
||||
local-directory=".">
|
||||
<int:poller fixed-rate="1000"/>
|
||||
</int-ftp:inbound-channel-adapter>]]></programlisting>
|
||||
|
||||
As you can see from the configuration above you can configure an <emphasis>FTP Inbound Channel Adapter</emphasis> via the <code>inbound-channel-adapter</code>
|
||||
element while also providing values for various attributes such as <code>local-directory</code>, <code>filename-pattern</code>
|
||||
As you can see from the configuration above you can configure an <emphasis>FTP Inbound Channel Adapter</emphasis> via the <code>inbound-channel-adapter</code>
|
||||
element while also providing values for various attributes such as <code>local-directory</code>, <code>filename-pattern</code>
|
||||
(which is based on simple pattern matching, not regular expressions), and of course the reference to a <code>session-factory</code>.
|
||||
</para>
|
||||
<para>
|
||||
By default the transferred file will carry the same name as the original file. If you want to override this behavior you
|
||||
can set the <code>local-filename-generator-expression</code> attribute which allows you to provide a SpEL Expression to generate
|
||||
the name of the local file. Unlike outbound gateways and adapters where the root object of the SpEL Evaluation Context
|
||||
By default the transferred file will carry the same name as the original file. If you want to override this behavior you
|
||||
can set the <code>local-filename-generator-expression</code> attribute which allows you to provide a SpEL Expression to generate
|
||||
the name of the local file. Unlike outbound gateways and adapters where the root object of the SpEL Evaluation Context
|
||||
is a <classname>Message</classname>, this inbound adapter does not yet have the Message at the time of evaluation since
|
||||
that's what it ultimately generates with the transferred file as its payload. So, the root object of the SpEL Evaluation Context
|
||||
is the original name of the remote file (String).
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
Sometimes file filtering based on the simple pattern specified via <code>filename-pattern</code> attribute might not be
|
||||
sufficient. If this is the case, you can use the <code>filename-regex</code> attribute to specify a Regular Expression
|
||||
sufficient. If this is the case, you can use the <code>filename-regex</code> attribute to specify a Regular Expression
|
||||
(e.g. <code>filename-regex=".*\.test$"</code>). And of course if you need complete control you can use <code>filter</code>
|
||||
attribute and provide a reference to any custom implementation of the
|
||||
attribute and provide a reference to any custom implementation of the
|
||||
<classname>org.springframework.integration.file.filters.FileListFilter</classname>, a strategy interface for filtering a
|
||||
list of files.
|
||||
</para>
|
||||
@@ -181,12 +181,12 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/ftp
|
||||
file separator character to use if the default '/' is not applicable for your particular environment.
|
||||
</note>
|
||||
<para>
|
||||
Please refer to the schema for more details on these attributes.
|
||||
Please refer to the schema for more details on these attributes.
|
||||
</para>
|
||||
<para>
|
||||
It is also important to understand that the <emphasis>FTP Inbound Channel Adapter</emphasis> is a <emphasis>Polling Consumer</emphasis> and
|
||||
It is also important to understand that the <emphasis>FTP Inbound Channel Adapter</emphasis> is a <emphasis>Polling Consumer</emphasis> and
|
||||
therefore you must configure a poller (either via a global default or a local sub-element).
|
||||
Once a file has been transferred, a Message with a <classname>java.io.File</classname> as its payload will be generated and sent to the channel
|
||||
Once a file has been transferred, a Message with a <classname>java.io.File</classname> as its payload will be generated and sent to the channel
|
||||
identified by the <code>channel</code> attribute.
|
||||
</para>
|
||||
<para>
|
||||
@@ -198,18 +198,17 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/ftp
|
||||
As a user in most cases you are only interested in files that are complete and would like to filter only files that are complete.
|
||||
To handle these scenarios you can use the filtering support provided by the <code>filename-pattern</code>, <code>filename-regex</code>
|
||||
and <code>filter</code> attributes. Here is an example that uses a custom Filter implementation.
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-ftp:inbound-channel-adapter
|
||||
channel="ftpChannel"
|
||||
session-factory="ftpSessionFactory"
|
||||
filter="customFilter"
|
||||
local-directory="file:/my_transfers">
|
||||
remote-directory="some/remote/path"
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-ftp:inbound-channel-adapter
|
||||
channel="ftpChannel"
|
||||
session-factory="ftpSessionFactory"
|
||||
filter="customFilter"
|
||||
local-directory="file:/my_transfers">
|
||||
remote-directory="some/remote/path"
|
||||
<int:poller fixed-rate="1000"/>
|
||||
</int-ftp:inbound-channel-adapter>
|
||||
|
||||
<bean id="customFilter" class="org.example.CustomFilter"/>
|
||||
]]></programlisting>
|
||||
|
||||
<bean id="customFilter" class="org.example.CustomFilter"/>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
<emphasis>Poller configuration notes for the inbound FTP adapter</emphasis>
|
||||
@@ -219,25 +218,25 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/ftp
|
||||
<emphasis>1) Communicate with a remote server in order to transfer files from a remote directory to a local directory.</emphasis>
|
||||
<emphasis>2) For each transferred file, generate a Message with that file as a payload and send it to the channel identified by the 'channel' attribute.</emphasis>
|
||||
|
||||
That is why they are called 'channel-adapters' rather than just 'adapters'. The main job of such an adapter is to generate a
|
||||
Message to be sent to a Message Channel. Essentially, the second task mentioned above takes precedence in such a way that
|
||||
*IF* your local directory already has one or more files it will first generate Messages from those, and *ONLY*
|
||||
That is why they are called 'channel-adapters' rather than just 'adapters'. The main job of such an adapter is to generate a
|
||||
Message to be sent to a Message Channel. Essentially, the second task mentioned above takes precedence in such a way that
|
||||
*IF* your local directory already has one or more files it will first generate Messages from those, and *ONLY*
|
||||
when all local files have been processed, will it initiate the remote communication to retrieve more files.
|
||||
</para>
|
||||
<para>
|
||||
Also, when configuring a trigger on the poller you should pay close attention to the <code>max-messages-per-poll</code>
|
||||
Also, when configuring a trigger on the poller you should pay close attention to the <code>max-messages-per-poll</code>
|
||||
attribute. Its default value is 1 for all <classname>SourcePollingChannelAdapter</classname> instances (including FTP).
|
||||
This means that as soon as one file is processed, it will wait for the next execution time as determined by your
|
||||
trigger configuration. If you happened to have one or more files sitting in the <code>local-directory</code>, it would process
|
||||
those files before it would initiate communication with the remote FTP server. And, if the <code>max-messages-per-poll</code>
|
||||
This means that as soon as one file is processed, it will wait for the next execution time as determined by your
|
||||
trigger configuration. If you happened to have one or more files sitting in the <code>local-directory</code>, it would process
|
||||
those files before it would initiate communication with the remote FTP server. And, if the <code>max-messages-per-poll</code>
|
||||
were set to 1 (default), then it would be processing only one file at a time with intervals as defined by your trigger,
|
||||
essentially working as <emphasis>one-poll = one-file</emphasis>.
|
||||
</para>
|
||||
<para>
|
||||
For typical file-transfer use cases, you most likely want the opposite behavior: to process all the files you can for each
|
||||
For typical file-transfer use cases, you most likely want the opposite behavior: to process all the files you can for each
|
||||
poll and only then wait for the next poll. If that is the case, set <code>max-messages-per-poll</code> to -1. Then, on
|
||||
each poll, the adapter will attempt to generate as many Messages as it possibly can. In other words, it will process
|
||||
everything in the local directory, and then it will connect to the remote directory to transfer everything that is available
|
||||
each poll, the adapter will attempt to generate as many Messages as it possibly can. In other words, it will process
|
||||
everything in the local directory, and then it will connect to the remote directory to transfer everything that is available
|
||||
there to be processed locally. Only then is the poll operation considered complete, and the poller will wait for the next execution time.
|
||||
</para>
|
||||
<para>
|
||||
@@ -245,51 +244,51 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/ftp
|
||||
from files with each poll. For example, a value of 10 means that on each poll it will attempt to process no more than 10 files.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="ftp-outbound">
|
||||
<title>FTP Outbound Channel Adapter</title>
|
||||
|
||||
|
||||
<para>
|
||||
The <emphasis>FTP Outbound Channel Adapter</emphasis> relies upon a <classname>MessageHandler</classname> implementation that will connect to the
|
||||
FTP server and initiate an FTP transfer for every file it receives in the payload of incoming Messages. It also supports several
|
||||
The <emphasis>FTP Outbound Channel Adapter</emphasis> relies upon a <classname>MessageHandler</classname> implementation that will connect to the
|
||||
FTP server and initiate an FTP transfer for every file it receives in the payload of incoming Messages. It also supports several
|
||||
representations of the <emphasis>File</emphasis> so you are not limited only to java.io.File typed payloads.
|
||||
The <emphasis>FTP Outbound Channel Adapter</emphasis>
|
||||
supports the following payloads: 1) <classname>java.io.File</classname> - the actual file object;
|
||||
2) <classname>byte[]</classname> - a byte array that represents the file contents; and 3) <classname>java.lang.String</classname> -
|
||||
The <emphasis>FTP Outbound Channel Adapter</emphasis>
|
||||
supports the following payloads: 1) <classname>java.io.File</classname> - the actual file object;
|
||||
2) <classname>byte[]</classname> - a byte array that represents the file contents; and 3) <classname>java.lang.String</classname> -
|
||||
text that represents the file contents.
|
||||
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-ftp:outbound-channel-adapter id="ftpOutbound"
|
||||
channel="ftpChannel"
|
||||
session-factory="ftpSessionFactory"
|
||||
charset="UTF-8"
|
||||
remote-file-separator="/"
|
||||
auto-create-directory="true"
|
||||
remote-directory-expression="headers.['remote_dir']"
|
||||
temporary-remote-directory-expression="headers.['temp_remote_dir']"
|
||||
filename-generator="fileNameGenerator"/>]]></programlisting>
|
||||
channel="ftpChannel"
|
||||
session-factory="ftpSessionFactory"
|
||||
charset="UTF-8"
|
||||
remote-file-separator="/"
|
||||
auto-create-directory="true"
|
||||
remote-directory-expression="headers.['remote_dir']"
|
||||
temporary-remote-directory-expression="headers.['temp_remote_dir']"
|
||||
filename-generator="fileNameGenerator"/>]]></programlisting>
|
||||
|
||||
As you can see from the configuration above you can configure an <emphasis>FTP Outbound Channel Adapter</emphasis> via the
|
||||
<code>outbound-channel-adapter</code> element while also providing values for various attributes such as <code>filename-generator</code>
|
||||
<code>outbound-channel-adapter</code> element while also providing values for various attributes such as <code>filename-generator</code>
|
||||
(an implementation of the <classname>org.springframework.integration.file.FileNameGenerator</classname> strategy interface),
|
||||
a reference to a <code>session-factory</code>, as well as other attributes. You can also see
|
||||
some examples of <code>*expression</code> attributes which allow you to use SpEL
|
||||
to configure things like <code>remote-directory-expression</code>, <code>temporary-remote-directory-expression</code> and <code>remote-filename-generator-expression</code>
|
||||
(a SpEL alternative to <code>filename-generator</code> shown above). As with any component that allows the usage of SpEL, access to Payload and Message Headers is available via
|
||||
some examples of <code>*expression</code> attributes which allow you to use SpEL
|
||||
to configure things like <code>remote-directory-expression</code>, <code>temporary-remote-directory-expression</code> and <code>remote-filename-generator-expression</code>
|
||||
(a SpEL alternative to <code>filename-generator</code> shown above). As with any component that allows the usage of SpEL, access to Payload and Message Headers is available via
|
||||
'payload' and 'headers' variables.
|
||||
Please refer to the schema for more details on
|
||||
the available attributes.
|
||||
<note>
|
||||
By default Spring Integration will use <classname>org.springframework.integration.file.DefaultFileNameGenerator</classname> if none is specified.
|
||||
By default Spring Integration will use <classname>o.s.i.file.DefaultFileNameGenerator</classname> if none is specified.
|
||||
<classname>DefaultFileNameGenerator</classname> will determine the file name based on the value of the <code>file_name</code> header (if it exists)
|
||||
in the MessageHeaders, or if the payload of the Message is already a <classname>java.io.File</classname>, then it will use the original name of that file.
|
||||
</note>
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
<important>
|
||||
Defining certain values (e.g., remote-directory) might be platform/ftp server dependent. For example as it
|
||||
was reported on this forum http://forum.springsource.org/showthread.php?p=333478&posted=1#post333478 on some
|
||||
platforms you must add slash to the end of the directory definition (e.g., remote-directory="/foo/bar/"
|
||||
Defining certain values (e.g., remote-directory) might be platform/ftp server dependent. For example as it
|
||||
was reported on this forum http://forum.springsource.org/showthread.php?p=333478&posted=1#post333478 on some
|
||||
platforms you must add slash to the end of the directory definition (e.g., remote-directory="/foo/bar/"
|
||||
instead of remote-directory="/foo/bar")
|
||||
</important>
|
||||
</para>
|
||||
@@ -407,13 +406,12 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/ftp
|
||||
<para>
|
||||
Here is an example of a gateway configured for an ls command...
|
||||
<programlisting language="xml"><![CDATA[<int-ftp:outbound-gateway id="gateway1"
|
||||
session-factory="ftpSessionFactory"
|
||||
request-channel="inbound1"
|
||||
command="ls"
|
||||
command-options="-1"
|
||||
expression="payload"
|
||||
reply-channel="toSplitter"/>
|
||||
]]></programlisting>
|
||||
session-factory="ftpSessionFactory"
|
||||
request-channel="inbound1"
|
||||
command="ls"
|
||||
command-options="-1"
|
||||
expression="payload"
|
||||
reply-channel="toSplitter"/>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
The payload of the message sent to the toSplitter channel is a list of String objects
|
||||
@@ -433,23 +431,23 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/ftp
|
||||
of the <classname>CachingSessionFactory</classname> instance. That provides the <code>sessionCacheSize</code> and <code>sessionWaitTimeout</code>
|
||||
properties. As its name suggests, the <code>sessionCacheSize</code> property controls how many active sessions this adapter will
|
||||
maintain in its cache (the DEFAULT is unbounded). If the <code>sessionCacheSize</code> threshold has been reached, any attempt to
|
||||
acquire another session will block until either one of the cached sessions becomes available or until the wait time for a Session
|
||||
acquire another session will block until either one of the cached sessions becomes available or until the wait time for a Session
|
||||
expires (the DEFAULT wait time is Integer.MAX_VALUE). The <code>sessionWaitTimeout</code> property enables configuration of that value.
|
||||
</para>
|
||||
<para>
|
||||
If you want your Sessions to be cached, simply configure your default Session Factory as described above and then
|
||||
wrap it in an instance of <classname>CachingSessionFactory</classname> where you may provide those additional properties.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<bean id="ftpSessionFactory" class="o.s.i.ftp.session.DefaultFtpSessionFactory">
|
||||
<property name="host" value="localhost"/>
|
||||
</bean>
|
||||
|
||||
<programlisting language="xml"><![CDATA[<bean id="ftpSessionFactory" class="org.springframework.integration.ftp.session.DefaultFtpSessionFactory">
|
||||
<property name="host" value="localhost"/>
|
||||
</bean>
|
||||
|
||||
<bean id="cachingSessionFactory" class="org.springframework.integration.file.remote.session.CachingSessionFactory">
|
||||
<constructor-arg ref="ftpSessionFactory"/>
|
||||
<constructor-arg value="10"/>
|
||||
<property name="sessionWaitTimeout" value="1000"/>
|
||||
</bean>]]></programlisting>
|
||||
|
||||
<bean id="cachingSessionFactory" class="o.s.i.file.remote.session.CachingSessionFactory">
|
||||
<constructor-arg ref="ftpSessionFactory"/>
|
||||
<constructor-arg value="10"/>
|
||||
<property name="sessionWaitTimeout" value="1000"/>
|
||||
</bean>]]></programlisting>
|
||||
<para>
|
||||
In the above example you see a <classname>CachingSessionFactory</classname> created with the
|
||||
<code>sessionCacheSize</code> set to 10 and the <code>sessionWaitTimeout</code> set to 1 second (its value is in millliseconds).
|
||||
|
||||
|
||||
@@ -89,12 +89,12 @@ public interface Cafe {
|
||||
<classname>@Gateway</classname> annotation.
|
||||
</para>
|
||||
|
||||
<programlisting language="java"><![CDATA[ public interface Cafe {
|
||||
<programlisting language="java"><![CDATA[public interface Cafe {
|
||||
|
||||
@Gateway(requestChannel="orders")
|
||||
void placeOrder(Order order);
|
||||
@Gateway(requestChannel="orders")
|
||||
void placeOrder(Order order);
|
||||
|
||||
}]]></programlisting>
|
||||
}]]></programlisting>
|
||||
<para>
|
||||
You may alternatively provide such content in <code>method</code> sub-elements if you prefer XML configuration (see the next paragraph).
|
||||
</para>
|
||||
@@ -102,12 +102,12 @@ public interface Cafe {
|
||||
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:
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[ public interface FileWriter {
|
||||
<programlisting language="java"><![CDATA[public interface FileWriter {
|
||||
|
||||
@Gateway(requestChannel="filesOut")
|
||||
void write(byte[] content, @Header(FileHeaders.FILENAME) String filename);
|
||||
@Gateway(requestChannel="filesOut")
|
||||
void write(byte[] content, @Header(FileHeaders.FILENAME) String filename);
|
||||
|
||||
}]]></programlisting>
|
||||
}]]></programlisting>
|
||||
|
||||
<para>
|
||||
If you prefer the XML approach of configuring Gateway methods, you can provide <emphasis>method</emphasis> sub-elements
|
||||
@@ -391,20 +391,18 @@ int finalResult = result.get(1000, TimeUnit.SECONDS);</programlisting>
|
||||
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.
|
||||
</para>
|
||||
<para>
|
||||
<note>
|
||||
<para>
|
||||
* <emphasis>reply-timeout</emphasis> is unbounded for <emphasis><gateway/></emphasis>
|
||||
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 <emphasis>reply-timeout</emphasis> is 1000
|
||||
milliseconds (1 second). If a downstream async handoff 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.
|
||||
</para>
|
||||
</note>
|
||||
</para>
|
||||
</important>
|
||||
</important>
|
||||
<note>
|
||||
<para>
|
||||
* <emphasis>reply-timeout</emphasis> is unbounded for <emphasis><gateway/></emphasis>
|
||||
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 <emphasis>reply-timeout</emphasis> is 1000
|
||||
milliseconds (1 second). If a downstream async handoff 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.
|
||||
</para>
|
||||
</note>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -9,22 +9,22 @@
|
||||
<section id="gemfire-intro">
|
||||
<title>Introduction</title>
|
||||
<para>
|
||||
VMWare vFabric GemFire (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
|
||||
VMWare vFabric GemFire (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. This guide assumes
|
||||
some familiarity with <ulink url="http://www.vmware.com/support/pubs/vfabric-gemfire.html">GemFire</ulink>
|
||||
and its <ulink url="http://www.vmware.com/support/developer/vfabric-gemfire/662-api/index.html">API</ulink>.
|
||||
</para>
|
||||
<para>
|
||||
Spring integration provides support for GemFire by providing inbound adapters for entry and continuous query events,
|
||||
an outbound adapter to write entries to the cache, and <classname>MessageStore</classname> and <classname>MessageGroupStore</classname> implementations.
|
||||
Spring integration leverages the
|
||||
Spring integration provides support for GemFire by providing inbound adapters for entry and continuous query events,
|
||||
an outbound adapter to write entries to the cache, and <classname>MessageStore</classname> and <classname>MessageGroupStore</classname> implementations.
|
||||
Spring integration leverages the
|
||||
<ulink url="http://www.springsource.org/spring-gemfire">Spring Gemfire</ulink> project, providing a thin wrapper over its components.
|
||||
</para>
|
||||
<para>
|
||||
To configure the 'int-gfe' namespace, include the following elements within the headers of your XML configuration file:
|
||||
|
||||
|
||||
<programlisting language="xml"><![CDATA[xmlns:int-gfe="http://www.springframework.org/schema/integration/gemfire"
|
||||
xsi:schemaLocation="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"]]></programlisting>
|
||||
</para>
|
||||
</section>
|
||||
@@ -32,106 +32,97 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/gemfire
|
||||
<title>Inbound Channel Adapter</title>
|
||||
<para>
|
||||
The <emphasis>inbound-channel-adapter</emphasis> produces messages on a channel triggered by a GemFire <classname>EntryEvent</classname>. 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. 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.
|
||||
|
||||
<programlisting language="xml">
|
||||
<![CDATA[<gfe:cache/>
|
||||
<gfe:replicated-region id="region"/>
|
||||
<int-gfe:inbound-channel-adapter id="inputChannel" region="region"
|
||||
cache-events="CREATED" expression="newValue"/>]]>
|
||||
</programlisting>
|
||||
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. 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.
|
||||
|
||||
In the above configuration, we are creating a GemFire <classname>Cache</classname> and <classname>Region</classname> 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 <code>cache-events</code>
|
||||
<programlisting language="xml"><![CDATA[<gfe:cache/>
|
||||
<gfe:replicated-region id="region"/>
|
||||
<int-gfe:inbound-channel-adapter id="inputChannel" region="region"
|
||||
cache-events="CREATED" expression="newValue"/>]]></programlisting>
|
||||
|
||||
In the above configuration, we are creating a GemFire <classname>Cache</classname> and <classname>Region</classname> 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 <code>cache-events</code>
|
||||
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 <code>channel</code> attribute is provided, the channel will be created from the <code>id</code> attribute. This adapter also supports an <code>error-channel</code>.
|
||||
If <code>expression</code> is not provided the message payload will be a GemFire <classname>EntryEvent</classname>
|
||||
If <code>expression</code> is not provided the message payload will be a GemFire <classname>EntryEvent</classname>
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Continuous Query Inbound Channel Adapter</title>
|
||||
<para>
|
||||
The <emphasis>cq-inbound-channel-adapter</emphasis> produces messages a channel triggered by a GemFire continuous query or <classname>CqEvent</classname> event. Spring GemFire introduced
|
||||
continuous query support in release 1.1, including a <classname>ContinuousQueryListenerContainer</classname> 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 <code>query</code> and executes the query. The continuous query acts as an event source that will fire whenever its
|
||||
result set changes state.
|
||||
continuous query support in release 1.1, including a <classname>ContinuousQueryListenerContainer</classname> 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 <code>query</code> and executes the query. The continuous query acts as an event source that will fire 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 <ulink url="http://www.gemstone.com/docs/6.6.RC/product/docs/html/user_guide/UserGuide_GemFire.html#Continuous%20Querying">GemFire documentation</ulink> for more information on
|
||||
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 <ulink url="http://www.gemstone.com/docs/6.6.RC/product/docs/html/user_guide/UserGuide_GemFire.html#Continuous%20Querying">GemFire documentation</ulink> for more information on
|
||||
implementing continuous queries.
|
||||
</note>
|
||||
|
||||
|
||||
<programlisting language="xml"><![CDATA[<gfe:client-cache id="client-cache" pool-name="client-pool"/>
|
||||
|
||||
<gfe:pool id="client-pool" subscription-enabled="true" >
|
||||
<!--configure server or locator here required to address the cache server -->
|
||||
</gfe:pool>
|
||||
|
||||
<gfe:client-region id="test" cache-ref="client-cache" pool-name="client-pool"/>
|
||||
|
||||
<gfe:cq-listener-container id="queryListenerContainer" cache="client-cache"
|
||||
pool-name="client-pool"/>
|
||||
|
||||
<int-gfe:cq-inbound-channel-adapter id="inputChannel"
|
||||
cq-listener-container="queryListenerContainer"
|
||||
query="select * from /test"/>
|
||||
]]>
|
||||
</programlisting>
|
||||
</note>
|
||||
|
||||
In the above configuration, we are creating a GemFire client cache
|
||||
|
||||
<programlisting language="xml"><![CDATA[<gfe:client-cache id="client-cache" pool-name="client-pool"/>
|
||||
|
||||
<gfe:pool id="client-pool" subscription-enabled="true" >
|
||||
<!--configure server or locator here required to address the cache server -->
|
||||
</gfe:pool>
|
||||
|
||||
<gfe:client-region id="test" cache-ref="client-cache" pool-name="client-pool"/>
|
||||
|
||||
<gfe:cq-listener-container id="queryListenerContainer" cache="client-cache"
|
||||
pool-name="client-pool"/>
|
||||
|
||||
<int-gfe:cq-inbound-channel-adapter id="inputChannel"
|
||||
cq-listener-container="queryListenerContainer"
|
||||
query="select * from /test"/>]]></programlisting>
|
||||
|
||||
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 <classname>ContinuousQueryListenerContainer</classname>
|
||||
using Spring GemFire. The continuous query inbound channel adapter requires a <code>cq-listener-container</code> attribute which contains a reference to the <classname>ContinuousQueryListenerContainer</classname>. Optionally,
|
||||
it accepts an <code>expression</code> attribute which uses SpEL to transform the <code>CqEvent</code> or extract an individual property as needed. The cq-inbound-channel-adapter provides a
|
||||
<code>query-events</code> 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, REGION_INVALIDATED. CREATED and UPDATED are enabled by default. Additional optional attributes include, <code>query-name</code> which provides an optional query name, and
|
||||
REGION_DESTROYED, REGION_INVALIDATED. CREATED and UPDATED are enabled by default. Additional optional attributes include, <code>query-name</code> which provides an optional query name, and
|
||||
<code>expression</code> which works as described in the above section, and <code>durable</code> - a boolean value indicating if the query is durable (false by default).
|
||||
Note that this adapter conforms to Spring integration conventions.
|
||||
If no <code>channel</code> attribute is provided, the channel will be created from the <code>id</code> attribute. This adapter also supports an <code>error-channel</code>
|
||||
If no <code>channel</code> attribute is provided, the channel will be created from the <code>id</code> attribute. This adapter also supports an <code>error-channel</code>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section>
|
||||
<title>Outbound Channel Adapter</title>
|
||||
<para>
|
||||
The <emphasis>outbound-channel-adapter</emphasis> writes cache entries mapped from the message payload. In its simplest form, it expects a
|
||||
payload of type <classname>java.util.Map</classname> and puts the map entries into its configured region.
|
||||
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<int-gfe:outbound-channel-adapter id="cacheChannel" region="region"/>]]>
|
||||
</programlisting>
|
||||
|
||||
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.
|
||||
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<int-gfe:outbound-channel-adapter id="cacheChannel" region="region">
|
||||
<int-gfe:cache-entries>
|
||||
The <emphasis>outbound-channel-adapter</emphasis> writes cache entries mapped from the message payload. In its simplest form, it expects a
|
||||
payload of type <classname>java.util.Map</classname> and puts the map entries into its configured region.
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-gfe:outbound-channel-adapter id="cacheChannel" region="region"/>]]></programlisting>
|
||||
|
||||
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.
|
||||
|
||||
<programlisting language="xml"><![CDATA[<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'"/>
|
||||
</int-gfe:cache-entries>
|
||||
</int-gfe:outbound-channel-adapter>
|
||||
]]>
|
||||
</programlisting>
|
||||
In the above configuration, the inner element <code>cache-entries</code> is semantically equivalent to Spring 'map' element. The adapter interprets the <code>key</code> and
|
||||
<code>value</code> 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
|
||||
<code>cacheChannel</code> has a String payload with a value "Hello", two entries <code>[HELLO:hello, foo:bar]</code> will be written (created or updated) in the cache region.
|
||||
This adapter also supports the <code>order</code> attribute which may be useful if it is bound to a PublishSubscribeChannel.
|
||||
</int-gfe:cache-entries>
|
||||
</int-gfe:outbound-channel-adapter>]]></programlisting>
|
||||
In the above configuration, the inner element <code>cache-entries</code> is semantically equivalent to Spring 'map' element. The adapter interprets the <code>key</code> and
|
||||
<code>value</code> 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
|
||||
<code>cacheChannel</code> has a String payload with a value "Hello", two entries <code>[HELLO:hello, foo:bar]</code> will be written (created or updated) in the cache region.
|
||||
This adapter also supports the <code>order</code> attribute which may be useful if it is bound to a PublishSubscribeChannel.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="gemfire-message-store">
|
||||
<title>Gemfire Message Store</title>
|
||||
|
||||
|
||||
<para>
|
||||
As described in EIP, a <ulink url="http://www.eaipatterns.com/MessageStore.html">Message Store</ulink> allows you to persist Messages.
|
||||
This can be very useful when dealing with components that have a capability to buffer messages
|
||||
(<emphasis>QueueChannel, Aggregator, Resequencer</emphasis>, etc.) if reliability is a concern.
|
||||
In Spring Integration, the MessageStore strategy also provides the foundation for the
|
||||
In Spring Integration, the MessageStore strategy also provides the foundation for the
|
||||
<ulink url="http://www.eaipatterns.com/StoreInLibrary.html">ClaimCheck</ulink> pattern, which is described in EIP as well.
|
||||
</para>
|
||||
|
||||
@@ -140,11 +131,10 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/gemfire
|
||||
the <classname>MessageStore</classname> strategy (mainly used by the <emphasis>QueueChannel</emphasis> and <emphasis>ClaimCheck</emphasis>
|
||||
patterns) and the <classname>MessageGroupStore</classname> strategy (mainly used by the <emphasis>Aggregator</emphasis> and
|
||||
<emphasis>Resequencer</emphasis> patterns).
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<bean id="gemfireMessageStore" class="org.springframework.integration.gemfire.store.GemfireMessageStore">
|
||||
<constructor-arg ref="myCache"/>
|
||||
<programlisting language="xml"><![CDATA[<bean id="gemfireMessageStore" class="o.s.i.gemfire.store.GemfireMessageStore">
|
||||
<constructor-arg ref="myCache"/>
|
||||
</bean>
|
||||
|
||||
|
||||
<bean id="myCache" class="org.springframework.data.gemfire.CacheFactoryBean"/>
|
||||
|
||||
<int:channel id="somePersistentQueueChannel">
|
||||
@@ -152,32 +142,28 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/gemfire
|
||||
<int:channel>
|
||||
|
||||
<int:aggregator input-channel="inputChannel" output-channel="outputChannel"
|
||||
message-store="gemfireMessageStore"/>
|
||||
]]></programlisting>
|
||||
message-store="gemfireMessageStore"/>]]></programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Above is a sample <classname>GemfireMessageStore</classname> configuration that shows its usage by a <emphasis>QueueChannel</emphasis> and an <emphasis>Aggregator</emphasis>. As you can see it is a normal Spring bean configuration. The simplest configuration requires a reference to a <classname>GemFireCache</classname> (created by <classname>CacheFactoryBean</classname>) as a constructor argument. If the cache is standalone, i.e., embedded in the same JVM, the MessageStore will create a message store region named "messageStoreRegion". If your application requires customization of the messageStore region, for example, multiple Gemfire message stores each with its own region, you can configure a region for each message store instance and use the <classname>Region</classname> as the constructor argument:
|
||||
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<bean id="gemfireMessageStore" class="org.springframework.integration.gemfire.store.GemfireMessageStore">
|
||||
<constructor-arg ref="myRegion"/>
|
||||
<programlisting language="xml"><![CDATA[<bean id="gemfireMessageStore" class="o.s.i.gemfire.store.GemfireMessageStore">
|
||||
<constructor-arg ref="myRegion"/>
|
||||
</bean>
|
||||
|
||||
<gfe:cache/>
|
||||
|
||||
<gfe:replicated-region id="myRegion"/>
|
||||
]]></programlisting>
|
||||
<gfe:replicated-region id="myRegion"/>]]></programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In the above examle, 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 (See the
|
||||
<ulink url="http://www.vmware.com/support/pubs/vfabric-gemfire.html">GemFire product documentation</ulink> for more details). In this case, you configure a client cache, client region, and client pool and inject the region into the MessageStore. Here is an example:
|
||||
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<bean id="gemfireMessageStore"
|
||||
class="org.springframework.integration.gemfire.store.GemfireMessageStore">
|
||||
<constructor-arg ref="myRegion"/>
|
||||
<programlisting language="xml"><![CDATA[<bean id="gemfireMessageStore"
|
||||
class="org.springframework.integration.gemfire.store.GemfireMessageStore">
|
||||
<constructor-arg ref="myRegion"/>
|
||||
</bean>
|
||||
|
||||
<gfe:client-cache/>
|
||||
@@ -185,9 +171,8 @@ In the above examle, the cache and region are configured using the spring-gemfir
|
||||
<gfe:client-region id="myRegion" shortcut="PROXY" pool-name="messageStorePool"/>
|
||||
|
||||
<gfe:pool id="messageStorePool">
|
||||
<gfe:server host="localhost" port="40404" />
|
||||
</gfe:pool>
|
||||
]]></programlisting>
|
||||
<gfe:server host="localhost" port="40404" />
|
||||
</gfe:pool>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Note the <emphasis>pool</emphasis> 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.
|
||||
|
||||
@@ -30,13 +30,13 @@
|
||||
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:
|
||||
</para>
|
||||
<para><programlisting><![CDATA[<int-http:outbound-gateway id="withAdvice"
|
||||
url-expression="'http://localhost/test1'"
|
||||
request-channel="requests"
|
||||
reply-channel="nextChannel">
|
||||
<int:request-handler-advice-chain>
|
||||
<ref bean="myRetryAdvice" />
|
||||
</request-handler-advice-chain>
|
||||
<para><programlisting language="xml"><![CDATA[<int-http:outbound-gateway id="withAdvice"
|
||||
url-expression="'http://localhost/test1'"
|
||||
request-channel="requests"
|
||||
reply-channel="nextChannel">
|
||||
<int:request-handler-advice-chain>
|
||||
<ref bean="myRetryAdvice" />
|
||||
</request-handler-advice-chain>
|
||||
</int-http:outbound-gateway>]]></programlisting></para>
|
||||
<para>
|
||||
In this case, <emphasis>myRetryAdvice</emphasis> will only be applied locally to this gateway and
|
||||
@@ -76,7 +76,7 @@
|
||||
<section id="retry-advice">
|
||||
<title>Retry Advice</title>
|
||||
<para>
|
||||
The retry advice (<classname>org.springframework.integration.handler.advice.RequestHandlerRetryAdvice</classname>)
|
||||
The retry advice (<classname>o.s.i.handler.advice.RequestHandlerRetryAdvice</classname>)
|
||||
leverages the rich retry mechanisms provided by the
|
||||
<ulink url="https://github.com/SpringSource/spring-retry">spring-retry</ulink> project. The core component
|
||||
of <emphasis>spring-retry</emphasis> is the <classname>RetryTemplate</classname>, which allows configuration
|
||||
@@ -113,11 +113,11 @@
|
||||
<title>Configuring the Retry Advice</title>
|
||||
<para>
|
||||
The following examples use a simple <service-activator />> that always throws an exception:
|
||||
<programlisting><![CDATA[public class FailingService {
|
||||
<programlisting language="java"><![CDATA[public class FailingService {
|
||||
|
||||
public void service(String message) {
|
||||
throw new RuntimeException("foo");
|
||||
}
|
||||
public void service(String message) {
|
||||
throw new RuntimeException("foo");
|
||||
}
|
||||
}]]></programlisting>
|
||||
</para>
|
||||
<para><emphasis>Simple Stateless Retry</emphasis></para>
|
||||
@@ -130,13 +130,12 @@
|
||||
environment, this final exception might be handled using an <emphasis>error-channel</emphasis> on
|
||||
the inbound endpoint.
|
||||
</para>
|
||||
<para><programlisting><![CDATA[<int:service-activator input-channel="input" ref="failer" method="service">
|
||||
<int:request-handler-advice-chain>
|
||||
<bean class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice" />
|
||||
</request-handler-advice-chain>
|
||||
<para><programlisting language="xml"><![CDATA[<int:service-activator input-channel="input" ref="failer" method="service">
|
||||
<int:request-handler-advice-chain>
|
||||
<bean class="o.s.i.handler.advice.RequestHandlerRetryAdvice"/>
|
||||
</request-handler-advice-chain>
|
||||
</int:service-activator>
|
||||
|
||||
|
||||
DEBUG [task-scheduler-2]preSend on channel 'input', message: [Payload=...]
|
||||
DEBUG [task-scheduler-2]Retry: count=0
|
||||
DEBUG [task-scheduler-2]Checking for rethrow: count=1
|
||||
@@ -151,19 +150,18 @@ DEBUG [task-scheduler-2]Retry failed last attempt: count=3]]></programlisting></
|
||||
above example; it uses a <classname>ErrorMessageSendingRecoverer</classname>
|
||||
to send an <emphasis>ErrorMessage</emphasis> to a channel.
|
||||
</para>
|
||||
<para><programlisting><![CDATA[<int:service-activator input-channel="input" ref="failer" method="service">
|
||||
<int:request-handler-advice-chain>
|
||||
<bean class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice">
|
||||
<property name="recoveryCallback">
|
||||
<bean class="org.springframework.integration.handler.advice.ErrorMessageSendingRecoverer">
|
||||
<constructor-arg ref="myErrorChannel" />
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
</request-handler-advice-chain>
|
||||
<para><programlisting language="xml"><![CDATA[<int:service-activator input-channel="input" ref="failer" method="service">
|
||||
<int:request-handler-advice-chain>
|
||||
<bean class="o.s.i.handler.advice.RequestHandlerRetryAdvice">
|
||||
<property name="recoveryCallback">
|
||||
<bean class="o.s.i.handler.advice.ErrorMessageSendingRecoverer">
|
||||
<constructor-arg ref="myErrorChannel" />
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
</request-handler-advice-chain>
|
||||
</int:int:service-activator>
|
||||
|
||||
|
||||
DEBUG [task-scheduler-2]preSend on channel 'input', message: [Payload=...]
|
||||
DEBUG [task-scheduler-2]Retry: count=0
|
||||
DEBUG [task-scheduler-2]Checking for rethrow: count=1
|
||||
@@ -181,17 +179,17 @@ DEBUG [task-scheduler-2]Sending ErrorMessage :failedMessage:[Payload=...]]]></pr
|
||||
where the first retry waits 1 second, the second waits 5 seconds and the third waits 25 (for 4
|
||||
attempts in all).
|
||||
</para>
|
||||
<para><programlisting><![CDATA[<int:service-activator input-channel="input" ref="failer" method="service">
|
||||
<int:request-handler-advice-chain>
|
||||
<bean class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice">
|
||||
<property name="recoveryCallback">
|
||||
<bean class="org.springframework.integration.handler.advice.ErrorMessageSendingRecoverer">
|
||||
<constructor-arg ref="myErrorChannel" />
|
||||
</bean>
|
||||
</property>
|
||||
<property name="retryTemplate" ref="retryTemplate" />
|
||||
</bean>
|
||||
</request-handler-advice-chain>
|
||||
<para><programlisting language="xml"><![CDATA[<int:service-activator input-channel="input" ref="failer" method="service">
|
||||
<int:request-handler-advice-chain>
|
||||
<bean class="o.s.i.handler.advice.RequestHandlerRetryAdvice">
|
||||
<property name="recoveryCallback">
|
||||
<bean class="o.s.i.handler.advice.ErrorMessageSendingRecoverer">
|
||||
<constructor-arg ref="myErrorChannel" />
|
||||
</bean>
|
||||
</property>
|
||||
<property name="retryTemplate" ref="retryTemplate" />
|
||||
</bean>
|
||||
</request-handler-advice-chain>
|
||||
</int:service-activator>
|
||||
|
||||
<bean id="retryTemplate" class="org.springframework.retry.support.RetryTemplate">
|
||||
@@ -208,7 +206,6 @@ DEBUG [task-scheduler-2]Sending ErrorMessage :failedMessage:[Payload=...]]]></pr
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
||||
27.058 DEBUG [task-scheduler-1]preSend on channel 'input', message: [Payload=...]
|
||||
27.071 DEBUG [task-scheduler-1]Retry: count=0
|
||||
27.080 DEBUG [task-scheduler-1]Sleeping for 1000
|
||||
@@ -233,25 +230,23 @@ DEBUG [task-scheduler-2]Sending ErrorMessage :failedMessage:[Payload=...]]]></pr
|
||||
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.
|
||||
</para>
|
||||
<para><programlisting><![CDATA[<int:service-activator input-channel="input" ref="failer" method="service">
|
||||
<int:request-handler-advice-chain>
|
||||
<bean class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice">
|
||||
<property name="retryStateGenerator">
|
||||
<bean class="org.springframework.integration.handler.advice.SpelExpressionRetryStateGenerator">
|
||||
<constructor-arg value="headers['jms_messageId']" />
|
||||
</bean>
|
||||
</property>
|
||||
<property name="recoveryCallback">
|
||||
<bean class="org.springframework.integration.handler.advice.ErrorMessageSendingRecoverer">
|
||||
<constructor-arg ref="myErrorChannel" />
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
</int:request-handler-advice-chain>
|
||||
<para><programlisting language="xml"><![CDATA[<int:service-activator input-channel="input" ref="failer" method="service">
|
||||
<int:request-handler-advice-chain>
|
||||
<bean class="o.s.i.handler.advice.RequestHandlerRetryAdvice">
|
||||
<property name="retryStateGenerator">
|
||||
<bean class="o.s.i.handler.advice.SpelExpressionRetryStateGenerator">
|
||||
<constructor-arg value="headers['jms_messageId']" />
|
||||
</bean>
|
||||
</property>
|
||||
<property name="recoveryCallback">
|
||||
<bean class="o.s.i.handler.advice.ErrorMessageSendingRecoverer">
|
||||
<constructor-arg ref="myErrorChannel" />
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
</int:request-handler-advice-chain>
|
||||
</int:service-activator>
|
||||
|
||||
|
||||
|
||||
24.351 DEBUG [Container#0-1]preSend on channel 'input', message: [Payload=...]
|
||||
24.368 DEBUG [Container#0-1]Retry: count=0
|
||||
24.387 DEBUG [Container#0-1]Checking for rethrow: count=1
|
||||
@@ -299,7 +294,7 @@ Caused by: java.lang.RuntimeException: foo
|
||||
<para>
|
||||
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
|
||||
<classname>org.springframework.integration.handler.advice.RequestHandlerCircuitBreakerAdvice</classname>
|
||||
<classname>o.s.i.handler.advice.RequestHandlerCircuitBreakerAdvice</classname>
|
||||
implements this pattern. When the circuit breaker is in the <emphasis>closed</emphasis> state,
|
||||
the endpoint will attempt to invoke the
|
||||
service. The circuit breaker goes to the <emphasis>open</emphasis> state
|
||||
@@ -327,16 +322,15 @@ Caused by: java.lang.RuntimeException: foo
|
||||
1000 milliseconds.
|
||||
</para>
|
||||
<para>Example:</para>
|
||||
<para><programlisting><![CDATA[<int:service-activator input-channel="input" ref="failer" method="service">
|
||||
<int:request-handler-advice-chain>
|
||||
<bean class="org.springframework.integration.handler.advice.RequestHandlerCircuitBreakerAdvice">
|
||||
<property name="threshold" value="2" />
|
||||
<property name="halfOpenAfter" value="12000" />
|
||||
</bean>
|
||||
</int:request-handler-advice-chain>
|
||||
<para><programlisting language="xml"><![CDATA[<int:service-activator input-channel="input" ref="failer" method="service">
|
||||
<int:request-handler-advice-chain>
|
||||
<bean class="o.s.i.handler.advice.RequestHandlerCircuitBreakerAdvice">
|
||||
<property name="threshold" value="2" />
|
||||
<property name="halfOpenAfter" value="12000" />
|
||||
</bean>
|
||||
</int:request-handler-advice-chain>
|
||||
</int:service-activator>
|
||||
|
||||
|
||||
05.617 DEBUG [task-scheduler-1]preSend on channel 'input', message: [Payload=...]
|
||||
05.638 ERROR [task-scheduler-1]org.springframework.integration.MessageHandlingException: java.lang.RuntimeException: foo
|
||||
...
|
||||
@@ -366,7 +360,7 @@ Caused by: java.lang.RuntimeException: foo
|
||||
<title>Expression Evaluating Advice</title>
|
||||
<para>
|
||||
The final supplied advice class is the
|
||||
<classname>org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice</classname>.
|
||||
<classname>o.s.i.handler.advice.ExpressionEvaluatingRequestHandlerAdvice</classname>.
|
||||
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. Optionally, a message containing the evaluation result, together with the input message,
|
||||
@@ -395,23 +389,23 @@ Caused by: java.lang.RuntimeException: foo
|
||||
<para>
|
||||
In addition to the provided Advice classes above, you can implement your own Adivce classes. While you can
|
||||
provide any implementation of <classname>org.aopalliance.aop.Advice</classname>, it is generally recommended
|
||||
that you subclass <classname>org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice</classname>.
|
||||
that you subclass <classname>o.s.i.handler.advice.AbstractRequestHandlerAdvice</classname>.
|
||||
This has the benefit of avoiding writing low-level <emphasis>Aspect Oriented Programming</emphasis> code as well
|
||||
as providing a starting point that is specifically tailored for use in this environment.
|
||||
</para>
|
||||
<para>
|
||||
Subclasses need to implement the doInvoke() method:
|
||||
</para>
|
||||
<para><programlisting><![CDATA[ /**
|
||||
* Subclasses implement this method to apply behavior to the {@link MessageHandler} callback.execute()
|
||||
* invokes the handler method and returns its result, or null).
|
||||
* @param callback Subclasses invoke the execute() method on this interface to invoke the handler method.
|
||||
* @param target The target handler.
|
||||
* @param message The message that will be sent to the handler.
|
||||
* @return the result after invoking the {@link MessageHandler}.
|
||||
* @throws Exception
|
||||
*/
|
||||
protected abstract Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception;]]></programlisting>
|
||||
<para><programlisting language="java"><![CDATA[/**
|
||||
* Subclasses implement this method to apply behavior to the {@link MessageHandler} callback.execute()
|
||||
* invokes the handler method and returns its result, or null).
|
||||
* @param callback Subclasses invoke the execute() method on this interface to invoke the handler method.
|
||||
* @param target The target handler.
|
||||
* @param message The message that will be sent to the handler.
|
||||
* @return the result after invoking the {@link MessageHandler}.
|
||||
* @throws Exception
|
||||
*/
|
||||
protected abstract Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception;]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
The <emphasis>callback</emphasis> parameter is simply a convenience to avoid subclasses dealing with AOP directly; invoking the
|
||||
@@ -438,15 +432,15 @@ Caused by: java.lang.RuntimeException: foo
|
||||
ability to modify the return value. Note that only <classname>AbstractReplyProducingMessageHandler</classname>s
|
||||
return a value.
|
||||
</para>
|
||||
<para><programlisting><![CDATA[public class MyAdvice extends AbstractRequestHandlerAdvice {
|
||||
<para><programlisting language="java"><![CDATA[public class MyAdvice extends AbstractRequestHandlerAdvice {
|
||||
|
||||
@Override
|
||||
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
|
||||
// add code before the invocation
|
||||
Object result = callback.execute();
|
||||
// add code after the invocation
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
|
||||
// add code before the invocation
|
||||
Object result = callback.execute();
|
||||
// add code after the invocation
|
||||
return result;
|
||||
}
|
||||
}]]></programlisting></para>
|
||||
<note>
|
||||
<para>
|
||||
|
||||
@@ -200,7 +200,7 @@ In the case of the Outbound Gateway, the reply message produced by the gateway w
|
||||
<emphasis>inbound-gateway</emphasis>:
|
||||
</para>
|
||||
|
||||
<programlisting language="xml"><![CDATA[ <int-http:inbound-gateway id="inboundGateway"
|
||||
<programlisting language="xml"><![CDATA[<int-http:inbound-gateway id="inboundGateway"
|
||||
request-channel="requests"
|
||||
reply-channel="responses"/>]]></programlisting>
|
||||
|
||||
@@ -267,10 +267,10 @@ In the case of the Outbound Gateway, the reply message produced by the gateway w
|
||||
</para>
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-http:inbound-channel-adapter id="inboundAdapterWithExpressions"
|
||||
path="/first-name/{firstName}/last-name/{lastName}"
|
||||
channel="requests"
|
||||
payload-expression="#pathVariables.firstName">
|
||||
<int-http:header name="lname" expression="#pathVariables.lastName"/>
|
||||
path="/first-name/{firstName}/last-name/{lastName}"
|
||||
channel="requests"
|
||||
payload-expression="#pathVariables.firstName">
|
||||
<int-http:header name="lname" expression="#pathVariables.lastName"/>
|
||||
</int-http:inbound-channel-adapter>]]></programlisting>
|
||||
|
||||
<para>
|
||||
@@ -349,15 +349,15 @@ In the case of the Outbound Gateway, the reply message produced by the gateway w
|
||||
a successful response will simply execute without sending any Messages to a reply channel. In the case of any non-successful response
|
||||
status code, it will throw an exception. The configuration looks very similar to the gateway:
|
||||
<programlisting language="xml"><![CDATA[<int-http:outbound-channel-adapter id="example"
|
||||
url="http://localhost/example"
|
||||
http-method="GET"
|
||||
channel="requests"
|
||||
charset="UTF-8"
|
||||
extract-payload="false"
|
||||
expected-response-type="java.lang.String"
|
||||
request-factory="someRequestFactory"
|
||||
order="3"
|
||||
auto-startup="false"/>]]></programlisting>
|
||||
url="http://localhost/example"
|
||||
http-method="GET"
|
||||
channel="requests"
|
||||
charset="UTF-8"
|
||||
extract-payload="false"
|
||||
expected-response-type="java.lang.String"
|
||||
request-factory="someRequestFactory"
|
||||
order="3"
|
||||
auto-startup="false"/>]]></programlisting>
|
||||
</para>
|
||||
<note>
|
||||
<para>
|
||||
@@ -386,10 +386,10 @@ In the case of the Outbound Gateway, the reply message produced by the gateway w
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-http:outbound-gateway id="trafficGateway"
|
||||
url="http://local.yahooapis.com/trafficData?appid=YdnDemo&zip={zipCode}"
|
||||
request-channel="trafficChannel"
|
||||
http-method="GET"
|
||||
expected-response-type="java.lang.String">
|
||||
<int-http:uri-variable name="zipCode" expression="payload.getZip()"/>
|
||||
request-channel="trafficChannel"
|
||||
http-method="GET"
|
||||
expected-response-type="java.lang.String">
|
||||
<int-http:uri-variable name="zipCode" expression="payload.getZip()"/>
|
||||
</int-http:outbound-gateway>]]></programlisting>
|
||||
<para>
|
||||
The <code>uri-variable</code> sub-element defines two attributes:
|
||||
@@ -632,20 +632,20 @@ In the case of the Outbound Gateway, the reply message produced by the gateway w
|
||||
If for any reason, you need more explicit control over the proxy configuration, you can use Spring's
|
||||
<classname>SimpleClientHttpRequestFactory</classname> and configure its 'proxy' property as such:
|
||||
<programlisting language="xml"><![CDATA[<bean id="requestFactory"
|
||||
class="org.springframework.http.client.SimpleClientHttpRequestFactory">
|
||||
<property name="proxy">
|
||||
<bean id="proxy" class="java.net.Proxy">
|
||||
<constructor-arg>
|
||||
<util:constant static-field="java.net.Proxy.Type.HTTP"/>
|
||||
</constructor-arg>
|
||||
<constructor-arg>
|
||||
<bean class="java.net.InetSocketAddress">
|
||||
<constructor-arg value="123.0.0.1"/>
|
||||
<constructor-arg value="8080"/>
|
||||
</bean>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
</property>
|
||||
class="org.springframework.http.client.SimpleClientHttpRequestFactory">
|
||||
<property name="proxy">
|
||||
<bean id="proxy" class="java.net.Proxy">
|
||||
<constructor-arg>
|
||||
<util:constant static-field="java.net.Proxy.Type.HTTP"/>
|
||||
</constructor-arg>
|
||||
<constructor-arg>
|
||||
<bean class="java.net.InetSocketAddress">
|
||||
<constructor-arg value="123.0.0.1"/>
|
||||
<constructor-arg value="8080"/>
|
||||
</bean>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
</property>
|
||||
</bean>]]></programlisting>
|
||||
</para>
|
||||
|
||||
@@ -668,15 +668,15 @@ In the case of the Outbound Gateway, the reply message produced by the gateway w
|
||||
HTTP_RESPONSE_HEADERS. Here are some examples:
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-http:outbound-gateway id="httpGateway"
|
||||
url="http://localhost/test2"
|
||||
mapped-request-headers="foo, bar"
|
||||
mapped-response-headers="X-*, HTTP_RESPONSE_HEADERS"
|
||||
channel="someChannel"/>
|
||||
url="http://localhost/test2"
|
||||
mapped-request-headers="foo, bar"
|
||||
mapped-response-headers="X-*, HTTP_RESPONSE_HEADERS"
|
||||
channel="someChannel"/>
|
||||
|
||||
<int-http:outbound-channel-adapter id="httpAdapter"
|
||||
url="http://localhost/test2"
|
||||
mapped-request-headers="foo, bar, HTTP_REQUEST_HEADERS"
|
||||
channel="someChannel"/>]]></programlisting>
|
||||
url="http://localhost/test2"
|
||||
mapped-request-headers="foo, bar, HTTP_REQUEST_HEADERS"
|
||||
channel="someChannel"/>]]></programlisting>
|
||||
|
||||
The adapters and gateways will use the <classname>DefaultHttpHeaderMapper</classname> which now provides
|
||||
two static factory methods for "inbound" and "outbound" adapters so that the proper direction can be
|
||||
@@ -687,13 +687,13 @@ In the case of the Outbound Gateway, the reply message produced by the gateway w
|
||||
and inject it into the adapter via the <code>header-mapper</code> attribute.
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-http:outbound-gateway id="httpGateway"
|
||||
url="http://localhost/test2"
|
||||
header-mapper="headerMapper"
|
||||
channel="someChannel"/>
|
||||
url="http://localhost/test2"
|
||||
header-mapper="headerMapper"
|
||||
channel="someChannel"/>
|
||||
|
||||
<bean id="headerMapper" class="org.springframework.integration.http.support.DefaultHttpHeaderMapper">
|
||||
<property name="inboundHeaderNames" value="foo*, *bar, baz"/>
|
||||
<property name="outboundHeaderNames" value="a*b, d"/>
|
||||
<bean id="headerMapper" class="o.s.i.http.support.DefaultHttpHeaderMapper">
|
||||
<property name="inboundHeaderNames" value="foo*, *bar, baz"/>
|
||||
<property name="outboundHeaderNames" value="a*b, d"/>
|
||||
</bean>]]></programlisting>
|
||||
</para>
|
||||
<para>Of course, you can even implement the HeaderMapper strategy interface directly and provide a reference to that
|
||||
@@ -728,36 +728,35 @@ That is all for the client.
|
||||
<para>
|
||||
On the server side we have the following configuration:
|
||||
<programlisting language="xml"><![CDATA[<int-http:inbound-channel-adapter id="httpInboundAdapter"
|
||||
channel="receiveChannel"
|
||||
name="/inboundAdapter.htm"
|
||||
supported-methods="GET, POST" />
|
||||
channel="receiveChannel"
|
||||
name="/inboundAdapter.htm"
|
||||
supported-methods="GET, POST"/>
|
||||
|
||||
<int:channel id="receiveChannel"/>
|
||||
|
||||
<int:service-activator input-channel="receiveChannel">
|
||||
<bean class="org.springframework.integration.samples.multipart.MultipartReceiver"/>
|
||||
<bean class="org.springframework.integration.samples.multipart.MultipartReceiver"/>
|
||||
</int:service-activator>
|
||||
|
||||
<bean id="multipartResolver"
|
||||
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
|
||||
]]></programlisting>
|
||||
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
The 'httpInboundAdapter' will receive the request, convert it to a <classname>Message</classname> with a payload
|
||||
that is a <classname>LinkedMultiValueMap</classname>. We then are parsing that in the 'multipartReceiver' service-activator;
|
||||
<programlisting language="java"><![CDATA[public void receive(LinkedMultiValueMap<String, Object> multipartRequest){
|
||||
System.out.println("### Successfully received multipart request ###");
|
||||
for (String elementName : multipartRequest.keySet()) {
|
||||
if (elementName.equals("company")){
|
||||
System.out.println("\t" + elementName + " - " +
|
||||
((String[]) multipartRequest.getFirst("company"))[0]);
|
||||
System.out.println("### Successfully received multipart request ###");
|
||||
for (String elementName : multipartRequest.keySet()) {
|
||||
if (elementName.equals("company")){
|
||||
System.out.println("\t" + elementName + " - " +
|
||||
((String[]) multipartRequest.getFirst("company"))[0]);
|
||||
}
|
||||
else if (elementName.equals("company-logo")){
|
||||
System.out.println("\t" + elementName + " - as UploadedMultipartFile: " +
|
||||
((UploadedMultipartFile) multipartRequest
|
||||
.getFirst("company-logo")).getOriginalFilename());
|
||||
}
|
||||
}
|
||||
else if (elementName.equals("company-logo")){
|
||||
System.out.println("\t" + elementName + " - as UploadedMultipartFile: " +
|
||||
((UploadedMultipartFile) multipartRequest.getFirst("company-logo")).
|
||||
getOriginalFilename());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
]]></programlisting>
|
||||
|
||||
@@ -37,18 +37,18 @@
|
||||
or I/O error occurs.
|
||||
</para>
|
||||
<para>
|
||||
The TCP and UDP inbound channel adapters, and the TCP inbound gateway, support the "error-channel" attribute.
|
||||
The TCP and UDP inbound channel adapters, and the TCP inbound gateway, support the "error-channel" attribute.
|
||||
This provides the same basic functionality as described in <xref linkend="gateway-proxy"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="udp-adapters">
|
||||
<title>UDP Adapters</title>
|
||||
<para>
|
||||
<programlisting language="xml"><![CDATA[ <int-ip:udp-outbound-channel-adapter id="udpOut"
|
||||
<programlisting language="xml"><![CDATA[<int-ip:udp-outbound-channel-adapter id="udpOut"
|
||||
host="somehost"
|
||||
port="11111"
|
||||
multicast="false"
|
||||
channel="exampleChannel" />]]></programlisting>
|
||||
channel="exampleChannel"/>]]></programlisting>
|
||||
A simple UDP outbound channel adapter.
|
||||
<tip>
|
||||
When setting multicast to true, provide the multicast address in the host
|
||||
@@ -62,12 +62,12 @@
|
||||
short the contain the packet, the packet can be truncated. The length header provides a mechanism to detect this.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting language="xml"><![CDATA[ <int-ip:udp-outbound-channel-adapter id="udpOut"
|
||||
<programlisting language="xml"><![CDATA[<int-ip:udp-outbound-channel-adapter id="udpOut"
|
||||
host="somehost"
|
||||
port="11111"
|
||||
multicast="false"
|
||||
check-length="true"
|
||||
channel="exampleChannel" />]]></programlisting>
|
||||
channel="exampleChannel"/>]]></programlisting>
|
||||
An outbound channel adapter that adds length checking to the datagram packets.
|
||||
<tip>
|
||||
The recipient of the packet must also be configured to expect a length to precede the
|
||||
@@ -80,7 +80,7 @@
|
||||
must send an acknowledgment to the sender within a specified time.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting language="xml"><![CDATA[ <int-ip:udp-outbound-channel-adapter id="udpOut"
|
||||
<programlisting language="xml"><![CDATA[<int-ip:udp-outbound-channel-adapter id="udpOut"
|
||||
host="somehost"
|
||||
port="11111"
|
||||
multicast="false"
|
||||
@@ -89,7 +89,7 @@
|
||||
ack-host="thishost"
|
||||
ack-port="22222"
|
||||
ack-timeout="10000"
|
||||
channel="exampleChannel" />]]></programlisting>
|
||||
channel="exampleChannel"/>]]></programlisting>
|
||||
An outbound channel adapter that adds length checking to the datagram packets and waits for an acknowledgment.
|
||||
<tip>
|
||||
Setting acknowledge to true implies the recipient of the packet can interpret the header added to the packet
|
||||
@@ -105,26 +105,26 @@
|
||||
For even more reliable networking, TCP can be used.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting language="xml"><![CDATA[ <int-ip:udp-inbound-channel-adapter id="udpReceiver"
|
||||
<programlisting language="xml"><![CDATA[<int-ip:udp-inbound-channel-adapter id="udpReceiver"
|
||||
channel="udpOutChannel"
|
||||
port="11111"
|
||||
receive-buffer-size="500"
|
||||
multicast="false"
|
||||
check-length="true" />]]></programlisting>
|
||||
check-length="true"/>]]></programlisting>
|
||||
A basic unicast inbound udp channel adapter.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting language="xml"><![CDATA[ <int-ip:udp-inbound-channel-adapter id="udpReceiver"
|
||||
<programlisting language="xml"><![CDATA[<int-ip:udp-inbound-channel-adapter id="udpReceiver"
|
||||
channel="udpOutChannel"
|
||||
port="11111"
|
||||
receive-buffer-size="500"
|
||||
multicast="true"
|
||||
multicast-address="225.6.7.8"
|
||||
check-length="true" />]]></programlisting>
|
||||
check-length="true"/>]]></programlisting>
|
||||
A basic multicast inbound udp channel adapter.
|
||||
</para>
|
||||
<para>
|
||||
By default, reverse DNS lookups are done on inbound packets to convert IP addresses to
|
||||
By default, reverse DNS lookups are done on inbound packets to convert IP addresses to
|
||||
hostnames for use in message headers.
|
||||
In environments where DNS is not configured, this can cause delays.
|
||||
This default behavior can be overridden by setting the <literal>lookup-host</literal>
|
||||
@@ -175,46 +175,38 @@
|
||||
<classname>java.nio.channel.SocketChannel</classname> are provided.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<int-ip:tcp-connection-factory id="server"
|
||||
<programlisting language="xml"><![CDATA[<int-ip:tcp-connection-factory id="server"
|
||||
type="server"
|
||||
port="1234"
|
||||
/>]]></programlisting>
|
||||
port="1234"/>]]></programlisting>
|
||||
A simple server connection factory that uses <classname>java.net.Socket</classname>
|
||||
connections.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<int-ip:tcp-connection-factory id="server"
|
||||
<programlisting language="xml"><![CDATA[<int-ip:tcp-connection-factory id="server"
|
||||
type="server"
|
||||
port="1234"
|
||||
using-nio="true"
|
||||
/>]]></programlisting>
|
||||
using-nio="true"/>]]></programlisting>
|
||||
A simple server connection factory that uses <classname>java.nio.channel.SocketChannel</classname>
|
||||
connections.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<int-ip:tcp-connection-factory id="client"
|
||||
<programlisting language="xml"><![CDATA[<int-ip:tcp-connection-factory id="client"
|
||||
type="client"
|
||||
host="localhost"
|
||||
port="1234"
|
||||
single-use="true"
|
||||
so-timeout="10000"
|
||||
/>]]></programlisting>
|
||||
so-timeout="10000"/>]]></programlisting>
|
||||
A client connection factory that uses <classname>java.net.Socket</classname>
|
||||
connections and creates a new connection for each message.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<int-ip:tcp-connection-factory id="client"
|
||||
<programlisting language="xml"><![CDATA[<int-ip:tcp-connection-factory id="client"
|
||||
type="client"
|
||||
host="localhost"
|
||||
port="1234"
|
||||
single-use="true"
|
||||
so-timeout="10000"
|
||||
using-nio=true
|
||||
/>]]></programlisting>
|
||||
using-nio=true/>]]></programlisting>
|
||||
A client connection factory that uses <classname>java.nio.channel.Socket</classname>
|
||||
connections and creates a new connection for each message.
|
||||
</para>
|
||||
@@ -226,24 +218,24 @@
|
||||
deserializer and serializer for inbound and outbound messages respectively.
|
||||
A number of standard (de)serializers are provided.
|
||||
</para>
|
||||
<para>
|
||||
<para>
|
||||
The <classname>ByteArrayCrlfSerializer</classname>,
|
||||
converts a byte array to a stream of bytes followed by carriage
|
||||
return and linefeed characters (\r\n). This is the default (de)serializer and can be used with
|
||||
telnet as a client, for example.
|
||||
telnet as a client, for example.
|
||||
</para>
|
||||
<para>
|
||||
<para>
|
||||
The <classname>ByteArrayStxEtxSerializer</classname>,
|
||||
converts a byte array to a stream of bytes preceded by an STX (0x02) and
|
||||
followed by an ETX (0x03).
|
||||
followed by an ETX (0x03).
|
||||
</para>
|
||||
<para>
|
||||
<para>
|
||||
The <classname>ByteArrayLengthHeaderSerializer</classname>,
|
||||
converts a byte array to a stream of bytes preceded by a binary
|
||||
length in network byte order (big endian). This a very efficient deserializer
|
||||
length in network byte order (big endian). This a very efficient deserializer
|
||||
because it does not have to parse every byte looking for a termination
|
||||
character sequence. It can also be used for payloads containing binary data;
|
||||
the above serializers only support text in the payload. The default size of
|
||||
the above serializers only support text in the payload. The default size of
|
||||
the length header is 4 bytes (Integer), allowing for messages up to 2**31-1
|
||||
bytes. However, the length header can be a single byte (unsigned) for
|
||||
messages up to 255 bytes, or an unsigned short (2 bytes) for messages up to
|
||||
@@ -251,12 +243,12 @@
|
||||
this class and provide implementations for the readHeader and writeHeader
|
||||
methods. The absolute maximum data size supported is 2**31-1 bytes.
|
||||
</para>
|
||||
<para>
|
||||
<para>
|
||||
The <classname>ByteArrayRawSerializer</classname>,
|
||||
converts a byte array to a stream of bytes and adds no additional message
|
||||
demarcation data; with this (de)serializer, the end of a message is indicated
|
||||
by the client closing the socket in an orderly fashion. When using this serializer,
|
||||
message reception will hang until the client closes the socket, or a timeout occurs;
|
||||
by the client closing the socket in an orderly fashion. When using this serializer,
|
||||
message reception will hang until the client closes the socket, or a timeout occurs;
|
||||
a timeout will NOT result in a message. When this serializer is being used, and the client
|
||||
is a Spring Integration application, the client must use a connection factory that is
|
||||
configured with single-use=true - this causes the adapter to close the socket after sending
|
||||
@@ -273,7 +265,7 @@
|
||||
<classname>AbstractByteArraySerializer</classname> for serialization
|
||||
will also accept a String which will be converted to a byte array first.
|
||||
Each of these (de)serializers converts an input stream containing the
|
||||
corresponding format to a byte array payload.
|
||||
corresponding format to a byte array payload.
|
||||
</para>
|
||||
<para>
|
||||
To avoid memory exhaustion due to a badly behaved client (one that does not adhere to
|
||||
@@ -294,27 +286,25 @@
|
||||
<para>
|
||||
To implement a custom (de)serializer pair, implement the
|
||||
<classname>org.springframework.core.serializer.Deserializer</classname> and
|
||||
<classname>org.springframework.core.serializer.Serializer</classname> interfaces.
|
||||
<classname>org.springframework.core.serializer.Serializer</classname> interfaces.
|
||||
</para>
|
||||
<para>
|
||||
<para>
|
||||
If you do not wish to use
|
||||
the default (de)serializer (<classname>ByteArrayCrLfSerializer</classname>), you must supply
|
||||
<classname>serializer</classname> and
|
||||
<classname>deserializer</classname> attributes on the connection factory (example below).
|
||||
</para>
|
||||
<para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<bean id="javaSerializer"
|
||||
class="org.springframework.core.serializer.DefaultSerializer" />
|
||||
<bean id="javaDeserializer"
|
||||
class="org.springframework.core.serializer.DefaultDeserializer" />
|
||||
<programlisting language="xml"><![CDATA[<bean id="javaSerializer"
|
||||
class="org.springframework.core.serializer.DefaultSerializer" />
|
||||
<bean id="javaDeserializer"
|
||||
class="org.springframework.core.serializer.DefaultDeserializer" />
|
||||
|
||||
<int-ip:tcp-connection-factory id="server"
|
||||
<int-ip:tcp-connection-factory id="server"
|
||||
type="server"
|
||||
port="1234"
|
||||
deserializer="javaDeserializer"
|
||||
serializer="javaSerializer"
|
||||
/>]]></programlisting>
|
||||
serializer="javaSerializer"/>]]></programlisting>
|
||||
A server connection factory that uses <classname>java.net.Socket</classname>
|
||||
connections and uses Java serialization on the wire.
|
||||
</para>
|
||||
@@ -323,7 +313,7 @@
|
||||
reference at the end of this section.
|
||||
</para>
|
||||
<para>
|
||||
By default, reverse DNS lookups are done on inbound packets to convert IP addresses to
|
||||
By default, reverse DNS lookups are done on inbound packets to convert IP addresses to
|
||||
hostnames for use in message headers.
|
||||
In environments where DNS is not configured, this can cause connection delays.
|
||||
This default behavior can be overridden by setting the <literal>lookup-host</literal>
|
||||
@@ -365,7 +355,7 @@
|
||||
factories until either the message can be sent, or no connection
|
||||
can be found. Initially, the first factory in the configured
|
||||
list is used; if a connection subsequently fails the next
|
||||
factory will become the current factory.
|
||||
factory will become the current factory.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting language="xml"><![CDATA[<bean id="failCF" class="o.s.i.ip.tcp.connection.FailoverClientConnectionFactory">
|
||||
@@ -375,15 +365,14 @@
|
||||
<ref bean="clientFactory2"/>
|
||||
</list>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
]]></programlisting>
|
||||
</bean>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
<note>When using the failover connection factory, the singleUse property must be consistent between
|
||||
the factory itself and the list of factories it is configured to use.
|
||||
</note>
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
<section id="ip-interceptors">
|
||||
<title>TCP Connection Interceptors</title>
|
||||
@@ -426,31 +415,29 @@
|
||||
</para>
|
||||
<para>
|
||||
<programlisting language="xml"><![CDATA[<bean id="helloWorldInterceptorFactory"
|
||||
class="org.springframework.integration.ip.tcp.connection.TcpConnectionInterceptorFactoryChain">
|
||||
<property name="interceptors">
|
||||
<array>
|
||||
<bean class="org.springframework.integration.ip.tcp.connection.HelloWorldInterceptorFactory"/>
|
||||
</array>
|
||||
</property>
|
||||
class="o.s.i.ip.tcp.connection.TcpConnectionInterceptorFactoryChain">
|
||||
<property name="interceptors">
|
||||
<array>
|
||||
<bean class="o.s.i.ip.tcp.connection.HelloWorldInterceptorFactory"/>
|
||||
</array>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<int-ip:tcp-connection-factory id="server"
|
||||
type="server"
|
||||
port="12345"
|
||||
using-nio="true"
|
||||
single-use="true"
|
||||
interceptor-factory-chain="helloWorldInterceptorFactory"
|
||||
/>
|
||||
type="server"
|
||||
port="12345"
|
||||
using-nio="true"
|
||||
single-use="true"
|
||||
interceptor-factory-chain="helloWorldInterceptorFactory"/>
|
||||
|
||||
<int-ip:tcp-connection-factory id="client"
|
||||
type="client"
|
||||
host="localhost"
|
||||
port="12345"
|
||||
single-use="true"
|
||||
so-timeout="100000"
|
||||
using-nio="true"
|
||||
interceptor-factory-chain="helloWorldInterceptorFactory"
|
||||
/>]]></programlisting>
|
||||
type="client"
|
||||
host="localhost"
|
||||
port="12345"
|
||||
single-use="true"
|
||||
so-timeout="100000"
|
||||
using-nio="true"
|
||||
interceptor-factory-chain="helloWorldInterceptorFactory"/>]]></programlisting>
|
||||
Configuring a connection interceptor factory chain.
|
||||
</para>
|
||||
</section>
|
||||
@@ -498,54 +485,51 @@
|
||||
to a connection factory.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<bean id="javaSerializer"
|
||||
class="org.springframework.core.serializer.DefaultSerializer" />
|
||||
<bean id="javaDeserializer"
|
||||
class="org.springframework.core.serializer.DefaultDeserializer" />
|
||||
<programlisting language="xml"><![CDATA[<bean id="javaSerializer"
|
||||
class="org.springframework.core.serializer.DefaultSerializer"/>
|
||||
<bean id="javaDeserializer"
|
||||
class="org.springframework.core.serializer.DefaultDeserializer"/>
|
||||
|
||||
<int-ip:tcp-connection-factory id="server"
|
||||
<int-ip:tcp-connection-factory id="server"
|
||||
type="server"
|
||||
port="1234"
|
||||
deserializer="javaDeserializer"
|
||||
serializer="javaSerializer"
|
||||
using-nio="true"
|
||||
single-use="true"
|
||||
/>
|
||||
single-use="true"/>
|
||||
|
||||
<int-ip:tcp-connection-factory id="client"
|
||||
<int-ip:tcp-connection-factory id="client"
|
||||
type="client"
|
||||
host="localhost"
|
||||
port="#{server.port}"
|
||||
single-use="true"
|
||||
so-timeout="10000"
|
||||
deserializer="javaDeserializer"
|
||||
serializer="javaSerializer"
|
||||
/>
|
||||
serializer="javaSerializer"/>
|
||||
|
||||
<int:channel id="input" />
|
||||
<int:channel id="input" />
|
||||
|
||||
<int:channel id="replies">
|
||||
<int:channel id="replies">
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
</int:channel>
|
||||
|
||||
<int-ip:tcp-outbound-channel-adapter id="outboundClient"
|
||||
<int-ip:tcp-outbound-channel-adapter id="outboundClient"
|
||||
channel="input"
|
||||
connection-factory="client"/>
|
||||
|
||||
<int-ip:tcp-inbound-channel-adapter id="inboundClient"
|
||||
<int-ip:tcp-inbound-channel-adapter id="inboundClient"
|
||||
channel="replies"
|
||||
connection-factory="client"/>
|
||||
|
||||
<int-ip:tcp-inbound-channel-adapter id="inboundServer"
|
||||
<int-ip:tcp-inbound-channel-adapter id="inboundServer"
|
||||
channel="loop"
|
||||
connection-factory="server"/>
|
||||
|
||||
<int-ip:tcp-outbound-channel-adapter id="outboundServer"
|
||||
<int-ip:tcp-outbound-channel-adapter id="outboundServer"
|
||||
channel="loop"
|
||||
connection-factory="server"/>
|
||||
|
||||
<int:channel id="loop" />]]></programlisting>
|
||||
<int:channel id="loop"/>]]></programlisting>
|
||||
In this configuration, messages arriving in channel 'input'
|
||||
are serialized over connections created by 'client' received
|
||||
at the server and placed on channel 'loop'. Since 'loop' is
|
||||
@@ -571,7 +555,7 @@
|
||||
<para>
|
||||
Two additional attributes are used to support this mechanism:
|
||||
<emphasis>retry-interval</emphasis> specifies (in milliseconds)
|
||||
how often the framework will attempt to reconnect after a
|
||||
how often the framework will attempt to reconnect after a
|
||||
connection failure. <emphasis>scheduler</emphasis> is used to
|
||||
supply a <classname>TaskScheduler</classname> used to
|
||||
schedule the connection attempts, and to test that the connection is
|
||||
@@ -584,7 +568,7 @@
|
||||
when the adapter is started. Adapters are automatically started
|
||||
by default. Again, the connection factory must be of type client
|
||||
and have <emphasis>single-use</emphasis> set to false and
|
||||
<emphasis>retry-interval</emphasis> and
|
||||
<emphasis>retry-interval</emphasis> and
|
||||
<emphasis>scheduler</emphasis> are also supported. If a connection
|
||||
fails, it will be re-established either by the scheduler or
|
||||
when the next message is sent.
|
||||
@@ -616,19 +600,19 @@
|
||||
For the inbound gateway, care must be taken to retain, or populate, the
|
||||
<emphasis>ip_connnection_id</emphasis> header because it is used to
|
||||
correlate the message to a connection. Messages that originate at the
|
||||
gateway will automatically have the header set. If the reply is
|
||||
gateway will automatically have the header set. If the reply is
|
||||
constructed as a new message, you will need to set the header. The
|
||||
header value can be captured from the incoming message.
|
||||
</para>
|
||||
</note>
|
||||
</para>
|
||||
<para>
|
||||
As with inbound adapters, inbound gateways normally use a
|
||||
As with inbound adapters, inbound gateways normally use a
|
||||
type="server" connection
|
||||
factory, which listens for incoming connection requests.
|
||||
In some cases, it is desireable to establish the connection
|
||||
in reverse, whereby the inbound gateway connects to an
|
||||
external server and then waits for, and replies to, inbound
|
||||
external server and then waits for, and replies to, inbound
|
||||
messages on that connection.
|
||||
</para>
|
||||
<para>
|
||||
@@ -640,7 +624,7 @@
|
||||
<para>
|
||||
Two additional attributes are used to support this mechanism:
|
||||
<emphasis>retry-interval</emphasis> specifies (in milliseconds)
|
||||
how often the framework will attempt to reconnect after a
|
||||
how often the framework will attempt to reconnect after a
|
||||
connection failure. <emphasis>scheduler</emphasis> is used to
|
||||
supply a <classname>TaskScheduler</classname> used to
|
||||
schedule the connection attempts, and to test that the connection is
|
||||
@@ -664,25 +648,23 @@
|
||||
</para>
|
||||
<para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<int-ip:tcp-inbound-gateway id="inGateway"
|
||||
<int-ip:tcp-inbound-gateway id="inGateway"
|
||||
request-channel="tcpChannel"
|
||||
reply-channel="replyChannel"
|
||||
connection-factory="cfServer"
|
||||
reply-timeout="10000"
|
||||
/>]]></programlisting>
|
||||
reply-timeout="10000"/>]]></programlisting>
|
||||
A simple inbound TCP gateway; if a connection factory configured with the default
|
||||
(de)serializer is used, messages will be \r\n delimited data and the gateway can be
|
||||
used by a simple client such as telnet.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<int-ip:tcp-outbound-gateway id="outGateway"
|
||||
<int-ip:tcp-outbound-gateway id="outGateway"
|
||||
request-channel="tcpChannel"
|
||||
reply-channel="replyChannel"
|
||||
connection-factory="cfClient"
|
||||
request-timeout="10000"
|
||||
remote-timeout="10000"
|
||||
/>]]></programlisting>
|
||||
remote-timeout="10000"/>]]></programlisting>
|
||||
A simple outbound TCP gateway.
|
||||
</para>
|
||||
</section>
|
||||
@@ -692,11 +674,11 @@
|
||||
<title>Overview</title>
|
||||
<para>
|
||||
One goal of the IP Endpoints is to provide communication with systems other
|
||||
than another Spring Integration application. For this reason, only
|
||||
than another Spring Integration application. For this reason, only
|
||||
message payloads are sent
|
||||
and received. No message correlation is provided by the framework,
|
||||
except when using the gateways, or collaborating channel adapters on the
|
||||
server side. In the paragraphs below we discuss the various
|
||||
except when using the gateways, or collaborating channel adapters on the
|
||||
server side. In the paragraphs below we discuss the various
|
||||
correlation techniques available to applications. In most cases, this
|
||||
requires specific application-level correlation of messages, even when
|
||||
message payloads contain some natural correlation data (such as an order
|
||||
@@ -707,17 +689,17 @@
|
||||
<title>Gateways</title>
|
||||
<para>
|
||||
The gateways will automatically correlate messages. However, an outbound
|
||||
gateway should only be used for relatively low-volume use.
|
||||
gateway should only be used for relatively low-volume use.
|
||||
When the connection factory is configured for a single shared connection
|
||||
to be used for all message pairs ('single-use="false"'),
|
||||
to be used for all message pairs ('single-use="false"'),
|
||||
only one message can be processed at a time.
|
||||
A new message will have to wait until the reply to the previous message has
|
||||
been received.
|
||||
When a connection factory is configured for each new message to use a new connection
|
||||
been received.
|
||||
When a connection factory is configured for each new message to use a new connection
|
||||
('single-use="true"'), the above restriction does not apply.
|
||||
While this may give higher throughput
|
||||
than a shared connection environment, it comes with the overhead of opening
|
||||
and closing a new connection for each message pair.
|
||||
and closing a new connection for each message pair.
|
||||
</para>
|
||||
<para>
|
||||
Therefore, for high-volume messages, consider using a collaborating pair of
|
||||
@@ -760,39 +742,39 @@
|
||||
This can be done in a number of ways.
|
||||
</para>
|
||||
<para>
|
||||
If the message payload has some natural correlation data, such as a
|
||||
If the message payload has some natural correlation data, such as a
|
||||
transaction id or an order number, AND there is no need to retain any
|
||||
information (such as a reply channel header) from the original outbound message,
|
||||
the correlation is simple and would done at the application level in any case.
|
||||
the correlation is simple and would done at the application level in any case.
|
||||
</para>
|
||||
<para>
|
||||
If the message payload has some natural correlation data, such as a
|
||||
If the message payload has some natural correlation data, such as a
|
||||
transaction id or an order number, but there is a need to retain some
|
||||
information (such as a reply channel header) from the original outbound message,
|
||||
information (such as a reply channel header) from the original outbound message,
|
||||
you may need to retain a copy of the original outbound message (perhaps
|
||||
by using a publish-subscribe channel) and use an aggregator to recombine
|
||||
the necessary data.
|
||||
</para>
|
||||
<para>
|
||||
For either of the previous two paragraphs, if the payload has no natural
|
||||
For either of the previous two paragraphs, if the payload has no natural
|
||||
correlation data, you may need to provide a transformer upstream of the
|
||||
outbound channel adapter to enhance the payload with such data. Such a
|
||||
outbound channel adapter to enhance the payload with such data. Such a
|
||||
transformer may transform the original payload to a new object containing
|
||||
both the original payload and some subset of the message headers. Of course,
|
||||
live objects (such as reply channels) from the headers can not be
|
||||
live objects (such as reply channels) from the headers can not be
|
||||
included in the transformed payload.
|
||||
</para>
|
||||
<para>
|
||||
If such a strategy is chosen you will need to ensure the connection factory
|
||||
has an appropriate serializer/deserializer pair to handle such a payload,
|
||||
such as the <classname>DefaultSerializer/Deserializer</classname> which use java
|
||||
serialization, or a custom serializer and deserializer.
|
||||
such as the <classname>DefaultSerializer/Deserializer</classname> which use java
|
||||
serialization, or a custom serializer and deserializer.
|
||||
The <classname>ByteArray*Serializer</classname> options
|
||||
mentioned in <xref linkend="connection-factories" />,
|
||||
including the default <classname>ByteArrayCrLfSerializer</classname>,
|
||||
including the default <classname>ByteArrayCrLfSerializer</classname>,
|
||||
do not support such payloads,
|
||||
unless the transformed payload is a <classname>String</classname> or
|
||||
<classname>byte[]</classname>,
|
||||
unless the transformed payload is a <classname>String</classname> or
|
||||
<classname>byte[]</classname>,
|
||||
</para>
|
||||
<para>
|
||||
<note>
|
||||
@@ -819,11 +801,11 @@
|
||||
<section id="note_nio">
|
||||
<title>A Note About NIO</title>
|
||||
<para>
|
||||
Using NIO (see <classname>using-nio</classname> in
|
||||
Using NIO (see <classname>using-nio</classname> in
|
||||
<xref linkend="ip-endpoint-reference" />)
|
||||
avoids dedicating a thread to read from each socket. For a small number
|
||||
of sockets, you will likely find that <emphasis>not</emphasis> using NIO,
|
||||
together with an async handoff (e.g. to a <classname>QueueChannel</classname>),
|
||||
of sockets, you will likely find that <emphasis>not</emphasis> using NIO,
|
||||
together with an async handoff (e.g. to a <classname>QueueChannel</classname>),
|
||||
will perform as well as, or better than, using NIO.
|
||||
</para>
|
||||
<para>
|
||||
@@ -835,7 +817,7 @@
|
||||
messages arriving on the <emphasis>same</emphasis> socket <emphasis>might</emphasis>
|
||||
be processed by different threads. This means that the order in which the messages are
|
||||
sent to the channel is indeterminate; the strict ordering of the messages arriving on the
|
||||
socket is not maintained.
|
||||
socket is not maintained.
|
||||
</para>
|
||||
<para>
|
||||
For some applications, this is not an issue; for others it is. If strict ordering
|
||||
@@ -926,19 +908,19 @@
|
||||
<classname>TcpSSLContextSupport</classname> bean, and provide a reference to that bean
|
||||
to the connection factory.
|
||||
</para>
|
||||
<para><programlisting language="xml"><![CDATA[ <bean id="sslContextSupport"
|
||||
class="o.sf.integration.ip.tcp.connection.support.DefaultTcpSSLContextSupport">
|
||||
<constructor-arg value="client.ks"/>
|
||||
<constructor-arg value="client.truststore.ks"/>
|
||||
<constructor-arg value="secret"/>
|
||||
<constructor-arg value="secret"/>
|
||||
</bean>
|
||||
<para><programlisting language="xml"><![CDATA[<bean id="sslContextSupport"
|
||||
class="o.sf.integration.ip.tcp.connection.support.DefaultTcpSSLContextSupport">
|
||||
<constructor-arg value="client.ks"/>
|
||||
<constructor-arg value="client.truststore.ks"/>
|
||||
<constructor-arg value="secret"/>
|
||||
<constructor-arg value="secret"/>
|
||||
</bean>
|
||||
|
||||
<ip:tcp-connection-factory id="clientFactory"
|
||||
type="client"
|
||||
host="localhost"
|
||||
port="1234"
|
||||
ssl-context-support="sslContextSupport"]]></programlisting>
|
||||
<ip:tcp-connection-factory id="clientFactory"
|
||||
type="client"
|
||||
host="localhost"
|
||||
port="1234"
|
||||
ssl-context-support="sslContextSupport"]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
The <classname>DefaulTcpSSLContextSupport</classname> class also has an optional
|
||||
@@ -1124,7 +1106,7 @@
|
||||
<entry>Y</entry>
|
||||
<entry></entry>
|
||||
<entry>Defaults to 0 (infinity), except for
|
||||
server connection factories with single-use="true".
|
||||
server connection factories with single-use="true".
|
||||
In that case, it
|
||||
defaults to the default reply timeout (10 seconds).
|
||||
</entry>
|
||||
@@ -1348,8 +1330,8 @@
|
||||
<row>
|
||||
<entry>error-channel</entry>
|
||||
<entry></entry>
|
||||
<entry>If an Exception is thrown by a downstream
|
||||
component, the MessagingException message containing the exception
|
||||
<entry>If an Exception is thrown by a downstream
|
||||
component, the MessagingException message containing the exception
|
||||
and failed message is sent to this channel.</entry>
|
||||
</row>
|
||||
<row>
|
||||
@@ -1503,17 +1485,17 @@
|
||||
<row>
|
||||
<entry>connection-factory</entry>
|
||||
<entry></entry>
|
||||
<entry>If the connection factory has a type 'server', the factory is 'owned'
|
||||
by this adapter. If it has a type 'client', it is 'owned' by an
|
||||
outbound channel adapter and this adapter will receive any
|
||||
incoming messages on the connection created by the
|
||||
<entry>If the connection factory has a type 'server', the factory is 'owned'
|
||||
by this adapter. If it has a type 'client', it is 'owned' by an
|
||||
outbound channel adapter and this adapter will receive any
|
||||
incoming messages on the connection created by the
|
||||
outbound adapter.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>error-channel</entry>
|
||||
<entry></entry>
|
||||
<entry>If an Exception is thrown by a downstream
|
||||
component, the MessagingException message containing the exception
|
||||
<entry>If an Exception is thrown by a downstream
|
||||
component, the MessagingException message containing the exception
|
||||
and failed message is sent to this channel.</entry>
|
||||
</row>
|
||||
<row>
|
||||
@@ -1540,7 +1522,7 @@
|
||||
<entry>scheduler</entry>
|
||||
<entry>true, false</entry>
|
||||
<entry>
|
||||
Specifies a <classname>TaskScheduler</classname> to use for managing the
|
||||
Specifies a <classname>TaskScheduler</classname> to use for managing the
|
||||
<emphasis>client-mode</emphasis> connection. Defaults to a
|
||||
<classname>ThreadPoolTaskScheduler</classname> with a pool size of `.
|
||||
</entry>
|
||||
@@ -1571,10 +1553,10 @@
|
||||
<row>
|
||||
<entry>connection-factory</entry>
|
||||
<entry></entry>
|
||||
<entry>If the connection factory has a type 'client', the factory is
|
||||
'owned' by this adapter. If it has a type 'server', it is 'owned'
|
||||
by an inbound channel adapter and this adapter will attempt
|
||||
to correlate messages to the connection on which an
|
||||
<entry>If the connection factory has a type 'client', the factory is
|
||||
'owned' by this adapter. If it has a type 'server', it is 'owned'
|
||||
by an inbound channel adapter and this adapter will attempt
|
||||
to correlate messages to the connection on which an
|
||||
original inbound message was received. </entry>
|
||||
</row>
|
||||
<row>
|
||||
@@ -1602,7 +1584,7 @@
|
||||
<entry>scheduler</entry>
|
||||
<entry>true, false</entry>
|
||||
<entry>
|
||||
Specifies a <classname>TaskScheduler</classname> to use for managing the
|
||||
Specifies a <classname>TaskScheduler</classname> to use for managing the
|
||||
<emphasis>client-mode</emphasis> connection. Defaults to a
|
||||
<classname>ThreadPoolTaskScheduler</classname> with a pool size of `.
|
||||
</entry>
|
||||
@@ -1651,8 +1633,8 @@
|
||||
<row>
|
||||
<entry>error-channel</entry>
|
||||
<entry></entry>
|
||||
<entry>If an Exception is thrown by a downstream
|
||||
component, the MessagingException message containing the exception
|
||||
<entry>If an Exception is thrown by a downstream
|
||||
component, the MessagingException message containing the exception
|
||||
and failed message is sent to this channel;
|
||||
any reply from that flow will then be returned as a response by
|
||||
the gateway.</entry>
|
||||
@@ -1662,7 +1644,7 @@
|
||||
<entry>true, false</entry>
|
||||
<entry>
|
||||
When true, the inbound gateway will act as a client, with respect to establishing
|
||||
the connection and then receive (and reply to) incoming messages on that connection.
|
||||
the connection and then receive (and reply to) incoming messages on that connection.
|
||||
Default = false.
|
||||
Also see <emphasis>retry-interval</emphasis> and <emphasis>scheduler</emphasis>.
|
||||
The connection factory must be of type 'client' and have <emphasis>single-use</emphasis>
|
||||
@@ -1682,7 +1664,7 @@
|
||||
<entry>scheduler</entry>
|
||||
<entry>true, false</entry>
|
||||
<entry>
|
||||
Specifies a <classname>TaskScheduler</classname> to use for managing the
|
||||
Specifies a <classname>TaskScheduler</classname> to use for managing the
|
||||
<emphasis>client-mode</emphasis> connection. Defaults to a
|
||||
<classname>ThreadPoolTaskScheduler</classname> with a pool size of `.
|
||||
</entry>
|
||||
@@ -1732,7 +1714,7 @@
|
||||
<row>
|
||||
<entry>request-timeout</entry>
|
||||
<entry></entry>
|
||||
<entry>If a single-use connection factory is not being used, The time in milliseconds
|
||||
<entry>If a single-use connection factory is not being used, The time in milliseconds
|
||||
for which the gateway will wait to get access to the shared connection.</entry>
|
||||
</row>
|
||||
<row>
|
||||
|
||||
@@ -782,9 +782,9 @@
|
||||
</para>
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-jdbc:sql-parameter-definition name="" ]]><co id="sp-parameter-definition-xml01-co" linkends="sp-parameter-definition-xml01" /><![CDATA[
|
||||
direction="IN" ]]><co id="sp-parameter-definition-xml02-co" linkends="sp-parameter-definition-xml02" /><![CDATA[
|
||||
type="STRING" ]]><co id="sp-parameter-definition-xml03-co" linkends="sp-parameter-definition-xml03" /><![CDATA[
|
||||
scale=""/> ]]><co id="sp-parameter-definition-xml04-co" linkends="sp-parameter-definition-xml04" /></programlisting>
|
||||
direction="IN" ]]><co id="sp-parameter-definition-xml02-co" linkends="sp-parameter-definition-xml02" /><![CDATA[
|
||||
type="STRING" ]]><co id="sp-parameter-definition-xml03-co" linkends="sp-parameter-definition-xml03" /><![CDATA[
|
||||
scale=""/> ]]><co id="sp-parameter-definition-xml04-co" linkends="sp-parameter-definition-xml04" /></programlisting>
|
||||
<para>
|
||||
<calloutlist>
|
||||
<callout arearefs="sp-parameter-definition-xml01-co" id="sp-parameter-definition-xml01">
|
||||
@@ -873,9 +873,9 @@
|
||||
database. In that case the following configuration for a Stored
|
||||
Procedure Oubound Adapter will be sufficient:
|
||||
</para>
|
||||
<programlisting><![CDATA[<int-jdbc:stored-proc-outbound-channel-adapter data-source="dataSource"
|
||||
channel="insertCoffeeProcedureRequestChannel"
|
||||
stored-procedure-name="INSERT_COFFEE"/>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int-jdbc:stored-proc-outbound-channel-adapter data-source="dataSource"
|
||||
channel="insertCoffeeProcedureRequestChannel"
|
||||
stored-procedure-name="INSERT_COFFEE"/>]]></programlisting>
|
||||
<para>
|
||||
For more sophisticated options consider passing in one or more
|
||||
<classname>ProcedureParameter</classname>.
|
||||
@@ -1150,9 +1150,9 @@ EXTERNAL NAME 'org.springframework.integration.jdbc.storedproc.derby.DerbyStored
|
||||
request-channel="findAllProcedureRequestChannel"
|
||||
expect-single-result="true"
|
||||
stored-procedure-name="FIND_ALL_COFFEE_BEVERAGES">
|
||||
<int-jdbc:returning-resultset name="coffeeBeverages"
|
||||
row-mapper="org.springframework.integration.support.CoffeBeverageMapper"/>
|
||||
</int-jdbc:stored-proc-outbound-gateway>]]></programlisting>
|
||||
<int-jdbc:returning-resultset name="coffeeBeverages"
|
||||
row-mapper="org.springframework.integration.support.CoffeBeverageMapper"/>
|
||||
</int-jdbc:stored-proc-outbound-gateway>]]></programlisting>
|
||||
|
||||
<para>
|
||||
In the second example, we call a Stored Procedure named
|
||||
@@ -1165,7 +1165,7 @@ EXTERNAL NAME 'org.springframework.integration.jdbc.storedproc.derby.DerbyStored
|
||||
...
|
||||
}]]></programlisting>
|
||||
|
||||
<programlisting><![CDATA[CREATE PROCEDURE FIND_COFFEE(IN ID INTEGER, OUT COFFEE_DESCRIPTION VARCHAR(200)) \
|
||||
<programlisting language="sql"><![CDATA[CREATE PROCEDURE FIND_COFFEE(IN ID INTEGER, OUT COFFEE_DESCRIPTION VARCHAR(200)) \
|
||||
PARAMETER STYLE JAVA LANGUAGE JAVA EXTERNAL NAME \
|
||||
'org.springframework.integration.jdbc.storedproc.derby.DerbyStoredProcedures.findCoffee';]]></programlisting>
|
||||
|
||||
|
||||
@@ -45,9 +45,9 @@
|
||||
instance or both <interfacename>ConnectionFactory</interfacename> and <interfacename>Destination</interfacename>
|
||||
(a 'destinationName' can be provided in place of the 'destination' reference). The following example defines an
|
||||
inbound Channel Adapter with a <classname>Destination</classname> reference.
|
||||
<programlisting language="xml"><![CDATA[ <int-jms:inbound-channel-adapter id="jmsIn" destination="inQueue" channel="exampleChannel">
|
||||
<int:poller fixed-rate="30000"/>
|
||||
</int-jms:inbound-channel-adapter>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int-jms:inbound-channel-adapter id="jmsIn" destination="inQueue" channel="exampleChannel">
|
||||
<int:poller fixed-rate="30000"/>
|
||||
</int-jms:inbound-channel-adapter>]]></programlisting>
|
||||
<tip>
|
||||
Notice from the configuration that the inbound-channel-adapter is a Polling Consumer. That means that
|
||||
it invokes receive() when triggered. This should only be used in situations where polling is done relatively
|
||||
@@ -68,12 +68,12 @@
|
||||
String-based payload, a JMS BytesMessage will produce a byte array payload, and a JMS ObjectMessage's
|
||||
Serializable instance will become the Spring Integration Message's payload. If instead you prefer to have
|
||||
the raw JMS Message as the Spring Integration Message's payload, then set 'extract-payload' to false.
|
||||
<programlisting language="xml"><![CDATA[ <int-jms:inbound-channel-adapter id="jmsIn"
|
||||
destination="inQueue"
|
||||
channel="exampleChannel"
|
||||
extract-payload="false"/>
|
||||
<int:poller fixed-rate="30000"/>
|
||||
</int-jms:inbound-channel-adapter>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int-jms:inbound-channel-adapter id="jmsIn"
|
||||
destination="inQueue"
|
||||
channel="exampleChannel"
|
||||
extract-payload="false"/>
|
||||
<int:poller fixed-rate="30000"/>
|
||||
</int-jms:inbound-channel-adapter>]]></programlisting>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
<interfacename>ConnectionFactory</interfacename> and <interfacename>Destination</interfacename>
|
||||
(a 'destinationName' can be provided in place of the 'destination' reference). The following example defines a
|
||||
message-driven Channel Adapter with a <classname>Destination</classname> reference.
|
||||
<programlisting language="xml"><![CDATA[ <int-jms:message-driven-channel-adapter id="jmsIn" destination="inQueue" channel="exampleChannel"/>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int-jms:message-driven-channel-adapter id="jmsIn" destination="inQueue" channel="exampleChannel"/>]]></programlisting>
|
||||
<note>
|
||||
The Message-Driven adapter also accepts several properties that pertain to the MessageListener container.
|
||||
These values are only considered if you do not provide an actual 'container' reference. In that case,
|
||||
@@ -104,10 +104,9 @@
|
||||
</para>
|
||||
<para>Finally, the <message-driven-channel-adapter> also accepts the 'error-channel' attribute. This
|
||||
provides the same basic functionality as described in <xref linkend="gateway-proxy"/>.
|
||||
<programlisting language="xml"><![CDATA[ <int-jms:message-driven-channel-adapter id="jmsIn" destination="inQueue"
|
||||
channel="exampleChannel"
|
||||
error-channel="exampleErrorChannel"/>
|
||||
]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int-jms:message-driven-channel-adapter id="jmsIn" destination="inQueue"
|
||||
channel="exampleChannel"
|
||||
error-channel="exampleErrorChannel"/>]]></programlisting>
|
||||
When comparing this to the generic gateway configuration, or the JMS 'inbound-gateway' that will
|
||||
be discussed below, the key difference here is that we are in a one-way flow
|
||||
since this is a 'channel-adapter', not a gateway. Therefore, the flow downstream from the
|
||||
@@ -154,9 +153,9 @@
|
||||
<interfacename>ConnectionFactory</interfacename>, and a request <interfacename>Destination</interfacename> (or
|
||||
'requestDestinationName'). The following example defines a JMS "inbound-gateway" that receives from the JMS
|
||||
queue referenced by the bean id "inQueue" and sends to the Spring Integration channel named "exampleChannel".
|
||||
<programlisting language="xml"><![CDATA[ <int-jms:inbound-gateway id="jmsInGateway"
|
||||
request-destination="inQueue"
|
||||
request-channel="exampleChannel"/>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int-jms:inbound-gateway id="jmsInGateway"
|
||||
request-destination="inQueue"
|
||||
request-channel="exampleChannel"/>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Since the gateways provide request/reply behavior instead of unidirectional send <emphasis>or</emphasis>
|
||||
@@ -220,10 +219,10 @@
|
||||
</para>
|
||||
</caution>
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[ <int-jms:outbound-gateway id="jmsOutGateway"
|
||||
request-destination="outQueue"
|
||||
request-channel="outboundJmsRequests"
|
||||
reply-channel="jmsReplies"/>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int-jms:outbound-gateway id="jmsOutGateway"
|
||||
request-destination="outQueue"
|
||||
request-channel="outboundJmsRequests"
|
||||
reply-channel="jmsReplies"/>]]></programlisting>
|
||||
<para>
|
||||
The 'outbound-gateway' payload extraction properties are inversely related to those of the
|
||||
'inbound-gateway' (see the discussion above). That means that the 'extract-request-payload' property value
|
||||
@@ -267,10 +266,10 @@
|
||||
caution above; therefore cached consumers should not be used in this case.
|
||||
</para>
|
||||
</caution>
|
||||
<programlisting language="xml"><![CDATA[ <int-jms:outbound-gateway id="jmsOutGateway"
|
||||
request-destination="outQueue"
|
||||
request-channel="outboundJmsRequests"
|
||||
reply-channel="jmsReplies">
|
||||
<programlisting language="xml"><![CDATA[<int-jms:outbound-gateway id="jmsOutGateway"
|
||||
request-destination="outQueue"
|
||||
request-channel="outboundJmsRequests"
|
||||
reply-channel="jmsReplies">
|
||||
<int-jms:reply-listener />
|
||||
</int-jms-outbound-gateway>]]></programlisting>
|
||||
<para>
|
||||
@@ -284,8 +283,7 @@
|
||||
</para>
|
||||
<section>
|
||||
<title>Attribute Reference</title>
|
||||
<programlisting><![CDATA[
|
||||
<int-jms:outbound-gateway
|
||||
<programlisting language="xml"><![CDATA[<int-jms:outbound-gateway
|
||||
connection-factory="connectionFactory"]]><co id="jog010" /><![CDATA[
|
||||
correlation-key=""]]><co id="jog020" /><![CDATA[
|
||||
delivery-persistent=""]]><co id="jog030" /><![CDATA[
|
||||
@@ -310,8 +308,7 @@
|
||||
request-pub-sub-domain=""]]><co id="jog220" /><![CDATA[
|
||||
time-to-live="">]]><co id="jog230" /><![CDATA[
|
||||
<int-jms:reply-listener />]]><co id="jog240" /><![CDATA[
|
||||
</int-jms:outbound-gateway>
|
||||
]]></programlisting>
|
||||
</int-jms:outbound-gateway>]]></programlisting>
|
||||
<calloutlist>
|
||||
<callout arearefs="jog010">
|
||||
<para>
|
||||
@@ -529,20 +526,19 @@
|
||||
Also, to provide some consistency with Marshaller and Unmarshaller interfaces Spring provides <interfacename>MarshallingMessageConverter</interfacename>
|
||||
which you can configure with your own custom Marshallers and Unmarshallers
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[ <int-jms:inbound-gateway request-destination="requestQueue"
|
||||
request-channel="inbound-gateway-channel"
|
||||
message-converter="marshallingMessageConverter"/>
|
||||
<programlisting language="xml"><![CDATA[<int-jms:inbound-gateway request-destination="requestQueue"
|
||||
request-channel="inbound-gateway-channel"
|
||||
message-converter="marshallingMessageConverter"/>
|
||||
|
||||
<bean id="marshallingMessageConverter"
|
||||
class="org.springframework.jms.support.converter.MarshallingMessageConverter">
|
||||
<constructor-arg>
|
||||
<bean class="org.bar.SampleMarshaller"/>
|
||||
</constructor-arg>
|
||||
<constructor-arg>
|
||||
<bean class="org.bar.SampleUnmarshaller"/>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
]]></programlisting>
|
||||
<bean id="marshallingMessageConverter"
|
||||
class="org.springframework.jms.support.converter.MarshallingMessageConverter">
|
||||
<constructor-arg>
|
||||
<bean class="org.bar.SampleMarshaller"/>
|
||||
</constructor-arg>
|
||||
<constructor-arg>
|
||||
<bean class="org.bar.SampleUnmarshaller"/>
|
||||
</constructor-arg>
|
||||
</bean>]]></programlisting>
|
||||
|
||||
|
||||
<note>
|
||||
@@ -574,7 +570,7 @@
|
||||
required even though conceptually the goal is to have a single Message Channel. A better option is
|
||||
supported as of Spring Integration version 2.0. Now it is possible to define a single "channel" when
|
||||
using the JMS namespace.
|
||||
<programlisting language="xml"><![CDATA[ <int-jms:channel id="jmsChannel" queue="exampleQueue"/>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int-jms:channel id="jmsChannel" queue="exampleQueue"/>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
The channel in the above example will behave much like a normal <channel/> element from the main
|
||||
@@ -598,13 +594,13 @@
|
||||
Since the example above is referencing a JMS Queue instance, it will act as a point-to-point channel. If
|
||||
on the other hand, publish/subscribe behavior is needed, then a separate element can be used, and a JMS
|
||||
Topic can be referenced instead.
|
||||
<programlisting language="xml"><![CDATA[ <int-jms:publish-subscribe-channel id="jmsChannel" topic="exampleTopic"/>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int-jms:publish-subscribe-channel id="jmsChannel" topic="exampleTopic"/>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
For either type of JMS-backed channel, the name of the destination may be provided instead of a reference.
|
||||
<programlisting language="xml"><![CDATA[ <int-jms:channel id="jmsQueueChannel" queue-name="exampleQueueName"/>
|
||||
<programlisting language="xml"><![CDATA[<int-jms:channel id="jmsQueueChannel" queue-name="exampleQueueName"/>
|
||||
|
||||
<jms:publish-subscribe-channel id="jmsTopicChannel" topic-name="exampleTopicName"/>]]></programlisting>
|
||||
<jms:publish-subscribe-channel id="jmsTopicChannel" topic-name="exampleTopicName"/>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
In the examples above, the Destination names would be resolved by Spring's default
|
||||
@@ -613,9 +609,9 @@
|
||||
<interfacename>ConnectionFactory</interfacename> is a required property of the channel, but by default
|
||||
the expected bean name would be "connectionFactory". The example below provides both a custom instance
|
||||
for resolution of the JMS Destination names and a different name for the ConnectionFactory.
|
||||
<programlisting language="xml"><![CDATA[ <int-jms:channel id="jmsChannel" queue-name="exampleQueueName"
|
||||
destination-resolver="customDestinationResolver"
|
||||
connection-factory="customConnectionFactory"/>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int-jms:channel id="jmsChannel" queue-name="exampleQueueName"
|
||||
destination-resolver="customDestinationResolver"
|
||||
connection-factory="customConnectionFactory"/>]]></programlisting>
|
||||
</para>
|
||||
</section>
|
||||
<section id="jms-selectors">
|
||||
|
||||
@@ -61,13 +61,13 @@
|
||||
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 <emphasis>object-name</emphasis>
|
||||
attribute can contain a SpEL reference to a <util:list/> of ObjectName patterns:
|
||||
<programlisting><![CDATA[<jmx:notification-listening-channel-adapter id="manyNotificationsAdapter"
|
||||
channel="manyNotificationsChannel"
|
||||
object-name="#{patterns}"/>
|
||||
<programlisting language="xml"><![CDATA[<jmx:notification-listening-channel-adapter id="manyNotificationsAdapter"
|
||||
channel="manyNotificationsChannel"
|
||||
object-name="#{patterns}"/>
|
||||
|
||||
<util:list id="patterns">
|
||||
<value>org.foo:type=Foo,name=*</value>
|
||||
<value>org.foo:type=Bar,name=*</value>
|
||||
<value>org.foo:type=Foo,name=*</value>
|
||||
<value>org.foo:type=Bar,name=*</value>
|
||||
</util:list>]]></programlisting>
|
||||
The names of the located MBean(s) will be logged when DEBUG level logging is enabled.
|
||||
</note>
|
||||
|
||||
@@ -169,30 +169,30 @@
|
||||
</para>
|
||||
|
||||
<programlisting language="xml"><![CDATA[<bean id="jpaExecutor" class="o.s.i.jpa.core.JpaExecutor">
|
||||
<constructor-arg name="entityManager" ref="entityManager"/>
|
||||
<property name="entityClass" value="o.s.i.jpa.test.entity.StudentDomain"/>
|
||||
<property name="jpaQuery" value="select s from Student s where s.id = :id"/>
|
||||
<property name="expectSingleResult" value="true"/>
|
||||
<property name="jpaParameters" >
|
||||
<util:list>
|
||||
<bean class="org.springframework.integration.jpa.support.JpaParameter">
|
||||
<property name="name" value="id"/>
|
||||
<property name="expression" value="payload"/>
|
||||
</bean>
|
||||
</util:list>
|
||||
</property>
|
||||
<constructor-arg name="entityManager" ref="entityManager"/>
|
||||
<property name="entityClass" value="o.s.i.jpa.test.entity.StudentDomain"/>
|
||||
<property name="jpaQuery" value="select s from Student s where s.id = :id"/>
|
||||
<property name="expectSingleResult" value="true"/>
|
||||
<property name="jpaParameters" >
|
||||
<util:list>
|
||||
<bean class="org.springframework.integration.jpa.support.JpaParameter">
|
||||
<property name="name" value="id"/>
|
||||
<property name="expression" value="payload"/>
|
||||
</bean>
|
||||
</util:list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="jpaOutboundGateway" class="o.s.i.jpa.outbound.JpaOutboundGateway">
|
||||
<constructor-arg ref="jpaExecutor"/>
|
||||
<property name="gatewayType" value="RETRIEVING"/>
|
||||
<property name="outputChannel" ref="studentReplyChannel"/>
|
||||
<constructor-arg ref="jpaExecutor"/>
|
||||
<property name="gatewayType" value="RETRIEVING"/>
|
||||
<property name="outputChannel" ref="studentReplyChannel"/>
|
||||
</bean>
|
||||
|
||||
<bean id="getStudentEndpoint"
|
||||
class="org.springframework.integration.endpoint.EventDrivenConsumer">
|
||||
<constructor-arg name="inputChannel" ref="getStudentChannel"/>
|
||||
<constructor-arg name="handler" ref="jpaOutboundGateway"/>
|
||||
class="org.springframework.integration.endpoint.EventDrivenConsumer">
|
||||
<constructor-arg name="inputChannel" ref="getStudentChannel"/>
|
||||
<constructor-arg name="handler" ref="jpaOutboundGateway"/>
|
||||
</bean>]]></programlisting>
|
||||
<note>
|
||||
For more examples of constructing JPA components purely through Java,
|
||||
@@ -348,19 +348,18 @@
|
||||
adapters.The xml snippet below shows a sample where a transaction manager is configured with
|
||||
the poller used with an <emphasis>Inbound Channel Adapter</emphasis>.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<int-jpa:inbound-channel-adapter
|
||||
channel="inboundChannelAdapterOne"
|
||||
entity-manager="em"
|
||||
auto-startup="true"
|
||||
jpa-query="select s from Student s"
|
||||
expect-single-result="true"
|
||||
delete-after-poll="true">
|
||||
<int:poller fixed-rate="2000" >
|
||||
<int:transactional propagation="REQUIRED" transaction-manager="transactionManager"/>
|
||||
</int:poller>
|
||||
</int-jpa:inbound-channel-adapter>
|
||||
]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int-jpa:inbound-channel-adapter
|
||||
channel="inboundChannelAdapterOne"
|
||||
entity-manager="em"
|
||||
auto-startup="true"
|
||||
jpa-query="select s from Student s"
|
||||
expect-single-result="true"
|
||||
delete-after-poll="true">
|
||||
<int:poller fixed-rate="2000" >
|
||||
<int:transactional propagation="REQUIRED"
|
||||
transaction-manager="transactionManager"/>
|
||||
</int:poller>
|
||||
</int-jpa:inbound-channel-adapter>]]></programlisting>
|
||||
<para>
|
||||
However, it may be necessary to specifically start a transaction when using an <emphasis>Outbound Channel Adapter</emphasis>/<emphasis>Gateway</emphasis>.
|
||||
If a <emphasis>DirectChannel</emphasis> is an input channel for the outbound adapter/gateway,
|
||||
@@ -369,15 +368,16 @@
|
||||
in a new transaction as below.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<int-jpa:outbound-gateway
|
||||
request-channel="namedQueryRequestChannel"
|
||||
reply-channel="namedQueryResponseChannel"
|
||||
named-query="updateStudentByRollNumber"
|
||||
entity-manager="em"
|
||||
gateway-type="UPDATING">
|
||||
<int-jpa:parameter name="lastName" expression="payload"/>
|
||||
<int-jpa:parameter name="rollNumber" expression="headers['rollNumber']"/>]]>
|
||||
request-channel="namedQueryRequestChannel"
|
||||
reply-channel="namedQueryResponseChannel"
|
||||
named-query="updateStudentByRollNumber"
|
||||
entity-manager="em"
|
||||
gateway-type="UPDATING">
|
||||
<int-jpa:parameter name="lastName" expression="payload"/>
|
||||
<int-jpa:parameter name="rollNumber" expression="headers['rollNumber']"/>]]>
|
||||
<emphasis role="bold">
|
||||
<![CDATA[<int-jpa:transactional propagation="REQUIRES_NEW" transaction-manager="transactionManager"/>]]>
|
||||
<![CDATA[ <int-jpa:transactional propagation="REQUIRES_NEW"
|
||||
transaction-manager="transactionManager"/>]]>
|
||||
</emphasis><![CDATA[
|
||||
</int-jpa:outbound-gateway>]]></programlisting>
|
||||
<para>
|
||||
@@ -594,9 +594,9 @@
|
||||
Adapter to persist an entity to the database.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<int-jpa:outbound-channel-adapter channel="entityTypeChannel" ]]><co id="entityTypeChannel"/><![CDATA[
|
||||
entity-class="org.springframework.integration.jpa.test.entity.Student" ]]><co id="entityClass"/><![CDATA[
|
||||
persist-mode="PERSIST" ]]><co id="persistMode"/><![CDATA[
|
||||
entity-manager="em"/ >]]><co id="entityMgr"/></programlisting>
|
||||
entity-class="org.springframework.integration.jpa.test.entity.Student" ]]><co id="entityClass"/><![CDATA[
|
||||
persist-mode="PERSIST" ]]><co id="persistMode"/><![CDATA[
|
||||
entity-manager="em"/ >]]><co id="entityMgr"/></programlisting>
|
||||
<calloutlist>
|
||||
<callout arearefs="entityTypeChannel">
|
||||
<para>
|
||||
@@ -815,10 +815,10 @@ public class Student {
|
||||
order="" ]]><co id="outAdaptOrder"/><![CDATA[
|
||||
parameter-source-factory="" ]]><co id="outAdaptParamSourceFact"/><![CDATA[
|
||||
persist-mode="MERGE" ]]><co id="outAdaptPersistMode"/><![CDATA[
|
||||
use-payload-as-parameter-source="true" ]]><co id="outAdaptUserPayloadAsParamSrc"/><![CDATA[>
|
||||
use-payload-as-parameter-source="true" ]]><co id="outAdaptUserPayloadAsParamSrc"/><![CDATA[
|
||||
<int:poller/>
|
||||
<int-jpa:transactional/> ]]><co id="outAdaptTransactional"/><![CDATA[>
|
||||
<int-jpa:parameter/> ]]><co id="outAdaptParam"/><![CDATA[>
|
||||
<int-jpa:transactional/> ]]><co id="outAdaptTransactional"/><![CDATA[
|
||||
<int-jpa:parameter/> ]]><co id="outAdaptParam"/><![CDATA[
|
||||
</int-jpa:outbound-channel-adapter>]]></programlisting>
|
||||
<calloutlist>
|
||||
<callout arearefs="outAdaptAutoStart">
|
||||
@@ -1053,29 +1053,28 @@ public class Student {
|
||||
<section id="jpa-updating-outbound-gateway">
|
||||
<title>Updating Outbound Gateway</title>
|
||||
<programlisting language="xml"><![CDATA[<int-jpa:updating-outbound-gateway request-channel="" ]]><co id="outGateRequestChannel"/><![CDATA[
|
||||
auto-startup="true"
|
||||
entity-class=""
|
||||
entity-manager=""
|
||||
entity-manager-factory=""
|
||||
id=""
|
||||
jpa-operations=""
|
||||
jpa-query=""
|
||||
named-query=""
|
||||
native-query=""
|
||||
order=""
|
||||
parameter-source-factory=""
|
||||
persist-mode="MERGE"
|
||||
reply-channel="" ]]><co id="outGateReplyChannel"/><![CDATA[
|
||||
reply-timeout="" ]]><co id="outGateReplyTimeout"/><![CDATA[
|
||||
use-payload-as-parameter-source="true">
|
||||
auto-startup="true"
|
||||
entity-class=""
|
||||
entity-manager=""
|
||||
entity-manager-factory=""
|
||||
id=""
|
||||
jpa-operations=""
|
||||
jpa-query=""
|
||||
named-query=""
|
||||
native-query=""
|
||||
order=""
|
||||
parameter-source-factory=""
|
||||
persist-mode="MERGE"
|
||||
reply-channel="" ]]><co id="outGateReplyChannel"/><![CDATA[
|
||||
reply-timeout="" ]]><co id="outGateReplyTimeout"/><![CDATA[
|
||||
use-payload-as-parameter-source="true">
|
||||
|
||||
<int:poller/>
|
||||
<int-jpa:transactional/>
|
||||
<int:poller/>
|
||||
<int-jpa:transactional/>
|
||||
|
||||
<int-jpa:parameter name="" type="" value=""/>
|
||||
<int-jpa:parameter name="" expression=""/>
|
||||
|
||||
</int-jpa:updating-outbound-gateway>]]></programlisting>
|
||||
<int-jpa:parameter name="" type="" value=""/>
|
||||
<int-jpa:parameter name="" expression=""/>
|
||||
</int-jpa:updating-outbound-gateway>]]></programlisting>
|
||||
|
||||
<calloutlist>
|
||||
<callout arearefs="outGateRequestChannel">
|
||||
@@ -1102,31 +1101,30 @@ public class Student {
|
||||
<section id="jpa-retrieving-outbound-gateway">
|
||||
<title>Retrieving Outbound Gateway</title>
|
||||
<programlisting language="xml"><![CDATA[<int-jpa:retrieving-outbound-gateway request-channel=""
|
||||
auto-startup="true"
|
||||
delete-after-poll="false"
|
||||
delete-in-batch="false"
|
||||
entity-class=""
|
||||
entity-manager=""
|
||||
entity-manager-factory=""
|
||||
expect-single-result="false" ]]><co id="outGateExpectSingleResult"/><![CDATA[
|
||||
id=""
|
||||
jpa-operations=""
|
||||
jpa-query=""
|
||||
max-number-of-results="" ]]><co id="outGateMaxNumOfResults"/><![CDATA[
|
||||
named-query=""
|
||||
native-query=""
|
||||
order=""
|
||||
parameter-source-factory=""
|
||||
reply-channel=""
|
||||
reply-timeout=""
|
||||
use-payload-as-parameter-source="true">
|
||||
<int:poller></int:poller>
|
||||
<int-jpa:transactional/>
|
||||
auto-startup="true"
|
||||
delete-after-poll="false"
|
||||
delete-in-batch="false"
|
||||
entity-class=""
|
||||
entity-manager=""
|
||||
entity-manager-factory=""
|
||||
expect-single-result="false" ]]><co id="outGateExpectSingleResult"/><![CDATA[
|
||||
id=""
|
||||
jpa-operations=""
|
||||
jpa-query=""
|
||||
max-number-of-results="" ]]><co id="outGateMaxNumOfResults"/><![CDATA[
|
||||
named-query=""
|
||||
native-query=""
|
||||
order=""
|
||||
parameter-source-factory=""
|
||||
reply-channel=""
|
||||
reply-timeout=""
|
||||
use-payload-as-parameter-source="true">
|
||||
<int:poller></int:poller>
|
||||
<int-jpa:transactional/>
|
||||
|
||||
<int-jpa:parameter name="" type="" value=""/>
|
||||
<int-jpa:parameter name="" expression=""/>
|
||||
|
||||
</int-jpa:retrieving-outbound-gateway>]]></programlisting>
|
||||
<int-jpa:parameter name="" type="" value=""/>
|
||||
<int-jpa:parameter name="" expression=""/>
|
||||
</int-jpa:retrieving-outbound-gateway>]]></programlisting>
|
||||
<calloutlist>
|
||||
<callout arearefs="outGateExpectSingleResult">
|
||||
<para>
|
||||
@@ -1188,9 +1186,9 @@ public class Student {
|
||||
as JPA defining parameter.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<int-jpa:updating-outbound-gateway request-channel="entityRequestChannel" ]]><co id="gatewayEntityReqChannel"/><![CDATA[
|
||||
reply-channel="entityResponseChannel" ]]><co id="gatewayEntityRepChannel"/><![CDATA[
|
||||
entity-class="org.springframework.integration.jpa.test.entity.Student"
|
||||
entity-manager="em"/>]]></programlisting>
|
||||
reply-channel="entityResponseChannel" ]]><co id="gatewayEntityRepChannel"/><![CDATA[
|
||||
entity-class="org.springframework.integration.jpa.test.entity.Student"
|
||||
entity-manager="em"/>]]></programlisting>
|
||||
<calloutlist>
|
||||
<callout arearefs="gatewayEntityReqChannel">
|
||||
<para>
|
||||
@@ -1219,12 +1217,12 @@ public class Student {
|
||||
<emphasis>Updating Outbound Gateway</emphasis>.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<int-jpa:updating-outbound-gateway request-channel="jpaqlRequestChannel"
|
||||
reply-channel="jpaqlResponseChannel"
|
||||
jpa-query="update Student s set s.lastName = :lastName where s.rollNumber = :rollNumber" ]]><co id="jpaQlGatewayQuery"/><![CDATA[
|
||||
entity-manager="em">
|
||||
<int-jpa:parameter name="lastName" expression="payload"/>
|
||||
<int-jpa:parameter name="rollNumber" expression="headers['rollNumber']"/>
|
||||
</int-jpa:updating-outbound-gateway>]]></programlisting>
|
||||
reply-channel="jpaqlResponseChannel"
|
||||
jpa-query="update Student s set s.lastName = :lastName where s.rollNumber = :rollNumber" ]]><co id="jpaQlGatewayQuery"/><![CDATA[
|
||||
entity-manager="em">
|
||||
<int-jpa:parameter name="lastName" expression="payload"/>
|
||||
<int-jpa:parameter name="rollNumber" expression="headers['rollNumber']"/>
|
||||
</int-jpa:updating-outbound-gateway>]]></programlisting>
|
||||
<calloutlist>
|
||||
<callout arearefs="jpaQlGatewayQuery">
|
||||
<para>
|
||||
@@ -1250,12 +1248,12 @@ public class Student {
|
||||
</para>
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-jpa:retrieving-outbound-gateway request-channel="retrievingGatewayReqChannel"
|
||||
reply-channel="retrievingGatewayReplyChannel"
|
||||
jpa-query="select s from Student s where s.firstName = :firstName and s.lastName = :lastName"
|
||||
entity-manager="em">
|
||||
<int-jpa:parameter name="firstName" expression="payload"/>
|
||||
<int-jpa:parameter name="lastName" expression="headers['lastName']"/>
|
||||
</int-jpa:outbound-gateway>]]></programlisting>
|
||||
reply-channel="retrievingGatewayReplyChannel"
|
||||
jpa-query="select s from Student s where s.firstName = :firstName and s.lastName = :lastName"
|
||||
entity-manager="em">
|
||||
<int-jpa:parameter name="firstName" expression="payload"/>
|
||||
<int-jpa:parameter name="lastName" expression="headers['lastName']"/>
|
||||
</int-jpa:outbound-gateway>]]></programlisting>
|
||||
|
||||
<para><emphasis>Update using a Named Query</emphasis></para>
|
||||
<para>
|
||||
@@ -1264,12 +1262,12 @@ public class Student {
|
||||
attribute is used instead, as seen in the xml snippet below.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<int-jpa:updating-outbound-gateway request-channel="namedQueryRequestChannel"
|
||||
reply-channel="namedQueryResponseChannel"
|
||||
named-query="updateStudentByRollNumber"
|
||||
entity-manager="em">
|
||||
<int-jpa:parameter name="lastName" expression="payload"/>
|
||||
<int-jpa:parameter name="rollNumber" expression="headers['rollNumber']"/>
|
||||
</int-jpa:outbound-gateway>]]></programlisting>
|
||||
reply-channel="namedQueryResponseChannel"
|
||||
named-query="updateStudentByRollNumber"
|
||||
entity-manager="em">
|
||||
<int-jpa:parameter name="lastName" expression="payload"/>
|
||||
<int-jpa:parameter name="rollNumber" expression="headers['rollNumber']"/>
|
||||
</int-jpa:outbound-gateway>]]></programlisting>
|
||||
<note>
|
||||
<para>
|
||||
You can find a complete Sample application for using Spring
|
||||
|
||||
@@ -51,8 +51,7 @@
|
||||
Integration's own <interfacename>MailReceiver</interfacename> interface, and there are two implementations:
|
||||
<classname>Pop3MailReceiver</classname> and <classname>ImapMailReceiver</classname>. The easiest way to
|
||||
instantiate either of these is by passing the 'uri' for a Mail store to the receiver's constructor. For example:
|
||||
<programlisting language="java"><![CDATA[ MailReceiver receiver = new Pop3MailReceiver("pop3://usr:pwd@localhost/INBOX");
|
||||
]]></programlisting>
|
||||
<programlisting language="java"><![CDATA[MailReceiver receiver = new Pop3MailReceiver("pop3://usr:pwd@localhost/INBOX");]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Another option for receiving mail is the IMAP "idle" command (if supported by the mail server you are using).
|
||||
@@ -69,20 +68,20 @@
|
||||
Spring Integration provides a namespace for mail-related configuration. To use it, configure the following schema
|
||||
locations.<programlisting language="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int-mail="http://www.springframework.org/schema/integration/mail"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration/mail
|
||||
http://www.springframework.org/schema/integration/mail/spring-integration-mail.xsd">]]></programlisting>
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int-mail="http://www.springframework.org/schema/integration/mail"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration/mail
|
||||
http://www.springframework.org/schema/integration/mail/spring-integration-mail.xsd">]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
To configure an outbound Channel Adapter, provide the channel to receive from, and the MailSender:
|
||||
<programlisting language="xml"><![CDATA[<int-mail:outbound-channel-adapter channel="outboundMail"
|
||||
mail-sender="mailSender"/>]]></programlisting>
|
||||
mail-sender="mailSender"/>]]></programlisting>
|
||||
Alternatively, provide the host, username, and password:
|
||||
<programlisting language="xml"><![CDATA[<int-mail:outbound-channel-adapter channel="outboundMail"
|
||||
host="somehost" username="someuser" password="somepassword"/>]]></programlisting>
|
||||
host="somehost" username="someuser" password="somepassword"/>]]></programlisting>
|
||||
<note>
|
||||
Keep in mind, as with any outbound Channel Adapter, if the referenced channel is a PollableChannel, a
|
||||
<poller> sub-element should be provided with either an interval-trigger or cron-trigger.
|
||||
@@ -124,14 +123,14 @@
|
||||
<prop key="mail.debug">false</prop>
|
||||
</util:properties>]]></programlisting>
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
By default, the <classname>ImapMailReceiver</classname> will search for Messages based on the default <classname>SearchTerm</classname>
|
||||
which is <emphasis>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)</emphasis>.
|
||||
Since version 2.2, the <classname>SearchTerm</classname> used by the <classname>ImapMailReceiver</classname> is fully configurable
|
||||
By default, the <classname>ImapMailReceiver</classname> will search for Messages based on the default <classname>SearchTerm</classname>
|
||||
which is <emphasis>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)</emphasis>.
|
||||
Since version 2.2, the <classname>SearchTerm</classname> used by the <classname>ImapMailReceiver</classname> is fully configurable
|
||||
via the <code>SearchTermStrategy</code> which you can inject via the <code>search-term-strategy</code> attribute.
|
||||
<classname>SearchTermStrategy</classname> is a simple strategy interface with a single method that allows you to create an
|
||||
<classname>SearchTermStrategy</classname> is a simple strategy interface with a single method that allows you to create an
|
||||
instance of the <classname>SearchTerm</classname> that will be used by the <classname>ImapMailReceiver</classname>.
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[public interface SearchTermStrategy {
|
||||
@@ -144,46 +143,47 @@ For example:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<mail:imap-idle-channel-adapter id="customAdapter"
|
||||
store-uri="imap:foo"
|
||||
. . .
|
||||
…
|
||||
search-term-strategy="searchTermStrategy"/>
|
||||
|
||||
<bean id="searchTermStrategy" class="org.springframework.integration.mail.config.ImapIdleChannelAdapterParserTests.TestSearchTermStrategy"/>]]></programlisting>
|
||||
|
||||
<bean id="searchTermStrategy"
|
||||
class="o.s.i.mail.config.ImapIdleChannelAdapterParserTests.TestSearchTermStrategy"/>]]></programlisting>
|
||||
<para>
|
||||
In the above example instead of relying on the default <classname>SearchTermStrategy</classname> the
|
||||
In the above example instead of relying on the default <classname>SearchTermStrategy</classname> the
|
||||
<classname>TestSearchTermStrategy</classname> will be used instead
|
||||
</para>
|
||||
|
||||
|
||||
<para><emphasis>IMAP IDLE and lost connection</emphasis></para>
|
||||
|
||||
|
||||
<para>
|
||||
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
|
||||
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
|
||||
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.
|
||||
</para>
|
||||
<para>
|
||||
<emphasis>
|
||||
The following behavior was observed with GMAIL but should provide you with some tips on how to solve re-connect
|
||||
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.
|
||||
</emphasis>
|
||||
</para>
|
||||
<para>
|
||||
With Java Mail 1.4.1 if <code>mail.imaps.timeout</code> property is set for a relatively short period of time (e.g., ~ 5 min)
|
||||
then <code>IMAPFolder.idle()</code> will throw <classname>FolderClosedException</classname> after this timeout.
|
||||
However if this property is not set (should be indefinite) the behavior that was observed is that <code>IMAPFolder.idle()</code>
|
||||
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
|
||||
<code>IMAPFolder.idle()</code> will not throw <classname>FolderClosedException</classname> 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 <code>mail.imaps.timeout</code> property explicitly
|
||||
to some value, but it also means that such value shoudl be relatively short (under 10 min) and the connection should be
|
||||
With Java Mail 1.4.1 if <code>mail.imaps.timeout</code> property is set for a relatively short period of time (e.g., ~ 5 min)
|
||||
then <code>IMAPFolder.idle()</code> will throw <classname>FolderClosedException</classname> after this timeout.
|
||||
However if this property is not set (should be indefinite) the behavior that was observed is that <code>IMAPFolder.idle()</code>
|
||||
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
|
||||
<code>IMAPFolder.idle()</code> will not throw <classname>FolderClosedException</classname> 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 <code>mail.imaps.timeout</code> property explicitly
|
||||
to some value, but it also means that such value shoudl be relatively short (under 10 min) and the connection should be
|
||||
re-estabished 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 <code>IMAPFolder.idle()</code> method to return via <classname>StoreClosedException</classname> or
|
||||
With Java Mail 1.4.3 there was significant improvements to the API ensuring that there will always be a condition which
|
||||
will force <code>IMAPFolder.idle()</code> method to return via <classname>StoreClosedException</classname> or
|
||||
<classname>FolderClosedException</classname> or simply return, thus allowing us to proceed with auto-reconnect.
|
||||
Currently auto-reconnect will run infinitely making attempts to reconnect every 10 sec.
|
||||
Currently auto-reconnect will run infinitely making attempts to reconnect every 10 sec.
|
||||
</para>
|
||||
|
||||
<important>
|
||||
@@ -230,57 +230,57 @@ In the above example instead of relying on the default <classname>SearchTermStra
|
||||
overwrite="false"/>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Finally, 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
|
||||
Finally, 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.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="mail-filtering">
|
||||
<title>Email Message Filtering</title>
|
||||
<para>
|
||||
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 <emphasis>Subject</emphasis> line). This
|
||||
could be easily accomplished by connecting Inbound Mail adapter with an expression-based <emphasis>Filter</emphasis>. 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 <code>should-mark-messages-as-read</code> 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
|
||||
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 <emphasis>Subject</emphasis> line). This
|
||||
could be easily accomplished by connecting Inbound Mail adapter with an expression-based <emphasis>Filter</emphasis>. 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 <code>should-mark-messages-as-read</code> 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.
|
||||
</para>
|
||||
<para>
|
||||
In Spring Integration 2.0.4 we've introduced <code>mail-filter-expression</code> attribute on <code>inbound-channel-adapter</code> and
|
||||
<code>imap-idle-channel-adapter</code>. This attribute allows you to provide an expression which is a combination of SpEL and Regular Expression.
|
||||
In Spring Integration 2.0.4 we've introduced <code>mail-filter-expression</code> attribute on <code>inbound-channel-adapter</code> and
|
||||
<code>imap-idle-channel-adapter</code>. 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 <code>mail-filter-expression</code> attribute
|
||||
like this this: <code>mail-filter-expression="subject matches '(?i).*Spring Integration.*"</code>
|
||||
</para>
|
||||
<para>
|
||||
Since <classname>javax.mail.internet.MimeMessage</classname> 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
|
||||
Since <classname>javax.mail.internet.MimeMessage</classname> 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.
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
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 <code>mail-filter-expression</code> only messages that contain 'Spring Integration' in the subject line will be
|
||||
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 <code>mail-filter-expression</code> only messages that contain 'Spring Integration' in the subject line will be
|
||||
produced by this adapter.
|
||||
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-mail:imap-idle-channel-adapter id="customAdapter"
|
||||
store-uri="imaps://some_google_address:${password}@imap.gmail.com/INBOX"
|
||||
channel="receiveChannel"
|
||||
channel="receiveChannel"
|
||||
should-mark-messages-as-read="true"
|
||||
java-mail-properties="javaMailProperties"
|
||||
mail-filter-expression="subject matches '(?i).*Spring Integration.*'"/>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
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
|
||||
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.
|
||||
</para>
|
||||
</section>
|
||||
@@ -301,47 +301,47 @@ In the above example instead of relying on the default <classname>SearchTermStra
|
||||
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:
|
||||
</para>
|
||||
<programlisting><![CDATA[<int-mail:imap-idle-channel-adapter id="customAdapter"
|
||||
store-uri="imaps://foo.com:password@imap.foo.com/INBOX"
|
||||
channel="receiveChannel"
|
||||
auto-startup="true"
|
||||
should-delete-messages="false"
|
||||
java-mail-properties="javaMailProperties">
|
||||
<int:transactional synchronization-factory="syncFactory"/>
|
||||
<programlisting language="xml"><![CDATA[<int-mail:imap-idle-channel-adapter id="customAdapter"
|
||||
store-uri="imaps://foo.com:password@imap.foo.com/INBOX"
|
||||
channel="receiveChannel"
|
||||
auto-startup="true"
|
||||
should-delete-messages="false"
|
||||
java-mail-properties="javaMailProperties">
|
||||
<int:transactional synchronization-factory="syncFactory"/>
|
||||
</int-mail:imap-idle-channel-adapter>
|
||||
|
||||
<int:transaction-synchronization-factory id="syncFactory">
|
||||
<int:after-commit expression="@syncProcessor.process(payload)"/>
|
||||
<int:after-commit expression="@syncProcessor.process(payload)"/>
|
||||
</int:transaction-synchronization-factory>
|
||||
|
||||
<bean id="syncProcessor" class="foo.bar.Mover"/>
|
||||
<bean id="syncProcessor" class="foo.bar.Mover"/>]]></programlisting>
|
||||
|
||||
public class Mover {
|
||||
<programlisting language="java"><![CDATA[public class Mover {
|
||||
|
||||
public void process(MimeMessage message) throws Exception{
|
||||
Folder folder = message.getFolder();
|
||||
folder.open(Folder.READ_WRITE);
|
||||
String messageId = message.getMessageID();
|
||||
Message[] messages = folder.getMessages();
|
||||
FetchProfile contentsProfile = new FetchProfile();
|
||||
contentsProfile.add(FetchProfile.Item.ENVELOPE);
|
||||
contentsProfile.add(FetchProfile.Item.CONTENT_INFO);
|
||||
contentsProfile.add(FetchProfile.Item.FLAGS);
|
||||
folder.fetch(messages, contentsProfile);
|
||||
// find this message and mark for deletion
|
||||
for (int i = 0; i < messages.length; i++) {
|
||||
if (((MimeMessage) messages[i]).getMessageID().equals(messageId)) {
|
||||
messages[i].setFlag(Flags.Flag.DELETED, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
public void process(MimeMessage message) throws Exception{
|
||||
Folder folder = message.getFolder();
|
||||
folder.open(Folder.READ_WRITE);
|
||||
String messageId = message.getMessageID();
|
||||
Message[] messages = folder.getMessages();
|
||||
FetchProfile contentsProfile = new FetchProfile();
|
||||
contentsProfile.add(FetchProfile.Item.ENVELOPE);
|
||||
contentsProfile.add(FetchProfile.Item.CONTENT_INFO);
|
||||
contentsProfile.add(FetchProfile.Item.FLAGS);
|
||||
folder.fetch(messages, contentsProfile);
|
||||
// find this message and mark for deletion
|
||||
for (int i = 0; i < messages.length; i++) {
|
||||
if (((MimeMessage) messages[i]).getMessageID().equals(messageId)) {
|
||||
messages[i].setFlag(Flags.Flag.DELETED, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Folder fooFolder = store.getFolder("FOO"));
|
||||
fooFolder.appendMessages(new MimeMessage[]{message});
|
||||
folder.expunge();
|
||||
folder.close(true);
|
||||
fooFolder.close(false);
|
||||
}
|
||||
Folder fooFolder = store.getFolder("FOO"));
|
||||
fooFolder.appendMessages(new MimeMessage[]{message});
|
||||
folder.expunge();
|
||||
folder.close(true);
|
||||
fooFolder.close(false);
|
||||
}
|
||||
}]]></programlisting>
|
||||
<important>
|
||||
For the message to be still available for manipulation after the transaction, <emphasis>should-delete-messages</emphasis>
|
||||
|
||||
@@ -374,7 +374,7 @@ static class BankingOperationsImpl implements BankingOperations {
|
||||
<int:poller trigger="customTrigger"/>
|
||||
</int:inbound-channel-adapter>
|
||||
|
||||
<beans:bean id="customTrigger" class="org.springframework.scheduling.support.PeriodicTrigger">
|
||||
<beans:bean id="customTrigger" class="o.s.scheduling.support.PeriodicTrigger">
|
||||
<beans:constructor-arg value="9999"/>
|
||||
</beans:bean>]]></programlisting>
|
||||
|
||||
|
||||
@@ -4,50 +4,50 @@
|
||||
<title>Message Store</title>
|
||||
<para>
|
||||
Enterprise Integration Patterns (EIP) identifies several patterns that have the capability to buffer messages. For example,
|
||||
an <emphasis>Aggregator</emphasis> buffers messages until they can be released and a <emphasis>QueueChannel</emphasis> 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
|
||||
an <emphasis>Aggregator</emphasis> buffers messages until they can be released and a <emphasis>QueueChannel</emphasis> 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.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To mitigate the risk of losing Messages, EIP defines the <ulink url="http://eaipatterns.com/MessageStore.html">Message Store</ulink> pattern which allows
|
||||
To mitigate the risk of losing Messages, EIP defines the <ulink url="http://eaipatterns.com/MessageStore.html">Message Store</ulink> pattern which allows
|
||||
EIP components to store <emphasis>Messages</emphasis> typically in some type of persistent store (e.g. RDBMS).
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
Spring Integration provides support for the <emphasis>Message Store</emphasis> pattern by
|
||||
Spring Integration provides support for the <emphasis>Message Store</emphasis> pattern by
|
||||
a) defining a <classname>org.springframework.integration.store.MessageStore</classname> strategy interface,
|
||||
b) providing several implementations of this interface, and
|
||||
c) exposing a <code>message-store</code> attribute on all components that have the capability to buffer messages
|
||||
so that you can inject any instance that implements the <classname>MessageStore</classname> interface.
|
||||
</para>
|
||||
|
||||
<para>Details on how to configure a specific <emphasis>Message Store</emphasis> implementation and/or how to inject
|
||||
a <classname>MessageStore</classname> implementation into a specific buffering component are described
|
||||
throughout the manual (see the specific component, such as <emphasis>QueueChannel</emphasis>, <emphasis>Aggregator</emphasis>,
|
||||
|
||||
<para>Details on how to configure a specific <emphasis>Message Store</emphasis> implementation and/or how to inject
|
||||
a <classname>MessageStore</classname> implementation into a specific buffering component are described
|
||||
throughout the manual (see the specific component, such as <emphasis>QueueChannel</emphasis>, <emphasis>Aggregator</emphasis>,
|
||||
<emphasis>Resequencer</emphasis> etc.), but here are a couple of samples to give you an idea:
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
QueueChannel
|
||||
<programlisting language="xml"><![CDATA[<int:channel id="myQueueChannel">
|
||||
<int:queue message-store="refToMessageStore"/>
|
||||
<int:channel>]]></programlisting>
|
||||
<int:queue message-store="refToMessageStore"/>
|
||||
<int:channel>]]></programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Aggregator
|
||||
<programlisting language="xml"><![CDATA[<int:aggregator . . . message-store="refToMessageStore"/>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int:aggregator … message-store="refToMessageStore"/>]]></programlisting>
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
By default <emphasis>Messages</emphasis> are stored in-memory using <classname>org.springframework.integration.store.SimpleMessageStore</classname>,
|
||||
an implementation of <classname>MessageStore</classname>. 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:
|
||||
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><xref linkend="jdbc-message-store"/> - uses RDBMS to store Messages</listitem>
|
||||
<listitem><xref linkend="redis-message-store"/> - uses Redis key/value datastore to store Messages</listitem>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<title>Connecting to MongoDb</title>
|
||||
|
||||
<para>To begin interacting with MongoDB you first need to connect to it. Spring Integration builds on the support provided by another
|
||||
Spring project, <ulink url="http://www.springsource.org/spring-data/mongodb">Spring Data MongoDB</ulink>, which provides a factory
|
||||
Spring project, <ulink url="http://www.springsource.org/spring-data/mongodb">Spring Data MongoDB</ulink>, which provides a factory
|
||||
class called <classname>MongoDbFactory</classname> that simplifies integration with the MongoDB Client API.</para>
|
||||
|
||||
<para><emphasis>MongoDbFactory</emphasis> </para>
|
||||
@@ -29,19 +29,19 @@
|
||||
<para>
|
||||
To connect to MongoDB you can use an implementation of the <classname>MongoDbFactory</classname> interface:
|
||||
|
||||
<programlisting lang="java"><![CDATA[public interface MongoDbFactory {
|
||||
<programlisting language="java"><![CDATA[public interface MongoDbFactory {
|
||||
|
||||
/**
|
||||
* Creates a default {@link DB} instance.
|
||||
*
|
||||
*
|
||||
* @return the DB instance
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
DB getDb() throws DataAccessException;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a {@link DB} instance to access the database with the given name.
|
||||
*
|
||||
*
|
||||
* @param dbName must not be {@literal null} or empty.
|
||||
*
|
||||
* @return the DB instance
|
||||
@@ -54,11 +54,11 @@
|
||||
<para>The example below shows <classname>SimpleMongoDbFactory</classname>, the out-of-the-box implementation:</para>
|
||||
|
||||
<para>In Java:
|
||||
<programlisting lang="java"><![CDATA[MongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(new Mongo(), "test");]]></programlisting>
|
||||
<programlisting language="java"><![CDATA[MongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(new Mongo(), "test");]]></programlisting>
|
||||
</para>
|
||||
|
||||
<para>Or in Spring's XML configuration:
|
||||
<programlisting lang="xml"><![CDATA[<bean id="mongoDbFactory" class="org.springframework.data.mongodb.core.SimpleMongoDbFactory">
|
||||
<programlisting language="xml"><![CDATA[<bean id="mongoDbFactory" class="o.s.data.mongodb.core.SimpleMongoDbFactory">
|
||||
<constructor-arg>
|
||||
<bean class="com.mongodb.Mongo"/>
|
||||
</constructor-arg>
|
||||
@@ -69,7 +69,7 @@
|
||||
<para>
|
||||
As you can see <classname>SimpleMongoDbFactory</classname> takes two arguments: 1) a <classname>Mongo</classname> instance and
|
||||
2) a String specifying the name of the database. If you need to configure properties such as <code>host</code>, <code>port</code>, etc,
|
||||
you can pass those using one of the constructors provided by the underlying <classname>Mongo</classname> class.
|
||||
you can pass those using one of the constructors provided by the underlying <classname>Mongo</classname> class.
|
||||
For more information on how to configure MongoDB, please refer to the
|
||||
<ulink url="http://static.springsource.org/spring-data/data-document/docs/current/reference/html/">Spring-Data-Document</ulink> reference.
|
||||
</para>
|
||||
@@ -82,10 +82,10 @@
|
||||
As described in EIP, a <ulink url="http://www.eaipatterns.com/MessageStore.html">Message Store</ulink> allows you to persist Messages.
|
||||
This can be very useful when dealing with components that have a capability to buffer messages
|
||||
(<emphasis>QueueChannel, Aggregator, Resequencer</emphasis>, etc.) if reliability is a concern.
|
||||
In Spring Integration, the MessageStore strategy also provides the foundation for the
|
||||
In Spring Integration, the MessageStore strategy also provides the foundation for the
|
||||
<ulink url="http://www.eaipatterns.com/StoreInLibrary.html">ClaimCheck</ulink> pattern, which is described in EIP as well.
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
Spring Integration's MongoDB module provides the <classname>MongoDbMessageStore</classname> which is an implementation of both
|
||||
the <classname>MessageStore</classname> strategy (mainly used by the <emphasis>QueueChannel</emphasis> and <emphasis>ClaimCheck</emphasis>
|
||||
@@ -94,7 +94,7 @@
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<programlisting lang="xml"><![CDATA[<bean id="mongoDbMessageStore" class="org.springframework.integration.mongodb.store.MongoDbMessageStore">
|
||||
<programlisting language="xml"><![CDATA[<bean id="mongoDbMessageStore" class="o.s.i.mongodb.store.MongoDbMessageStore">
|
||||
<constructor-arg ref="mongoDbFactory"/>
|
||||
</bean>
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
<classname>MongoDbFactory</classname> as a constructor argument.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="mongodb-inbound-channel-adapter">
|
||||
<title>MongoDB Inbound Channel Adapter</title>
|
||||
|
||||
@@ -120,8 +120,8 @@
|
||||
The <emphasis>MongoDb Inbound Channel Adapter</emphasis> is a polling consumer that reads data
|
||||
from MongoDb and sends it as a Message payload.
|
||||
</para>
|
||||
|
||||
<programlisting lang="xml"><![CDATA[<int-mongodb:inbound-channel-adapter id="mongoInboundAdapter"
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-mongodb:inbound-channel-adapter id="mongoInboundAdapter"
|
||||
channel="replyChannel"
|
||||
query="{'name' : 'Bob'}"
|
||||
entity-class="java.lang.Object"
|
||||
@@ -131,10 +131,10 @@
|
||||
|
||||
<para>
|
||||
As you can see from the configuration above, you configure a <emphasis>MongoDb Inbound Channel Adapter</emphasis> using
|
||||
the <code>inbound-channel-adapter</code> element, providing values for various attributes such as:
|
||||
the <code>inbound-channel-adapter</code> element, providing values for various attributes such as:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><code>query</code> or <code>query-expression</code> - a
|
||||
<para><code>query</code> or <code>query-expression</code> - a
|
||||
JSON query (see <ulink url="http://www.mongodb.org/display/DOCS/Querying">MongoDb Querying</ulink>) </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
@@ -147,11 +147,11 @@
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><code>mongodb-factory</code> -
|
||||
reference to an instance of <classname>org.springframework.data.mongodb.MongoDbFactory</classname> </para>
|
||||
reference to an instance of <classname>o.s.data.mongodb.MongoDbFactory</classname> </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><code>mongo-template</code> -
|
||||
reference to an instance of <classname>org.springframework.data.mongodb.core.MongoTemplate</classname>
|
||||
reference to an instance of <classname>o.s.data.mongodb.core.MongoTemplate</classname>
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
@@ -171,7 +171,7 @@
|
||||
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.
|
||||
</para>
|
||||
<programlisting lang="xml"><![CDATA[<int-mongodb:inbound-channel-adapter id="mongoInboundAdapter"
|
||||
<programlisting language="xml"><![CDATA[<int-mongodb:inbound-channel-adapter id="mongoInboundAdapter"
|
||||
channel="replyChannel"
|
||||
query="{'name' : 'Bob'}"
|
||||
entity-class="java.lang.Object"
|
||||
@@ -184,12 +184,12 @@
|
||||
<int:transaction-synchronization-factory id="syncFactory">
|
||||
<int:after-commit expression="@documentCleaner.remove(#mongoTemplate, payload, headers.mongo_collectionName)" channe="someChannel"/>
|
||||
</int:transaction-synchronization-factory>
|
||||
|
||||
|
||||
<bean id="documentCleaner" class="foo.bar.DocumentCleaner"/>
|
||||
|
||||
<bean id="transactionManager" class="org.springframework.integration.transaction.PseudoTransactionManager"/>]]></programlisting>
|
||||
<bean id="transactionManager" class="o.s.i.transaction.PseudoTransactionManager"/>]]></programlisting>
|
||||
|
||||
<programlisting lang="java"><![CDATA[public class DocumentCleaner {
|
||||
<programlisting language="java"><![CDATA[public class DocumentCleaner {
|
||||
public void remove(MongoOperations mongoOperations, Object target, String collectionName) {
|
||||
if (target instanceof List<?>){
|
||||
List<?> documents = (List<?>) target;
|
||||
@@ -198,12 +198,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}]]></programlisting>
|
||||
}]]></programlisting>
|
||||
<para>
|
||||
As you can see from the above, all you need to do is declare your poller to be transactional with a <code>transactional</code> 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
|
||||
<classname>org.springframework.integration.transaction.PseudoTransactionManager</classname> which is an implementation
|
||||
<classname>org.springframework.integration.transaction.PseudoTransactionManager</classname> which is an implementation
|
||||
of Spring's <classname>PlatformTransactionManager</classname> and enables the use of the transaction synchronization
|
||||
features of the mongo adapter when there is no actual transaction.
|
||||
</para>
|
||||
@@ -212,8 +212,8 @@
|
||||
or after failure (rollback).
|
||||
</important>
|
||||
<para>
|
||||
Once your poller is transactional all you need to do is set an instance of the
|
||||
<classname>org.springframework.integration.transaction.TransactionSynchronizationFactory</classname> on the <code>transactional</code> element.
|
||||
Once your poller is transactional all you need to do is set an instance of the
|
||||
<classname>org.springframework.integration.transaction.TransactionSynchronizationFactory</classname> on the <code>transactional</code> element.
|
||||
<classname>TransactionSynchronizationFactory</classname> will create an instance of the <classname>TransactioinSynchronization</classname>.
|
||||
For your convenience, we've exposed a default SpEL-based <classname>TransactionSynchronizationFactory</classname> which allows
|
||||
you to configure SpEL expressions, with their execution being coordinated (synchronized) with a transaction.
|
||||
@@ -222,25 +222,25 @@
|
||||
attributes. If only the <code>channel</code> attribute is present the received Message will be sent there as part of the particular
|
||||
synchronization scenario. If only the <code>expression</code> 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 <code>channel</code> attribute. If the result of an
|
||||
logs (DEBUG). If you want the evaluation result to go to a specific channel add a <code>channel</code> attribute. If the result of an
|
||||
expression is null or void, no Message will be generated.
|
||||
</para>
|
||||
<para>
|
||||
For more information about transaction synchronization, see <xref linkend="transaction-synchronization"/>.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="mongodb-outbound-channel-adapter">
|
||||
<title>MongoDB Outbound Channel Adapter</title>
|
||||
|
||||
<para>
|
||||
The <emphasis>MongoDb Outbound Channel Adapter</emphasis> allows you to write the Message payload to a MongoDb document store
|
||||
</para>
|
||||
|
||||
<programlisting lang="xml"><![CDATA[<int-mongodb:outbound-channel-adapter id="fullConfigWithCollectionExpression"
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-mongodb:outbound-channel-adapter id="fullConfigWithCollectionExpression"
|
||||
collection-name="myCollection"
|
||||
mongo-converter="mongoConverter"
|
||||
mongodb-factory="mongoDbFactory" />]]></programlisting>
|
||||
mongodb-factory="mongoDbFactory" />]]></programlisting>
|
||||
|
||||
<para>
|
||||
As you can see from the configuration above, you configure a <emphasis>MongoDb Outbound Channel Adapter</emphasis> using
|
||||
@@ -252,16 +252,16 @@
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><code>mongo-converter</code> -
|
||||
reference to an instance of <classname>org.springframework.data.mongodb.core.convert.MongoConverter</classname> to assist with
|
||||
reference to an instance of <classname>o.s.data.mongodb.core.convert.MongoConverter</classname> to assist with
|
||||
converting a raw java object to a JSON document representation </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><code>mongodb-factory</code> -
|
||||
reference to an instance of <classname>org.springframework.data.mongodb.MongoDbFactory</classname> </para>
|
||||
reference to an instance of <classname>o.s.data.mongodb.MongoDbFactory</classname> </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><code>mongo-template</code> -
|
||||
reference to an instance of <classname>org.springframework.data.mongodb.core.MongoTemplate</classname>
|
||||
reference to an instance of <classname>o.s.data.mongodb.core.MongoTemplate</classname>
|
||||
(NOTE: you can not have both mongo-template and mongodb-factory set)</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
@@ -20,19 +20,19 @@
|
||||
|
||||
<section id="redis-connection">
|
||||
<title>Connecting to Redis</title>
|
||||
|
||||
|
||||
<para>To begin interacting with Redis you first need to connect to it. Spring Integration uses support provided by another Spring project,
|
||||
<ulink url="https://github.com/SpringSource/spring-data-redis">Spring Data Redis</ulink>, which provides typical Spring constructs:
|
||||
<classname>ConnectionFactory</classname> and <classname>Template</classname>. Those abstractions
|
||||
simplify integration with several Redis-client Java APIs. Currently Spring-Data-Redis supports
|
||||
simplify integration with several Redis-client Java APIs. Currently Spring-Data-Redis supports
|
||||
<ulink url="https://github.com/xetorthio/jedis">jedis</ulink>, <ulink url="http://code.google.com/p/jredis/">jredis</ulink> and <ulink url="https://github.com/e-mzungu/rjc">rjc</ulink></para>
|
||||
|
||||
|
||||
<para><emphasis>RedisConnectionFactory</emphasis> </para>
|
||||
|
||||
<para>
|
||||
To connect to Redis you would use one of the implementations of the <classname>RedisConnectionFactory</classname> interface:
|
||||
|
||||
<programlisting lang="java"><![CDATA[public interface RedisConnectionFactory extends PersistenceExceptionTranslator {
|
||||
<programlisting language="java"><![CDATA[public interface RedisConnectionFactory extends PersistenceExceptionTranslator {
|
||||
|
||||
/**
|
||||
* Provides a suitable connection for interacting with Redis.
|
||||
@@ -42,27 +42,28 @@
|
||||
RedisConnection getConnection();
|
||||
}]]></programlisting>
|
||||
</para>
|
||||
|
||||
|
||||
<para>The example below shows how to create a <classname>JedisConnectionFactory</classname>.</para>
|
||||
|
||||
|
||||
<para>In Java:
|
||||
<programlisting lang="java"><![CDATA[JedisConnectionFactory jcf = new JedisConnectionFactory();
|
||||
<programlisting language="java"><![CDATA[JedisConnectionFactory jcf = new JedisConnectionFactory();
|
||||
jcf.afterPropertiesSet();]]></programlisting>
|
||||
</para>
|
||||
|
||||
|
||||
<para>Or in Spring's XML configuration:
|
||||
<programlisting lang="xml"><![CDATA[<bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
|
||||
<property name="port" value="7379" />
|
||||
<programlisting language="xml"><![CDATA[<bean id="redisConnectionFactory"
|
||||
class="o.s.data.redis.connection.jedis.JedisConnectionFactory">
|
||||
<property name="port" value="7379" />
|
||||
</bean>]]></programlisting>
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
The implementations of RedisConnectionFactory provide a set of properties such as port and host that can be set if needed.
|
||||
Once an instance of RedisConnectionFactory is created, you can create an instance of RedisTemplate and inject it with the RedisConnectionFactory.
|
||||
</para>
|
||||
|
||||
<para><emphasis>RedisTemplate</emphasis> </para>
|
||||
|
||||
|
||||
<para>
|
||||
As with other template classes in Spring (e.g., <classname>JdbcTemplate</classname>, <classname>JmsTemplate</classname>)
|
||||
<classname>RedisTemplate</classname> is a helper class that simplifies Redis data access code.
|
||||
@@ -73,12 +74,12 @@ jcf.afterPropertiesSet();]]></programlisting>
|
||||
<para>The code below shows how to create an instance of <classname>RedisTemplate</classname>:</para>
|
||||
|
||||
<para>In Java:
|
||||
<programlisting lang="java"><![CDATA[RedisTemplate rt = new RedisTemplate<String, Object>();
|
||||
<programlisting language="java"><![CDATA[RedisTemplate rt = new RedisTemplate<String, Object>();
|
||||
rt.setConnectionFactory(redisConnectionFactory);]]></programlisting>
|
||||
</para>
|
||||
|
||||
|
||||
<para>Or in Spring's XML configuration::
|
||||
<programlisting lang="xml"><![CDATA[<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
|
||||
<programlisting language="xml"><![CDATA[<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
|
||||
<property name="connectionFactory" ref="redisConnectionFactory"/>
|
||||
</bean>]]></programlisting>
|
||||
</para>
|
||||
@@ -88,7 +89,7 @@ rt.setConnectionFactory(redisConnectionFactory);]]></programlisting>
|
||||
<title>Messaging with Redis</title>
|
||||
|
||||
<para>
|
||||
As mentioned in the introduction Redis provides support for Publish-Subscribe messaging via its PUBLISH, SUBSCRIBE and UNSUBSCRIBE
|
||||
As mentioned in the introduction Redis provides support for Publish-Subscribe messaging via its PUBLISH, SUBSCRIBE and UNSUBSCRIBE
|
||||
commands. As with JMS and AMQP, Spring Integration provides Message Channels and adapters for sending and receiving messages via Redis.
|
||||
</para>
|
||||
|
||||
@@ -96,14 +97,14 @@ rt.setConnectionFactory(redisConnectionFactory);]]></programlisting>
|
||||
<title>Redis Publish/Subscribe channel</title>
|
||||
|
||||
<para>
|
||||
Similar to the JMS there are cases where both the producer and consumer are intended to be part of the same application, running
|
||||
within the same process. This could be accomplished by using a pair of inbound and outbound Channel Adapters,
|
||||
Similar to the JMS there are cases where both the producer and consumer are intended to be part of the same application, running
|
||||
within the same process. This could be accomplished by using a pair of inbound and outbound Channel Adapters,
|
||||
however just like with Spring Integration's JMS support, there is a simpler approach to address this use case.
|
||||
<programlisting lang="xml"><![CDATA[<int-redis:publish-subscribe-channel id="redisChannel" topic-name="si.test.topic"/>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int-redis:publish-subscribe-channel id="redisChannel" topic-name="si.test.topic"/>]]></programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The publish-subscribe-channel (above) will behave much like a normal <code><publish-subscribe-channel/></code> element from the
|
||||
The publish-subscribe-channel (above) will behave much like a normal <code><publish-subscribe-channel/></code> element from the
|
||||
main Spring Integration namespace. It can be referenced by both <code>input-channel</code> and <code>output-channel</code> attributes of
|
||||
any endpoint. The difference is that this channel is backed by a Redis topic name - a String value specified by the <code>topic-name</code>
|
||||
attribute. However unlike JMS this topic doesn't have to be created in advance or even auto-created by Redis. In Redis topics are simple
|
||||
@@ -120,16 +121,17 @@ rt.setConnectionFactory(redisConnectionFactory);]]></programlisting>
|
||||
<title>Redis Inbound Channel Adapter</title>
|
||||
<para>
|
||||
The Redis-based Inbound Channel Adapter adapts incoming Redis messages into Spring Integration Messages in the same way as other
|
||||
inbound adapters. It receives platform-specific messages (Redis in this case) and converts them to Spring Integration Messages using
|
||||
inbound adapters. It receives platform-specific messages (Redis in this case) and converts them to Spring Integration Messages using
|
||||
a <classname>MessageConverter</classname> strategy.
|
||||
|
||||
<programlisting lang="xml"><![CDATA[<int-redis:inbound-channel-adapter id="redisAdapter"
|
||||
<programlisting language="xml"><![CDATA[<int-redis:inbound-channel-adapter id="redisAdapter"
|
||||
topics="foo, bar"
|
||||
channel="receiveChannel"
|
||||
error-channel="testErrorChannel"
|
||||
message-converter="testConverter" />
|
||||
|
||||
<bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
|
||||
<bean id="redisConnectionFactory"
|
||||
class="o.s.data.redis.connection.jedis.JedisConnectionFactory">
|
||||
<property name="port" value="7379" />
|
||||
</bean>
|
||||
|
||||
@@ -137,35 +139,36 @@ rt.setConnectionFactory(redisConnectionFactory);]]></programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Above is a simple but complete configuration of a Redis Inbound Channel Adapter. Note that the above configuration relies on the
|
||||
Above is a simple but complete configuration of a Redis Inbound Channel Adapter. Note that the above configuration relies on the
|
||||
familiar Spring paradigm of auto-discovering certain beans. In this case the <code>redisConnectionFactory</code> is implicitly
|
||||
injected into the adapter. You can of course specify it explicitly using the <code>connection-factory</code> attribute instead.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Also, note that the above configuration injects the adapter with a custom <code>MessageConverter</code>.
|
||||
The approach is similar to JMS where <code>MessageConverters</code> are used to convert between
|
||||
The approach is similar to JMS where <code>MessageConverters</code> are used to convert between
|
||||
Redis Messages and the Spring Integration Message payloads. The default is a <code>SimpleMessageConverter</code>.
|
||||
</para>
|
||||
|
||||
<para>Inbound adapters can subscribe to multiple topic names hence the comma-delimited set of values in the
|
||||
<code>topics</code> attribute.</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="redis-outbound-channel-adapter">
|
||||
<title>Redis Outbound Channel Adapter</title>
|
||||
<para>
|
||||
The Redis-based Outbound Channel Adapter adapts outgoing Spring Integration messages into Redis messages in the same way as
|
||||
The Redis-based Outbound Channel Adapter adapts outgoing Spring Integration messages into Redis messages in the same way as
|
||||
other outbound adapters. It receives Spring Integration messages and converts them to platform-specific messages (Redis in this case)
|
||||
using a <classname>MessageConverter</classname> strategy.
|
||||
|
||||
<programlisting lang="xml"><![CDATA[<int-redis:outbound-channel-adapter id="outboundAdapter"
|
||||
channel="sendChannel"
|
||||
topic="foo"
|
||||
message-converter="testConverter"/>
|
||||
<programlisting language="xml"><![CDATA[<int-redis:outbound-channel-adapter id="outboundAdapter"
|
||||
channel="sendChannel"
|
||||
topic="foo"
|
||||
message-converter="testConverter"/>
|
||||
|
||||
<bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
|
||||
<property name="port" value="7379"/>
|
||||
<bean id="redisConnectionFactory"
|
||||
class="o.s.data.redis.connection.jedis.JedisConnectionFactory">
|
||||
<property name="port" value="7379"/>
|
||||
</bean>
|
||||
|
||||
<bean id="testConverter" class="foo.bar.SampleMessageConverter" />]]></programlisting>
|
||||
@@ -173,19 +176,19 @@ rt.setConnectionFactory(redisConnectionFactory);]]></programlisting>
|
||||
<para>
|
||||
As you can see the configuration is similar to the Redis Inbound Channel Adapter. The adapter is implicitly injected with
|
||||
a <classname>RedisConnectionFactory</classname> which was defined with '<code>redisConnectionFactory</code>' as its bean name.
|
||||
This example also includes the optional, custom <classname>MessageConverter</classname> (the '<code>testConverter</code>' bean).
|
||||
This example also includes the optional, custom <classname>MessageConverter</classname> (the '<code>testConverter</code>' bean).
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="redis-message-store">
|
||||
<title>Redis Message Store</title>
|
||||
|
||||
|
||||
<para>
|
||||
As described in EIP, a <ulink url="http://www.eaipatterns.com/MessageStore.html">Message Store</ulink> allows you to persist Messages.
|
||||
This can be very useful when dealing with components that have a capability to buffer messages
|
||||
(<emphasis>QueueChannel, Aggregator, Resequencer</emphasis>, etc.) if reliability is a concern.
|
||||
In Spring Integration, the MessageStore strategy also provides the foundation for the
|
||||
In Spring Integration, the MessageStore strategy also provides the foundation for the
|
||||
<ulink url="http://www.eaipatterns.com/StoreInLibrary.html">ClaimCheck</ulink> pattern, which is described in EIP as well.
|
||||
</para>
|
||||
|
||||
@@ -197,7 +200,7 @@ rt.setConnectionFactory(redisConnectionFactory);]]></programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<programlisting lang="xml"><![CDATA[<bean id="redisMessageStore" class="org.springframework.integration.redis.store.RedisMessageStore">
|
||||
<programlisting language="xml"><![CDATA[<bean id="redisMessageStore" class="o.s.i.redis.store.RedisMessageStore">
|
||||
<constructor-arg ref="redisConnectionFactory"/>
|
||||
</bean>
|
||||
|
||||
@@ -220,7 +223,7 @@ rt.setConnectionFactory(redisConnectionFactory);]]></programlisting>
|
||||
the <code>valueSerializer</code> property of the <classname>RedisMessageStore</classname>.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="redis-store-inbound-channel-adapter">
|
||||
<title>RedisStore Inbound Channel Adapter</title>
|
||||
|
||||
@@ -229,35 +232,26 @@ rt.setConnectionFactory(redisConnectionFactory);]]></programlisting>
|
||||
from a Redis collection and sends it as a Message payload.
|
||||
</para>
|
||||
|
||||
<programlisting lang="xml"><![CDATA[<int-redis:store-inbound-channel-adapter id="listAdapter"
|
||||
connection-factory="redisConnectionFactory"
|
||||
key="myCollection"
|
||||
channel="redisChannel"
|
||||
collection-type="LIST" >
|
||||
<int:poller fixed-rate="2000" max-messages-per-poll="10"/>
|
||||
</int-redis:store-inbound-channel-adapter>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int-redis:store-inbound-channel-adapter id="listAdapter"
|
||||
connection-factory="redisConnectionFactory"
|
||||
key="myCollection"
|
||||
channel="redisChannel"
|
||||
collection-type="LIST" >
|
||||
<int:poller fixed-rate="2000" max-messages-per-poll="10"/>
|
||||
</int-redis:store-inbound-channel-adapter>]]></programlisting>
|
||||
|
||||
<para>
|
||||
As you can see from the configuration above you configure a <emphasis>Redis Store Inbound Channel Adapter</emphasis> using
|
||||
the <code>store-inbound-channel-adapter</code> element, providing values for various attributes such as:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><code>key</code> or <code>key-expression</code> - The name of the key for the collection being used. </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><code>collection-type</code> - enumeration of the Collection types supported by this adapter. Supported Collections are: LIST, SET, ZSET, PROPERTIES, MAP </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><code>connection-factory</code> -
|
||||
reference to an instance of <classname>org.springframework.data.redis.connection.RedisConnectionFactory</classname> </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><code>redis-template</code> -
|
||||
reference to an instance of <classname>org.springframework.data.redis.core.RedisTemplate</classname>
|
||||
</para>
|
||||
</listitem>
|
||||
the <code>store-inbound-channel-adapter</code> element, providing values for various attributes such as:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem><code>key</code> or <code>key-expression</code> - The name of the key for the collection being used.</listitem>
|
||||
<listitem><code>collection-type</code> - enumeration of the Collection types supported by this adapter. Supported Collections are: LIST, SET, ZSET, PROPERTIES, MAP</listitem>
|
||||
<listitem><code>connection-factory</code> - reference to an instance of <classname>o.s.data.redis.connection.RedisConnectionFactory</classname></listitem>
|
||||
<listitem><code>redis-template</code> - reference to an instance of <classname>o.s.data.redis.core.RedisTemplate</classname></listitem>
|
||||
</itemizedlist>
|
||||
and other attributes that are common across all other inbound adapters (e.g., 'channel').
|
||||
<para>
|
||||
and other attributes that are common across all other inbound adapters (e.g., 'channel').
|
||||
</para>
|
||||
<note>
|
||||
You cannot set both <code>redis-template</code> and <code>connection-factory</code>.
|
||||
@@ -270,14 +264,14 @@ rt.setConnectionFactory(redisConnectionFactory);]]></programlisting>
|
||||
For example, if the store is written to using a RedisStore Outbound Adapter that has its
|
||||
<code>extract-payload-elements</code> set to false, you must provide a
|
||||
<classname>RedisTemplate</classname> configured thus:
|
||||
<programlisting><![CDATA[<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
|
||||
<property name="connectionFactory" ref="redisConnectionFactory"/>
|
||||
<property name="keySerializer">
|
||||
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
|
||||
</property>
|
||||
<property name="hashKeySerializer">
|
||||
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
|
||||
</property>
|
||||
<programlisting language="xml"><![CDATA[<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
|
||||
<property name="connectionFactory" ref="redisConnectionFactory"/>
|
||||
<property name="keySerializer">
|
||||
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
|
||||
</property>
|
||||
<property name="hashKeySerializer">
|
||||
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
|
||||
</property>
|
||||
</bean>]]></programlisting>
|
||||
<para>
|
||||
This uses String serializers for keys and hash keys and the default JDK Serialization serializers for
|
||||
@@ -292,9 +286,9 @@ rt.setConnectionFactory(redisConnectionFactory);]]></programlisting>
|
||||
<para>
|
||||
Also, you may wish to perform some post-processing to the successfully processed data that was read from the Redis collection.
|
||||
For example; you may want to move or remove the value after its been processed.
|
||||
You can do this using the Transaction Synchronization feature that was added with Spring Integration 2.2.
|
||||
You can do this using the Transaction Synchronization feature that was added with Spring Integration 2.2.
|
||||
</para>
|
||||
<programlisting lang="xml"><![CDATA[<int-redis:store-inbound-channel-adapter id="zsetAdapterWithSingleScoreAndSynchronization"
|
||||
<programlisting language="xml"><![CDATA[<int-redis:store-inbound-channel-adapter id="zsetAdapterWithSingleScoreAndSynchronization"
|
||||
connection-factory="redisConnectionFactory"
|
||||
key-expression="'presidents'"
|
||||
channel="otherRedisChannel"
|
||||
@@ -309,14 +303,14 @@ rt.setConnectionFactory(redisConnectionFactory);]]></programlisting>
|
||||
<int:after-commit expression="payload.removeByScore(18, 18)"/>
|
||||
</int:transaction-synchronization-factory>
|
||||
|
||||
<bean id="transactionManager" class="org.springframework.integration.transaction.PseudoTransactionManager"/>]]></programlisting>
|
||||
<bean id="transactionManager" class="o.s.i.transaction.PseudoTransactionManager"/>]]></programlisting>
|
||||
|
||||
|
||||
<para>
|
||||
As you can see from the above all, you need to do is declare your poller to be transactional with a <code>transactional</code> 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
|
||||
<classname>org.springframework.integration.transaction.PseudoTransactionManager</classname> which is an implementation
|
||||
<classname>o.s.i.transaction.PseudoTransactionManager</classname> which is an implementation
|
||||
of Spring's <classname>PlatformTransactionManager</classname> and enables the use of the transaction synchronization
|
||||
features of the redis adapter when there is no actual transaction.
|
||||
</para>
|
||||
@@ -326,7 +320,7 @@ rt.setConnectionFactory(redisConnectionFactory);]]></programlisting>
|
||||
</important>
|
||||
<para>
|
||||
Once your poller is transactional all you need to do is set an instance of the
|
||||
<classname>org.springframework.integration.transaction.TransactionSynchronizationFactory</classname> on the <code>transactional</code> element.
|
||||
<classname>org.springframework.integration.transaction.TransactionSynchronizationFactory</classname> on the <code>transactional</code> element.
|
||||
<classname>TransactionSynchronizationFactory</classname> will create an instance of the <classname>TransactionSynchronization</classname>.
|
||||
For your convenience we've exposed a default SpEL-based <classname>TransactionSynchronizationFactory</classname> which allows
|
||||
you to configure SpEL expressions, with their execution being coordinated (synchronized) with a transaction.
|
||||
@@ -342,7 +336,7 @@ rt.setConnectionFactory(redisConnectionFactory);]]></programlisting>
|
||||
For more information about transaction synchronization, see <xref linkend="transaction-synchronization"/>.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="redis-store-outbound-channel-adapter">
|
||||
<title>RedisStore Outbound Channel Adapter</title>
|
||||
|
||||
@@ -350,48 +344,37 @@ rt.setConnectionFactory(redisConnectionFactory);]]></programlisting>
|
||||
The <emphasis>RedisStore Outbound Channel Adapter</emphasis> allows you to write a Message payload to a Redis collection
|
||||
</para>
|
||||
|
||||
<programlisting lang="xml"><![CDATA[<int-redis:store-outbound-channel-adapter id="redisListAdapter"
|
||||
<programlisting language="xml"><![CDATA[<int-redis:store-outbound-channel-adapter id="redisListAdapter"
|
||||
collection-type="LIST"
|
||||
channel="requestChannel"
|
||||
key="myCollection" />]]></programlisting>
|
||||
key="myCollection" />]]></programlisting>
|
||||
|
||||
<para>
|
||||
As you can see from the configuration above, you configure a <emphasis>Redis Store Outbound Channel Adapter</emphasis> using
|
||||
the <code>store-inbound-channel-adapter</code> element, providing values for various attributes such as:
|
||||
the <code>store-inbound-channel-adapter</code> element, providing values for various attributes such as:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><code>key</code> or <code>key-expression</code> - The name of the key for the collection being used. </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><code>extract-payload-elements</code> - If set to <code>true</code> (Default) and the payload is an instance of a "multi-
|
||||
<listitem><code>key</code> or <code>key-expression</code> - The name of the key for the collection being used.</listitem>
|
||||
<listitem><code>extract-payload-elements</code> - If set to <code>true</code> (Default) and the payload is an instance of a "multi-
|
||||
value" object (i.e., Collection or Map) it will be stored using addAll/
|
||||
putAll semantics. Otherwise, if set to <code>false</code> the payload will be stored
|
||||
as a single entry regardless of its type. If the payload is not an instance
|
||||
of a "multi-value" object, the value of this attribute is ignored and the
|
||||
payload will always be stored as a single entry.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><code>collection-type</code> - enumeration of the Collection types supported by this adapter. Supported Collections are: LIST, SET, ZSET, PROPERTIES, MAP </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><code>map-key-expression</code> - SpEL expression that returns the name of the key for entry being
|
||||
putAll semantics. Otherwise, if set to <code>false</code> the payload will be stored
|
||||
as a single entry regardless of its type. If the payload is not an instance
|
||||
of a "multi-value" object, the value of this attribute is ignored and the
|
||||
payload will always be stored as a single entry.</listitem>
|
||||
<listitem><code>collection-type</code> - enumeration of the Collection types supported by this adapter. Supported Collections are: LIST, SET, ZSET, PROPERTIES, MAP</listitem>
|
||||
<listitem><code>map-key-expression</code> - SpEL expression that returns the name of the key for entry being
|
||||
stored. Only applies if the <code>collection-type</code> is MAP or PROPERTIES and
|
||||
'extract-payload-elements' is false. </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><code>connection-factory</code> -
|
||||
reference to an instance of <classname>org.springframework.data.redis.connection.RedisConnectionFactory</classname> </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><code>redis-template</code> -
|
||||
reference to an instance of <classname>org.springframework.data.redis.core.RedisTemplate</classname></para>
|
||||
</listitem>
|
||||
'extract-payload-elements' is false.</listitem>
|
||||
<listitem><code>connection-factory</code> - reference to an instance of <classname>o.s.data.redis.connection.RedisConnectionFactory</classname></listitem>
|
||||
<listitem><code>redis-template</code> - reference to an instance of <classname>o.s.data.redis.core.RedisTemplate</classname></listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
and other attributes that are common across all other inbound adapters (e.g., 'channel').
|
||||
</para>
|
||||
<note>
|
||||
You cannot set both <code>redis-template</code> and <code>connection-factory</code>.
|
||||
</note>
|
||||
</para>
|
||||
|
||||
<important>
|
||||
By default, the adapter uses a <classname>StringRedisTemplate</classname>; this uses
|
||||
<classname>StringRedisSerializer</classname>s for keys, values, hash keys and hash values. However, if
|
||||
|
||||
@@ -17,11 +17,10 @@
|
||||
</para>
|
||||
<para>
|
||||
To configure the outbound gateway write a bean definition like this:
|
||||
<programlisting language="xml"><![CDATA[ <bean id="rmiOutGateway" class=org.spf.integration.rmi.RmiOutboundGateway>
|
||||
<programlisting language="xml"><![CDATA[<bean id="rmiOutGateway" class=org.spf.integration.rmi.RmiOutboundGateway>
|
||||
<constructor-arg value="rmi://host"/>
|
||||
<property name="replyChannel" value="replies"/>
|
||||
</bean>]]>
|
||||
</programlisting>
|
||||
</bean>]]></programlisting>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
@@ -29,10 +28,9 @@
|
||||
<title>Inbound RMI</title>
|
||||
<para>
|
||||
To receive messages over RMI you need to use a <classname>RmiInboundGateway</classname>. This gateway can be configured like this
|
||||
<programlisting language="xml"><![CDATA[ <bean id="rmiOutGateway" class=org.spf.integration.rmi.RmiInboundGateway>
|
||||
<property name="requestChannel" value="requests"/>
|
||||
</bean>]]>
|
||||
</programlisting>
|
||||
<programlisting language="xml"><![CDATA[<bean id="rmiOutGateway" class=org.spf.integration.rmi.RmiInboundGateway>
|
||||
<property name="requestChannel" value="requests"/>
|
||||
</bean>]]></programlisting>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
@@ -40,26 +38,26 @@
|
||||
<title>RMI namespace support</title>
|
||||
<para>
|
||||
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.
|
||||
<programlisting language="xml"><![CDATA[ <int-rmi:inbound-gateway id="gatewayWithDefaults" request-channel="testChannel"/>
|
||||
<programlisting language="xml"><![CDATA[<int-rmi:inbound-gateway id="gatewayWithDefaults" request-channel="testChannel"/>
|
||||
|
||||
<int-rmi:inbound-gateway id="gatewayWithCustomProperties" request-channel="testChannel"
|
||||
expect-reply="false" request-timeout="123" reply-timeout="456"/>
|
||||
<int-rmi:inbound-gateway id="gatewayWithCustomProperties" request-channel="testChannel"
|
||||
expect-reply="false" request-timeout="123" reply-timeout="456"/>
|
||||
|
||||
<int-rmi:inbound-gateway id="gatewayWithHost" request-channel="testChannel"
|
||||
registry-host="localhost"/>
|
||||
<int-rmi:inbound-gateway id="gatewayWithHost" request-channel="testChannel"
|
||||
registry-host="localhost"/>
|
||||
|
||||
<int-rmi:inbound-gateway id="gatewayWithPort" request-channel="testChannel"
|
||||
registry-port="1234"/>
|
||||
<int-rmi:inbound-gateway id="gatewayWithPort" request-channel="testChannel"
|
||||
registry-port="1234"/>
|
||||
|
||||
<int-rmi:inbound-gateway id="gatewayWithExecutorRef" request-channel="testChannel"
|
||||
remote-invocation-executor="invocationExecutor"/>]]></programlisting>
|
||||
<int-rmi:inbound-gateway id="gatewayWithExecutorRef" request-channel="testChannel"
|
||||
remote-invocation-executor="invocationExecutor"/>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
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.
|
||||
<programlisting language="xml"><![CDATA[ <int-rmi:outbound-gateway id="gateway"
|
||||
request-channel="localChannel"
|
||||
remote-channel="testChannel"
|
||||
host="localhost"/>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int-rmi:outbound-gateway id="gateway"
|
||||
request-channel="localChannel"
|
||||
remote-channel="testChannel"
|
||||
host="localhost"/>]]></programlisting>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -26,18 +26,18 @@
|
||||
<section id="samples-get">
|
||||
<title>Where to get Samples</title>
|
||||
<para>
|
||||
The Spring Integration Samples project is hosted on <ulink url="https://github.com/SpringSource/spring-integration-samples/">GitHub</ulink>.
|
||||
The Spring Integration Samples project is hosted on <ulink url="https://github.com/SpringSource/spring-integration-samples/">GitHub</ulink>.
|
||||
You can find the repository at:
|
||||
</para>
|
||||
<para>
|
||||
<ulink url="https://github.com/SpringSource/spring-integration-samples">https://github.com/SpringSource/spring-integration-samples</ulink>
|
||||
</para>
|
||||
<para>
|
||||
In order to check out or <emphasis>clone</emphasis> (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.
|
||||
<ulink url="http://eclipse.org/egit/">EGit</ulink> for the Eclipse IDE.
|
||||
A simple Google search will help you find them. Of course you can also just
|
||||
In order to check out or <emphasis>clone</emphasis> (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.
|
||||
<ulink url="http://eclipse.org/egit/">EGit</ulink> 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 <link linkend="http://git-scm.com/">Git</link>.
|
||||
</para>
|
||||
<note>
|
||||
@@ -45,48 +45,48 @@
|
||||
<ulink url="http://git-scm.com/">http://git-scm.com/</ulink>.
|
||||
</note>
|
||||
<para>
|
||||
In order to checkout (clone in Git terms) the Spring Integration samples
|
||||
In order to checkout (clone in Git terms) the Spring Integration samples
|
||||
repository using the Git command line tool, issue the following commands:
|
||||
</para>
|
||||
|
||||
|
||||
<programlisting language="xml"><![CDATA[$ git clone https://github.com/SpringSource/spring-integration-samples.git]]></programlisting>
|
||||
|
||||
<para>
|
||||
That is all you need to do in order to clone the entire samples repository
|
||||
into a directory named <emphasis>spring-integration-samples</emphasis> within
|
||||
That is all you need to do in order to clone the entire samples repository
|
||||
into a directory named <emphasis>spring-integration-samples</emphasis> within
|
||||
the working directory where you issued that <emphasis>git</emphasis> command.
|
||||
Since the samples repository is a live repository, you might want to perform
|
||||
periodic <emphasis>pulls</emphasis> (updates) to get new samples, as well as updates to
|
||||
the existing samples. In order to do so issue the following git
|
||||
Since the samples repository is a live repository, you might want to perform
|
||||
periodic <emphasis>pulls</emphasis> (updates) to get new samples, as well as updates to
|
||||
the existing samples. In order to do so issue the following git
|
||||
<emphasis>PULL</emphasis> command:
|
||||
</para>
|
||||
|
||||
|
||||
<programlisting language="xml"><![CDATA[$ git pull]]></programlisting>
|
||||
|
||||
</section>
|
||||
<section>
|
||||
<title>Submitting Samples or Sample Requests</title>
|
||||
<title>Submitting Samples or Sample Requests</title>
|
||||
<para>
|
||||
<emphasis>How can I contribute my own Samples?</emphasis>
|
||||
</para>
|
||||
<para>
|
||||
Github is for social coding: if you want to submit your own code examples
|
||||
to the Spring Integration Samples project, we encourage contributions
|
||||
through <ulink url="http://help.github.com/send-pull-requests/"><emphasis>pull
|
||||
requests</emphasis></ulink> from
|
||||
<ulink url="http://help.github.com/fork-a-repo/"><emphasis>forks</emphasis></ulink>
|
||||
of this repository. If you want to contribute code this way, please
|
||||
reference, if possible, a
|
||||
<ulink url="https://jira.springframework.org/browse/INTSAMPLES"><emphasis>JIRA Ticket</emphasis></ulink>
|
||||
Github is for social coding: if you want to submit your own code examples
|
||||
to the Spring Integration Samples project, we encourage contributions
|
||||
through <ulink url="http://help.github.com/send-pull-requests/"><emphasis>pull
|
||||
requests</emphasis></ulink> from
|
||||
<ulink url="http://help.github.com/fork-a-repo/"><emphasis>forks</emphasis></ulink>
|
||||
of this repository. If you want to contribute code this way, please
|
||||
reference, if possible, a
|
||||
<ulink url="https://jira.springframework.org/browse/INTSAMPLES"><emphasis>JIRA Ticket</emphasis></ulink>
|
||||
that provides some details regarding the provided sample.
|
||||
</para>
|
||||
<important>
|
||||
<title>Sign the contributor license agreement</title>
|
||||
<para>
|
||||
Very important: before we can accept your Spring Integration sample,
|
||||
we will 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
|
||||
Very important: before we can accept your Spring Integration sample,
|
||||
we will 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:
|
||||
</para>
|
||||
@@ -94,33 +94,33 @@
|
||||
<ulink url="https://support.springsource.com/spring_committer_signup"/>
|
||||
</para>
|
||||
<para>
|
||||
As Project, please select <emphasis>Spring Integration</emphasis>.
|
||||
As Project, please select <emphasis>Spring Integration</emphasis>.
|
||||
The Project Lead is <emphasis>Mark Fisher</emphasis>.
|
||||
</para>
|
||||
</important>
|
||||
<para><emphasis>Code Contribution Process</emphasis></para>
|
||||
<para>
|
||||
For the actual code contribution process, please read the the
|
||||
<emphasis>Contributor Guidelines</emphasis> for Spring Integration,
|
||||
For the actual code contribution process, please read the the
|
||||
<emphasis>Contributor Guidelines</emphasis> for Spring Integration,
|
||||
they apply for this project as well:
|
||||
</para>
|
||||
<para>
|
||||
<ulink url="https://github.com/SpringSource/spring-integration/wiki/Contributor-Guidelines"/>
|
||||
</para>
|
||||
<para>
|
||||
This process ensures that every commit gets peer-reviewed. As a matter of
|
||||
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!
|
||||
</para>
|
||||
|
||||
|
||||
<para><emphasis>Sample Requests</emphasis></para>
|
||||
|
||||
|
||||
<para>
|
||||
As mentioned earlier, the <emphasis>Spring Integration Samples</emphasis>
|
||||
project has a dedicated JIRA Issue tracking system. To submit new sample
|
||||
As mentioned earlier, the <emphasis>Spring Integration Samples</emphasis>
|
||||
project has a dedicated JIRA Issue tracking system. To submit new sample
|
||||
requests, please visit our JIRA Issue Tracking system:
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
<ulink url="https://jira.springframework.org/browse/INTSAMPLES">https://jira.springframework.org/browse/INTSAMPLES</ulink>.
|
||||
</para>
|
||||
@@ -128,7 +128,7 @@
|
||||
<section id="samples-structure">
|
||||
<title>Samples Structure</title>
|
||||
<para>
|
||||
Starting with Spring Integration 2.0, the structure of the <emphasis>samples</emphasis>
|
||||
Starting with Spring Integration 2.0, the structure of the <emphasis>samples</emphasis>
|
||||
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
|
||||
@@ -443,7 +443,7 @@ That includes Samples; so, if you can't find what you are looking for, let us kn
|
||||
</para>
|
||||
<para>
|
||||
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
|
||||
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.
|
||||
</para>
|
||||
@@ -480,129 +480,129 @@ That includes Samples; so, if you can't find what you are looking for, let us kn
|
||||
</para>
|
||||
<para>
|
||||
Here is the XML configuration:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:int-stream="http://www.springframework.org/schema/integration/stream"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration
|
||||
http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/stream
|
||||
http://www.springframework.org/schema/integration/stream/spring-integration-stream.xsd">
|
||||
<beans:beans xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:int-stream="http://www.springframework.org/schema/integration/stream"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration
|
||||
http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/stream
|
||||
http://www.springframework.org/schema/integration/stream/spring-integration-stream.xsd">
|
||||
|
||||
<int:gateway id="cafe" service-interface="org.springframework.integration.samples.cafe.Cafe"/>
|
||||
<int:gateway id="cafe" service-interface="o.s.i.samples.cafe.Cafe"/>
|
||||
|
||||
<int:channel id="orders"/>
|
||||
<int:splitter input-channel="orders" ref="orderSplitter" method="split" output-channel="drinks"/>
|
||||
<int:channel id="orders"/>
|
||||
<int:splitter input-channel="orders" ref="orderSplitter"
|
||||
method="split" output-channel="drinks"/>
|
||||
|
||||
<int:channel id="drinks"/>
|
||||
<int:router input-channel="drinks" ref="drinkRouter" method="resolveOrderItemChannel"/>
|
||||
<int:channel id="drinks"/>
|
||||
<int:router input-channel="drinks"
|
||||
ref="drinkRouter" method="resolveOrderItemChannel"/>
|
||||
|
||||
<int:channel id="coldDrinks">
|
||||
<int:queue capacity="10"/>
|
||||
</int:channel>
|
||||
<int:service-activator input-channel="coldDrinks" ref="barista"
|
||||
method="prepareColdDrink" output-channel="preparedDrinks"/>
|
||||
<int:channel id="coldDrinks"><int:queue capacity="10"/></int:channel>
|
||||
<int:service-activator input-channel="coldDrinks" ref="barista"
|
||||
method="prepareColdDrink" output-channel="preparedDrinks"/>
|
||||
|
||||
<int:channel id="hotDrinks">
|
||||
<int:queue capacity="10"/>
|
||||
</int:channel>
|
||||
<int:service-activator input-channel="hotDrinks" ref="barista"
|
||||
method="prepareHotDrink" output-channel="preparedDrinks"/>
|
||||
<int:channel id="hotDrinks"><int:queue capacity="10"/></int:channel>
|
||||
<int:service-activator input-channel="hotDrinks" ref="barista"
|
||||
method="prepareHotDrink" output-channel="preparedDrinks"/>
|
||||
|
||||
<int:channel id="preparedDrinks"/>
|
||||
<int:aggregator input-channel="preparedDrinks" ref="waiter"
|
||||
method="prepareDelivery" output-channel="deliveries"/>
|
||||
<int:channel id="preparedDrinks"/>
|
||||
<int:aggregator input-channel="preparedDrinks" ref="waiter"
|
||||
method="prepareDelivery" output-channel="deliveries"/>
|
||||
|
||||
<int-stream:stdout-channel-adapter id="deliveries"/>
|
||||
<int-stream:stdout-channel-adapter id="deliveries"/>
|
||||
|
||||
<beans:bean id="orderSplitter"
|
||||
class="org.springframework.integration.samples.cafe.xml.OrderSplitter"/>
|
||||
<beans:bean id="orderSplitter"
|
||||
class="org.springframework.integration.samples.cafe.xml.OrderSplitter"/>
|
||||
|
||||
<beans:bean id="drinkRouter"
|
||||
class="org.springframework.integration.samples.cafe.xml.DrinkRouter"/>
|
||||
<beans:bean id="drinkRouter"
|
||||
class="org.springframework.integration.samples.cafe.xml.DrinkRouter"/>
|
||||
|
||||
<beans:bean id="barista" class="org.springframework.integration.samples.cafe.xml.Barista"/>
|
||||
<beans:bean id="barista" class="o.s.i.samples.cafe.xml.Barista"/>
|
||||
<beans:bean id="waiter" class="o.s.i.samples.cafe.xml.Waiter"/>
|
||||
|
||||
<beans:bean id="waiter" class="org.springframework.integration.samples.cafe.xml.Waiter"/>
|
||||
<int:poller id="poller" default="true" fixed-rate="1000"/>
|
||||
|
||||
<int:poller id="poller" default="true" fixed-rate="1000"/>
|
||||
|
||||
</beans:beans>]]></programlisting>
|
||||
</beans:beans>]]></programlisting>
|
||||
<para>
|
||||
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"). Most importantly, notice that the objects are simple POJOs
|
||||
with strongly typed method arguments. For example, here is the Splitter:
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[public class OrderSplitter {
|
||||
|
||||
public List<OrderItem> split(Order order) {
|
||||
return order.getItems();
|
||||
}
|
||||
}]]></programlisting>
|
||||
public List<OrderItem> split(Order order) {
|
||||
return order.getItems();
|
||||
}
|
||||
}]]></programlisting>
|
||||
<para>
|
||||
In the case of the Router, the return value does not have to be a <interfacename>MessageChannel</interfacename>
|
||||
instance (although it can be). As you see in this example, a String-value representing the channel name is
|
||||
returned instead.
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[public class DrinkRouter {
|
||||
|
||||
public String resolveOrderItemChannel(OrderItem orderItem) {
|
||||
return (orderItem.isIced()) ? "coldDrinks" : "hotDrinks";
|
||||
}
|
||||
}]]></programlisting>
|
||||
</para>
|
||||
public String resolveOrderItemChannel(OrderItem orderItem) {
|
||||
return (orderItem.isIced()) ? "coldDrinks" : "hotDrinks";
|
||||
}
|
||||
}]]></programlisting>
|
||||
<para>
|
||||
Now turning back to the XML, you see that there are two <service-activator> elements. Each of these
|
||||
is delegating to the same <classname>Barista</classname> instance but different methods: 'prepareHotDrink'
|
||||
or 'prepareColdDrink' corresponding to the two channels where order items have been routed.
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[public class Barista {
|
||||
|
||||
private long hotDrinkDelay = 5000;
|
||||
private long coldDrinkDelay = 1000;
|
||||
private long hotDrinkDelay = 5000;
|
||||
private long coldDrinkDelay = 1000;
|
||||
|
||||
private AtomicInteger hotDrinkCounter = new AtomicInteger();
|
||||
private AtomicInteger coldDrinkCounter = new AtomicInteger();
|
||||
private AtomicInteger hotDrinkCounter = new AtomicInteger();
|
||||
private AtomicInteger coldDrinkCounter = new AtomicInteger();
|
||||
|
||||
public void setHotDrinkDelay(long hotDrinkDelay) {
|
||||
this.hotDrinkDelay = hotDrinkDelay;
|
||||
}
|
||||
public void setHotDrinkDelay(long hotDrinkDelay) {
|
||||
this.hotDrinkDelay = hotDrinkDelay;
|
||||
}
|
||||
|
||||
public void setColdDrinkDelay(long coldDrinkDelay) {
|
||||
this.coldDrinkDelay = coldDrinkDelay;
|
||||
}
|
||||
public void setColdDrinkDelay(long coldDrinkDelay) {
|
||||
this.coldDrinkDelay = coldDrinkDelay;
|
||||
}
|
||||
|
||||
public Drink prepareHotDrink(OrderItem orderItem) {
|
||||
try {
|
||||
Thread.sleep(this.hotDrinkDelay);
|
||||
System.out.println(Thread.currentThread().getName()
|
||||
+ " prepared hot drink #" + hotDrinkCounter.incrementAndGet()
|
||||
+ " for order #" + orderItem.getOrder().getNumber() + ": " + orderItem);
|
||||
return new Drink(orderItem.getOrder().getNumber(), orderItem.getDrinkType(),
|
||||
orderItem.isIced(), orderItem.getShots());
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public Drink prepareHotDrink(OrderItem orderItem) {
|
||||
try {
|
||||
Thread.sleep(this.hotDrinkDelay);
|
||||
System.out.println(Thread.currentThread().getName()
|
||||
+ " prepared hot drink #" + hotDrinkCounter.incrementAndGet()
|
||||
+ " for order #" + orderItem.getOrder().getNumber()
|
||||
+ ": " + orderItem);
|
||||
return new Drink(orderItem.getOrder().getNumber(), orderItem.getDrinkType(),
|
||||
orderItem.isIced(), orderItem.getShots());
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Drink prepareColdDrink(OrderItem orderItem) {
|
||||
try {
|
||||
Thread.sleep(this.coldDrinkDelay);
|
||||
System.out.println(Thread.currentThread().getName()
|
||||
+ " prepared cold drink #" + coldDrinkCounter.incrementAndGet()
|
||||
+ " for order #" + orderItem.getOrder().getNumber() + ": " + orderItem);
|
||||
return new Drink(orderItem.getOrder().getNumber(), orderItem.getDrinkType(),
|
||||
orderItem.isIced(), orderItem.getShots());
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}]]></programlisting>
|
||||
</para>
|
||||
public Drink prepareColdDrink(OrderItem orderItem) {
|
||||
try {
|
||||
Thread.sleep(this.coldDrinkDelay);
|
||||
System.out.println(Thread.currentThread().getName()
|
||||
+ " prepared cold drink #" + coldDrinkCounter.incrementAndGet()
|
||||
+ " for order #" + orderItem.getOrder().getNumber() + ": "
|
||||
+ orderItem);
|
||||
return new Drink(orderItem.getOrder().getNumber(), orderItem.getDrinkType(),
|
||||
orderItem.isIced(), orderItem.getShots());
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}]]></programlisting>
|
||||
<para>
|
||||
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). This simulates work being completed at different rates. When the
|
||||
@@ -611,30 +611,32 @@ That includes Samples; so, if you can't find what you are looking for, let us kn
|
||||
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 <interfacename>Cafe</interfacename> interface.
|
||||
<programlisting language="java">public interface Cafe {
|
||||
|
||||
@Gateway(requestChannel="orders")
|
||||
void placeOrder(Order order);
|
||||
|
||||
}</programlisting>
|
||||
Finally, have a look at the <methodname>main()</methodname> method of the <classname>CafeDemo</classname> itself.
|
||||
<programlisting language="java"><![CDATA[public static void main(String[] args) {
|
||||
AbstractApplicationContext context = null;
|
||||
if (args.length > 0) {
|
||||
context = new FileSystemXmlApplicationContext(args);
|
||||
}
|
||||
else {
|
||||
context = new ClassPathXmlApplicationContext("cafeDemo.xml", CafeDemo.class);
|
||||
}
|
||||
Cafe cafe = context.getBean("cafe", Cafe.class);
|
||||
for (int i = 1; i <= 100; i++) {
|
||||
Order order = new Order(i);
|
||||
order.addItem(DrinkType.LATTE, 2, false);
|
||||
order.addItem(DrinkType.MOCHA, 3, true);
|
||||
cafe.placeOrder(order);
|
||||
}
|
||||
}]]></programlisting>
|
||||
</para>
|
||||
<programlisting language="java">public interface Cafe {
|
||||
|
||||
@Gateway(requestChannel="orders")
|
||||
void placeOrder(Order order);
|
||||
|
||||
}</programlisting>
|
||||
<para>
|
||||
Finally, have a look at the <methodname>main()</methodname> method of the <classname>CafeDemo</classname> itself.
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[public static void main(String[] args) {
|
||||
AbstractApplicationContext context = null;
|
||||
if (args.length > 0) {
|
||||
context = new FileSystemXmlApplicationContext(args);
|
||||
}
|
||||
else {
|
||||
context = new ClassPathXmlApplicationContext("cafeDemo.xml", CafeDemo.class);
|
||||
}
|
||||
Cafe cafe = context.getBean("cafe", Cafe.class);
|
||||
for (int i = 1; i <= 100; i++) {
|
||||
Order order = new Order(i);
|
||||
order.addItem(DrinkType.LATTE, 2, false);
|
||||
order.addItem(DrinkType.MOCHA, 3, true);
|
||||
cafe.placeOrder(order);
|
||||
}
|
||||
}]]></programlisting>
|
||||
<tip>
|
||||
To run this sample as well as 8 others, refer to the <code>README.txt</code> within the "samples" directory
|
||||
of the main distribution as described at the beginning of this chapter.
|
||||
@@ -645,6 +647,7 @@ That includes Samples; so, if you can't find what you are looking for, let us kn
|
||||
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:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<int:service-activator input-channel="hotDrinks"
|
||||
ref="barista"
|
||||
method="prepareHotDrink"
|
||||
@@ -659,7 +662,6 @@ That includes Samples; so, if you can't find what you are looking for, let us kn
|
||||
</int:service-activator>
|
||||
|
||||
]]><emphasis><![CDATA[<task:executor id="pool" pool-size="5"/>]]></emphasis></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
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),
|
||||
@@ -684,10 +686,9 @@ That includes Samples; so, if you can't find what you are looking for, let us kn
|
||||
the XPath splitter component.
|
||||
<programlisting language="xml"><![CDATA[<int-xml:xpath-splitter id="orderItemSplitter" input-channel="ordersChannel"
|
||||
output-channel="stockCheckerChannel" create-documents="true">
|
||||
<int-xml:xpath-expression expression="/orderNs:order/orderNs:orderItem"
|
||||
<int-xml:xpath-expression expression="/orderNs:order/orderNs:orderItem"
|
||||
namespace-map="orderNamespaceMap" />
|
||||
</int-xml:xpath-splitter>
|
||||
]]></programlisting>
|
||||
</int-xml:xpath-splitter>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
A service activator is then used to pass the message into a stock checker POJO. The order item document is enriched with information
|
||||
@@ -697,16 +698,14 @@ That includes Samples; so, if you can't find what you are looking for, let us kn
|
||||
<si-xml:xpath-expression expression="/orderNs:orderItem/@in-stock" namespace-map="orderNamespaceMap" />
|
||||
<si-xml:mapping value="true" channel="warehouseDispatchChannel"/>
|
||||
<si-xml:mapping value="false" channel="outOfStockChannel"/>
|
||||
</si-xml:xpath-router>
|
||||
]]></programlisting>
|
||||
</si-xml:xpath-router>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Where the order item is not in stock the message is transformed using
|
||||
xslt into a format suitable for sending to the supplier.
|
||||
<programlisting language="xml"><![CDATA[<int-xml:xslt-transformer input-channel="outOfStockChannel"
|
||||
<programlisting language="xml"><![CDATA[<int-xml:xslt-transformer input-channel="outOfStockChannel"
|
||||
output-channel="resupplyOrderChannel"
|
||||
xsl-resource="classpath:org/springframework/integration/samples/xml/bigBooksSupplierTransformer.xsl"/>
|
||||
]]></programlisting>
|
||||
xsl-resource="classpath:org/springframework/integration/samples/xml/bigBooksSupplierTransformer.xsl"/>]]></programlisting>
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
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:
|
||||
<programlisting language="xml"><int:service-activator input-channel="exampleChannel" output-channel="replyChannel"
|
||||
ref="somePojo" method="someMethod"/></programlisting>
|
||||
ref="somePojo" method="someMethod"/></programlisting>
|
||||
If no "output-channel" is available, it will then check the Message's <literal>replyChannel</literal> header
|
||||
value. If that value is available, it will then check its type. If it is a
|
||||
<interfacename>MessageChannel</interfacename>, the reply message will be sent to that channel. If it is a
|
||||
@@ -68,35 +68,35 @@
|
||||
configuration is not allowed, as it creates an ambiguous condition and will result in an Exception being thrown.
|
||||
</para>
|
||||
</note>
|
||||
|
||||
|
||||
<para>
|
||||
<emphasis>Service Activators and the Spring Expression Language (SpEL)</emphasis>
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
Since Spring Integration 2.0, Service Activators can also benefit from 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).
|
||||
</para>
|
||||
<para>
|
||||
For example, you may now invoke any bean method without pointing to the bean via a <code>ref</code> attribute or including it as an
|
||||
For example, you may now invoke any bean method without pointing to the bean via a <code>ref</code> attribute or including it as an
|
||||
inner bean definition. For example:
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int:service-activator input-channel="in" output-channel="out"
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int:service-activator input-channel="in" output-channel="out"
|
||||
expression="@accountService.processAccount(payload, headers.accountId)"/>
|
||||
|
||||
|
||||
<bean id="accountService" class="foo.bar.Account"/>]]></programlisting>
|
||||
|
||||
|
||||
In the above configuration instead of injecting 'accountService' using a <code>ref</code> or as an inner bean,
|
||||
we are simply using SpEL's <code>@beanId</code> 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 <emphasis>Service Activators</emphasis> do not even have to reference a bean if all logic can be encapsulated
|
||||
For simple scenarios your <emphasis>Service Activators</emphasis> do not even have to reference a bean if all logic can be encapsulated
|
||||
by such an expression.
|
||||
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int:service-activator input-channel="in" output-channel="out" expression="payload * 2"/>]]></programlisting>
|
||||
In the above configuration our service logic is to simply multiply the payload value by 2, and SpEL lets us handle it relatively easy.
|
||||
|
||||
|
||||
</para>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
|
||||
<programlisting language="xml"><![CDATA[xmlns:int-sftp="http://www.springframework.org/schema/integration/sftp"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration/sftp
|
||||
http://www.springframework.org/schema/integration/sftp/spring-integration-sftp.xsd"
|
||||
]]></programlisting>
|
||||
http://www.springframework.org/schema/integration/sftp/spring-integration-sftp.xsd"]]></programlisting>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<title>Programming model</title>
|
||||
|
||||
<para>The API for performing splitting consists of one base class,
|
||||
<classname>AbstractMessageSplitter</classname>, which is a
|
||||
<classname>AbstractMessageSplitter</classname>, which is a
|
||||
<interfacename>MessageHandler</interfacename> implementation,
|
||||
encapsulating features which are common to splitters, such as filling in
|
||||
the appropriate message headers CORRELATION_ID, SEQUENCE_SIZE, and
|
||||
@@ -24,28 +24,28 @@
|
||||
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
|
||||
example, in a
|
||||
<ulink url="http://www.eaipatterns.com/DistributionAggregate.html">
|
||||
Composed Message Processor</ulink> scenario.</para>
|
||||
|
||||
<para>An excerpt from <classname>AbstractMessageSplitter</classname> can be seen below:</para>
|
||||
|
||||
<programlisting lang="java">public abstract class AbstractMessageSplitter
|
||||
extends AbstractReplyProducingMessageConsumer {
|
||||
...
|
||||
protected abstract Object splitMessage(Message<?> message);
|
||||
<programlisting language="java"><![CDATA[public abstract class AbstractMessageSplitter
|
||||
extends AbstractReplyProducingMessageConsumer {
|
||||
...
|
||||
protected abstract Object splitMessage(Message<?> message);
|
||||
|
||||
}</programlisting>
|
||||
}]]></programlisting>
|
||||
|
||||
<para>To implement a specific Splitter in an application,
|
||||
<para>To implement a specific Splitter in an application,
|
||||
extend <classname>AbstractMessageSplitter</classname> and implement the <code>splitMessage</code> method,
|
||||
which contains logic for splitting the messages. The return
|
||||
value may be one of the following:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>a <interfacename>Collection</interfacename> (or subclass thereof) or an array of
|
||||
<interfacename>Message</interfacename> objects -
|
||||
<para>a <interfacename>Collection</interfacename> (or subclass thereof) or an array of
|
||||
<interfacename>Message</interfacename> objects -
|
||||
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
|
||||
@@ -106,7 +106,7 @@
|
||||
above .<emphasis>Optional</emphasis>.
|
||||
If reference to a bean is not provided, then it is assumed that the <emphasis>payload</emphasis>
|
||||
of the Message that arrived on the <code>input-channel</code> is
|
||||
an implementation of <interfacename>java.util.Collection</interfacename>
|
||||
an implementation of <interfacename>java.util.Collection</interfacename>
|
||||
and the default splitting logic will be applied to the Collection,
|
||||
incorporating each individual element into a Message and sending it to the <code>output-channel</code>.
|
||||
</para>
|
||||
@@ -152,7 +152,7 @@
|
||||
<para>The <interfacename>@Splitter</interfacename> annotation is
|
||||
applicable to methods that expect either the
|
||||
<interfacename>Message</interfacename> type or the message payload type,
|
||||
and the return values of the method should be a <interfacename>Collection</interfacename> of any type.
|
||||
and the return values of the method should be a <interfacename>Collection</interfacename> of any type.
|
||||
If the returned values are not actual <interfacename>Message</interfacename>
|
||||
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
|
||||
|
||||
@@ -72,20 +72,22 @@
|
||||
</para>
|
||||
<para>
|
||||
To configure the inbound channel adapter the following code snippet shows the different configuration options that are supported.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<int-stream:stdin-channel-adapter id="adapterWithDefaultCharset"/>
|
||||
|
||||
<int-stream:stdin-channel-adapter id="adapterWithProvidedCharset" charset="UTF-8"/>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
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.
|
||||
<programlisting language="xml"><![CDATA[<int-stream:stdout-channel-adapter id="stdoutAdapterWithDefaultCharset" channel="testChannel"/>
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<int-stream:stdout-channel-adapter id="stdoutAdapterWithDefaultCharset"
|
||||
channel="testChannel"/>
|
||||
|
||||
<int-stream:stdout-channel-adapter id="stdoutAdapterWithProvidedCharset" charset="UTF-8" channel="testChannel"/>
|
||||
<int-stream:stdout-channel-adapter id="stdoutAdapterWithProvidedCharset" charset="UTF-8"
|
||||
channel="testChannel"/>
|
||||
|
||||
<int-stream:stderr-channel-adapter id="stderrAdapter" channel="testChannel"/>
|
||||
|
||||
<int-stream:stdout-channel-adapter id="newlineAdapter" append-newline="true" channel="testChannel"/>
|
||||
]]></programlisting>
|
||||
</para>
|
||||
<int-stream:stdout-channel-adapter id="newlineAdapter" append-newline="true"
|
||||
channel="testChannel"/>]]></programlisting>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
@@ -189,18 +189,18 @@ as a convinience.
|
||||
</para>
|
||||
<para>
|
||||
Key strategy interfaces for this feature are
|
||||
<programlisting><![CDATA[public interface TransactionSynchronizationFactory {
|
||||
<programlisting language="java"><![CDATA[public interface TransactionSynchronizationFactory {
|
||||
|
||||
TransactionSynchronization create(Object key);
|
||||
TransactionSynchronization create(Object key);
|
||||
}
|
||||
|
||||
public interface TransactionSynchronizationProcessor {
|
||||
|
||||
void processBeforeCommit(IntegrationResourceHolder holder);
|
||||
void processBeforeCommit(IntegrationResourceHolder holder);
|
||||
|
||||
void processAfterCommit(IntegrationResourceHolder holder);
|
||||
void processAfterCommit(IntegrationResourceHolder holder);
|
||||
|
||||
void processAfterRollback(IntegrationResourceHolder holder);
|
||||
void processAfterRollback(IntegrationResourceHolder holder);
|
||||
|
||||
}]]></programlisting>
|
||||
|
||||
@@ -229,19 +229,19 @@ public interface TransactionSynchronizationProcessor {
|
||||
To simplify configuration of these components, namespace support for the default factory has been provided. Configuration is best
|
||||
described using an example:
|
||||
</para>
|
||||
<programlisting><![CDATA[<int-file:inbound-channel-adapter id="inputDirPoller"
|
||||
channel="someChannel"
|
||||
directory="/foo/bar"
|
||||
filter="filter"
|
||||
comparator="testComparator">
|
||||
<int:poller fixed-rate="5000">
|
||||
<int:transactional transaction-manager="transactionManager" synchronization-factory="syncFactory" />
|
||||
</int:poller>
|
||||
<programlisting language="xml"><![CDATA[<int-file:inbound-channel-adapter id="inputDirPoller"
|
||||
channel="someChannel"
|
||||
directory="/foo/bar"
|
||||
filter="filter"
|
||||
comparator="testComparator">
|
||||
<int:poller fixed-rate="5000">
|
||||
<int:transactional transaction-manager="transactionManager" synchronization-factory="syncFactory" />
|
||||
</int:poller>
|
||||
</int-file:inbound-channel-adapter>
|
||||
|
||||
<int:transaction-synchronization-factory id="syncFactory">
|
||||
<int:after-commit expression="payload.renameTo('/success/' + payload.name)" channel="committedChannel" />
|
||||
<int:after-rollback expression="payload.renameTo('/failed/' + payload.name)" channel="rolledBackChannel" />
|
||||
<int:after-commit expression="payload.renameTo('/success/' + payload.name)" channel="committedChannel" />
|
||||
<int:after-rollback expression="payload.renameTo('/failed/' + payload.name)" channel="rolledBackChannel" />
|
||||
</int:transaction-synchronization-factory>]]></programlisting>
|
||||
<para>
|
||||
The result of the SpEL evaluation is sent as the payload to either the <emphasis>committedChannel</emphasis>
|
||||
@@ -261,10 +261,10 @@ public interface TransactionSynchronizationProcessor {
|
||||
followed by the synchronized action.
|
||||
</para>
|
||||
<para>
|
||||
It is also important to understand that if you provide a custom <classname>TransactionSynchronizationFactory</classname>,
|
||||
it is responsible for creating a resource synchronization that will cause the bound resource to be unbound automatically,
|
||||
when the transaction completes.
|
||||
The default <classname>TransactionSynchronizationFactory</classname> does this by returning a subclass
|
||||
It is also important to understand that if you provide a custom <classname>TransactionSynchronizationFactory</classname>,
|
||||
it is responsible for creating a resource synchronization that will cause the bound resource to be unbound automatically,
|
||||
when the transaction completes.
|
||||
The default <classname>TransactionSynchronizationFactory</classname> does this by returning a subclass
|
||||
of <classname>ResourceHolderSynchronization</classname>, with the default
|
||||
<emphasis>shouldUnbindAtCompletion()</emphasis> returning <code>true</code>.
|
||||
</para>
|
||||
@@ -296,6 +296,6 @@ public interface TransactionSynchronizationProcessor {
|
||||
To use a <classname>PseudoTransactionManager</classname>, simply define it as a <bean/>, in the same
|
||||
way you would configure a real transaction manager:
|
||||
</para>
|
||||
<programlisting><![CDATA[<bean id="transactionManager" class="org.springframework.integration.transaction.PseudoTransactionManager" />]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<bean id="transactionManager" class="o.s.i.transaction.PseudoTransactionManager" />]]></programlisting>
|
||||
</section>
|
||||
</appendix>
|
||||
|
||||
@@ -26,11 +26,11 @@
|
||||
</note>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="transformer-config">
|
||||
<title>Configuring Transformer</title>
|
||||
|
||||
|
||||
|
||||
|
||||
<section id="transformer-namespace">
|
||||
<title>Configuring Transformer with XML</title>
|
||||
<para>
|
||||
@@ -69,29 +69,29 @@
|
||||
Returning <code>null</code> 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
|
||||
behavior (where a component might return NULL and that should not be considered an error), a
|
||||
<emphasis>service-activator</emphasis> could be used. Its <code>requires-reply</code> 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.
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
<emphasis>Transformers and Spring Expression Language (SpEL)</emphasis>
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
Just like Routers, Aggregators and other components, as of Spring Integration 2.0 Transformers can also benefit from SpEL support
|
||||
(http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html)
|
||||
whenever transformation logic is relatively simple.
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int:transformer input-channel="inChannel"
|
||||
output-channel="outChannel"
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int:transformer input-channel="inChannel"
|
||||
output-channel="outChannel"
|
||||
expression="payload.toUpperCase() + '- [' + T(java.lang.System).currentTimeMillis() + ']'"/>]]></programlisting>
|
||||
|
||||
In the above configuration we are achieving a simple transformation of the <emphasis>payload</emphasis> with a simple SpEL
|
||||
expression and without writing a custom transformer. Our <emphasis>payload</emphasis> (assuming String) will be
|
||||
|
||||
In the above configuration we are achieving a simple transformation of the <emphasis>payload</emphasis> with a simple SpEL
|
||||
expression and without writing a custom transformer. Our <emphasis>payload</emphasis> (assuming String) will be
|
||||
upper-cased and concatenated with the current timestamp with some simple formatting.
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
<emphasis>Common Transformers</emphasis>
|
||||
</para>
|
||||
@@ -101,7 +101,7 @@
|
||||
<classname>ObjectToStringTransformer</classname> 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.
|
||||
|
||||
<programlisting language="xml"><![CDATA[ <int:object-to-string-transformer input-channel="in" output-channel="out"/>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<int:object-to-string-transformer input-channel="in" output-channel="out"/>]]></programlisting>
|
||||
|
||||
A potential example for this would be sending some arbitrary object to the 'outbound-channel-adapter' in the
|
||||
<emphasis>file</emphasis> namespace. Whereas that Channel Adapter only supports String, byte-array, or
|
||||
@@ -136,9 +136,9 @@
|
||||
by default, but you can provide an implementation of Spring 3.0's Serializer or Deserializer strategies via the
|
||||
'serializer' and 'deserializer' attributes, respectively.
|
||||
|
||||
<programlisting language="xml"><![CDATA[ <int:payload-serializing-transformer input-channel="objectsIn" output-channel="bytesOut"/>
|
||||
<programlisting language="xml"><![CDATA[<int:payload-serializing-transformer input-channel="objectsIn" output-channel="bytesOut"/>
|
||||
|
||||
<int:payload-deserializing-transformer input-channel="bytesIn" output-channel="objectsOut"/>]]></programlisting>
|
||||
<int:payload-deserializing-transformer input-channel="bytesIn" output-channel="objectsOut"/>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
<emphasis>Object-to-Map Transformer</emphasis>
|
||||
@@ -209,7 +209,7 @@ public class Kid {
|
||||
<emphasis>JSON Transformers</emphasis>
|
||||
</para>
|
||||
<para>
|
||||
<emphasis>Object to JSON</emphasis> and <emphasis>JSON to Object</emphasis> transformers are provided.
|
||||
<emphasis>Object to JSON</emphasis> and <emphasis>JSON to Object</emphasis> transformers are provided.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting language="xml"><![CDATA[<int:object-to-json-transformer input-channel="objectMapperInput"/>]]></programlisting>
|
||||
@@ -231,7 +231,7 @@ public class Kid {
|
||||
</para>
|
||||
<para>
|
||||
<programlisting language="java"><![CDATA[public class ObjectMapperFactory {
|
||||
|
||||
|
||||
public static ObjectMapper getMapper() {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
|
||||
@@ -280,7 +280,7 @@ public class Kid {
|
||||
String. This solution requires a change to just the outbound application.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting><![CDATA[<object-to-json-transformer ... content-type="text/x-json"/>]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<object-to-json-transformer ... content-type="text/x-json"/>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
The second solution is to eliminate the json transformers altogether and use an
|
||||
@@ -304,7 +304,8 @@ public class Kid {
|
||||
how to configure the message converter; it requires <code>spring-amqp</code> 1.1.3 or
|
||||
above.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<bean id="jsonConverterWithPOType" class="org.springframework.amqp.support.converter.JsonMessageConverter">
|
||||
<programlisting language="xml"><![CDATA[<bean id="jsonConverterWithPOType"
|
||||
class="org.springframework.amqp.support.converter.JsonMessageConverter">
|
||||
<property name="classMapper">
|
||||
<bean class="org.springframework.amqp.support.converter.DefaultClassMapper">
|
||||
<property name="defaultType"
|
||||
@@ -336,22 +337,22 @@ Order generateOrder(String productId, @Header("customerName") String customer) {
|
||||
}</programlisting>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
<section id="header-filter">
|
||||
<title>Header Filter</title>
|
||||
|
||||
|
||||
Some times your transformation use case might be as simple as removing a few headers.
|
||||
For such a use case, Spring Integration provides a <emphasis>Header Filter</emphasis> 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 <emphasis>Header Filter</emphasis> is the opposite of the <emphasis>Header Enricher</emphasis>.
|
||||
Basically the <emphasis>Header Filter</emphasis> is the opposite of the <emphasis>Header Enricher</emphasis>.
|
||||
The latter is discussed in <xref linkend="header-enricher"/>
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int:header-filter input-channel="inputChannel"
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int:header-filter input-channel="inputChannel"
|
||||
output-channel="outputChannel" header-names="lastName, state"/>]]></programlisting>
|
||||
|
||||
|
||||
As you can see, configuration of a <emphasis>Header Filter</emphasis> is quite simple. It is a typical endpoint with input/output channels
|
||||
and a <code>header-names</code> 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.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<title>Twitter Adapter</title>
|
||||
<para>
|
||||
Spring Integration provides support for interacting with Twitter. With the Twitter adapters you can both
|
||||
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.
|
||||
</para>
|
||||
@@ -12,13 +12,13 @@
|
||||
<section id="twitter-intro">
|
||||
<title>Introduction</title>
|
||||
<para>
|
||||
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
|
||||
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.
|
||||
</para>
|
||||
<para>
|
||||
<important>
|
||||
Previous versions of Spring Integration were dependent upon the <link linkend="http://twitter4j.org/en/index.html">Twitter4J API</link>,
|
||||
Previous versions of Spring Integration were dependent upon the <link linkend="http://twitter4j.org/en/index.html">Twitter4J API</link>,
|
||||
but with the release of <link linkend="http://www.springsource.org/spring-social">Spring Social 1.0 GA</link>,
|
||||
Spring Integration, as of version 2.1, now builds directly upon Spring Social's Twitter support, instead of Twitter4J.
|
||||
</important>
|
||||
@@ -28,23 +28,23 @@
|
||||
Spring Integration provides a convenient namespace configuration to define Twitter artifacts. You can enable it by adding
|
||||
the following within your XML header.
|
||||
<programlisting language="xml"><![CDATA[xmlns:int-twitter="http://www.springframework.org/schema/integration/twitter"
|
||||
xsi:schemaLocation="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"]]></programlisting>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="twitter-oauth">
|
||||
<title>Twitter OAuth Configuration</title>
|
||||
|
||||
|
||||
<para>
|
||||
The Twitter API allows for both authenticated and anonymous operations. 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 <link linkend="http://oauth.net/">http://oauth.net/</link> or
|
||||
in this article <link linkend="http://hueniverse.com/oauth/">http://hueniverse.com/oauth/</link> from Hueniverse.
|
||||
Please also see <link linkend="http://dev.twitter.com/pages/oauth_faq">OAuth FAQ</link> for more information about OAuth and Twitter.
|
||||
The Twitter API allows for both authenticated and anonymous operations. 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 <link linkend="http://oauth.net/">http://oauth.net/</link> or
|
||||
in this article <link linkend="http://hueniverse.com/oauth/">http://hueniverse.com/oauth/</link> from Hueniverse.
|
||||
Please also see <link linkend="http://dev.twitter.com/pages/oauth_faq">OAuth FAQ</link> for more information about OAuth and Twitter.
|
||||
</para>
|
||||
<para>
|
||||
In order to use OAuth authentication/authorization with Twitter you must create a new Application on the Twitter Developers site.
|
||||
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:
|
||||
</para>
|
||||
<para>
|
||||
@@ -53,105 +53,105 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/twitter
|
||||
<para>Go to <link linkend="http://dev.twitter.com/">http://dev.twitter.com/</link></para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Click on the <code>Register an app</code> link and fill out all required fields on the form provided;
|
||||
set <code>Application Type</code> to <code>Client</code> and depending on the nature of your application select
|
||||
<code>Default Access Type</code> as <emphasis>Read & Write</emphasis> or <emphasis>Read-only</emphasis>
|
||||
and Submit the form. If everything is successful you'll be presented with the <code>Consumer Key</code>
|
||||
<para>Click on the <code>Register an app</code> link and fill out all required fields on the form provided;
|
||||
set <code>Application Type</code> to <code>Client</code> and depending on the nature of your application select
|
||||
<code>Default Access Type</code> as <emphasis>Read & Write</emphasis> or <emphasis>Read-only</emphasis>
|
||||
and Submit the form. If everything is successful you'll be presented with the <code>Consumer Key</code>
|
||||
and <code>Consumer Secret</code>. Copy both values in a safe place.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>On the same page you should see a <code>My Access Token</code> button on the side bar (right).
|
||||
Click on it and you'll be presented with two more values: <code>Access Token</code> and <code>Access Token Secret</code>.
|
||||
<para>On the same page you should see a <code>My Access Token</code> button on the side bar (right).
|
||||
Click on it and you'll be presented with two more values: <code>Access Token</code> and <code>Access Token Secret</code>.
|
||||
Copy these values in a safe place as well.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
|
||||
</section>
|
||||
<section>
|
||||
<title>Twitter Template</title>
|
||||
|
||||
<para>
|
||||
As mentioned above, Spring Integration relies upon Spring Social, and that library provides an implementation of the template
|
||||
pattern, <classname>org.springframework.social.twitter.api.impl.TwitterTemplate</classname> to interact with Twitter.
|
||||
pattern, <classname>o.s.social.twitter.api.impl.TwitterTemplate</classname> to interact with Twitter.
|
||||
For anonymous operations (e.g., search), you don't have to define an instance of <classname>TwitterTemplate</classname> 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 <classname>TwitterTemplate</classname> as a bean and
|
||||
(update status, send direct message, etc.), you must configure a <classname>TwitterTemplate</classname> as a bean and
|
||||
inject it explicitly into the endpoint, because the authentication configuration is required.
|
||||
Below is a sample configuration of TwitterTemplate:
|
||||
|
||||
<programlisting language="xml"><![CDATA[<bean id="twitterTemplate" class="org.springframework.social.twitter.api.impl.TwitterTemplate">
|
||||
<programlisting language="xml"><![CDATA[<bean id="twitterTemplate" class="o.s.social.twitter.api.impl.TwitterTemplate">
|
||||
<constructor-arg value="4XzBPacJQxyBzzzH"/>
|
||||
<constructor-arg value="AbRxUAvyCtqQtvxFK8w5ZMtMj20KFhB6o"/>
|
||||
<constructor-arg value="21691649-4YZY5iJEOfz2A9qCFd9SjBRGb3HLmIm4HNE"/>
|
||||
<constructor-arg value="AbRxUAvyNCtqQtxFK8w5ZMtMj20KFhB6o"/>
|
||||
</bean>]]></programlisting>
|
||||
<note>The values above are not real.</note>
|
||||
|
||||
As you can see from the configuration above, all we need to do is to provide
|
||||
|
||||
As you can see from the configuration above, all we need to do is to provide
|
||||
OAuth <code>attributes</code> as constructor arguments. The values would be those you obtained in the previous step.
|
||||
The order of constructor arguments is: 1) <code>consumerKey</code>, 2) <code>consumerSecret</code>,
|
||||
3) <code>accessToken</code>, and 4) <code>accessTokenSecret</code>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
A more practical way to manage OAuth connection attributes would be via Spring's property placeholder support by simply
|
||||
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):
|
||||
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[twitter.oauth.consumerKey=4XzBPacJQxyBzzzH
|
||||
twitter.oauth.consumerSecret=AbRxUAvyCtqQtvxFK8w5ZMtMj20KFhB6o
|
||||
twitter.oauth.accessToken=21691649-4YZY5iJEOfz2A9qCFd9SjBRGb3HLmIm4HNE
|
||||
twitter.oauth.accessTokenSecret=AbRxUAvyNCtqQtxFK8w5ZMtMj20KFhB6o]]></programlisting>
|
||||
|
||||
Then, you can configure a <code>property-placeholder</code> to point to the above property file:
|
||||
<para>
|
||||
Then, you can configure a <code>property-placeholder</code> to point to the above property file:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<context:property-placeholder location="classpath:oauth.properties"/>
|
||||
|
||||
<programlisting language="java"><![CDATA[<context:property-placeholder
|
||||
location="classpath:oauth.properties"/>
|
||||
|
||||
<bean id="twitterTemplate" class="org.springframework.social.twitter.api.impl.TwitterTemplate">
|
||||
<constructor-arg value="${twitter.oauth.consumerKey}"/>
|
||||
<constructor-arg value="${twitter.oauth.consumerSecret}"/>
|
||||
<constructor-arg value="${twitter.oauth.accessToken}"/>
|
||||
<constructor-arg value="${twitter.oauth.accessTokenSecret}"/>
|
||||
<bean id="twitterTemplate" class="o.s.social.twitter.api.impl.TwitterTemplate">
|
||||
<constructor-arg value="${twitter.oauth.consumerKey}"/>
|
||||
<constructor-arg value="${twitter.oauth.consumerSecret}"/>
|
||||
<constructor-arg value="${twitter.oauth.accessToken}"/>
|
||||
<constructor-arg value="${twitter.oauth.accessTokenSecret}"/>
|
||||
</bean>]]></programlisting>
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<section id="twitter-inbound">
|
||||
<title>Twitter Inbound Adapters</title>
|
||||
<para>
|
||||
Twitter inbound adapters allow you to receive Twitter Messages. There are several types of
|
||||
Twitter inbound adapters allow you to receive Twitter Messages. There are several types of
|
||||
<link linkend="http://support.twitter.com/groups/31-twitter-basics/topics/109-tweets-messages/articles/119138-types-of-tweets-and-where-they-appear">twitter messages, or tweets</link>
|
||||
</para>
|
||||
<para>
|
||||
The current release of Spring Integration provides support for receiving tweets as <emphasis>Timeline Updates</emphasis>,
|
||||
The current release of Spring Integration provides support for receiving tweets as <emphasis>Timeline Updates</emphasis>,
|
||||
<emphasis>Direct Messages</emphasis>, <emphasis>Mention Messages</emphasis> as well as Search Results.
|
||||
</para>
|
||||
<para>
|
||||
Every Inbound Twitter Channel Adapter is a <emphasis>Polling Consumer</emphasis> which means you have to provide a poller
|
||||
configuration. However, there is one important thing you must understand about Twitter since its inner-workings are slightly
|
||||
different than other polling consumers. Twitter defines a concept of Rate Limiting. You can read more about
|
||||
it here: <link linkend="http://dev.twitter.com/pages/rate-limiting">Rate Limiting</link>. In a nutshell, Rate Limiting
|
||||
Every Inbound Twitter Channel Adapter is a <emphasis>Polling Consumer</emphasis> which means you have to provide a poller
|
||||
configuration. However, there is one important thing you must understand about Twitter since its inner-workings are slightly
|
||||
different than other polling consumers. Twitter defines a concept of Rate Limiting. You can read more about
|
||||
it here: <link linkend="http://dev.twitter.com/pages/rate-limiting">Rate Limiting</link>. In a nutshell, Rate Limiting
|
||||
is the way Twitter manages how often an application can poll for updates. You should consider this when setting your
|
||||
poller intervals, but we are also doing a few things to limit excessively aggressive polling within our adapters.
|
||||
poller intervals, but we are also doing a few things to limit excessively aggressive polling within our adapters.
|
||||
</para>
|
||||
<para>
|
||||
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
|
||||
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). Spring Integration provides an elegant mechanism for handling these situations.
|
||||
The latest Tweet timestamp will be stored in an instance of the <classname>org.springframework.integration.store.MetadataStore</classname> which is a
|
||||
The latest Tweet timestamp will be stored in an instance of the <classname>org.springframework.integration.store.MetadataStore</classname> which is a
|
||||
strategy interface designed for storing various types of metadata (e.g., last retrieved tweet in this case). That strategy helps components such as
|
||||
these Twitter adapters avoid duplicates. By default, Spring Integration will look for a bean of type
|
||||
<classname>org.springframework.integration.store.MetadataStore</classname> in the ApplicationContext.
|
||||
If one is found then it will be used, otherwise it will create a new instance of <classname>SimpleMetadataStore</classname>
|
||||
If one is found then it will be used, otherwise it will create a new instance of <classname>SimpleMetadataStore</classname>
|
||||
which is a simple in-memory implementation that will only persist metadata within the lifecycle of the currently running application context.
|
||||
That means upon restart you may end up with duplicate entries. If you need to persist metadata between Application Context
|
||||
That means upon restart you may end up with duplicate entries. If you need to persist metadata between Application Context
|
||||
restarts, you may use the <classname>PropertiesPersistingMetadataStore</classname> (which is backed by a properties file, and a persister
|
||||
strategy), or you may create your own custom implementation of the <classname>MetadataStore</classname> interface (e.g., JdbcMetadatStore)
|
||||
and configure it as a bean named 'metadataStore' within the Application Context.
|
||||
<programlisting language="java"><![CDATA[<bean id="metadataStore" class="org.springframework.integration.store.PropertiesPersistingMetadataStore"/>
|
||||
<programlisting language="xml"><![CDATA[<bean id="metadataStore" class="o.s.i.store.PropertiesPersistingMetadataStore"/>
|
||||
]]></programlisting>
|
||||
The Poller that is configured as part of any Inbound Twitter Adapter (see below) will simply poll from this MetadataStore to determine the latest tweet
|
||||
received.
|
||||
@@ -160,105 +160,105 @@ received.
|
||||
<title>Inbound Message Channel Adapter</title>
|
||||
<para>
|
||||
This adapter allows you to receive updates from everyone you follow. It's essentially the "Timeline Update" adapter.
|
||||
<programlisting language="xml"><![CDATA[<int-twitter:inbound-channel-adapter
|
||||
twitter-template="twitterTemplate"
|
||||
<programlisting language="xml"><![CDATA[<int-twitter:inbound-channel-adapter
|
||||
twitter-template="twitterTemplate"
|
||||
channel="inChannel">
|
||||
<int:poller fixed-rate="5000" max-messages-per-poll="3"/>
|
||||
</int-twitter:inbound-channel-adapter>]]></programlisting>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="inbound-twitter-direct">
|
||||
<title>Direct Inbound Message Channel Adapter</title>
|
||||
<para>
|
||||
This adapter allows you to receive Direct Messages that were sent to you from other Twitter users.
|
||||
<programlisting language="xml"><![CDATA[<int-twitter:dm-inbound-channel-adapter
|
||||
twitter-template="twiterTemplate"
|
||||
<programlisting language="xml"><![CDATA[<int-twitter:dm-inbound-channel-adapter
|
||||
twitter-template="twiterTemplate"
|
||||
channel="inboundDmChannel">
|
||||
<int-poller fixed-rate="5000" max-messages-per-poll="3"/>
|
||||
</int-twitter:dm-inbound-channel-adapter>]]></programlisting>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="inbound-twitter-mention">
|
||||
<title>Mentions Inbound Message Channel Adapter</title>
|
||||
<para>
|
||||
This adapter allows you to receive Twitter Messages that Mention you via @user syntax.
|
||||
<programlisting language="xml"><![CDATA[<int-twitter:mentions-inbound-channel-adapter
|
||||
twitter-template="twiterTemplate"
|
||||
<programlisting language="xml"><![CDATA[<int-twitter:mentions-inbound-channel-adapter
|
||||
twitter-template="twiterTemplate"
|
||||
channel="inboundMentionsChannel">
|
||||
<int:poller fixed-rate="5000" max-messages-per-poll="3"/>
|
||||
</int-twitter:mentions-inbound-channel-adapter>]]></programlisting>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="inbound-twitter-search">
|
||||
<title>Search Inbound Message Channel Adapter</title>
|
||||
<para>
|
||||
This adapter allows you to perform searches. As you can see it is not necessary to define twitter-template
|
||||
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.
|
||||
<programlisting language="xml"><![CDATA[<int-twitter:search-inbound-channel-adapter
|
||||
query="#springintegration"
|
||||
<programlisting language="xml"><![CDATA[<int-twitter:search-inbound-channel-adapter
|
||||
query="#springintegration"
|
||||
channel="inboundMentionsChannel">
|
||||
<int:poller fixed-rate="5000" max-messages-per-poll="3"/>
|
||||
</int-twitter:search-inbound-channel-adapter>]]></programlisting>
|
||||
|
||||
|
||||
</para>
|
||||
<para>
|
||||
Here is a link that will help you learn more about Twitter queries: http://search.twitter.com/operators
|
||||
</para>
|
||||
</section>
|
||||
<para>
|
||||
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 <code>twitter-template</code>. Once received each Twitter Message would be
|
||||
encapsulated in a Spring Integration Message and sent to the channel specified by the <code>channel</code> attribute.
|
||||
Currently the Payload type of any Message is <classname>org.springframework.integration.twitter.core.Tweet</classname>
|
||||
which is very similar to the object with the same name in Spring Social. As we migrate to Spring Social
|
||||
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 <code>twitter-template</code>. Once received each Twitter Message would be
|
||||
encapsulated in a Spring Integration Message and sent to the channel specified by the <code>channel</code> attribute.
|
||||
Currently the Payload type of any Message is <classname>org.springframework.integration.twitter.core.Tweet</classname>
|
||||
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.
|
||||
</para>
|
||||
<para>
|
||||
To get the text from the <classname>org.springframework.social.twitter.api.Tweet</classname>
|
||||
simply invoke the <code>getText()</code> method.
|
||||
simply invoke the <code>getText()</code> method.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="twitter-outbound">
|
||||
<title>Twitter Outbound Adapter</title>
|
||||
<para>
|
||||
Twitter outbound channel adapters allow you to send Twitter Messages, or tweets.
|
||||
</para>
|
||||
<para>
|
||||
The current release of Spring Integration supports sending <emphasis>Status Update Messages</emphasis> and <emphasis>Direct Messages</emphasis>.
|
||||
The current release of Spring Integration supports sending <emphasis>Status Update Messages</emphasis> and <emphasis>Direct Messages</emphasis>.
|
||||
Twitter outbound channel adapters will take the Message payload and send it as a Twitter message. Currently the only supported payload type is
|
||||
<classname>String</classname>, so consider adding a <emphasis>transformer</emphasis> if the payload of the incoming message is not a String.
|
||||
<classname>String</classname>, so consider adding a <emphasis>transformer</emphasis> if the payload of the incoming message is not a String.
|
||||
</para>
|
||||
|
||||
|
||||
<section id="outbound-twitter-update">
|
||||
<title>Twitter Outbound Update Channel Adapter</title>
|
||||
<para>
|
||||
This adapter allows you to send regular status updates by simply sending a Message to the channel
|
||||
identified by the <code>channel</code> attribute.
|
||||
<programlisting language="xml"><![CDATA[<int-twitter:outbound-channel-adapter
|
||||
twitter-template="twitterTemplate"
|
||||
This adapter allows you to send regular status updates by simply sending a Message to the channel
|
||||
identified by the <code>channel</code> attribute.
|
||||
<programlisting language="xml"><![CDATA[<int-twitter:outbound-channel-adapter
|
||||
twitter-template="twitterTemplate"
|
||||
channel="twitterChannel"/>]]></programlisting>
|
||||
The only extra configuration that is required for this adapter is the <code>twitter-template</code> reference.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="outbound-twitter-direct">
|
||||
<title>Twitter Outbound Direct Message Channel Adapter</title>
|
||||
<para>
|
||||
This adapter allows you to send Direct Twitter Messages (i.e., @user) by simply sending a Message to the channel
|
||||
identified by the <code>channel</code> attribute.
|
||||
<programlisting language="xml"><![CDATA[<int-twitter:dm-outbound-channel-adapter
|
||||
twitter-template="twitterTemplate"
|
||||
This adapter allows you to send Direct Twitter Messages (i.e., @user) by simply sending a Message to the channel
|
||||
identified by the <code>channel</code> attribute.
|
||||
<programlisting language="xml"><![CDATA[<int-twitter:dm-outbound-channel-adapter
|
||||
twitter-template="twitterTemplate"
|
||||
channel="twitterChannel"/>]]></programlisting>
|
||||
The only extra configuration that is required for this adapter is the <code>twitter-template</code> reference.
|
||||
</para>
|
||||
<para>
|
||||
When it comes to Twitter Direct Messages, you must specify who you are sending the message to - the <emphasis>target userid</emphasis>.
|
||||
When it comes to Twitter Direct Messages, you must specify who you are sending the message to - the <emphasis>target userid</emphasis>.
|
||||
The Twitter Outbound Direct Message Channel Adapter will look for a target userid in the Message headers under the name
|
||||
<code>twitter_dmTargetUserId</code> which is also identified by the following constant: <classname>TwitterHeaders.DM_TARGET_USER_ID</classname>.
|
||||
So when creating a Message all you need to do is add a value for that header.
|
||||
@@ -270,20 +270,21 @@ received.
|
||||
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>.
|
||||
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="in" output-channel="out">
|
||||
<int:header name="twitter_dmTargetUserId" value="z_oleg"/>
|
||||
<int:header name="twitter_dmTargetUserId" value="z_oleg"/>
|
||||
</int:header-enricher>]]></programlisting>
|
||||
</para>
|
||||
|
||||
<para>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>.
|
||||
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="in" output-channel="out">
|
||||
<int:header name="twitter_dmTargetUserId" expression="@twitterIdService.lookup(headers.username)"/>
|
||||
<int:header name="twitter_dmTargetUserId"
|
||||
expression="@twitterIdService.lookup(headers.username)"/>
|
||||
</int:header-enricher>]]></programlisting>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<para>
|
||||
<important>Twitter does not allow you to post duplicate Messages. This is a common problem during testing when
|
||||
<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>
|
||||
|
||||
@@ -136,9 +136,9 @@ as per standard Spring Web Services configuration.
|
||||
For URIs with an <emphasis>http:</emphasis> (or <emphasis>https:</emphasis>) scheme,
|
||||
<uri-variable/> substitution is supported:
|
||||
</para>
|
||||
<programlisting><![CDATA[<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"/>
|
||||
<programlisting language="xml"><![CDATA[<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"/>
|
||||
</ws:outbound-gateway>]]></programlisting>
|
||||
<para>
|
||||
For other schemes, such as <emphasis>jms:</emphasis>, or if a <classname>DestinationProvider</classname>
|
||||
|
||||
@@ -110,12 +110,12 @@
|
||||
Here is an overview of all available configuration parameters of the
|
||||
<code>xpath-expression</code> element:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<int-xml:xpath-expression expression="" ]]><co id="xpath-expression-xml01-co" linkends="xpath-expression-xml01" /><![CDATA[
|
||||
id="" ]]><co id="xpath-expression-xml02-co" linkends="xpath-expression-xml02" /><![CDATA[
|
||||
namespace-map="" ]]><co id="xpath-expression-xml03-co" linkends="xpath-expression-xml03" /><![CDATA[
|
||||
ns-prefix="" ]]><co id="xpath-expression-xml04-co" linkends="xpath-expression-xml04" /><![CDATA[
|
||||
ns-uri=""> ]]><co id="xpath-expression-xml05-co" linkends="xpath-expression-xml05" /><![CDATA[
|
||||
<map></map> ]]><co id="xpath-expression-xml06-co" linkends="xpath-expression-xml06" /><![CDATA[
|
||||
<programlisting language="xml"><![CDATA[<int-xml:xpath-expression expression=""]]><co id="xpath-expression-xml01-co" linkends="xpath-expression-xml01" /><![CDATA[
|
||||
id="" ]]><co id="xpath-expression-xml02-co" linkends="xpath-expression-xml02" /><![CDATA[
|
||||
namespace-map="" ]]><co id="xpath-expression-xml03-co" linkends="xpath-expression-xml03" /><![CDATA[
|
||||
ns-prefix="" ]]><co id="xpath-expression-xml04-co" linkends="xpath-expression-xml04" /><![CDATA[
|
||||
ns-uri=""> ]]><co id="xpath-expression-xml05-co" linkends="xpath-expression-xml05" /><![CDATA[
|
||||
<map></map> ]]><co id="xpath-expression-xml06-co" linkends="xpath-expression-xml06" /><![CDATA[
|
||||
</int-xml:xpath-expression>]]></programlisting>
|
||||
<para>
|
||||
<calloutlist>
|
||||
|
||||
@@ -27,46 +27,44 @@
|
||||
</ulink>
|
||||
</para>
|
||||
<para>
|
||||
Spring integration provides support for XMPP via XMPP adapters which support sending and receiving both XMPP chat messages and
|
||||
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. As with other adapters, the XMPP adapters come with support for a
|
||||
convenient namespace-based configuration.
|
||||
convenient namespace-based configuration.
|
||||
To configure the XMPP namespace, include the following elements in the headers of your XML configuration file:
|
||||
|
||||
|
||||
<programlisting language="xml"><![CDATA[xmlns:int-xmpp="http://www.springframework.org/schema/integration/xmpp"
|
||||
xsi:schemaLocation="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"]]></programlisting>
|
||||
|
||||
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="xmpp-connection">
|
||||
<title>XMPP Connection</title>
|
||||
<para>
|
||||
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 -
|
||||
connection object could be shared by all XMPP adapters connected to a particular account. Typically this requires - at a minimum -
|
||||
<code>user</code>, <code>password</code>, and <code>host</code>.
|
||||
|
||||
To create a basic XMPP connection, you can utilize the convenience of the namespace.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<int-xmpp:xmpp-connection
|
||||
id="myConnection"
|
||||
user="user"
|
||||
password="password"
|
||||
host="host"
|
||||
port="port"
|
||||
resource="theNameOfTheResource"
|
||||
subscription-mode="accept_all"/>]]></programlisting>
|
||||
|
||||
<programlisting lang="xml"><![CDATA[<int-xmpp:xmpp-connection
|
||||
id="myConnection"
|
||||
user="user"
|
||||
password="password"
|
||||
host="host"
|
||||
port="port"
|
||||
resource="theNameOfTheResource"
|
||||
subscription-mode="accept_all"/>
|
||||
]]></programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<note>
|
||||
For added convenience you can rely on the default naming convention and omit the <code>id</code> attribute.
|
||||
For added convenience you can rely on the default naming convention and omit the <code>id</code> attribute.
|
||||
The default name <emphasis>xmppConnection</emphasis> will be used for this connection bean.
|
||||
</note>
|
||||
</para>
|
||||
<para>
|
||||
If the XMPP Connection goes stale, reconnection attempts will be made with an automatic login as long as the previous connection
|
||||
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 <classname>ConnectionListener</classname> which will log connection events
|
||||
if the DEBUG logging level is enabled.
|
||||
</para>
|
||||
@@ -84,55 +82,55 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/xmpp
|
||||
<classname>java.lang.String</classname> if you set the <code>extract-payload</code> attribute's value to 'true'
|
||||
when configuring an adapter.
|
||||
Configuration support for the XMPP <emphasis>Inbound Message Channel Adapter</emphasis> is provided via the <code>inbound-channel-adapter</code> element.
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-xmpp:inbound-channel-adapter id="xmppInboundAdapter"
|
||||
channel="xmppInbound"
|
||||
xmpp-connection="testConnection"
|
||||
extract-payload="false"
|
||||
auto-startup="true"/>]]></programlisting>
|
||||
|
||||
As you can see amongst the usual attributes this adapter also requires a reference to an XMPP Connection.
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-xmpp:inbound-channel-adapter id="xmppInboundAdapter"
|
||||
channel="xmppInbound"
|
||||
xmpp-connection="testConnection"
|
||||
extract-payload="false"
|
||||
auto-startup="true"/>]]></programlisting>
|
||||
|
||||
As you can see amongst the usual attributes this adapter also requires a reference to an XMPP Connection.
|
||||
</para>
|
||||
<para>
|
||||
It is also important to mention that the XMPP inbound adapter is an <emphasis>event driven adapter</emphasis>
|
||||
and a <classname>Lifecycle</classname> implementation.
|
||||
and a <classname>Lifecycle</classname> implementation.
|
||||
When started it will register a <classname>PacketListener</classname> 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 <classname>channel</classname>. It will unregister the <classname>PacketListener</classname>
|
||||
when it is stopped.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
<section id="xmpp-message-outbound-channel-adapter">
|
||||
<title>Outbound Message Channel Adapter</title>
|
||||
|
||||
|
||||
<para>
|
||||
You may also send chat messages to other users on XMPP using the <emphasis>Outbound Message Channel Adapter</emphasis>.
|
||||
You may also send chat messages to other users on XMPP using the <emphasis>Outbound Message Channel Adapter</emphasis>.
|
||||
Configuration support for the XMPP <emphasis>Outbound Message Channel Adapter</emphasis> is provided via the <code>outbound-channel-adapter</code> element.
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-xmpp:outbound-channel-adapter id="outboundEventAdapter"
|
||||
channel="outboundEventChannel"
|
||||
xmpp-connection="testConnection"/>]]></programlisting>
|
||||
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-xmpp:outbound-channel-adapter id="outboundEventAdapter"
|
||||
channel="outboundEventChannel"
|
||||
xmpp-connection="testConnection"/>]]></programlisting>
|
||||
|
||||
The adapter expects as its input - at a minimum - a payload of type <classname>java.lang.String</classname>, and a header value
|
||||
for <classname>XmppHeaders.CHAT_TO</classname> that specifies to which user the Message should be sent.
|
||||
To create a message you might use the following Java code:
|
||||
|
||||
|
||||
<programlisting language="java"><![CDATA[Message<String> xmppOutboundMsg = MessageBuilder.withPayload("Hello, XMPP!" )
|
||||
.setHeader(XmppHeaders.CHAT_TO, "userhandle")
|
||||
.build();]]></programlisting>
|
||||
|
||||
Another mechanism of setting the header is by using the XMPP header-enricher support. Here is an example.
|
||||
|
||||
.build();]]></programlisting>
|
||||
|
||||
Another mechanism of setting the header is by using the XMPP header-enricher support. Here is an example.
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-xmpp:header-enricher input-channel="input" output-channel="output">
|
||||
<int-xmpp:chat-to value="test1@example.org"/>
|
||||
</int-xmpp:header-enricher>]]></programlisting>
|
||||
</int-xmpp:header-enricher>]]></programlisting>
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
<section id="xmpp-presence">
|
||||
<title>XMPP Presence</title>
|
||||
|
||||
@@ -142,52 +140,52 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/xmpp
|
||||
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.
|
||||
</para>
|
||||
|
||||
</para>
|
||||
|
||||
<section id="xmpp-roster-inbound-channel-adapter">
|
||||
<title>Inbound Presence Message Channel Adapter</title>
|
||||
<para>
|
||||
Spring Integration provides an <emphasis>Inbound Presence Message Channel Adapter</emphasis> 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 <classname>RosterListener</classname> and forwards received Presence update events as Messages to the channel
|
||||
Spring Integration provides an <emphasis>Inbound Presence Message Channel Adapter</emphasis> 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 <classname>RosterListener</classname> and forwards received Presence update events as Messages to the channel
|
||||
identified by the <code>channel</code> attribute. The payload of the Message will be a <classname>org.jivesoftware.smack.packet.Presence</classname>
|
||||
object (see http://www.igniterealtime.org/builds/smack/docs/3.1.0/javadoc/org/jivesoftware/smack/packet/Presence.html).
|
||||
</para>
|
||||
<para>
|
||||
Configuration support for the XMPP <emphasis>Inbound Presence Message Channel Adapter</emphasis> is provided via
|
||||
Configuration support for the XMPP <emphasis>Inbound Presence Message Channel Adapter</emphasis> is provided via
|
||||
the <code>presence-inbound-channel-adapter</code> element.
|
||||
<programlisting language="xml"><![CDATA[<int-xmpp:presence-inbound-channel-adapter channel="outChannel"
|
||||
xmpp-connection="testConnection" auto-startup="false"/>]]></programlisting>
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-xmpp:presence-inbound-channel-adapter channel="outChannel"
|
||||
xmpp-connection="testConnection" auto-startup="false"/>]]></programlisting>
|
||||
|
||||
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 <classname>Lifecycle</classname> implementation.
|
||||
It will register a <classname>RosterListener</classname> when started and will unregister that <classname>RosterListener</classname>
|
||||
It is also important to mention that this adapter is an event driven adapter and a <classname>Lifecycle</classname> implementation.
|
||||
It will register a <classname>RosterListener</classname> when started and will unregister that <classname>RosterListener</classname>
|
||||
when stopped.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="xmpp-roster-outbound-channel-adapter">
|
||||
<title>Outbound Presence Message Channel Adapter</title>
|
||||
|
||||
|
||||
<para>
|
||||
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 <emphasis>Outbound Presence Message Channel Adapter</emphasis> it extracts the payload,
|
||||
which is expected to be of type <classname>org.jivesoftware.smack.packet.Presence</classname>
|
||||
(see http://www.igniterealtime.org/builds/smack/docs/3.1.0/javadoc/org/jivesoftware/smack/packet/Presence.html) and sends it to
|
||||
(see http://www.igniterealtime.org/builds/smack/docs/3.1.0/javadoc/org/jivesoftware/smack/packet/Presence.html) and sends it to
|
||||
the XMPP Connection, thus advertising your presence events to the rest of the network.
|
||||
</para>
|
||||
<para>
|
||||
Configuration support for the XMPP <emphasis>Outbound Presence Message Channel Adapter</emphasis> is provided via
|
||||
Configuration support for the XMPP <emphasis>Outbound Presence Message Channel Adapter</emphasis> is provided via
|
||||
the <code>presence-outbound-channel-adapter</code> element.
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-xmpp:presence-outbound-channel-adapter id="eventOutboundPresenceChannel"
|
||||
xmpp-connection="testConnection"/>]]></programlisting>
|
||||
|
||||
It can also be a <emphasis>Polling Consumer</emphasis> (if it receives Messages from a Pollable Channel) in which case you would
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-xmpp:presence-outbound-channel-adapter id="eventOutboundPresenceChannel"
|
||||
xmpp-connection="testConnection"/>]]></programlisting>
|
||||
|
||||
It can also be a <emphasis>Polling Consumer</emphasis> (if it receives Messages from a Pollable Channel) in which case you would
|
||||
need to register a Poller.
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-xmpp:presence-outbound-channel-adapter id="pollingOutboundPresenceAdapter"
|
||||
xmpp-connection="testConnection"
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int-xmpp:presence-outbound-channel-adapter id="pollingOutboundPresenceAdapter"
|
||||
xmpp-connection="testConnection"
|
||||
channel="pollingChannel">
|
||||
<int:poller fixed-rate="1000" max-messages-per-poll="1"/>
|
||||
</int-xmpp:presence-outbound-channel-adapter>]]></programlisting>
|
||||
@@ -197,7 +195,7 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/xmpp
|
||||
</section>
|
||||
<para>
|
||||
<note>
|
||||
If you are relying on the default naming convention for an XMPP Connection bean (described earlier), and you have only one
|
||||
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 <code>xmpp-connection</code> attribute.
|
||||
In that case, the bean with the name <emphasis>xmppConnection</emphasis> will be located and injected into the adapter.
|
||||
</note>
|
||||
@@ -205,48 +203,48 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/xmpp
|
||||
</section>
|
||||
<section id="xmpp-appendices">
|
||||
<title>Appendices</title>
|
||||
|
||||
|
||||
<para>
|
||||
Since Spring Integration XMPP support is based on the Smack 3.1 API (http://www.igniterealtime.org/downloads/index.jsp), it is important
|
||||
Since Spring Integration XMPP support is based on the Smack 3.1 API (http://www.igniterealtime.org/downloads/index.jsp), it is important
|
||||
to know a few details related to more complex configuration of the XMPP Connection object.
|
||||
</para>
|
||||
<para>
|
||||
As stated earlier the <code>xmpp-connection</code> namespace support is designed to simplify basic connection configuration and
|
||||
As stated earlier the <code>xmpp-connection</code> namespace support is designed to simplify basic connection configuration and
|
||||
only supports a few common configuration attributes. However, the <classname>org.jivesoftware.smack.ConnectionConfiguration</classname>
|
||||
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 <classname>XmppConnectionFactoryBean</classname> as a regular bean, and inject a
|
||||
<classname>org.jivesoftware.smack.ConnectionConfiguration</classname> as a constructor argument to that FactoryBean. Every property
|
||||
<classname>org.jivesoftware.smack.ConnectionConfiguration</classname> 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:
|
||||
|
||||
<programlisting language="xml"><![CDATA[<bean id="xmppConnection" class="org.springframework.integration.xmpp.XmppConnectionFactoryBean">
|
||||
<constructor-arg>
|
||||
<bean class="org.jivesoftware.smack.ConnectionConfiguration">
|
||||
<constructor-arg value="myServiceName"/>
|
||||
<property name="truststorePath" value="..."/>
|
||||
<property name="socketFactory" ref="..."/>
|
||||
</bean>
|
||||
</constructor-arg>
|
||||
|
||||
<programlisting language="xml"><![CDATA[<bean id="xmppConnection" class="o.s.i.xmpp.XmppConnectionFactoryBean">
|
||||
<constructor-arg>
|
||||
<bean class="org.jivesoftware.smack.ConnectionConfiguration">
|
||||
<constructor-arg value="myServiceName"/>
|
||||
<property name="truststorePath" value="..."/>
|
||||
<property name="socketFactory" ref="..."/>
|
||||
</bean>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
<int:channel id="outboundEventChannel"/>
|
||||
|
||||
<int-xmpp:outbound-channel-adapter id="outboundEventAdapter"
|
||||
channel="outboundEventChannel"
|
||||
xmpp-connection="xmppConnection"/>]]></programlisting>
|
||||
|
||||
<int-xmpp:outbound-channel-adapter id="outboundEventAdapter"
|
||||
channel="outboundEventChannel"
|
||||
xmpp-connection="xmppConnection"/>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
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 <classname>SASLAuthentication</classname>, 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
|
||||
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 <classname>SASLAuthentication</classname>, 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.
|
||||
|
||||
|
||||
<programlisting language="java"><![CDATA[@Configuration
|
||||
public class CustomConnectionConfiguration {
|
||||
@Bean
|
||||
public XMPPConnection xmppConnection() {
|
||||
SASLAuthentication.supportSASLMechanism("EXTERNAL", 0); // static initializer
|
||||
|
||||
|
||||
ConnectionConfiguration config = new ConnectionConfiguration("localhost", 5223);
|
||||
config.setTrustorePath("path_to_truststore.jks");
|
||||
config.setSecurityEnabled(true);
|
||||
|
||||
Reference in New Issue
Block a user