INT-2388 Update Gradle build

This is a significant update to the build system, including the changes
listed below. README.md has been updated with instructions on the most
important day-to-day commands.

 - Eliminate buildSrc submodule

   In favor of using the new bundlor and docbook-reference plugins. The
   net effect is a large reduction in number of lines of build code.
   Common docbook resources, stylesheets, etc are stored directly in the
   docbook plugin.

   This means that --recursive is no longer required when cloning and
   there will never be a need to use `git submodule` commands. README
   files have been updated to reflect.

   Use of the new bundlor plugin also means the removal of template.mf
   files from the source tree in favor of an inline approach. See
   build.gradle for details. Bundlor 'import templates' are built up
   programmatically and kept physically close to gradle dependency
   declarations, leading to more convenience when changing these values
   and hopefully fewer errors / version inconsistencies over time.

   Certain tests depended on the presence of template.mf files, all of
   which have recently been removed from the source tree in favor of the
   new bundlor plugin which allows for inlining bundlor configuration
   within the Gradle build script. These tests now create temp files
   using the java.io.File API instead.

 - Upgrade to Gradle 1.0-milestone-6

   The m6 release is significantly faster when resolving dependencies
   and has a number of valuable new features over the earlier m3
   version. Review the release notes for Gradle 1.0-milestone-6 online
   for full details.

 - Switch to repo.springsource.org repository

   Previously the project build declared as many repositories as
   necessary to resolve all project dependencies.

   Now depending on a single 'virtual repository' defined within the
   SpringSource Artifactory instance at http://repo.springsource.org.
   Currently, the virtual repository in use is 'libs-milestone', which
   allows for the resolution of all "milestone-or-better" versions of
   all S2 and third-party dependencies.

   Should snapshot dependencies become required, this value may be
   changed from 'libs-milestone' to 'libs-snapshot'. To build only
   against GA releases, change the value to 'libs-release'.

 - New build plan(s)

   Spring Integration build plans have been updated to use the
   Artifactory Bamboo plugin and publish to repo.springsource.org.
   Build plans have names like 2.1.x to reflect the version under
   development, not necessarily the name of the branch, as this may
   change over time and across major releases.

 - Improve release process

   As mentioned above, Spring Integration will now use the Artifactory
   Bamboo plugin to publish releases and also use Artifactory's support
   for pushing builds directly into Maven Central via oss.sonatype.org.

   Generate poms that contain all necessary fields for onboarding at
   Maven central (scm, developers, organization, licenses, etc).

   Generate -source and -javadoc poms to comply with Maven Central
   onboarding rules (and for general good practice anyway).

   Generation of PGP signatures, sha1 and md5 checksums are all handled
   automatically by Artifactory. These are also requirements for
   automated entry into Maven Central.

 - Remove source-level pom generation

   Automatic generation of Maven poms suitable for use in building
   Spring Integration is no longer supported. Generation and
   publication of poms for the purpose of dependency management remains
   supported.

   Sonar support has to date depended on these poms, but will be
   switched over to use the Gradle Sonar plugin shortly.

 - Eliminate docs subproject

   Move docs/src to the root of the project and eliminate docs as a
   formal subproject. This simplifies the build in a number of ways,
   including removing the need for distinguishing between 'subprojects'
   and 'javaprojects' as well as allowing users to build both 'api' and
   'reference' docs without qualifying with a ':docs' prefix.

   Also rename the src/info directory to src/dist to better reflect that
   these files are packaged with the distribution. For example, the
   readme.txt there is really the distribution readme, distinct from the
   README.md at the root of the project which is for building from source,
   etc.
This commit is contained in:
Chris Beams
2011-11-08 14:38:53 -08:00
committed by Mark Fisher
parent ae0ecff886
commit f30da932e8
225 changed files with 811 additions and 7510 deletions

View File

