Merge branch 'master' into buildSrc-extraction

Conflicts:
	spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/Twitter4jTemplate.java
This commit is contained in:
Chris Beams
2010-11-11 10:13:14 -08:00
104 changed files with 2458 additions and 1470 deletions

View File

@@ -584,6 +584,49 @@ public Message<?> receive(final PollableChannel<?> channel) { ... }]]></programl
<emphasis>true</emphasis> enables logging of all headers in addition to the payload.
</tip>
</para>
<para>
<emphasis>A little more on Wite Tap</emphasis>
</para>
<para>
One of the common misconception about the wire tap and some time other similar components (<xref linkend="message-publishing-config"/>)
that they are asynchronous in nature. Wire-tap as a component is neither <emphasis>sync</emphasis> nor <emphasis>async</emphasis>.
In fact non of the components in SI are <emphasis>sync</emphasis> or <emphasis>async</emphasis> except for. . . well read on.
What makes certain parts of the message flow <emphasis>sync</emphasis> or <emphasis>async</emphasis> is the <emphasis>Message Channel</emphasis>
abstraction. That is why from the inception of the framework we always emphasize the need and the value of the <emphasis>Message Channel</emphasis>
and that is why Spring Integration is the only framework at the time of writing where <emphasis>Message Channel</emphasis>
is a "first class citizen" of the framework (not an internal realization of EIP pattern) fulle exposed to you - the end user.
So, Wire-tap component is ONLY responsible to perform the following 3 tasks:
<itemizedlist>
<listitem>
<para>wire-tap into a message flow by tapping into a channel (e.g., channelA)</para>
</listitem>
<listitem>
<para>grab a copy of a message</para>
</listitem>
<listitem>
<para>send it to another channel (e.g., channelB)</para>
</listitem>
</itemizedlist>
Look at it as a variation of the Bridge (nothing more). But by bridging one channel with another wire-tap is essentially
initiates (forks) another message flow. Is this flow <emphasis>synchronous</emphasis> or <emphasis>asynchronous</emphasis>?
That is the ultimate question and the answer simply depends on the type of <emphasis>Message Channel</emphasis> 'channelB' is.
And as you know we have: <emphasis>Direct Channel</emphasis>, <emphasis>Pollable Channel</emphasis> and <emphasis>Executor Channel</emphasis>.
The last two do break the thread boundary making communication via such channels <emphasis>asynchronous</emphasis> simply because
the dispatching of the message from the channel happens on the different thread then the one that sent the message to that channel
and that is what is going to make your wire-tap flow <emphasis>sync</emphasis> or <emphasis>async</emphasis>.
It is consistent with other components within the framework (e.g., Message Publisher) and if you think about it its in a way
brings a level of simplicity by sparing you form worrying in advance (other then writing thread safe code) wether a
particular piece of code should be implemented as <emphasis>sync</emphasis> or <emphasis>async</emphasis>. In fact its always neither,
the code is just a function. The actual wiring of two pieces of code (component A and component B) via <emphasis>Message Channel</emphasis>
is what's going to make their collaboration <emphasis>sync</emphasis> or <emphasis>async</emphasis>. You may even want to change
from <emphasis>sync</emphasis> to <emphasis>async</emphasis> in the future and <emphasis>Message Channel</emphasis> is what's going
to allow you to do it swiftly without ever touching the code
</para>
</section>
<note>

View File

@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<section xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="claim-check"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Claim Check</title>
<section id="claim-check-introduction">
<title>Introduction</title>
<para>
In the earlier sections we've covered several Content Enricher type components that helps you deal with situations where a
message is missing a piece of data. We also discussed Content Filtering which lets you remove uninteresting data items from a message.
However there are times when we want to remove some data temporarily. For example; In a distributed system we may receive a
Message with a very large payload. Some intermittent message processing steps may not need access to this payload and some may only
need to access parts of the payload so carrying the large Message through each processing step may cause performance degradation
and makes debugging harder.
</para>
<para>
<link href="http://www.eaipatterns.com/StoreInLibrary.html">Claim Check</link> pattern describes mechanism that allows you
to store data in a well known place while only maintaining a pointer (Claim Check) to where that data is and pass such
pointer around as a payload of a new Message allowing any component within the message flow to get the actual data as soon as
it needs it. This approach is very similar to the Certified Mail process where you'll get Claim Check in your mailbox and
would have to go to the Post Office to claim your actual package or mail.
</para>
<para>
Spring Integration provides two types of Claim Check transformers - <emphasis>Incoming Claim Check Transformer</emphasis> and
<emphasis>Outgoing Claim Check Transformer</emphasis> as well as convenient namespace-based mechanism to configure them.
</para>
<section id="claim-check-in">
<title>Incoming Claim Check Transformer</title>
<para>
<emphasis>Incoming Claim Check Transformer</emphasis> - will transform incoming Message by storing it in the Message Store
identified by <code>message-store</code> attribute.
<programlisting language="xml"><![CDATA[<int:claim-check-in id="checkin"
input-channel="checkinChannel"
message-store="testMessageStore"
output-channel="checkoutChannel"/>]]></programlisting>
In the above configuration the Message that is received on the <code>input-channel</code> will be persisted to the
Message Store identified with <code>message-store</code> attribute and indexed with generated ID. That ID is the Claim Check for that Message.
This Claim Check will also become the payload of the new (transformed) Message that will be sent to the <code>output-channel</code>.
</para>
<para>
Now, lets assume that at some point you do need access to the actual Message. You can of course access the Message Store
manually and get the contents of the Message or you can use the same approach as before except now you will be transforming
the Claim Check to the actual Message by using <emphasis>Outgoing Claim Check Transformer</emphasis>.
</para>
</section>
<section id="claim-check-out">
<title>Outgoing Claim Check Transformer</title>
<para>
<emphasis>Incoming Claim Check Transformer</emphasis> allows you to transform a Message from the Message with just a Claim Check
to the Message with the original content.
<programlisting language="xml"><![CDATA[<claim-check-out id="checkout"
input-channel="checkoutChannel"
message-store="testMessageStore"/>]]></programlisting>
In the above configuration the Message that is received on the <code>input-channel</code> has a Claim Check as a payload
and <emphasis>Outgoing Claim Check Transformer</emphasis> will transform it into an original Message by simply querying the
Message store for a Message identified by a Claim Check provided and sending the new Message to the <code>output-channel</code>.
</para>
</section>
<para>
Although we rarely care about the protocol of the claim checks as long as they work, but it is still worth knowing that current
implementation of the actual Claim Check (the pointer) in Spring Integration is UUID to ensure uniqueness.
</para>
<para>
<emphasis>A word on Message Store</emphasis>
</para>
<para>
<classname>org.springframework.integration.store.MessageStore</classname> is a strategy interface for storing and retrieving messages.
Spring Integration provides two convenient implementations of it. <classname>SimpleMessageStore</classname> - In memory Map-based
implementation (default, good for testing) and <classname>JdbcMessageStore</classname> - Implementation of <classname>MessageStore</classname>
that uses relational database via JDBC.
</para>
</section>
</section>

