1428 lines
63 KiB
XML
1428 lines
63 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!--
|
|
/*
|
|
* Copyright 2002-2008 the original author or authors.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
-->
|
|
<chapter xml:id="messaging" xmlns="http://docbook.org/ns/docbook" version="5">
|
|
<title>Message Oriented Middleware</title>
|
|
|
|
<section>
|
|
<title>Introduction</title>
|
|
|
|
<para>The goal of Spring's messaging is to increase your productiviity
|
|
when writing an enterprise strength messaging middleware applications.
|
|
Spring achieves these goals in several ways. First it provides several
|
|
helper classes that remove from the developer the incidental complexity
|
|
and resource management issues that arise when using messaging APIs.
|
|
Second, the design of these messaging helper classes promote best
|
|
practices in designing a messaging application by promoting a clear
|
|
separation between the messaging middleware specific code and business
|
|
processing that is technology agnostic. This is generally referred to a
|
|
"plain old .NET object" (or PONO) programming model.</para>
|
|
|
|
<para>This chapter discusses Spring's messaging support for providers
|
|
whose API was modeled after the Java Message Service (JMS) API. Vendors
|
|
who provide a JMS inspired API include Apache, TIBCO, IBM, and Progress
|
|
Software. If you are using Microsoft's Message Queue, please refer to the
|
|
specific <link linkend="msmq">MSMQ section</link>.</para>
|
|
|
|
<para>As there is no de facto-standard common API across messaging
|
|
vendors, Spring provides an implementation of its helper classes for each
|
|
of the major messaging middleware vendors. The naming of the classes you
|
|
will interact with most frequently will either be identical for each
|
|
provider, but located in a different namespace, or have their prefix
|
|
change to be the three-letter-acronym commonly associated with the message
|
|
provider. The list of providers supported by Spring is show below along
|
|
with their namespace and prefix.</para>
|
|
|
|
<orderedlist>
|
|
<listitem>
|
|
<para>Apache NMS in namespace <literal>Spring.Messaging.Nms</literal>.
|
|
'Nms' is sometimes used as the class prefix</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>TIBCO EMS in namespace <literal>Spring.Messaging.Ems</literal>.
|
|
'Ems' is sometimes used as the class prefix</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>SonicMQ in namespace <literal>Spring.Messaging.Sonic</literal>,
|
|
'Jms' is sometimes used as the class prefix. (available 1.2
|
|
RC1)</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Websphere MQ in namespace
|
|
<literal>Spring.Messaging.Xms</literal>, 'Xms' is sometimes used as
|
|
the class prefix (available 1.2 RC1)</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
|
|
<para>JMS can be roughly divided into two areas of functionality, namely
|
|
the production and consumption of messages. For message production and the
|
|
synchronous consumption of messages the a template class, named
|
|
<literal>NmsTemplate</literal>, <literal> EmsTemplate</literal> (etc.) is
|
|
used. Asynchronous message consumption is performed though a
|
|
multi-threaded message listener container,
|
|
<literal>SimpleMessageListenerContainer</literal>. This message
|
|
listener container is used to create Message-Driven PONOs (MDPs) which
|
|
refer to a messaging callback class that consists of just 'plain .NET
|
|
object's and is devoid of any specific messaging types or other artifacts.
|
|
The <literal>IMessageConverter</literal> interface is used by both the
|
|
template class and the message listener container to convert between
|
|
provider message types and PONOs.</para>
|
|
|
|
<para>The namespace
|
|
<literal>Spring.Messaging.<Vendor>.Core</literal> contains the
|
|
messing template class (e.g. <literal>NmsTemplate</literal>). The template
|
|
class simplifies the use of the messaging APIs by handling the creation
|
|
and release of resources, much like the <literal>AdoTemplate</literal>
|
|
does for ADO.NET. The JMS inspired APIs are low-level API, much like
|
|
ADO.NET. As such, even the simplest of operations requires 10s of lines of
|
|
code with the bulk of that code related to resource management of
|
|
intermediate API objects Spring's messaging support, both in Java and
|
|
.NET, addresses the error-prone boiler plate coding style one needs when
|
|
using these APIs.</para>
|
|
|
|
<para>The design principle common to Spring template classes is to provide
|
|
helper methods to perform common operations and for more sophisticated
|
|
usage, delegate the essence of the processing task to user implemented
|
|
callback interfaces. The messaging template follows the same design. The
|
|
message template class offer various convenience methods for the sending
|
|
of messages, consuming a message synchronously, and exposing the message
|
|
Session and MessageProducer to the user.</para>
|
|
|
|
<para>The namespace
|
|
<literal>Spring.Messaging.<VendorAcronym>.Support.Converter</literal>
|
|
provides a <literal>IMessageConverter</literal> abstraction to convert
|
|
between .NET objects and messages. The namespace
|
|
<literal>Spring.Messaging.<VendorAcronym>.Support.Destinations</literal>
|
|
provides various strategies for managing destinations, such as providing a
|
|
service locater for destinations stored in a directory service.</para>
|
|
|
|
<para>Finally, the namespace
|
|
<literal>Spring.Messaging.<VendorAcronym>.Connections</literal>
|
|
provides an implementations of the ConnectionFactory suitable for use in
|
|
standalone applications.</para>
|
|
|
|
<para>The rest of the sections in this chapter discusses each of the major
|
|
helper classes in detail. Please refer to the sample application that
|
|
ships with Spring for additional hands-on usage.</para>
|
|
|
|
<note>
|
|
<para>To simplify documenting features that are common across all
|
|
provider implementations of Spring's helper classes a specific provider,
|
|
Apache ActiveMQ, was selected. As such when you see 'NmsTemplate' in the
|
|
documentation, it also refers to EmsTemplate, XmsTemplate, etc. unless
|
|
specifically documented otherwise. The provider specific API classes are
|
|
typically named after their JMS counterparts with the possible exception
|
|
of a leading 'I' in front of interfaces in order to follow .NET naming
|
|
conventions. In the documentation these API artifacts are referred to as
|
|
'ConnectionFactory', 'Session', 'Message', etc. without the leading
|
|
'I'.</para>
|
|
</note>
|
|
|
|
<section>
|
|
<title>Separation of Concerns</title>
|
|
|
|
<para>The use of MessageConverters and a PONO programming model promote
|
|
messaging best practices by applying the principal of Separation of
|
|
Concerns to messaging based architectures. The infrastructure concern of
|
|
publishing and consuming messages is separated from the concern of
|
|
business processing. These two concerns are reflected in the
|
|
architecture as two distinct layers, a message processing layer and a
|
|
business processing layer. The benefit of this approach is that your
|
|
business processing is decoupled from the messaging technology, making
|
|
it more likely to survive technological changes over time and also
|
|
easier to test. Spring's MessageConverters provides support for mapping
|
|
messaging data types to PONOs. Aside from being the link between the two
|
|
layers, MessageConverters provide a pluggable strategy to help support
|
|
the evolution of a loosely coupled architecture over time. Message
|
|
formats will change over time, typically by the addition of new fields.
|
|
MessageConverters can be implemented to detect different versions of
|
|
messages and perform the appropriate mapping logic to PONOs such so that
|
|
multiple versions of a message can be supported simultaneously, a common
|
|
requirement in enterprise messaging architectures.</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Interoperability and provider portability</title>
|
|
|
|
<para>Messaging is a traditional area of Interoperability across
|
|
heterogeneous systems with messaging vendors providing support on
|
|
multiple operating systems (Windows, UNIX, Mainframes OS's) as well as
|
|
multiple language bindings (C, C++, Java, .NET, Perl, etc.). In 199x the
|
|
Java Community Process came up with a specification to provide a common
|
|
API across messaging providers as well as define some common messaging
|
|
functionality. This specification is know as the Java Message Service.
|
|
From the API perspective, it can roughly be thought of as the messaging
|
|
counterpart to the ADO.NET or JDBC APIs that provide portability across
|
|
different database providers.</para>
|
|
|
|
<para>Given this history, when messaging vendors created their .NET
|
|
APIs, many did so by creating their own JMS inspired API in .NET. There
|
|
is no de facto-standard common API across messaging vendors. As such,
|
|
portability across vendors using Spring's helper classes is done by
|
|
changing the configuration schema in your configuration to a particular
|
|
vendor and doing a 'search-and-replace' on the code base, changing the
|
|
namespace and a few class names. While not ideal ,using Spring will push
|
|
you in the direction of isolating the messaging specific classes in its
|
|
own layer and therefore will reduce the impact of the changes you make
|
|
to the code when switch providers. You business logic classes called
|
|
into via Spring's messaging infrastructure will remain the same.</para>
|
|
|
|
<para>The NMS project from Apache addresses the lack of a common API
|
|
across .NET messaging providers by providing an abstract interface based
|
|
API for messaging and several implementations for different providers.
|
|
At the time of this writing, the project is close to releasing a 1.0
|
|
version that supports ApacheMQ, MSMQ, and TIBCO EMS. There are a few
|
|
outstanding issues at the moment that prevent one using NMS as a common
|
|
API for all messaging providers but hopefully these issues will be
|
|
resolved. Note, that NMS serves 'double' duty as the preferred API for
|
|
messaging with ActiveMQ as well as a providing portability across
|
|
different messaging providers.</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>The role of Messaging API in a 'WCF world'</title>
|
|
|
|
<para>Windows Communication Foundation (WCF) also supports message
|
|
oriented middleware. Not surprisingly, a Microsoft Message Queuing
|
|
(MSMQ) binding is provided as part of WCF. The WCF programming model is
|
|
higher level than the traditional messaging APIs such as JMS and NMS
|
|
since you are programing to a service interface and use metadata (either
|
|
XML or attributes) to configure the messaging behavior. If you prefer to
|
|
use this service-oriented, RPC style approach, then look to see if a
|
|
vendor provides a WCF binding for your messaging provider. Note that
|
|
even with the option of using WCF, many people prefer to sit 'closer to
|
|
the metal' when using messaging middleware, to access specific features
|
|
and functionality not available in WCF, or simply because they are more
|
|
comfortable with that programming model.</para>
|
|
</section>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Using Spring Messaging</title>
|
|
|
|
<section>
|
|
<title>Messaging Template overview</title>
|
|
|
|
<para>Code that uses the messaging template classes
|
|
(<literal>NmsTemplate</literal>, <literal>EmsTemplate</literal>,
|
|
etc) only needs to implement callback interfaces giving them a clearly
|
|
defined contract. The <literal>IMessageCreator</literal> callback
|
|
interface creates a message given a Session provided by the calling code
|
|
in <literal>NmsTemplate</literal>. In order to allow for more complex
|
|
usage of the provider messaging API, the callback
|
|
<literal>ISessionCallback</literal> provides the user with the
|
|
provider specific messaging Session and the callback
|
|
<literal>IProducerCallback</literal> exposes a provider specific
|
|
Session and MessageProducer pair.</para>
|
|
|
|
<para>Provider messaging APIs typically expose two types of send
|
|
methods, one that takes delivery mode, priority, and time-to-live as
|
|
quality of service (QOS) parameters and one that takes no QOS parameters
|
|
which uses default values. Since there are many higher level send
|
|
methods in <literal>NmsTemplate</literal>, the setting of the QOS
|
|
parameters have been exposed as properties on the template class to
|
|
avoid duplication in the number of send methods. Similarly, the timeout
|
|
value for synchronous receive calls is set using the property
|
|
<literal>ReceiveTimeout</literal>.</para>
|
|
|
|
<note>
|
|
<para>Instances of the <literal>NmsTemplate</literal> class are
|
|
thread-safe once configured. This is important because it means that
|
|
you can configure a single instance of a
|
|
<literal>NmsTemplate</literal> and then safely inject this shared
|
|
reference into multiple collaborators. To be clear, the
|
|
<literal>NmsTemplate</literal> is stateful, in that it maintains a
|
|
reference to a <literal>ConnectionFactory</literal>, but this
|
|
state is not conversational state.</para>
|
|
</note>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Connections</title>
|
|
|
|
<para>The <literal>NmsTemplate</literal> requires a reference to a
|
|
ConnectionFactory. The ConnectionFactory serves as the entry point for
|
|
working with the provider's messaging API. It is used by the client
|
|
application as a factory to create connections to the messaging server
|
|
and encapsulates various configuration parameters, many of which are
|
|
vendor specific such as SSL configuration options.<note>
|
|
<para>The TIBCO EMS ConnectionFactory is not interface based and its
|
|
methods are not virtual so no additional functionality that may
|
|
otherwise be part of a ConnectionFactory 'Wrappers' (to be discussed
|
|
later) are provided.</para>
|
|
</note></para>
|
|
|
|
<section>
|
|
<title>Caching EMS Resources</title>
|
|
|
|
<para>The class
|
|
Spring.Messaging.Nms.Connections.CachingConnectionFactory</para>
|
|
</section>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Caching Messaging Resources</title>
|
|
|
|
<para>The standard API usage of NMS and other JMS inspired APIs involves
|
|
creating many intermediate objects. To send a message the following
|
|
'API' walk is performed</para>
|
|
|
|
<programlisting language="csharp">IConnectionFactory->IConnection->ISession->IMessageProducer->Send</programlisting>
|
|
|
|
<para>Between the ConnectionFactory and the Send operation there are
|
|
three intermediate objects that are created and destroyed. To optimise
|
|
the resource usage and increase performance two implementations of
|
|
IConnectionFactory are provided. Note that the TIBCO EMS implementation
|
|
is not based on interfaces and an alternative strategy was selected for
|
|
the time being. See the last section in this manual for details.</para>
|
|
|
|
<section>
|
|
<title>SingleConnectionFactory</title>
|
|
|
|
<para><literal>Spring.Messaging.Nms.Connections.SingleConnectionFactory
|
|
</literal>will return the same connection on all calls to
|
|
CreateConnection and ignore calls to Close.</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>CachingConnectionFactory</title>
|
|
|
|
<para><literal>Spring.Messaging.Nms.Connections.CachingConnectionFactory</literal>
|
|
extends the functionality of SingleConnectionFactory and adds the
|
|
caching of Sessions, MessageProducers, and MessageConsumers.</para>
|
|
|
|
<para>The initial cache size is set to 1, use the property
|
|
<literal>SessionCacheSize</literal> to increase the number of cached
|
|
sessions. Note that the number of actual cached sessions will be more
|
|
than that number as sessions are cached based on their acknowledgment
|
|
mode, so there can be up to 4 cached session instances when
|
|
SessionCacheSize is set to one, one for each
|
|
<literal>AcknowledgementMode</literal>.
|
|
<literal>MessageProducers</literal> and
|
|
<literal>MessageConsumers</literal> are cached within their owning
|
|
session and also take into account the unique properties of the
|
|
producers and consumers when caching.</para>
|
|
|
|
<para><literal>MessageProducers</literal> are cached based on
|
|
their destination. <literal>MessageConsumers</literal> are cached
|
|
based on a key composed of the destination, selector, noLocal delivery
|
|
flag, and the durable subscription name (if creating durable
|
|
consumers).</para>
|
|
</section>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Destination Management</title>
|
|
|
|
<para>In Java implementations of JMS, Connections and Destinations are
|
|
'administered objects' accessible though JNDI - a directory service much
|
|
like ActiveDirectory. In .NET each vendor has selected a different
|
|
approach to destination management. Some are JNDI inspired, allowing you
|
|
to retrieve Connections and Destinations that were configured
|
|
administratively. You can use these vendor specific APIs to perform
|
|
dependency injection on references to JMS Destination objects in
|
|
Spring's XML configuration file by creating am implementation of
|
|
<literal>IObjectFactory</literal> or alternatively configuring the
|
|
specific concrete class implementation for a messaging provider.</para>
|
|
|
|
<para>However, this approach of administered objects can be quite
|
|
cumbersome if there are a large number of destinations in the
|
|
application or if there are advanced destination management features
|
|
unique to the messaging provider. Examples of such advanced destination
|
|
management would be the creation of dynamic destinations or support for
|
|
a hierarchical namespace of destinations. The
|
|
<literal>NmsTemplate</literal> delegates the resolution of a
|
|
destination name to a destination object by delegating to an
|
|
implementation of the interface
|
|
<literal>IDestinationResolver</literal>.
|
|
<literal>DynamicDestinationResolver</literal> is the default
|
|
implementation used by <literal>NmsTemplate</literal> and
|
|
accommodates resolving dynamic destinations.</para>
|
|
|
|
<para>Quite often the destinations used in a messaging application are
|
|
only known at runtime and therefore cannot be administratively created
|
|
when the application is deployed. This is often because there is shared
|
|
application logic between interacting system components that create
|
|
destinations at runtime according to a well-known naming convention.
|
|
Even though the creation of dynamic destinations are not part of the
|
|
original JMS specification, most vendors have provided this
|
|
functionality. Dynamic destinations are created with a name defined by
|
|
the user which differentiates them from temporary destinations and are
|
|
often not registered in a directory service. The API used to create
|
|
dynamic destinations varies from provider to provider since the
|
|
properties associated with the destination are vendor specific. However,
|
|
a simple implementation choice that is sometimes made by vendors is to
|
|
use the <literal>TopicSession</literal> method
|
|
<literal>CreateTopic(string topicName)</literal> or the
|
|
<literal>QueueSession</literal> method <literal>CreateQueue(string
|
|
queueName)</literal> to create a new destination with default
|
|
destination properties. Depending on the vendor implementation,
|
|
<literal>DynamicDestinationResolver</literal> may then also create a
|
|
physical destination instead of only resolving one.</para>
|
|
|
|
<para>The boolean property <literal>PubSubDomain</literal> is used to
|
|
configure the <literal>NmsTemplate</literal> with knowledge of what
|
|
messaging 'domain' is being used. By default the value of this property
|
|
is false, indicating that the point-to-point domain, Queues, will be
|
|
used. This property is infrequently used as the provider messaging APIs
|
|
are now largely agnostic as to which messaging 'domain' is used,
|
|
referring to 'Destinations' rather than 'Queues' or 'Topics'. However,
|
|
this property does influence the behavior of dynamic destination
|
|
resolution via implementations of the
|
|
<literal>IDestinationResolver</literal> interface.</para>
|
|
|
|
<para>You can also configure the NmsTemplate with a default destination
|
|
via the property <literal>DefaultDestination</literal>. The default
|
|
destination will be used with send and receive operations that do not
|
|
refer to a specific destination.</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Message Listener Containers</title>
|
|
|
|
<para>One of the most common uses of JMS is to concurrently process
|
|
messages delivered asynchronously. A message listener container is used
|
|
to receive messages from a message queue and drive the
|
|
<literal>IMessageListener</literal> that is injected into it. The
|
|
listener container is responsible for all threading of message reception
|
|
and dispatches into the listener for processing. A message listener
|
|
container is the intermediary between an Message-Driven PONO (MDP) and a
|
|
messaging provider, and takes care of registering to receive messages,
|
|
resource acquisition and release, exception conversion and suchlike.
|
|
This allows you as an application developer to write the (possibly
|
|
complex) business logic associated with receiving a message (and
|
|
possibly responding to it), and delegates boilerplate messaging
|
|
infrastructure concerns to the framework.</para>
|
|
|
|
<para>A subclass of
|
|
<literal>AbstractMessageListenerContainer</literal> is used to
|
|
receive messages from JMS and drive the Message-Driven PONOs (MDPs) that
|
|
are injected into it. There are one subclasses of
|
|
<literal>AbstractMessageListenerContainer</literal> packaged with
|
|
Spring - <literal>SimpleMessageListenerContainer</literal>.
|
|
Additional subclasses, in particular to participate in distributed
|
|
transactions (if the provider supports it), will be provided in future
|
|
releases. SimpleMessageListenerContainer creates a fixed number of JMS
|
|
sessions at startup and uses them throughout the lifespan of the
|
|
container.</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Transaction Management</title>
|
|
|
|
<para>Spring provides a <literal>NmsTransactionManager</literal> that
|
|
manages transactions for a single ConnectionFactory. This allows
|
|
messaging applications to leverage the managed transaction features of
|
|
Spring as described in <xref linkend="transaction" />. The
|
|
<literal>NmsTransactionManager</literal> performs local resource
|
|
transactions, binding a Connection/Session pair from the specified
|
|
ConnectionFactory to the thread. <literal>NmsTemplate</literal>
|
|
automatically detects such transactional resources and operates on them
|
|
accordingly.</para>
|
|
|
|
<para>Using Spring's <literal>SingleConnectionFactory</literal> will
|
|
result in a shared Connection, with each transaction having its own
|
|
independent Session.</para>
|
|
</section>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Sending a Message</title>
|
|
|
|
<para>The <literal>NmsTemplate</literal> contains three convenience
|
|
methods to send a message. The methods are listed below.</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><literal>void Send(IDestination destination, IMessageCreator
|
|
messageCreator)</literal></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>void Send(string destinationName, IMessageCreator
|
|
messageCreator)</literal></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>void Send(IMessageCreator
|
|
messageCreator)</literal></para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>The method differ in how the destination is specified. In first case
|
|
the JMS Destination object is specified directly. The second case
|
|
specifies the destination using a string that is then resolved to a
|
|
messaging <literal>Destination</literal> object using the
|
|
<literal>IDestinationResolver</literal> associated with the template.
|
|
The last method sends the message to the destination specified by
|
|
<literal>NmsTemplate</literal>''s
|
|
<literal>DefaultDestination</literal> property.</para>
|
|
|
|
<para>All methods take as an argument an instance of
|
|
<literal>IMessageCreator</literal> which defines the API contract for
|
|
you to create the JMS message. The interface is show below</para>
|
|
|
|
<para><programlisting language="csharp">public interface IMessageCreator {
|
|
IMessage CreateMessage(ISession session);
|
|
}</programlisting>Intermediate Sessions and MessageProducers needed to send
|
|
the message are managed by <literal>NmsTemplate</literal>. The session
|
|
passed in to the method is never null. There is a similar set methods that
|
|
use a delegate instead of the interface, which can be convenient when
|
|
writing small implementation in .NET 2.0 using anonymous delegates.
|
|
Larger, more complex implementations of the method 'CreateMessage' are
|
|
better suited to an interface based implementation.</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><literal>void SendWithDelegate(IDestination destination,
|
|
MessageCreatorDelegate messageCreatorDelegate)</literal></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>void SendWithDelegate(string destinationName,
|
|
MessageCreatorDelegate messageCreatorDelegate)</literal></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>void SendWithDelegate(MessageCreatorDelegate
|
|
messageCreatorDelegate)</literal></para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>The declaration of the delegate is</para>
|
|
|
|
<programlisting language="csharp">public delegate IMessage MessageCreatorDelegate(ISession session);</programlisting>
|
|
|
|
<para>The following class shows how to use the SendWithDelegate method
|
|
with an anonymous delegate to create a MapMessage from the supplied
|
|
Session object. The use of the anonymous delegate allows for very terse
|
|
syntax and easy access to local variables. The
|
|
<literal>NmsTemplate</literal> is constructed by passing a reference to a
|
|
ConnectionFactory.</para>
|
|
|
|
<programlisting language="csharp"> public class SimplePublisher
|
|
{
|
|
private NmsTemplate template;
|
|
|
|
public SimplePublisher()
|
|
{
|
|
template = new NmsTemplate(new ConnectionFactory("tcp://localhost:61616"));
|
|
}
|
|
|
|
public void Publish(string ticker, double price)
|
|
{
|
|
template.SendWithDelegate("APP.STOCK.MARKETDATA",
|
|
delegate(ISession session)
|
|
{
|
|
IMapMessage message = session.CreateMapMessage();
|
|
message.Body.SetString("TICKER", ticker);
|
|
message.Body.SetDouble("PRICE", price);
|
|
message.NMSPriority = 2;
|
|
return message;
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
</programlisting>
|
|
|
|
<para>A zero argument constructor and ConnectionFactory property are also
|
|
provided. Alternatively consider deriving from Spring's
|
|
<literal>NmsGatewaySupport</literal> convenience base class which provides
|
|
a ConnectionFactory property that will instantiate a NmsTemplate instance
|
|
that is made available via the property NmsTemplate.</para>
|
|
|
|
<section>
|
|
<title>Using MessageConverters</title>
|
|
|
|
<para>In order to facilitate the sending of domain model objects, the
|
|
<literal>NmsTemplate</literal> has various send methods that take a
|
|
.NET object as an argument for a message's data content. The overloaded
|
|
methods <literal>ConvertAndSend</literal> and
|
|
<literal>ReceiveAndConvert</literal> in
|
|
<literal>NmsTemplate</literal> delegate the conversion process to an
|
|
instance of the <literal>IMessageConverter</literal>
|
|
interface. This interface defines a simple contract to convert between
|
|
.NET objects and JMS messages. The default implementation
|
|
<literal>SimpleMessageConverter</literal> supports conversion
|
|
between String and TextMessage, byte[] and BytesMesssage, and
|
|
System.Collections.IDictionary and MapMessage. By using the converter,
|
|
you and your application code can focus on the business object that is
|
|
being sent or received via messaging and not be concerned with the
|
|
details of how it is represented as a JMS message.</para>
|
|
|
|
<para>The sample applications contains a XmlMessageConverter that
|
|
converts objects to an XML string and vice-versa for sending via a
|
|
TextMessage.</para>
|
|
|
|
<para>The family of <literal>ConvertAndSend</literal> messages are
|
|
similar to that of the Send method with the additional argument of type
|
|
IMessagePostProcessor. These methods are listed below.</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><code>void ConvertAndSend(object message)</code></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><code>void ConvertAndSend(object message,
|
|
IMessagePostProcessor postProcessor)</code></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><code>void ConvertAndSend(string destinationName, object
|
|
message)</code></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><code>void ConvertAndSend(string destinationName, object
|
|
message, IMessagePostProcessor postProcessor);</code></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><code>void ConvertAndSend(Destination destination, object
|
|
message)</code></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><code>void ConvertAndSend(Destination destination, object
|
|
message, IMessagePostProcessor postProcessor)</code></para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>The example below uses the default message converter to send a
|
|
Hashtable as a message to the destination "APP.STOCK".</para>
|
|
|
|
<para><programlisting language="csharp">public void PublishUsingDict(string ticker, double price)
|
|
{
|
|
IDictionary marketData = new Hashtable();
|
|
marketData.Add("TICKER", ticker);
|
|
marketData.Add("PRICE", price);
|
|
template.ConvertAndSend("APP.STOCK.MARKETDATA", marketData);
|
|
}</programlisting>To accommodate the setting of message's properties, headers,
|
|
and body that can not be generally encapsulated inside a converter
|
|
class, the <literal>IMessageConverterPostProcessor</literal>
|
|
interface gives you access to the message after it has been converted
|
|
but before it is sent. The example below demonstrates how to modify a
|
|
message header and a property after a Hashtable is converted to a
|
|
message using the IMessagePostProcessor. The methods
|
|
<literal>ConvertAndSendUsingDelegate</literal> allow for the use of
|
|
a delegate to perform message post processing. This family of methods is
|
|
listed below</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><literal>void ConvertAndSendWithDelegate(object message,
|
|
MessagePostProcessorDelegate postProcessor)</literal></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>void ConvertAndSendWithDelegate(IDestination
|
|
destination, object message, MessagePostProcessorDelegate
|
|
postProcessor)</literal></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>void ConvertAndSendWithDelegate(string
|
|
destinationName, object message, MessagePostProcessorDelegate
|
|
postProcessor)</literal></para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>The declaration of the delegate is</para>
|
|
|
|
<programlisting language="csharp">public delegate IMessage MessagePostProcessorDelegate(IMessage message);</programlisting>
|
|
|
|
<para>The following code shows this in action.</para>
|
|
|
|
<para><programlisting language="csharp">public void PublishUsingDict(string ticker, double price)
|
|
{
|
|
IDictionary marketData = new Hashtable();
|
|
marketData.Add("TICKER", ticker);
|
|
marketData.Add("PRICE", price);
|
|
template.ConvertAndSendWithDelegate("APP.STOCK.MARKETDATA", marketData,
|
|
delegate(IMessage message)
|
|
{
|
|
message.NMSPriority = 2;
|
|
message.NMSCorrelationID = new Guid().ToString();
|
|
return message;
|
|
});
|
|
}</programlisting></para>
|
|
</section>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Session and Producer Callback</title>
|
|
|
|
<para>While the send operations cover many common usage scenarios, there
|
|
are cases when you want to perform multiple operations on a JMS Session or
|
|
MessageProducer. The SessionCallback and ProducerCallback expose the
|
|
Session and Session / MessageProducer pair respectfully. The Execute()
|
|
methods on NmsTemplate execute these callback methods.</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><code>public object Execute(IProducerCallback
|
|
action)</code></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>public object Execute(ProducerDelegate
|
|
action)</literal></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><code>public object Execute(ISessionCallback
|
|
action)</code></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>public object Execute(SessionDelegate
|
|
action)</literal></para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>Where ISessionCallback and IProducerCallback are</para>
|
|
|
|
<para><programlisting language="csharp">public interface IProducerCallback
|
|
{
|
|
object DoInJms(Session session, MessageProducer producer);
|
|
}</programlisting>and</para>
|
|
|
|
<programlisting language="csharp">public interface ISessionCallback
|
|
{
|
|
object DoInJms(Session session);
|
|
}</programlisting>
|
|
|
|
<para>The delegate signatures are listed below and mirror the interface
|
|
method signature</para>
|
|
|
|
<programlisting language="csharp">public delegate object SessionDelegate(ISession session);
|
|
|
|
public delegate object ProducerDelegate(ISession session, IMessageProducer producer);</programlisting>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Receiving a message</title>
|
|
|
|
<section>
|
|
<title>Synchronous Reception</title>
|
|
|
|
<para>While messaging middleware is typically associated with
|
|
asynchronous processing, it is possible to consume messages
|
|
synchronously. The overloaded <code>Receive(..)</code> methods on
|
|
<literal>NmsTemplate</literal> provide this functionality. During a
|
|
synchronous receive, the calling thread blocks until a message becomes
|
|
available. This can be a dangerous operation since the calling thread
|
|
can potentially be blocked indefinitely. The property
|
|
<code><property>ReceiveTimeout</property></code> on
|
|
<literal>NmsTemplate</literal> specifies how long the receiver
|
|
should wait before giving up waiting for a message.</para>
|
|
|
|
<para>The <methodname>Receive</methodname> methods are listed
|
|
below</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><code>public Message Receive()</code></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><code>public Message Receive(Destination
|
|
destination)</code></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><code>public Message Receive(string
|
|
destinationName)</code></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><code>public Message ReceiveSelected(string
|
|
messageSelector)</code></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><code>public Message ReceiveSelected(string destinationName,
|
|
string messageSelector)</code></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><code>public Message ReceiveSelected(Destination destination,
|
|
string messageSelector)</code></para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>The <methodname>Receive</methodname> method without arguments will
|
|
use the <property>DefaultDestination</property>. The
|
|
<methodname>ReceiveSelected</methodname> methods apply the provided
|
|
message selector string to the <literal>MessageConsumer</literal>
|
|
that is created.</para>
|
|
|
|
<para>The <methodname>ReceiveAndConvert</methodname> methods apply the
|
|
template's message converter when receiving a message. The message
|
|
converter to use is set using the property
|
|
<literal>MessageConverter</literal> and is the
|
|
<literal>SimpleMessageConverter</literal> implementation by default.
|
|
These methods are listed below.</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><code>public object ReceiveAndConvert()</code></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><code>public object ReceiveAndConvert(Destination
|
|
destination)</code></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><code>public object ReceiveAndConvert(string
|
|
destinationName)</code></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><code>public object ReceiveSelectedAndConvert(string
|
|
messageSelector)</code></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><code>public object ReceiveSelectedAndConvert(string
|
|
destinationName, string messageSelector)</code></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><code>public object ReceiveSelectedAndConvert(Destination
|
|
destination, string messageSelector)</code></para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Asynchronous Reception</title>
|
|
|
|
<para>Asynchronous reception of messages occurs by the messaging
|
|
provider invoking a callback function. This is commonly an interface
|
|
such as the IMessageListener interface shown below, taken from the TIBCO
|
|
EMS provider.</para>
|
|
|
|
<programlisting language="csharp">public interface IMessageListener
|
|
{
|
|
void OnMessage(Message message);
|
|
}</programlisting>
|
|
|
|
<para>Other vendors may provide a delegate based version of this
|
|
callback or even both a delegate and interface options. Apache ActiveMQ
|
|
supports only the use of delegates for message reception callbacks. As a
|
|
programming convenience in
|
|
<literal>Spring.Messaging.Nms.Core</literal> is an interface
|
|
<literal>IMessageListener</literal> that can be used with
|
|
NMS.</para>
|
|
|
|
<para>Below is a simple implementation of the IMessageListener interface
|
|
that processing a message.</para>
|
|
|
|
<programlisting language="csharp">using Spring.Messaging.Nms.Core;
|
|
using Apache.NMS;
|
|
using Common.Logging;
|
|
|
|
namespace MyApp
|
|
{
|
|
public class SimpleMessageListener : IMessageListener
|
|
{
|
|
private static readonly ILog LOG = LogManager.GetLogger(typeof(SimpleMessageListener));
|
|
|
|
private int messageCount;
|
|
|
|
public int MessageCount
|
|
{
|
|
get { return messageCount; }
|
|
}
|
|
|
|
public void OnMessage(IMessage message)
|
|
{
|
|
messageCount++;
|
|
LOG.Debug("Message listener count = " + messageCount);
|
|
ITextMessage textMessage = message as ITextMessage;
|
|
if (textMessage != null)
|
|
{
|
|
LOG.Info("Message Text = " + textMessage.Text);
|
|
} else
|
|
{
|
|
LOG.Warn("Can not process message of type " message.GetType());
|
|
}
|
|
}
|
|
}</programlisting>
|
|
|
|
<para>Once you've implemented your message listener, it's time to create
|
|
a message listener container.</para>
|
|
|
|
<para>You register you listener with a message listener container that
|
|
specifies various messaging configuration parameters, such as the
|
|
ConnectionFactory, and the number of concurrent consumers to create.
|
|
There is an abstract base class for message listener containers,
|
|
<literal>AbstractMessageListenerContainer</literal>, and one
|
|
concrete implementation,
|
|
<literal>SimpleMessageListenerContainer</literal>.
|
|
<literal>SimpleMessageListenerContainer</literal> creates a fixed
|
|
number of JMS Sessions/MessageConsumer pairs as set by the property
|
|
<property>ConcurrentConsumers</property>. Here is a sample
|
|
configuration</para>
|
|
|
|
<programlisting language="myxml">
|
|
<object id="ConnectionFactory" type="Apache.NMS.ActiveMQ.ConnectionFactory, Apache.NMS.ActiveMQ">
|
|
<constructor-arg index="0" value="tcp://localhost:61616"/>
|
|
</object>
|
|
|
|
<object id="MyMessageListener" type="MyApp.SimpleMessageListener, MyApp"/>
|
|
|
|
<object id="MessageListenerContainer" type="Spring.Messaging.Nms.Listener.SimpleMessageListenerContainer, Spring.Messaging.Nms">
|
|
<property name="ConnectionFactory" ref="ConnectionFactory"/>
|
|
<property name="DestinationName" value="APP.REQUEST"/>
|
|
<property name="ConcurrentConsumers" value="10"/>
|
|
<property name="MessageListener" ref="MyMessageListener"/>
|
|
</object>
|
|
</programlisting>
|
|
|
|
<para>The above configuration will create 10 threads that process
|
|
messages off of the queue named "APP.REQUEST". The threads are those
|
|
owned by the messaging provider as a result of creating a
|
|
MessageConsumer. Other important properties are
|
|
<property>ClientID</property>, used to set the ClientID of the
|
|
Connection and <property>MessageSelector</property> to specify the
|
|
'sql-like' message selector string. Durable subscriptions are supported
|
|
via the properties <property>SubscriptionDurable</property> and
|
|
<property>DurableSubscriptionName</property>. You may also register an
|
|
exception listener using the property
|
|
<literal>ExceptionListener</literal>.</para>
|
|
|
|
<para>A custom schema to create the
|
|
<literal>SimpleMessageListener</literal> container is also provided.
|
|
Using this schema the configuration above looks like the
|
|
following</para>
|
|
|
|
<programlisting language="myxml"><objects xmlns="http://www.springframework.net"
|
|
xmlns:nms="http://www.springframework.net/nms">
|
|
|
|
<!-- other object definitions -->
|
|
<nms:listener-container connection-factory="ConnectionFactory" concurrency="10">
|
|
<nms:listener ref="MyMessageListener" destination="APP.STOCK.REQUEST" />
|
|
</nms:listener-container>
|
|
|
|
</objects></programlisting>
|
|
|
|
<para>Exceptions that are thrown during message processing can be passed
|
|
to an implementation of <literal>IExceptionHandler</literal> and
|
|
registered with the container via the property
|
|
<literal>ExceptionListener</literal>. The registered
|
|
<literal>IExceptionHandler</literal> will be invoked if the
|
|
exception is of the type <literal>NMSException</literal> (or the
|
|
equivalent root exception type for other providers). The
|
|
SimpleMessageListenerContainer will logs the exception at error level
|
|
and not propagate the exception to the provider. All handling of
|
|
acknowledgement and/or transactions is done by the listener container.
|
|
You can override the method
|
|
<literal>HandleListenerException</literal> to change this
|
|
behavior.</para>
|
|
|
|
<para>Please refer to the Spring SDK documentation for additional
|
|
description of the features and properties of
|
|
<literal>SimpleMessageListenerContainer</literal>.</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>The ISessionAwareMessageListener interface</title>
|
|
|
|
<para>The <literal>ISessionAwareMessageListener</literal> interface
|
|
is a Spring-specific interface that provides a similar contract to the
|
|
messaging provider's <literal>IMessageListener</literal> interface
|
|
or Listener delegate/event, but also provides the message handling
|
|
method with access to the Session from which the Message was
|
|
received.</para>
|
|
|
|
<programlisting language="csharp">public interface ISessionAwareMessageListener
|
|
{
|
|
void OnMessage(IMessage message, ISession session);
|
|
}</programlisting>
|
|
|
|
<para>You can also choose to implement this interface and register it
|
|
with the message listener container</para>
|
|
</section>
|
|
|
|
<section xml:id="message-listener-adapter">
|
|
<title>MessageListenerAdapater</title>
|
|
|
|
<para>The MessageListenerAdapter class is the final component in
|
|
Spring's asynchronous messaging support: in a nutshell, it allows you to
|
|
expose almost any class to be invoked as a messaging callback (there are
|
|
of course some constraints).</para>
|
|
|
|
<para>Consider the following interface definition. Notice that although
|
|
the interface extends neither the
|
|
<literal>IMessageListener</literal> nor
|
|
<literal>ISessionAwareMessageListener</literal> interfaces, it can
|
|
still be used as a Message-Driven PONOs (MDP) via the use of the
|
|
<literal>MessageListenerAdapter</literal> class. Notice also how the
|
|
various message handling methods are strongly typed according to the
|
|
contents of the various Message types that they can receive and
|
|
handle.</para>
|
|
|
|
<programlisting language="csharp">public interface MessageHandler {
|
|
|
|
void HandleMessage(string message);
|
|
|
|
void HandleMessage(Hashtable message);
|
|
|
|
void HandleMessage(byte[] message);
|
|
|
|
}</programlisting>
|
|
|
|
<para>and a class that implements this interface...</para>
|
|
|
|
<programlisting language="csharp">public class DefaultMessageHandler : IMessageHandler {
|
|
// stub implementations elided for bevity...
|
|
}</programlisting>
|
|
|
|
<para>In particular, note how the above implementation of the
|
|
IMessageHandler interface (the above DefaultMessageHandler class) has no
|
|
messaging provider API dependencies at all. It truly is a PONO that we
|
|
will make into an MDP via the following configuration.</para>
|
|
|
|
<programlisting language="myxml"><object id="MessagleHandler" type="MyApp.DefaultMessageHandler, MyApp"/>
|
|
|
|
<object id="MessageListenerAdapter" type="Spring.Messaging.Nms.Listener.Adapter.MessageListenerAdapter, Spring.Messaging.Nms">
|
|
<property name="HandlerObject" ref="MessagleHandler"/>
|
|
</object>
|
|
|
|
<object id="MessageListenerContainer" type="Spring.Messaging.Nms.Listener.SimpleMessageListenerContainer, Spring.Messaging.Nms">
|
|
<property name="ConnectionFactory" ref="ConnectionFactory"/>
|
|
<property name="DestinationName" value="APP.REQUEST"/>
|
|
<property name="MessageListener" ref="MessageListenerAdapter"/>
|
|
</object></programlisting>
|
|
|
|
<para>The previous examples relies on the fact that the default
|
|
IMessageConverter implementation of the MessageListenerAdapter is
|
|
SimpleMessageConverter that can convert from messages to strings,
|
|
byte[], and hashtables and object from a ITextMessage, IBytesMessage,
|
|
IMapMessage, and IObjectMessage respectfully.</para>
|
|
|
|
<para>Below is an example of another MDP that can only handle the
|
|
receiving of NMS ITextMessage messages. Notice how the message handling
|
|
method is actually called 'Receive' (the name of the message handling
|
|
method in a MessageListenerAdapter defaults to 'HandleMessage'), but it
|
|
is configurable (as you will see below). Notice also how the
|
|
'Receive(..)' method is strongly typed to receive and respond only to
|
|
NMS ITextMessage messages.</para>
|
|
|
|
<programlisting language="csharp">public interface TextMessageHandler {
|
|
|
|
void Receive(ITextMessage message);
|
|
}</programlisting>
|
|
|
|
<programlisting language="csharp">public class TextMessageHandler implements ITextMessageHandler {
|
|
// implementation elided for clarity...
|
|
}</programlisting>
|
|
|
|
<para>The configuration of the attendant
|
|
<literal>MessageListenerAdapter</literal> would look like
|
|
this</para>
|
|
|
|
<programlisting language="myxml"><object id="MessagleHandler" type="MyApp.DefaultMessageHandler, MyApp"/>
|
|
|
|
<object id="MessageListenerAdapter" type="Spring.Messaging.Nms.Listener.Adapter.MessageListenerAdapter, Spring.Messaging.Nms">
|
|
<property name="HandlerObject" ref="TextMessagleHandler"/>
|
|
<property name="DefaultHandlerMethod" value="Receive"/>
|
|
<!-- we don't want automatic message context extraction -->
|
|
<property name="MessageConverter">
|
|
<null/>
|
|
</property>
|
|
</object></programlisting>
|
|
|
|
<para>Please note that if the above 'MessageListener' receives a Message
|
|
of a type other than ITextMessage, a
|
|
<literal>ListenerExecutionFailedException</literal> will be thrown
|
|
(and subsequently handled by the container by logging the
|
|
exception).</para>
|
|
|
|
<para>If your <literal>IMessageConverter</literal> implementation
|
|
will return multiple object types, overloading the handler method is
|
|
perfectly acceptable, the most specific matching method will be used. A
|
|
method with an object signature would be consider a 'catch-all' method
|
|
of last resort. For example, you can have an handler interface as shown
|
|
below.</para>
|
|
|
|
<para><programlisting language="csharp">public interface IMyHandler
|
|
{
|
|
void DoWork(string text);
|
|
void DoWork(OrderRequest orderRequest);
|
|
void DoWork(InvoiceRequest invoiceRequest);
|
|
void DoWork(object obj);
|
|
}</programlisting></para>
|
|
|
|
<para>Another of the capabilities of the MessageListenerAdapter class is
|
|
the ability to automatically send back a response Message if a handler
|
|
method returns a non-void value. The adapter's message converter will be
|
|
used to convert the methods return value to a message. The resulting
|
|
message will then be sent to the Destination defined in the JMS Reply-To
|
|
property of the original Message (if one exists) , or the default
|
|
Destination set on the MessageListenerAdapter (if one has been
|
|
configured). If no Destination is found then an
|
|
<literal>InvalidDestinationException</literal> will be thrown (and
|
|
please note that this exception will not be swallowed and will propagate
|
|
up the call stack).</para>
|
|
|
|
<para>An interface that is typical when used with a message converter
|
|
that supports multiple object types and has return values is shown
|
|
below.</para>
|
|
|
|
<programlisting language="csharp">public interface IMyHandler
|
|
{
|
|
string DoWork(string text);
|
|
OrderResponse DoWork(OrderRequest orderRequest);
|
|
InvoiceResponse DoWork(InvoiceRequest invoiceRequest);
|
|
void DoWork(object obj);
|
|
}</programlisting>
|
|
|
|
<para></para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Processing messages within a messaging transaction</title>
|
|
|
|
<para>Invoking a message listener within a transaction only requires
|
|
reconfiguration of the listener container. Local message transactions
|
|
can be activated by setting the property SessionAcknowledgeMode which
|
|
for NMS is of the enum type AcknowledgementMode, to
|
|
AcknowledgementMode.Transactional. Each message listener invocation will
|
|
then operate within an active messaging transaction, with message
|
|
reception rolled back in case of listener execution failure.</para>
|
|
|
|
<para>Sending a response message (via ISessionAwareMessageListener) will
|
|
be part of the same local transaction, but any other resource operations
|
|
(such as database access) will operate independently. This usually
|
|
requires duplicate message detection in the listener implementation,
|
|
covering the case where database processing has committed but message
|
|
processing failed to commit. See the discussion on the ActiveMQ web site
|
|
<ulink
|
|
url="http://activemq.apache.org/should-i-use-xa.html">here</ulink> for
|
|
more information combining local database and messaging
|
|
transactions.</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Messaging Namespace support</title>
|
|
|
|
<para>To use the NMS namespace elements you will need to reference the
|
|
NMS schema. For information on how to set this up refer to <xref
|
|
linkend="xsd-config-body-schemas-nms" />. The namespace consists of one
|
|
top level elements: <listener-container/> which can contain one or
|
|
more <listener/> child elements. Here is an example of a basic
|
|
configuration for two listeners.</para>
|
|
|
|
<programlisting language="myxml"><nms:listener-container>
|
|
|
|
<nms:listener destination="queue.orders" ref="OrderService" method="PlaceOrder"/>
|
|
|
|
<nms:listener destination="queue.confirmations" ref="ConfirmationLogger" method="Log"/>
|
|
|
|
</nms:listener-container></programlisting>
|
|
|
|
<para>The example above is equivalent to creating two distinct listener
|
|
container object definitions and two distinct MessageListenerAdapter
|
|
object definitions as demonstrated in the section entitled <xref
|
|
linkend="message-listener-adapter" />. In addition to the attributes
|
|
shown above, the listener element may contain several optional ones. The
|
|
following table describes all available attributes:</para>
|
|
|
|
<table id="nms-namespace-listener-tbl">
|
|
<title>Attributes of the NMS <literal><listener></literal>
|
|
element</title>
|
|
|
|
<tgroup cols="2">
|
|
<colspec colname="c1" colwidth="2*" />
|
|
|
|
<colspec colname="c2" colwidth="4*" />
|
|
|
|
<thead>
|
|
<row>
|
|
<entry>Attribute</entry>
|
|
|
|
<entry>Description</entry>
|
|
</row>
|
|
</thead>
|
|
|
|
<tbody>
|
|
<row>
|
|
<entry>id</entry>
|
|
|
|
<entry><para>A object name for the hosting listener container.
|
|
If not specified, a object name will be automatically
|
|
generated.</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>destination <emphasis
|
|
role="bold">(required)</emphasis></entry>
|
|
|
|
<entry><para>The destination name for this listener, resolved
|
|
through the <literal>IDestinationResolver</literal>
|
|
strategy.</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>ref <emphasis role="bold">(required)</emphasis></entry>
|
|
|
|
<entry><para>The object name of the handler
|
|
object.</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>method</entry>
|
|
|
|
<entry><para>The name of the handler method to invoke. If the
|
|
<literal>ref</literal> points to a
|
|
<literal>IMessageListener</literal> or Spring
|
|
<literal>ISessionAwareMessageListener</literal>,
|
|
this attribute may be omitted.</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>response-destination</entry>
|
|
|
|
<entry><para>The name of the default response destination to
|
|
send response messages to. This will be applied in case of a
|
|
request message that does not carry a "NMSReplyTo" field. The
|
|
type of this destination will be determined by the
|
|
listener-container's "destination-type" attribute. Note: This
|
|
only applies to a listener method with a return value, for which
|
|
each result object will be converted into a response
|
|
message.</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>subscription</entry>
|
|
|
|
<entry><para>The name of the durable subscription, if
|
|
any.</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>selector</entry>
|
|
|
|
<entry><para>An optional message selector for this
|
|
listener.</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>pubsub-domain</entry>
|
|
|
|
<entry><para>An optional boolean value. Set to true for the
|
|
publish-subscribe domain (Topics) or false (the default) for
|
|
point-to-point domain (Queues). This is useful when using the
|
|
default implementation for destination resolvers.
|
|
</para></entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
|
|
<para>The <listener-container/> element also accepts several
|
|
optional attributes. This allows for customization of the various
|
|
strategies (for example, DestinationResolver) as well as basic messaging
|
|
settings and resource references. Using these attributes, it is possible
|
|
to define highly-customized listener containers while still benefiting
|
|
from the convenience of the namespace.</para>
|
|
|
|
<programlisting language="myxml"><jms:listener-container connection-factory="MyConnectionFactory"
|
|
destination-resolver="MyDestinationResolver"
|
|
concurrency="10">
|
|
|
|
<jms:listener destination="queue.orders" ref="OrderService" method="PlaceOrder"/>
|
|
|
|
<jms:listener destination="queue.confirmations" ref="ConfirmationLogger" method="Log"/>
|
|
|
|
</jms:listener-container></programlisting>
|
|
|
|
<para>The following table describes all available attributes. Consult
|
|
the class-level SDK documentation of the
|
|
<literal>AbstractMessageListenerContainer</literal> and its subclass
|
|
<literal>SimpleMessageListenerContainer</literal> for more detail on
|
|
the individual properties.</para>
|
|
|
|
<table id="nms-namespace-listener-container-tbl">
|
|
<title>Attributes of the NMS
|
|
<literal><listener-container></literal> element</title>
|
|
|
|
<tgroup cols="2">
|
|
<colspec colname="c1" colwidth="2*" />
|
|
|
|
<colspec colname="c2" colwidth="4*" />
|
|
|
|
<thead>
|
|
<row>
|
|
<entry>Attribute</entry>
|
|
|
|
<entry>Description</entry>
|
|
</row>
|
|
</thead>
|
|
|
|
<tbody>
|
|
<row>
|
|
<entry>connection-factory</entry>
|
|
|
|
<entry><para>A reference to the NMS
|
|
<literal>ConnectionFactory</literal> object (the
|
|
default object name is
|
|
<literal>'ConnectionFactory'</literal>).</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>destination-resolver</entry>
|
|
|
|
<entry><para>A reference to the
|
|
<literal>IDestinationResolver</literal> strategy for
|
|
resolving JMS
|
|
<literal>Destinations</literal>.</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>message-converter</entry>
|
|
|
|
<entry><para>A reference to the
|
|
<literal>IMessageConverter</literal> strategy for
|
|
converting NMS Messages to listener method arguments. Default is
|
|
a <literal>SimpleMessageConverter</literal>.</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>destination-type</entry>
|
|
|
|
<entry><para>The NMS destination type for this listener:
|
|
<literal>queue</literal>, <literal>topic</literal> or
|
|
<literal>durableTopic</literal>. The default is
|
|
<literal>queue</literal>.</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>client-id</entry>
|
|
|
|
<entry><para>The NMS client id for this listener container.
|
|
Needs to be specified when using durable
|
|
subscriptions.</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>acknowledge</entry>
|
|
|
|
<entry><para>The native NMS acknowledge mode:
|
|
<literal>auto</literal>, <literal>client</literal>,
|
|
<literal>dups-ok</literal> or <literal>transacted</literal>. A
|
|
value of <literal>transacted</literal> activates a locally
|
|
transacted <literal>Session</literal>. As an
|
|
alternative, specify the <literal>transaction-manager</literal>
|
|
attribute described below. Default is
|
|
<literal>auto</literal>.</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>concurrency</entry>
|
|
|
|
<entry><para>The number of concurrent sessions/consumers to
|
|
start for each listener. Default is 1; keep concurrency limited
|
|
to 1 in case of a topic listener or if queue ordering is
|
|
important; consider raising it for general
|
|
queues.</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>recovery-interval</entry>
|
|
|
|
<entry><para>The time interval between connection recovery
|
|
attempts. The default is 5 seconds. Specify as a TimeSpan value
|
|
using Spring's TimeSpanConverter (e.g. 10s, 10m, 3h, etc)
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>max-recovery-time</entry>
|
|
|
|
<entry><para>The maximum time try reconnection attempts. The
|
|
default is 10 minutes. Specify as a TimeSpan value using
|
|
Spring's TimeSpanConverter (e.g. 10s, 10m, 3h, etc)
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>auto-startup</entry>
|
|
|
|
<entry>Set whether to automatically start the listeners after
|
|
initialization. Default is true, optionally set to
|
|
false.</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
</section>
|
|
</section>
|
|
|
|
<section>
|
|
<title>TIBCO EMS Specific Details</title>
|
|
|
|
<para>This section covers functionality and behavior that is different
|
|
than described previously and is specific to TIBCO EMS.</para>
|
|
|
|
<section>
|
|
<title>Resource Management</title>
|
|
|
|
<para>Caching of messaging resources is usually done by a wrapping the
|
|
'raw provider' provider implementation with an implementation that will
|
|
cache messaging resources. The resources that are candidates for caching
|
|
are the Connection, Session, and MessageProducer. The JMS specification
|
|
requires that the Connection be thread safe. The Session and
|
|
MessageProducer are not required to be thread safe but they are in
|
|
TIBCO's implementation. The most important resource to cache is the EMS
|
|
Connection since the flow of events in the message template class
|
|
(EmsTemplate) is to create/close a connection on each operation and this
|
|
is an expensive operation.</para>
|
|
|
|
<para>Spring provides a convenience class, SingleConnectionFactory, in
|
|
which the same Connection is returned on calls to CreateConnection() and
|
|
all calls to .Close() on the returned Connection are ignored. Since
|
|
TIBCO's Connection class does not have an interface nor virtual methods
|
|
this strategy is not possible. An alternative strategy used is to
|
|
'hard-code' the caching of these resources within EmsTemplate and
|
|
SimpleMessageContainer. This functionality is controlled by the property
|
|
<property>CacheJmsResources</property> and is set to true by default,
|
|
resulting in caching of Connection, Session, and MessageProducer. When
|
|
using the TIBCO EMS binding for the NMS APIs you do not need to set this
|
|
property and can instead use one of the wrapper implementations in
|
|
Spring based on the NMS API that performs caching of messaging
|
|
resources.</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Queue browsing in EmsTemplate</title>
|
|
|
|
<para>The following queue browsing operations are available on
|
|
EmsTemplate</para>
|
|
</section>
|
|
</section>
|
|
</chapter> |