@@ -0,0 +1,344 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="jms"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>JMS Support</title>
<para>
Spring Integration provides Channel Adapters for receiving and sending JMS messages. There are actually two
JMS-based inbound Channel Adapters. The first uses Spring's <classname>JmsTemplate</classname> to receive based on
a polling period. The second is "message-driven" and relies upon a Spring MessageListener container. There is also
an outbound Channel Adapter which uses the <classname>JmsTemplate</classname> to convert and send a JMS Message on
demand.
</para>
<para>
As you can see from above by using <classname>JmsTemplate</classname> and <classname>MessageListener</classname> container Spring Integration relies on Spring's JMS support.
This is important to understand since most of the attributes exposed on these adapters will configure the underlying Spring's <classname>JmsTemplate</classname> and/or
<classname>MessageListener</classname> container. For more details about <classname>JmsTemplate</classname> and <classname>MessageListener</classname> container please refer to
<ulink url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/jms.html">Spring JMS documentation</ulink>.
</para>
<para>
Whereas the JMS Channel Adapters are intended for unidirectional Messaging (send-only or receive-only), Spring
Integration also provides inbound and outbound JMS Gateways for request/reply operations. The inbound gateway
relies on one of Spring's MessageListener container implementations for Message-driven reception that is also
capable of sending a return value to the "reply-to" Destination as provided by the received Message. The outbound
Gateway sends a JMS Message to a "request-destination" and then receives a reply Message. The "reply-destination"
reference (or "reply-destination-name") can be configured explicitly or else the outbound gateway will use a
JMS TemporaryQueue.
</para>
<section id="jms-inbound-channel-adapter">
<title>Inbound Channel Adapter</title>
<para>
The inbound Channel Adapter requires a reference to either a single <classname>JmsTemplate</classname>
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>
<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
infrequently and timeliness is not important. For all other situations (a vast majority of JMS-based use-cases),
the <emphasis>message-driven-channel-adapter</emphasis> described below is a better option.
</tip>
<note>
All of the JMS adapters that require a reference to the ConnectionFactory will automatically look for
a bean named "connectionFactory" by default. That is why you don't see a "connection-factory" attribute
in many of the examples. However, if your JMS ConnectionFactory has a different bean name, then you will
need to provide that attribute.
</note>
</para>
<para>
If 'extract-payload' is set to true (which is the default), the received JMS Message will be passed through
the MessageConverter. When relying on the default SimpleMessageConverter, this means that the resulting Spring
Integration Message will have the JMS Message's body as its payload. A JMS TextMessage will produce a
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>
</para>
</section>
<section id="jms-message-driven-channel-adapter">
<title>Message-Driven Channel Adapter</title>
<para>
The "message-driven-channel-adapter" requires a reference to either an instance of a Spring MessageListener
container (any subclass of <classname>AbstractMessageListenerContainer</classname>) or both
<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>
<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,
an instance of DefaultMessageListenerContainer will be created and configured based on these properties.
For example, you can specify the "transaction-manager" reference, the "concurrent-consumers" value, and
several other property references and values. Refer to the JavaDoc and Spring Integration's JMS Schema
(spring-integration-jms.xsd) for more detail.
</note>
</para>
<para>
The 'extract-payload' property has the same effect as described above, and once again its default value
is 'true'. The poller sub-element is not applicable for a message-driven
Channel Adapter, as it will be actively invoked. For most usage scenarios, the message-driven approach is better since the Messages will
be passed along to the <interfacename>MessageChannel</interfacename> as soon as they are received from the underlying
JMS consumer.
</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>
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
'error-channel' should also be one-way. For example, it could simply send to a logging handler,
or it could be connected to a different JMS &lt;outbound-channel-adapter&gt; element.
</para>
</section>
<section id="jms-outbound-channel-adapter">
<title>Outbound Channel Adapter</title>
<para>
The <classname>JmsSendingMessageHandler</classname> implements the <interfacename>MessageHandler</interfacename>
interface and is capable of converting Spring Integration <interfacename>Messages</interfacename> to JMS messages
and then sending to a JMS destination. It requires either a 'jmsTemplate' reference or both 'connectionFactory' and
'destination' references (again, the 'destinationName' may be provided in place of the 'destination'). As with the
inbound Channel Adapter, the easiest way to configure this adapter is with the namespace support. The following
configuration will produce an adapter that receives Spring Integration Messages from the "exampleChannel" and then
converts those into JMS Messages and sends them to the JMS Destination reference whose bean name is "outQueue".
<programlisting language="xml"><![CDATA[<int-jms:outbound-channel-adapter id="jmsOut" destination="outQueue" channel="exampleChannel"/>]]></programlisting>
</para>
<para>
As with the inbound Channel Adapters, there is an 'extract-payload' property. However, the meaning is reversed
for the outbound adapter. Rather than applying to the JMS Message, the boolean property applies to the Spring
Integration Message payload. In other words, the decision is whether to pass the Spring Integration Message
<emphasis>itself</emphasis> as the JMS Message body or whether to pass the Spring Integration Message's
payload as the JMS Message body. The default value is once again 'true'. Therefore, if you pass a Spring
Integration Message whose payload is a String, a JMS TextMessage will be created. If on the other hand you
want to send the actual Spring Integration Message to another system via JMS, then simply set this to 'false'.
<note>
Regardless of the boolean value for payload extraction, the Spring Integration MessageHeaders will map to
JMS properties as long as you are relying on the default converter or provide a reference to another
instance of HeaderMappingMessageConverter (the same holds true for 'inbound' adapters except that in
those cases, it's the JMS properties mapping <emphasis>to</emphasis> Spring Integration MessageHeaders).
</note>
</para>
</section>
<section id="jms-inbound-gateway">
<title>Inbound Gateway</title>
<para>
Spring Integration's message-driven JMS inbound-gateway delegates to a
<interfacename>MessageListener</interfacename> container, supports dynamically adjusting concurrent consumers,
and can also handle replies. The inbound gateway requires references to a
<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>
</para>
<para>
Since the gateways provide request/reply behavior instead of unidirectional send <emphasis>or</emphasis>
receive, they also have two distinct properties for the "payload extraction" (as discussed above for the
Channel Adapters' 'extract-payload' setting). For an inbound-gateway, the 'extract-request-payload' property
determines whether the received JMS Message body will be extracted. If 'false', the JMS Message itself will
become the Spring Integration Message payload. The default is 'true'.
</para>
<para>
Similarly, for an inbound-gateway the 'extract-reply-payload' property applies to the Spring Integration Message
that is going to be converted into a reply JMS Message. If you want to pass the whole Spring Integration Message
(as the body of a JMS ObjectMessage) then set this to 'false'. By default, it is also 'true' such that the Spring
Integration Message <emphasis>payload</emphasis> will be converted into a JMS Message (e.g. String payload
becomes a JMS TextMessage).
</para>
<para>
As with anything else, Gateway invocation might result in error.
By default Producer will not be notified of the errors that might have occurred on the consumer side and will time out waiting for
the reply. However there might be times when you want to communicate an error condition back to the consumer,
in other words treat the Exception as a valid reply by mapping it to a Message. To accomplish this
JMS Inbound Gateway provides support for a Message Channel to which errors
can be sent for processing, potentially resulting in a reply Message payload
that conforms to some contract defining what a caller may expect as an "error"
reply. Such a channel can be configured via the <emphasis>error-channel</emphasis>
attribute.
<programlisting language="xml"><![CDATA[<int-jms:inbound-gateway request-destination="requestQueue"
request-channel="jmsinputchannel"
error-channel="errorTransformationChannel"/>
<int:transformer input-channel="exceptionTransformationChannel"
ref="exceptionTransformer" method="createErrorResponse"/>
]]></programlisting>
You might notice that this example looks very similar to that included
within <xref linkend="gateway-proxy"/>.
The same idea applies here: The <emphasis>exceptionTransformer</emphasis>
could be a simple POJO that creates error response objects, you could reference
the "nullChannel" to suppress the errors, or you could leave 'error-channel' out
to let the Exception propagate.
</para>
</section>
<section id="jms-outbound-gateway">
<title>Outbound Gateway</title>
<para>
The outbound Gateway creates JMS Messages from Spring Integration Messages and then sends to a
'request-destination'. It will then handle the JMS reply Message either by using a selector to
receive from the 'reply-destination' that you configure, or if no 'reply-destination' is provided,
it will create JMS TemporaryQueues. Notice that the "reply-channel" is also provided.
<programlisting language="xml"><![CDATA[ <int-jms:outbound-gateway id="jmsOutGateway"
request-destination="outQueue"
request-channel="outboundJmsRequests"
reply-channel="jmsReplies"/>]]></programlisting>
</para>
<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
applies to the Spring Integration Message that is being converted into a JMS Message to be
<emphasis>sent as a request</emphasis>, and the 'extract-reply-payload' property value applies to the
JMS Message that is <emphasis>received as a reply</emphasis> and then converted into a Spring Integration
Message to be subsequently sent to the 'reply-channel' as shown in the example configuration above.
</para>
</section>
<section id="jms-header-mapping">
<title>Mapping Message Headers to/from JMS Message</title>
<para>
JMS Message can contain meta-information such as JMS API headers as well as simple properties.
You can map those to/from Spring Integration Message Headers using <classname>JmsHeaderMapper</classname>.
The JMS API headers are passed to the appropriate setter methods (e.g. setJMSReplyTo) whereas other headers will
be copied to the general properties of the JMS Message.
JMS Outbound Gateway is bootstrapped with the default implementation of <classname>JmsHeaderMapper</classname> which will map
standard JMS API Headers as well as primitive/String Message Headers. Custom header mapper could also be
provided via <code>header-mapper</code> attribute of inbound and outbound gateways.
</para>
</section>
<section id="jms-conversion-and-marshalling">
<title>Message Conversion, Marshalling and Unmarshalling</title>
<para>
If you need to convert the message, all JMS adapters and gateways, allow you to
provide a <interfacename>MessageConverter</interfacename> via <emphasis>message-converter</emphasis> attribute. Simply provide the
bean name of an instance of <interfacename>MessageConverter</interfacename> that is available within the same
ApplicationContext.
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"/>
<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>
Note, however, that when you provide your own MessageConverter instance, it will still
be wrapped within the HeaderMappingMessageConverter. This means that the 'extract-request-payload'
and 'extract-reply-payload' properties may affect what actual objects are passed to your converter. The
HeaderMappingMessageConverter itself simply delegates to a target MessageConverter while also mapping the
Spring Integration MessageHeaders to JMS Message properties and vice-versa.
</note>
</section>
<section id="jms-channel">
<title>JMS Backed Message Channels</title>
<para>
The Channel Adapters and Gateways featured above are all intended for applications that are integrating
with other external systems. The inbound options assume that some other system is sending JMS Messages
to the JMS Destination and the outbound options assume that some other system is receiving from the
Destination. The other system may or may not be a Spring Integration application. Of course, when sending
the Spring Integration Message instance as the body of the JMS Message itself (with the 'extract-payload'
value set to false), it is assumed that the other system is based on Spring Integration. However,
that is by no means a requirement. That flexibility is one of the benefits of using a Message-based
integration option with the abstraction of "channels" or Destinations in the case of JMS.
</para>
<para>
There are cases where both the producer and consumer for a given JMS Destination 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. The problem with that approach is that two adapters are
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>
</para>
<para>
The channel in the above example will behave much like a normal &lt;channel/&gt; element from the main
Spring Integration namespace. It can be referenced by both "input-channel" and "output-channel" attributes
of any endpoint. The difference is that this channel is backed by a JMS Queue instance named "exampleQueue".
This means that asynchronous messaging is possible between the producing and consuming endpoints, but
unlike the simpler asynchronous Message Channels created by adding a &lt;queue/&gt; sub-element within a
non-JMS &lt;channel/&gt; element, the Messages are not just stored in an in-memory queue. Instead those
Messages are passed within a JMS Message body, and the full power of the underlying JMS provider is then
available for that channel. Probably the most common rationale for using this alternative would be to
take advantage of the persistence made available by the <emphasis>store and forward</emphasis> approach
of JMS messaging. If configured properly, the JMS-backed Message Channel also supports transactions.
In other words, a producer would not actually write to a transactional JMS-backed channel if its send
operation is part of a transaction that rolls back. Likewise, a consumer would not physically remove a
JMS Message from the channel if the reception of that Message is part of a transaction that rolls back.
Note that the producer and consumer transactions are separate in such a scenario. This is significantly
different than the propagation of a transactional context across the simple, synchronous &lt;channel/&gt;
element that has no &lt;queue/&gt; sub-element.
</para>
<para>
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>
</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"/>
<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
<classname>DynamicDestinationResolver</classname> implementation, but any implementation of the
<interfacename>DestinationResolver</interfacename> interface could be provided. Also, the JMS
<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>
</para>
</section>
<section id="jms-samples">
<title>JMS Samples</title>
<para>
To experiment with these JMS adapters, check out JMS samples available in our new Samples Git repository
available here: http://git.springsource.org/+spring-integration/spring-integration/samples .
There are two samples included. One provides inbound and outbound Channel Adapters, and the
other provides inbound and outbound Gateways. They are configured to run with an embedded ActiveMQ process, but
the "common.xml" file can easily be modified to support either a different JMS provider or a standalone
ActiveMQ process. In other words, you can split the configuration so that the inbound and outbound adapters are
running in separate JVMs. If you have ActiveMQ installed, simply modify the "brokerURL" property within the
configuration to use "tcp://localhost:61616" for example (instead of "vm://localhost"). Both of the samples
accept input via stdin and then echo back to stdout. Look at the configuration to see how these messages are
routed over JMS.
</para>
</section>
</chapter>