View File

@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<section xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="content-enricher"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Content Enricher</title>
<section id="content-enricher-introduction">
<title>Introduction</title>
<para>
Some time you may have a requirement to enhance a request with more information then it was
provided by the target system. <link href="http://www.eaipatterns.com/DataEnricher.html">Content Enricher</link> pattern
describes various scenarios as well as the component (Enricher), which allows you to address such requirements.
</para>
</section>
<section id="header-enricher">
<title>Header Enricher</title>
<para>
If you only need to add headers to a Message, and they are not dynamically determined by the Message content,
then referencing a custom implementation of the Transformer may be an overkill. For that reason,
Spring Integration provides the <emphasis>Header Enricher</emphasis> which is exposed via <code>&lt;header-enricher&gt;</code> element.
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="in" output-channel="out">
<int:header name="foo" value="123"/>
<int:header name="bar" ref="someBean"/>
</int:header-enricher>]]></programlisting>
</para>
<para>
<emphasis>Header Enricher</emphasis> also provides helpful sub-elements to set well known header names.
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="in" output-channel="out">
<int:error-channel ref="applicationErrorChannel"/>
<int:reply-channel ref="quoteReplyChannel"/>
<int:correlation-id value="123"/>
<int:priority value="HIGHEST"/>
<int:header name="bar" ref="someBean"/>
</int:header-enricher>]]></programlisting>
In the above configuration you can clearly see that for well known headers such as <code>errorChannel</code>,
<code>correlationId</code>, <code>priority</code>, <code>replyChannel</code>etc., instead of using generic <emphasis>&lt;header&gt;</emphasis>
sub-element where you would have to provide both header 'name' and 'value', you can use convenient sub-elements
allowing you to set those values directly.
</para>
<para>
<emphasis>SpEL Support</emphasis>
</para>
<para>
In Spring Integration 2.0 we are introducing convenience of
<link href="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html">Spring Expression Language (SpEL)</link>
to help configure many different components. <emphasis>Header Enricher</emphasis> is one of them.
A lot of times, header value cannot be defined statically and has to be computed dynamically. That is why
<emphasis>Header Enricher</emphasis> allows you to also specify bean 'ref' and 'method' that will calculate the
header value. Let's look at the following configuration:
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="in" output-channel="out">
<int:header name="foo" method="computeValue" ref="myBean"/>
</int:header-enricher>
<bean id="myBean" class="foo.bar.MyBean"/>]]></programlisting>
<programlisting language="java"><![CDATA[public class MyBean{
public String computeValue(String payload){
return payload.toUpperCase() + "_US";
}
}]]></programlisting>
As you can see that the computation logic to determine the header value is actually pretty simple and the
natural question would be is there a simpler way to accomplish this? And that is where SpEL shows its true power.
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="in" output-channel="out">
<int:header name="foo" expression="payload.toUpperCase() + '_US'"/>
</int:header-enricher>]]></programlisting>
As you can see, with SpEL for simple cases like above we no longer have to provide a separate class and configure
it in the application context. All we need is to use <emphasis>expression</emphasis> attribute and provide a valid
SpEL expression. You can also see that 'payload' and 'headers' are bound as variables to the SpEL Evaluation Context
giving you full access to the incoming Message.
</para>
<para>
<emphasis>Adapter specific Header Enrichers</emphasis>
</para>
<para>
As you go through the manual you will see that as an added convenience
Spring Integration provides adapter specific Header Enrichers (e.g., MAIL, XMPP, etc.)
</para>
</section>
</section>

View File

