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:
Gunnar Hillert
2013-01-15 17:46:21 -05:00
committed by Gary Russell
parent 35365990f9
commit 06831b9e22
39 changed files with 1666 additions and 1741 deletions

View File

@@ -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'
}
}

View File

@@ -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>&lt;aggregator/&gt;</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

View File

@@ -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 &lt;bridge&gt; 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"/>

View File

@@ -58,13 +58,13 @@
The &lt;chain&gt; 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 &lt;header-enricher&gt; element used in the above example will set a message header named "foo" with a value
@@ -75,14 +75,14 @@
<para>
The &lt;chain&gt; 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 &lt;chain&gt; some &lt;outbound-channel-adapter&gt;:
<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 &lt;gateway&gt; 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

View File

@@ -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>&lt;outbound-channel-adapter&gt;</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>

View File

@@ -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 &lt;interceptors/&gt; 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

View File

@@ -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
&lt;service-activator/&gt; 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
&lt;service-activator/&gt; 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 &lt;service-activator/&gt; 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.

View File

@@ -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'

View File

@@ -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>&lt;advice-chain&gt;</code>.
A sample configuration of the <code>&lt;delayer&gt;</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"/>.

View File

@@ -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 &lt;poller/&gt; 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>

View File

@@ -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 &lt;int-file:inbound-channel-adapter/&gt; 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

View File

@@ -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 &lt;filter&gt; 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 &lt;expression&gt; 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>

View File

@@ -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&amp;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&amp;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).

View File

@@ -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>&lt;gateway/&gt;</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>&lt;gateway/&gt;</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>

View File

@@ -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.

View File

@@ -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 &lt;request-handler-advice-chain /&gt; 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 &lt;service-activator />&gt; 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>

View File

@@ -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&amp;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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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 &lt;message-driven-channel-adapter&gt; 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 &lt;channel/&gt; 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">

View File

@@ -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 &lt;util:list/&gt; 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>

View File

@@ -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

View File

@@ -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
&lt;poller&gt; 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 &lt;imap-idle-channel-adapter/&gt; 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 &lt;imap-idle-channel-adapter/&gt; 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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>&lt;publish-subscribe-channel/&gt;</code> element from the
The publish-subscribe-channel (above) will behave much like a normal <code>&lt;publish-subscribe-channel/&gt;</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

View File

@@ -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>

View File

@@ -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 &lt;service-activator&gt; 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 &lt;gateway&gt; 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>

View File

@@ -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">&lt;int:service-activator input-channel="exampleChannel" output-channel="replyChannel"
ref="somePojo" method="someMethod"/&gt;</programlisting>
ref="somePojo" method="someMethod"/&gt;</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>

View File

@@ -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>

View File

@@ -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&lt;?&gt; 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

View File

@@ -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>

View File

@@ -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 &lt;bean/&gt;, 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>

View File

@@ -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 &lt;filter&gt; 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.

View File

@@ -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 &amp; 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 &amp; 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 &lt;header-enricher&gt;.
<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 &lt;header-enricher&gt;.
<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>

View File

@@ -136,9 +136,9 @@ as per standard Spring Web Services configuration.
For URIs with an <emphasis>http:</emphasis> (or <emphasis>https:</emphasis>) scheme,
&lt;uri-variable/&gt; 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>

View File

@@ -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>

View File

@@ -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);