@@ -3,12 +3,78 @@
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Feed Adapter</title>
<para>
Spring Integration provides support for Feed (RSS, Atom)
Spring Integration provides support for Syndication via Feed Adapters
</para>
<section id="feed-intro">
<title>Introduction</title>
<para>
TODO
As we know Web syndication is a form of syndication where material such as news items, press releases that is
available to any website is also made available via we feeds such as RSS, ATOM etc.
</para>
<para>
Spring integration provides support for Web Syndication via FEED adapter which comes with a convenient
namespace-based configuration.
To configure FEED namespace include the following elements into the headers of your XML configuration file:
<programlisting language="xml"><![CDATA[xmlns:int-feed="http://www.springframework.org/schema/integration/feed"
xsi:schemaLocation="http://www.springframework.org/schema/integration/feed
http://www.springframework.org/schema/integration/feed/spring-integration-feed-2.0.xsd"]]></programlisting>
</para>
</section>
<section>
<title>Feed Inbound Channel Adapter</title>
<para>
The only adapter that is really needed to provide support for retrieving feeds is an <emphasis>inbound channel adapter</emphasis>
which allows you to subscribe to a particular URL. Below is the configuration for such adapter:
<programlisting language="xml"><![CDATA[<int-feed:inbound-channel-adapter id="feedAdapter"
channel="feedChannel"
url="http://feeds.bbci.co.uk/news/rss.xml">
<int:poller fixed-rate="10000" max-messages-per-poll="100" />
</int-feed:inbound-channel-adapter>]]></programlisting>
In the above configuration we are subscribing to a URL identified by <code>url</code> attribute.
</para>
<para>
As news items are retrieved they will be converted to a Message and sent to a channel identified by <code>channel</code> attribute.
The payload of such message will be <classname>com.sun.syndication.feed.synd.SyndEntry</classname> which encapsulates
various data (i.e., content, dates, authors etc.) about a news item.
</para>
<para>
You can also see that <emphasis>Inbound Feed Channel Adapter</emphasis> is a Polling consumer which means you have to
provide a poller configuration. However, one important thing you must understand with regard to Feed sinc its inner-workings
are slightly different then any other poling consumer. When Inbound Feed adapter is started it does the first poll and
receives <classname>com.sun.syndication.feed.synd.SyndEntryyFeed</classname> which is an object that contains multiple
<classname>SyndEntry</classname> objects. Each entry is stored in the local entry queue and is released based on
the value in the <code>max-messages-per-poll</code> attribute where each Message will contain a single entry.
If during retrieval of the entries from the entry queue the queue had become empty the adapter will attempt to update
the Feed populating the queue with more entries (SyndEntry) if available, otherwise the next attempt to poll for a feed will
be determined by the trigger of the poller (e.g., every 10 seconds in the above configuration).
</para>
<para>
<emphasis>Duplicate Entries</emphasis>
</para>
<para>
Polling for a Feed might result in the entries that have already been processed ("I already read that news item, why are you showing it to me again?").
Spring Integration provides a convenient mechanism to eliminate the need to worry about duplicate entries.
Each feed entry will have <emphasis>publish date</emphasis> field. Every time the new Message is generated and sent,
Spring Integration will store the value of the <emphasis>publish date</emphasis> in the instance of the
<classname>org.springframework.integration.store.MetadataStore</classname> which is a strategy interface designed to store various
types of meta-data (e.g., publish date of the last feed entry that has been processed) to help components such as Feed to deal with
duplicates.
</para>
<para>
The default rule for locating this meta-data store is as follows; Spring Integration will look for a bean of type
<classname>org.springframework.integration.store.MetadataStore</classname> in the ApplicationContext. If one 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 meta-data within the life-cycle of the application context. This means that upon restart you may end up with
duplicate entries. If you need to persist meta-data between Application Context restarts, you may use
<classname>PropertiesPersistingMetadataStore</classname> which is a property file based persister or provide your own
implementation of the <classname>MetedataStore</classname> interface (e.g.,JdbcMetadatStore) and configure it as bean in the Application Context.
<programlisting language="xml"><![CDATA[<bean class="org.springframework.integration.store.PropertiesPersistingMetadataStore"/>]]></programlisting>
</para>
</section>
</chapter>

View File

@@ -31,7 +31,7 @@
</section>
<section id="filter-namespace">
<title>The &lt;filter&gt; Element</title>
<title>Namespace support for Filter - &lt;filter&gt; Element</title>
<para>
The &lt;filter&gt; element is used to create a Message-selecting endpoint. In addition to "input-channel"
and "output-channel" attributes, it requires a "ref". The "ref" may point to a MessageSelector implementation:

View File

@@ -140,14 +140,18 @@
the connection factory will not function without one). A reference to a server
connection factory can also be provided to an outbound adapter; that adapter
can then be used to send replies to incoming messages to the same connection.
<tip>Reply messages will only be routed to the connection if the reply contains
the header $ip_connection_id that was inserted into the original message by
the connection factory.</tip>
<tip>This is the extent of message correlation performed when sharing connection
<tip>
<para>Reply messages will only be routed to the connection if the reply contains
the header ip_connection_id that was inserted into the original message by
the connection factory.</para></tip>
<tip>
<para>This is the extent of message correlation performed when sharing connection
factories between inbound and outbound adapters. Such sharing allows for
asynchronous two-way communication over TCP. Only payload information is
transferred using TCP; therefore any message correlation must be performed
by downstream components such as aggregators or other endpoints.</tip>
by downstream components such as aggregators or other endpoints.
For more information refer to
<xref linkend="ip-correlation">TCP Message Correlationn</xref></para></tip>
</para>
<para>
A maximum of one adapter of each type may be given a reference to a connection
@@ -406,7 +410,7 @@
can process a single request/response at a time.
</para>
<para>
The intbound gateway, after constructing a message with the incoming payload and sending
The intbound gateway, after constructing a message with the incoming payload and sending
it to the requestChannel, waits for a response and sends the payload
from the response message by writing it to the connection.
</para>
@@ -416,9 +420,9 @@
Communications over the connections are single-threaded. Users should be aware that only one
message can be handled at a time and, if another thread attempts to send
a message before the current response has been received, it will block until
any previous requests are complete (or time out).
If, however, the client connection factory is configured for single-use connections
each new request gets its own connection and is processed immediately.
any previous requests are complete (or time out).
If, however, the client connection factory is configured for single-use connections
each new request gets its own connection and is processed immediately.
</para>
<para>
<programlisting language="xml"><![CDATA[
@@ -444,6 +448,93 @@
A simple oubound TCP gateway.
</para>
</section>
<section id="ip-correlation">
<title>TCP Message Correlation</title>
<section>
<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
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
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
number).
</para>
</section>
<section>
<title>Gateways</title>
<para>
The gateways will automatically correlate messages. However, an outbound
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"'),
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
('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.
</para>
<para>
Therefore, for high-volume messages, consider using a collaborating pair of
channel adapters. However, you will need to provide collaboration logic.
</para>
</section>
<section>
<title>Collaborating Outbound and Inbound Channel Adapters</title>
<para>
To achieve high-volume throughput (avoiding the pitfalls of using gateways
as mentioned above) you may consider configuring a pair of collaborating
outbound and inbound channel adapters. On the server side, message
correlation is automatically handled by the adapters because the inbound
adapter adds a header allowing the outbound adapter to determine which
connection to use to send the reply message. On the client side, however,
the application will have to provide its own correlation logic. This can
be done in a number of ways.
</para>
<para>
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.
</para>
<para>
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,
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
correlation data, you may need to provide a transformer upstream of the
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
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.
The <classname>ByteArray*Serializer</classname> options
mentioned in <xref linkend="connection-factories">Connection Factories</xref>,
including the default <classname>ByteArrayCrLfSerializer</classname>,
do not support such payloads,
unless the transformed payload is a <classname>String</classname> or
<classname>byte[]</classname>,
</para>
</section>
</section>
<section id="ip-endpoint-reference">
<title>IP Configuration Attributes</title>
<para>
@@ -611,7 +702,7 @@
using nio, specifies the number of tcp fragments that are concurrently
reassembled into complete messages.
It only applies in this sense if task-executor is not configured.
However, pool-size is also used for the server socket backlog,
However, pool-size is also used for the server socket backlog,
regardless of whether an external task executor is used. Defaults to 5.</entry>
</row>
<row>
@@ -830,6 +921,70 @@
</tbody>
</tgroup>
</table>
<table id="tcp-ib-adapter-attributes">
<title>TCP Inbound Channel Adapter Attributes</title>
<tgroup cols="3">
<colspec align="left" />
<colspec colnum="1" colname="col1" colwidth="1*"/>
<colspec colnum="2" colname="col2" colwidth="1*"/>
<colspec colnum="3" colname="col3" colwidth="3*"/>
<thead>
<row>
<entry align="center">Attribute Name</entry>
<entry align="left">Allowed Values</entry>
<entry align="center">Attribute Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>channel</entry>
<entry></entry>
<entry>The channel to which inbound messages will be sent.</entry>
</row>
<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
outbound adapter.</entry>
</row>
</tbody>
</tgroup>
</table>
<table id="tcp-ob-adapter-attributes">
<title>TCP Outbound Channel Adapter Attributes</title>
<tgroup cols="3">
<colspec align="left" />
<colspec colnum="1" colname="col1" colwidth="1*"/>
<colspec colnum="2" colname="col2" colwidth="1*"/>
<colspec colnum="3" colname="col3" colwidth="3*"/>
<thead>
<row>
<entry align="center">Attribute Name</entry>
<entry align="left">Allowed Values</entry>
<entry align="center">Attribute Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>channel</entry>
<entry></entry>
<entry>The channel on which outbound messages arrive.</entry>
</row>
<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
original inbound message was received. </entry>
</row>
</tbody>
</tgroup>
</table>
<table id="tcp-ib-gateway-attributes">
<title>TCP Inbound Gateway Attributes</title>
<tgroup cols="3">
@@ -846,9 +1001,26 @@
</thead>
<tbody>
<row>
<entry>port</entry>
<entry>connection-factory</entry>
<entry></entry>
<entry>The port on which the gateway listens.</entry>
<entry>The connection factory must be of type server. </entry>
</row>
<row>
<entry>request-channel</entry>
<entry></entry>
<entry>The channel to which incoming messages will be sent.</entry>
</row>
<row>
<entry>reply-channel</entry>
<entry></entry>
<entry>The channel on which reply messages may arrive. Usually replies will
arrive on a temporary reply channel added to the inbound message
header</entry>
</row>
<row>
<entry>reply-timeout</entry>
<entry></entry>
<entry>The time in milliseconds for which the gateway will wait for a reply.</entry>
</row>
</tbody>
</tgroup>
@@ -869,9 +1041,32 @@
</thead>
<tbody>
<row>
<entry>host</entry>
<entry>connection-factory</entry>
<entry></entry>
<entry>The host name or ip address of the destination.</entry>
<entry>The connection factory must be of type client. </entry>
</row>
<row>
<entry>request-channel</entry>
<entry></entry>
<entry>The channel on which outgoing messages will arrive.</entry>
</row>
<row>
<entry>reply-channel</entry>
<entry></entry>
<entry>Optional. The channel to which reply messages may be sent if the
original outbound message did not contain a reply channel header.
</entry>
</row>
<row>
<entry>reply-timeout</entry>
<entry></entry>
<entry>The time in milliseconds for which the gateway will wait for a reply.</entry>
</row>
<row>
<entry>request-timeout</entry>
<entry></entry>
<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>
</tbody>
</tgroup>

View File

@@ -5,5 +5,6 @@
<title>Message Transformation</title>
<xi:include href="./transformer.xml"/>
<xi:include href="./content-enrichment.xml"/>
<xi:include href="./claim-check.xml"/>
</chapter>

View File

@@ -28,7 +28,7 @@
</section>
<section id="transformer-namespace">
<title>The &lt;transformer&gt; Element</title>
<title>Namespace support for Transformer - &lt;transformer&gt; Element</title>
<para>
The &lt;transformer&gt; element is used to create a Message-transforming endpoint. In addition to "input-channel"
and "output-channel" attributes, it requires a "ref". The "ref" may either point to an Object that contains the
@@ -88,13 +88,8 @@
<payload-deserializing-transformer input-channel="bytesIn" output-channel="objectsOut"/>]]></programlisting>
</para>
<para>
If you only need to add headers to a Message, and they are not dynamically determined by Message content,
then referencing a custom implementation may be overkill. For that reason, Spring Integration provides the
'header-enricher' element. <programlisting language="xml"><![CDATA[ <header-enricher input-channel="in" output-channel="out">
<header name="foo" value="123"/>
<header name="bar" ref="someBean"/>
</header-enricher>]]></programlisting>
<para>
<emphasis>Object-to-Map Transformer</emphasis>
</para>
<para>
As added convenience, Spring Integration also provides <emphasis>Object-to-Map</emphasis> and <emphasis>Map-to-Object</emphasis> transformers which
@@ -161,7 +156,7 @@ public class Kid{
</section>
<section id="transformer-annotation">
<title>The @Transformer Annotation</title>
<title>Annotation support for Transformer - @Transformer</title>
<para>
The <interfacename>@Transformer</interfacename> annotation can also be added to methods that expect either the
<interfacename>Message</interfacename> type or the message payload type. The return value will be handled in the
@@ -179,5 +174,22 @@ Order generateOrder(String productId, @Header("customerName") String customer) {
}</programlisting>
</para>
</section>
<section id="header-filter">
<title>Header Filter</title>
Some time your transformation use case might be as simple as removing a few headers.
For this type of use cases Spring Integration provides <emphasis>Header Filter</emphasis> which allows you to specify which header should be
removed from the output Message.
Basically <emphasis>Header Filter</emphasis> is the opposite of <emphasis>Header Enricher</emphasis>
that is discussed in <xref linkend="header-enricher"/>
<programlisting language="xml"><![CDATA[<int:header-filter input-channel="inputChannel"
output-channel="outputChannel" header-names="lastName, state"/>]]></programlisting>
As you can see, configuration of <emphasis>Header Filter</emphasis> is quite simple. It is a typical endpoint with input/output channels
and <code>header-names</code> attribute which allows you to specify the names of the headers (delimited by coma if multiple)
that need to be removed. So, in the above example headers with the name 'lastName' and 'state' will be removed.
</section>
</section>

View File

@@ -7,10 +7,6 @@
</para>
<section id="xmpp-intro">
<title>Introduction</title>
<para>
Spring Integration provides adapters for sending and receiving both XMPP messages and status changes from other
entries in your roster as well as XMPP.
</para>
<para>
XMPP describes a way for multiple agents to communicate with each other in a distributed system.
The canonical use case is to send and receive instant messages, though XMPP can be, and is, used for far more
@@ -19,12 +15,8 @@
as broadcast status changes.
</para>
<para>
<!--
todo do we have to include TM for 'Facebook', 'GMail', and 'Gtalk'?
-->
XMPP provides the messaging fabric that underlies some of the biggest Instant Messaging networks in the world,
including Google Talk (GTalk)
- which is also available from within GMail - and Facebook Chat.
including Google Talk (GTalk) - which is also available from within GMail - and Facebook Chat.
There are many good open-source XMPP servers available. Two popular implementations are
<ulink url="http://www.igniterealtime.org/projects/openfire/">
<citetitle>Openfire</citetitle>
@@ -33,76 +25,28 @@
<ulink url="http://www.ejabberd.im">
<citetitle>ejabberd</citetitle>
</ulink>
.
</para>
<para>
In XMPP,
<emphasis>rosters</emphasis>
(the roster corresponds to the notion of a "buddy list" in your typical IM client) are used to manage a list of
other agents ("contacts", or "buddies", in an IM client)
in the system, called<emphasis>roster items</emphasis>.
The roster item contains - at a minimum - the roster item's JID which is its unique ID on the network.
An actor may subscribe to the state changes of another actor in the system. The subscription can be bidirectional,
as well.
The subscription settings determine whose status updates are broadcast, and to whom.
These subscriptions are stored on the XMPP server, and are thus durable.
Spring integration provides support for XMPP via XMPP adapters which support sending and receiving both XMPP messages and
status changes from other entries in your roster. As many other adapters, XMPP adapters come with a convenient namespace-based
configuration.
To configure XMPP namespace include the following elements into the headers of your XML configuration file:
<programlisting language="xml"><![CDATA[xmlns:xmpp="http://www.springframework.org/schema/integration/xmpp"
xsi:schemaLocation="http://www.springframework.org/schema/integration/xmpp
http://www.springframework.org/schema/integration/xmpp/spring-integration-xmpp-2.0.xsd"]]></programlisting>
</para>
</section>
<section id="xmpp-config">
<title>Using The Spring Integration XMPP Namespace
</title>
<para>
Using the Spring Integration XMPP namespace support is simple.
Its use is like any other module in the Spring framework: import the XML schema, and use it to define elements.
A prototypical XMPP-based integration might feature the following header. We won't repeat this in subsequent
examples, because it is uninteresting.
<programlisting lang="xml"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
xmlns="http://www.springframework.org/schema/integration"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xmpp="http://www.springframework.org/schema/integration/xmpp"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="
http://www.springframework.org/schema/integration/xmpp
http://www.springframework.org/schema/integration/xmpp/spring-integration-xmpp.xsd
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/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
">
...
</beans:beans>
]]></programlisting>
</para>
</section>
<section id="xmpp-connection">
<title>XMPP Connection</title>
<para>
To participate in the network, an actor must connect to an XMPP server. Typically this requires - at a minimum - a
<code>user</code>, a<code>password</code>, a<code>host</code>, and a<code>port</code>.
Before using inbound or outbound XMPP adapters to participate in the XMPP network actor must establish XMPP connection. This
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>, <code>host</code>.
To create an XMPP connection, you may use the XML namespace.
To create a basic XMPP connection, you can utilize the convenience of the namespace.
<programlisting lang="xml"><![CDATA[<xmpp:xmpp-connection
id="myConnection"
@@ -111,8 +55,7 @@ http://www.springframework.org/schema/context/spring-context-3.0.xsd
host="host"
port="port"
resource="theNameOfTheResource"
subscription-mode="accept_all"
/>
subscription-mode="accept_all"/>
]]></programlisting>
</para>
</section>
@@ -120,218 +63,62 @@ http://www.springframework.org/schema/context/spring-context-3.0.xsd
<section id="xmpp-messages">
<title>XMPP Messages</title>
<section id="xmpp-message-inbound-channel-adapter">
<title>Inbound Message Adapter</title>
<title>Inbound Message Channel Adapter</title>
<para>The Spring Integration adapters support receiving messages from other users in the system. To do this, the
adapter "logs in" as a user on your behalf and receives the messages sent to that user. Those messages are then
<emphasis>Inbound Message Channel Adapter</emphasis> "logs in" as a user on your behalf and receives the messages sent to that user. Those messages are then
forwarded to your Spring Integration client.
The payload of the inbound Spring Integration message may be of the raw type<classname>
org.jivesoftware.smack.packet.Message</classname>, or of the type
<classname>java.lang.String</classname>
- which is the type of the raw<code>
Message</code>'s
<code>body</code>
property -
depending on whether you specify
<code>extract-payload</code>
on the adapter's configuration or not.
Inbound Messages are typically small and are text-oriented. Messages received using the adapter have
a pretty standard layout, with known headers (all headers have keys defined on<classname>
org.springframework.integration.xmpp.XmppHeaders</classname>):
</para>
<table>
<title>Header Values</title>
<tgroup cols="2">
<colspec colname="c1"/>
<colspec colname="c2"/>
<thead>
<row>
<entry>Header Name</entry>
<entry>What It Describes</entry>
</row>
</thead>
<tbody>
<row>
<entry>XmppHeaders.TYPE</entry>
<entry>The value of the
the
<code>
org.jivesoftware.smack.packet.Message.Type
</code>
enum that describes the inbound message. Possible values are:
<code>normal</code>,
<code>chat</code>,
<code>groupchat</code>,
<code>headline</code>,
<code>error</code>.
</entry>
</row>
<row>
<entry>XmppHeaders.CHAT</entry>
<entry>A reference to the
<code>org.jivesoftware.smack.Chat</code>
class which represents the
threaded conversation containing the message.
</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
This adapter requires a reference to an XMPP Connection. You may
use the
<link linkend="xmpp-connection">xmpp-connection</link>
element to define one.
An example might look as follows:
<programlisting lang="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<beans:beans ... >
<context:component-scan
base-package="com.myxmppclient.inbound"/>
<context:property-placeholder
location="#{ systemProperties['user.home'] }/xmpp/xmppclient.properties"/>
<channel id="xmppInbound"/>
<xmpp:xmpp-connection
id="testConnection"
...
/> ]]>
<emphasis><![CDATA[<xmpp:message-inbound-channel-adapter
channel="xmppInbound"
xmpp-connection="testConnection"/>
]]></emphasis><![CDATA[
<service-activator input-channel="xmppInbound"
ref="xmppMessageConsumer"/>
</beans:beans>]]></programlisting>
<classname>java.lang.String</classname> if you set <code>extract-payload</code> value attribute to 'true'
when configuring an adapter.
Configuration support for XMPP <emphasis>Inbound Message Channel Adapter</emphasis> is provided via <code>message-inbound-channel-adapter</code> element.
<programlisting language="xml"><![CDATA[<xmpp:message-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>
In this example, the message is received from the XMPP adapter and passed to a
<code>service-activator</code>
component. Here's the declaration of the<code>service-activator</code>.
<programlisting lang="java"><![CDATA[package com.myxmppclient.inbound ;
import org.jivesoftware.smack.packet.Message;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.stereotype.Component;
@Component
public class XmppMessageConsumer {
@ServiceActivator
public void consume(Message input) throws Throwable {
String text = input.getBody();
System.out.println( "Received message: " + text ) ;
}
}
]]></programlisting>
It is also important to mention that XMPP inbound adapter is an <emphasis>event driven adapter</emphasis> and a <classname>LifeCycle</classname> object.
When started it will register a <classname>PacketListener</classname> which will listen for the incoming XMPP Messages forwarding them to the underlying
adapter which will convert them to Spring Integration Messages and send them to the <classname>output-channel</classname>. It will
unregister <classname>PacketListener</classname> when it is stopped.
</para>
</section>
<section id="xmpp-message-outbound-channel-adapter">
<title>Outbound Message Adapter</title>
<para>
You may also send messages to other users on XMPP using the
<code>outbound-message-channel-adapter</code>
adapter. The is configured like the
<link linkend="xmpp-message-inbound-channel-adapter">xmpp-message-inbound-channel-adapter</link>. The
adapter takes an
<code>xmpp-connection</code>
reference.
Here is a (necessarily) contrived example solution using the outbound adapter.
<programlisting lang="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<beans:beans ... >
<context:component-scan
base-package="com.myxmppproducer.outbound"/>
<context:property-placeholder
location="#{ systemProperties['user.home'] }/xmpp/xmppclient.properties"/>
<beans:bean id="xmppProducer"
class="com.myxmppproducer.outbound.XmppMessageProducer"
p:recipient="${user.2.login}"/>
<poller default="true" fixed-rate="10000"/>
<xmpp:xmpp-connection
id="testConnection"
...
/>
<inbound-channel-adapter ref="xmppProducer"
channel="outboundChannel"/>
<channel id="outboundChannel"/>
<xmpp:message-outbound-channel-adapter
channel="outboundChannel"
xmpp-connection="testConnection"/>
</beans:beans>]]>
</programlisting>
</para>
<para>
The adapter expects as its input - at a minimum - a payload of type <classname>java.lang.String</classname>, and
a header value
for
<code>XmppHeaders.CHAT_TO_USER</code>
that specifies to which the user the payload body should be sent to.
To create a message destined for the<code>outbound-message-channel-adapter</code>, you might use the following
Java code:
<programlisting lang="java">
<![CDATA[
Message<String> xmppOutboundMsg = MessageBuilder.withPayload("Hello, world!" )
.setHeader(XmppHeaders.CHAT_TO_USER, "userhandle")
.build();
]]></programlisting>
</para>
<para>
It's easy enough to use Java to update the <code>XmppHeaders.CHAT_TO_USER</code> header, and this has the advantage of dynamically updating the header at runtime in Java code.
If, however, the target is more static in nature, you can
configure it using the
XMPP enricher support. Here is an example using the enricher. The enricher enriches the Spring Integration
message
to support the header values that the outbound XMPP adapters expect.
<programlisting lang="xml">
<![CDATA[
<channel id="input"/>
<channel id="output"/>
<xmpp:header-enricher input-channel="input" output-channel="output">
<xmpp:message-to value="test1@example.org"/>
</xmpp:header-enricher>
]]></programlisting>
</para>
<title>Outbound Message Channel Adapter</title>
<para>
You may also send messages to other users on XMPP using the <emphasis>Outbound Message Channel Adapter</emphasis>.
Configuration support for XMPP <emphasis>Outbound Message Channel Adapter</emphasis> is provided via <code>message-outbound-channel-adapter</code> element.
<programlisting language="xml"><![CDATA[<int-xmpp:message-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_USER</classname> that specifies to which user the Message should be sent to. 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_USER, "userhandle")
.build();]]></programlisting>
Another mechanism of setting such header is by using the XMPP enricher support. Here is an example using the enricher.
<programlisting language="xml"><![CDATA[<int-xmpp:header-enricher input-channel="input" output-channel="output">
<int-xmpp:message-to value="test1@example.org"/>
</int-xmpp:header-enricher>]]></programlisting>
</para>
</section>
</section>
<section id="xmpp-presence">
<title>XMPP Presence</title>
@@ -345,98 +132,114 @@ public class XmppMessageConsumer {
If you would like to receive notification, or notify others, of state changes, you can use Spring Integration's "presence" adapters.
</para> <para>
The most important data for these adapters resides in the headers. The header keys are enumerated on
the <code>org.springframework.integration.xmpp.XmppHeaders</code> class.
The header keys specific to these "presence" adapters start with the token "PRESENCE_".
Not all headers are available for both inbound and outbound.
</para> <table>
<title>Header Values</title>
<tgroup cols="2">
<colspec colname="c1"/>
<colspec colname="c2"/>
<thead>
<row>
<entry>Header Name</entry>
<entry>What It Describes</entry>
</row>
</thead>
<tbody>
<row>
<entry>XmppHeaders.PRESENCE_LANGUAGE</entry>
<entry> The <code>java.lang.String</code> language in which the message was written.
</entry>
</row>
<row>
<entry>XmppHeaders.PRESENCE_PRIORITY</entry>
<entry>
The priority (int) of the message. Arbitrary, but it can be used to help assign relevance to a message which
in turn might be used in its handling.
</entry>
</row><row>
<entry>XmppHeaders.PRESENCE_MODE</entry>
<entry>
An instance of the enum <code>org.jivesoftware.smack.packet.Presence.Mode</code> that has one of the following values:
<code>chat,</code> <code>available,</code> <code>away,</code>
<code>xa,</code> <code>dnd</code>
</entry>
</row>
<row>
<entry>XmppHeaders.PRESENCE_TYPE</entry>
<entry>
An instance of the enum <code>org.jivesoftware.smack.packet.Presence.Type</code>
that has one of the following values:
<code>available,</code> <code>unavailable,</code> <code>subscribe,</code> <code>subscribed,</code>
<code>unsubscribe,</code> <code>unsubscribed,</code> and <code>error</code>.
</entry>
</row> <row>
<entry>XmppHeaders.PRESENCE_STATUS</entry>
<entry>
A <code>java.lang.String</code> string representing the status of the agent. This corresponds to an agents "away" message.
</entry>
</row>
<row>
<entry>XmppHeaders.PRESENCE_FROM</entry>
<entry>
A <code>java.lang.String</code> string representing the handle of the user whose state is being received.
</entry>
</row>
</tbody>
</tgroup>
</table>
<section id="xmpp-presence-inbound-channel-adapter">
<title>Inbound Presence Adapter</title>
</para>
<section id="xmpp-roster-inbound-channel-adapter">
<title>Inbound Roster Message Channel Adapter</title>
<para>
The first adapter supports receiving messages whenever an agent on your roster has updated its
state. Most of the important data comes in through the headers.
Spring Integration provides <emphasis>Inbound Roster Message Channel Adapter</emphasis> which supports receiving Presence (Roster)
events from other users in the system. To do this, the <emphasis>Inbound Roster Message Channel Adapter</emphasis> "logs in" as a user
on your behalf, registers <classname>RosterListener</classname> and forwards received roster events as Messages to the channel
identified by the <code>channel</code> attribute. The payload of the Message could either be <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) or
<classname>java.util.Collection&lt;String&gt;</classname> representing roster entries.
</para>
<para>
Configuration support for XMPP <emphasis>Inbound Roster Message Channel Adapter</emphasis> is provided via
<code>roster-event-inbound-channel-adapter</code> element.
<programlisting language="xml"><![CDATA[<int-xmpp:roster-event-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> object.
It will register <classname>RosterListener</classname> when started and will unregister <classname>RosterListener</classname>
when stopped.
</para>
</section>
<section id="xmpp-presence-outbound-channel-adapter">
<title>Outbound Presence Adapter</title>
<para>TBD</para>
<section id="xmpp-roster-outbound-channel-adapter">
<title>Outbound Roster Message Channel Adapter</title>
<para>
Spring Integration also supports sending Presence (Roster) events to be seen by other users in the network. When you send a Message
to the <emphasis>Outbound Roster 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
the XMPP Connection, thus advertising your presence events to the rest of the network.
</para>
<para>
Configuration support for XMPP <emphasis>Outbound Roster Message Channel Adapter</emphasis> is provided via
<code>roster-event-outbound-channel-adapter</code> element.
<programlisting language="xml"><![CDATA[<int-xmpp:roster-event-outbound-channel-adapter id="eventOutboundRosterChannel"
xmpp-connection="testConnection"/>]]></programlisting>
It can also be a <emphasis>polling consumer</emphasis> (if it receives Messages from the Polling Channel) in which case you would
need to register a Poller.
<programlisting language="xml"><![CDATA[<int-xmpp:roster-event-outbound-channel-adapter id="pollingOutboundRosterAdapter"
xmpp-connection="testConnection"
channel="pollingChannel">
<int:poller fixed-rate="1000" max-messages-per-poll="1"/>
</int-xmpp:roster-event-outbound-channel-adapter>]]></programlisting>
Similar to its Inbound counterpart it requires a reference to an XMPP Connection.
</para>
</section>
</section>
<!--<section id="xmpp-samples">
<title>XMPP Samples</title>
<section id="xmpp-appendices">
<title>Appendices</title>
<para>
Since Spring Integration XMPP support is based on 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 XMPP Connection object.
</para>
<para>
As it was said earlier the <code>xmpp-connection</code> namespace support is designed to simplify basic connection configuration and
only supports few configuration attributes. However, <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 <classname>XmppConnectionFactoryBean</classname> as a regular bean injecting
<classname>org.jivesoftware.smack.ConnectionConfiguration</classname> as a constructor argument and configuring every property
you may need. This way SSL or any other attributes could be set directly in a consistent Spring way. 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>
</bean>
<int:channel id="outboundEventChannel"/>
<int-xmpp:message-outbound-channel-adapter id="outboundEventAdapter"
channel="outboundEventChannel"
xmpp-connection="xmppConnection"/>]]></programlisting>
</para>
<para>
We really should have some samples...
Another important aspect of Smack API is static initializers. For more complex cases (e.g., registering 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 XMPP Connection
configuration where you can configure the entire component through Java code and execute all other necessary Java code including
static initializers.
<programlisting language="java"><![CDATA[@Configuration
public class CustomConnectionConfiguration {
@Bean
public XmppConnection knight() {
SASLAuthentication.supportSASLMechanism("EXTERNAL", 0); // static initializer
ConnectionConfiguration config = new ConnectionConfiguration("localhost", 5223);
config.setTrustorePath("path_to_truststore.jks");
config.setSecurityEnabled(true);
config.setSocketFactory(SSLSocketFactory.getDefault());
conn = new XMPPConnection(config);
}
}]]></programlisting>
For more information on Javaconfig style of Application Context configuration refere to the following section in Spring Reference Manual
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-java
</para>
</section>
-->
</section>
</chapter>