changes for 1.2 M1 release packaging.
This commit is contained in:
745
doc/reference/src/messaging.xml
Normal file
745
doc/reference/src/messaging.xml
Normal file
@@ -0,0 +1,745 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<chapter id="messaging">
|
||||
<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 arrise when using messaging APIs. In
|
||||
particular, this chapter deals with messaging providers whose API is done
|
||||
in the spirit of the Java Message Service (JMS) API. Vendors who provide a
|
||||
JMS inspired API include TIBCO, IBM, and Sonic Software. (If you are using
|
||||
Microsoft's Message Queue, please refer to the specific <link
|
||||
linkend="msmq">MSMQ section</link>). Second, the design of these messaging
|
||||
helper classes promote best practices in desigining a messaging
|
||||
applicaiton 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. Lastly, as there is no defacto-standard common
|
||||
API across messaging vendors, Spring provides an implementation for each
|
||||
of the major messaging middleware vendors. Portability across each vendor
|
||||
is promoted by providing a configuration schema that hides the actual
|
||||
class types used for Spring's helper class as well as consistent naming of
|
||||
the helper classes in different namespaces. The goal with vendor
|
||||
portability is to get as far as possible by simply changing your 'using'
|
||||
statements in code and schema name in the configuration. </para>
|
||||
|
||||
<para>JMS can be roughly divided into two areas of functionality, namely
|
||||
the production and consumption of messages. The
|
||||
<classname>MessageTemplate</classname> class is used for message
|
||||
production and synchronous message reception. For asynchronous reception,
|
||||
Spring provides a multi-threaded message listener container,
|
||||
<classname>SimpleMessageListenerContainer</classname>, that can be sued to
|
||||
to create Message-Driven PONOs (MDPs). The MessageConverter interface is
|
||||
used by both the MessageTemplate class and the message listener container
|
||||
to converte between provider message types and PONOs.</para>
|
||||
|
||||
<para>For each vendor there is a namespace of the type,
|
||||
<literal>Spring.Messaging.<VendorAcronym>.Core</literal>. For
|
||||
example, in the case of ActiveMQ, you would use
|
||||
<literal>Spring.Messaging.Nms.Core</literal>, for TIBCO you would use
|
||||
<literal>Spring.Messaging.Ems.Core</literal>. This namespace provides the
|
||||
core functionality for messaging. It contains the
|
||||
<literal>MessageTemplate</literal> class that simplifies the use of the
|
||||
messaging APIs by handling the creation and release of resources, much
|
||||
like the AdoTemplate does for ADO.NET. 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 classes 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>MessageConverter</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 locator 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>This chapter starts with a 'Quick tour for the impatient' that shows
|
||||
you how to get up and running quickly using Spring's message helper
|
||||
classes. You can also refer to the sample application that ships with
|
||||
Spring for additional hands-on usage. The rest of the sections in this
|
||||
chapter discusses each of the major helper classes in detail. </para>
|
||||
|
||||
<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 infrasructure 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 technology, making it more
|
||||
likely to survive technological changes over time. Spring's
|
||||
MessageConverters provide first class support for mapping messaging data
|
||||
types to PONOs. Aside from being the link between the two layers, having
|
||||
a pluggable strategy for message conversion helps support a loosely
|
||||
coupled architecture over time. Message formats will change over time,
|
||||
typically by the addition of new fields. MessageConverters can 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. In can loosely associate Spring's MessageConverters to
|
||||
XML/Object mappers but with a messaging twist.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Interopability</title>
|
||||
|
||||
<para>Messaging is a traditional area of interopabiltity across
|
||||
heterogenous 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 specifcation to provide a common
|
||||
API across messaing 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. Given this history, when messaging vendors
|
||||
created their .NET APIs, many did so by creating their own JMS inspired
|
||||
API in .NET. The NMS project's goal is to provide a common API for .NET
|
||||
thereby giving portability to the various .NET messaging providers. One
|
||||
downside of the NMS API is that it is a low-level API, much like ADO.NET
|
||||
and JDBC. Even the simplist of operations requires 10s of lines of code
|
||||
with the bulk of that code related to resource management of
|
||||
intermediate API objects. Note that the 'core' of the JMS/NMS API is
|
||||
much simplier than with ADO.NET/JDBC. So while NMS provides portability
|
||||
it also brings with it this API 'noise'. Spring's messaging support,
|
||||
both in Java and .NET, addresses the error-prone boiler plate coding
|
||||
style one needs when using thtese APIs.</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 sevice interface and use metadata (either
|
||||
XML or attributes) to configure the messaging behavior. This is a big
|
||||
improvement over using low-level vendor specific APIs. However, at the
|
||||
time of this writing, it is not clear that other messaging providers
|
||||
will provide WCF bindings. A Spring Extensions project, Spring-NMS,
|
||||
provides a WCF binding for NMS. This will let you use the WCF
|
||||
programming model but still retain portability across messaging
|
||||
providers.</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Using Spring JMS</title>
|
||||
|
||||
<section>
|
||||
<title>JmsTemplate</title>
|
||||
|
||||
<para>Code that uses the JmsTemplate only needs to implement callback
|
||||
interfaces giving them a clearly defined contract. The IMessageCreator
|
||||
callback interface creates a message given a Session provided by the
|
||||
calling code in JmsTemplate. In order to allow for more complex usage of
|
||||
the JMS API, the callback ISessionCallback provides the user with the
|
||||
JMS session and the callback IProducerCallback exposes a Session and
|
||||
MessageProducer pair.</para>
|
||||
|
||||
<para>The JMS API exposes 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 send methods in JmsTemplate, the setting of
|
||||
the QOS parameters have been exposed as bean properties to avoid
|
||||
duplication in the number of send methods. Similarly, the timeout value
|
||||
for synchronous receive calls is set using the property
|
||||
ReceiveTimeout.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Connections</title>
|
||||
|
||||
<para>The JmsTemplate requires a reference to a ConnectionFactory. The
|
||||
ConnectionFactory is part of the JMS specification and serves as the
|
||||
entry point for working with JMS. It is used by the client application
|
||||
as a factory to create connections with the JMS provider and
|
||||
encapsulates various configuration parameters, many of which are vendor
|
||||
specific such as SSL configuration options.</para>
|
||||
|
||||
<para>Note: The TIBCO implementation is not interface based and its
|
||||
methods are not virtual so no additional functionality that may
|
||||
otherewise be part of a ConnectionFactory 'Wrapper' are provided. This
|
||||
type of functionality wil be available when Spring.NET uses the
|
||||
implementation neutral NMS AP(s.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Destination Management</title>
|
||||
|
||||
<para>In Java implementations of JMS, Connections and Destinations are
|
||||
'administered objects' accessible though JNDI. In .NET each vendor has
|
||||
selected a different approach, generally JNDI inspired, 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 Sprng's XML configuration file
|
||||
by creating am implementation of IObjectFactory.</para>
|
||||
|
||||
<para>However, this approach of administerd 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 JMS provider. Examples of such advanced destination
|
||||
management would be the creation of dynamic destinations or support for
|
||||
a hierarchical namespace of destinations. The JmsTemplate delegates the
|
||||
resolution of a destination name to a JMS destination object to an
|
||||
implementation of the interface IDestinationResolver.
|
||||
DynamicDestinationResolver is the default implementation used by
|
||||
JmsTemplate and accommodates resolving dynamic destinations. A
|
||||
JndiDestinationResolver is also provided that acts as a service locator
|
||||
for destinations contained in JNDI and optionally falls back to the
|
||||
behavior contained in DynamicDestinationResolver.</para>
|
||||
|
||||
<para>Quite often the destinations used in a JMS 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 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 JNDI-like directory.. 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 disregard
|
||||
the warnings in the JMS specification and to use the TopicSession method
|
||||
createTopic(String topicName) or the QueueSession method
|
||||
createQueue(String queueName) to create a new destination with default
|
||||
destination properties. Depending on the vendor implementation,
|
||||
DynamicDestinationResolver may then also create a physical destination
|
||||
instead of only resolving one.</para>
|
||||
|
||||
<para>The boolean property PubSubDomain determines the behavior of
|
||||
dynamic destination resolution via implementations of the
|
||||
DestinationResolver interface.</para>
|
||||
|
||||
<para>You can also configure the JmsTemplate with a default destination
|
||||
via the property defaultDestination. 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.</para>
|
||||
|
||||
<para>A subclass of
|
||||
<classname>AbstractMessageListenerContainer</classname> is used to
|
||||
receive messages from JMS and drive the Message-Driven POCOs (MDPs) that
|
||||
are injected into it. The
|
||||
<classname>AbstractMessageListenerContainer</classname> is responsible
|
||||
for all threading of message reception and dispatch into the MDPs for
|
||||
processing. A message listener container is the intermediary between an
|
||||
MDP and a messaging provider, and takes care of registering to receive
|
||||
messages, participating in transactions, resource acquisition and
|
||||
release, exception conversion and suchlike. This allows you as an
|
||||
application developer to write the (posssibly complex) business logic
|
||||
associated with receiving a message (and possibly responding to it), and
|
||||
delegates boilerplate JMS infrastructure concerns to the framework.
|
||||
There are one subclasses of
|
||||
<classname>AbstractMessageListenerContainer</classname> packaged with
|
||||
Spring - <classname>SimpleMessageListenerContainer</classname>.</para>
|
||||
|
||||
<para>SimpleMessageListenerContainer creates a fixed number of JMS
|
||||
sessions at startup and uses them throughout the lifespan of the
|
||||
container. This subclass doesn't allow for dynamic adaption to runtime
|
||||
demands or participate in transactional reception of messages.</para>
|
||||
|
||||
<para>Spring.Java provides two other subclasses, one to support
|
||||
distributed transactions and the other to provide a dynamic session
|
||||
management to optimize concurrent processing. Distributed transaction
|
||||
support is not provided by .NET C# vendors (AFAIK) and neither is the
|
||||
dynamic session management support which is based on the
|
||||
ServerSessionPool SPI - an optional part of the JMS
|
||||
specification.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Transaction Management</title>
|
||||
|
||||
<para>TBD. This relates to integration with Spring's transaction
|
||||
management features, the ability to have transacted JMS sessions is
|
||||
supported.</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Sending a Message</title>
|
||||
|
||||
<para>The <classname>JmsTemplate</classname> contains three convenience
|
||||
methods to send a message. The methods are listed below.</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><literal>void Send(Destination 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>Which 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 JMS JMS
|
||||
<classname>Destination</classname> object using the DestinationResolver
|
||||
associated with the template. The last method sends the message to the
|
||||
destination specified by JmsTemplates
|
||||
<classname>DefaultDestination</classname> property.</para>
|
||||
|
||||
<para>All methods take as an argument an instance of IMessageCreator which
|
||||
defines the API contract for you to create the JMS message. The interface
|
||||
is show below</para>
|
||||
|
||||
<para><programlisting>public interface IMessageCreator {
|
||||
Message CreateMessage(Session session);
|
||||
}</programlisting>Intermediate JMS Sessions and MessageProducers needed to
|
||||
send the message are managed by JmsTemplate. 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 convenientwhen writing small
|
||||
implementaitons 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(Destination 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>public delegate Message MessageCreatorDelegate(Session session);</programlisting>
|
||||
|
||||
<para>The following class shows how to use the API with an anonymous
|
||||
delegate, making for very terse syntax and easy access to local variables.
|
||||
A more realistic example would create the JmsTemplate via dependency
|
||||
injection, allowing for easy configuration of the connection string. A
|
||||
convenience class, JmsGatewaySupport, already contains a property of type
|
||||
JmsTemplate for you to use in</para>
|
||||
|
||||
<programlisting> public class SimplePublisher
|
||||
{
|
||||
private JmsTemplate template;
|
||||
|
||||
public SimplePublisher()
|
||||
{
|
||||
template = new JmsTemplate(new ConnectionFactory("tcp://localhost:7222"));
|
||||
template.PubSubDomain = true;
|
||||
}
|
||||
|
||||
public void Publish(string ticker, double price)
|
||||
{
|
||||
template.SendWithDelegate("APP.STOCK",
|
||||
delegate(Session session)
|
||||
{
|
||||
MapMessage message = session.CreateMapMessage();
|
||||
message.SetString("TICKER", ticker);
|
||||
message.SetDouble("PRICE", price);
|
||||
message.Priority = 2;
|
||||
return message;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</programlisting>
|
||||
|
||||
<section>
|
||||
<title>Using MessageConverters</title>
|
||||
|
||||
<para>In order to facilitate the sending of domain model objects, the
|
||||
<classname>JmsTemplate</classname> has various send methods that take a
|
||||
.NET object as an argument for a message's data content. The overloaded
|
||||
methods ConvertAndSend and ReceiveAndConvert in
|
||||
<classname>JmsTemplate</classname> delegate the conversion process to an
|
||||
instance of the <interfacename>MessageConverter</interfacename>
|
||||
interface. This interface defines a simple contract to convert between
|
||||
.NET objects and JMS messages. The default implementation
|
||||
<classname>SimpleMessageConverter</classname> 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 JMS and not be concerned with the details of
|
||||
how it is represented as a JMS message.</para>
|
||||
|
||||
<para>The family of ConvertAndSend 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>In the previous example the message priority was set inside the
|
||||
callback. Generally speaking, converters should not be responsible for
|
||||
setting Quality of Service parameters since they are not aware of the
|
||||
context in which they are being called. The following code show this in
|
||||
action.</para>
|
||||
|
||||
<para><programlisting>public void PublishUsingDict(string ticker, double price)
|
||||
{
|
||||
IDictionary marketData = new Hashtable();
|
||||
marketData.Add("TICKER", ticker);
|
||||
marketData.Add("PRICE", price);
|
||||
template.ConvertAndSend("APP.STOCK", marketData);
|
||||
}</programlisting>A reflection based converter that can converter arbitrary
|
||||
objects is available as a seperate project.</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 JMS
|
||||
Session and Session / MessageProducer pair respectfully. The Execute()
|
||||
methods on JmsTemplate execute these callback methods.</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><code>public object Execute(IProducerCallback
|
||||
action)</code></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><code>public object Execute(ISessionCallback
|
||||
action)</code></para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>Where ISessionCallback and IProducerCallback are</para>
|
||||
|
||||
<para><programlisting>public interface IProducerCallback
|
||||
{
|
||||
object DoInJms(Session session, MessageProducer producer);
|
||||
}</programlisting>and</para>
|
||||
|
||||
<programlisting>public interface ISessionCallback
|
||||
{
|
||||
object DoInJms(Session session);
|
||||
}</programlisting>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Receiving a message</title>
|
||||
|
||||
<section>
|
||||
<title>Synchronous Reception</title>
|
||||
|
||||
<para>While JMS is typically associated with asynchronous processing, it
|
||||
is possible to consume messages synchronously. The overloaded
|
||||
<code>Receive(..)</code> methods 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> 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>Recieve</methodname> method without arguments used
|
||||
the <property>DefaultDestination</property>. The
|
||||
<methodname>RecieveSelected</methodname> methods apply the provided JMS
|
||||
message selector string to the <classname>MessageConsumer</classname>
|
||||
that is created.</para>
|
||||
|
||||
<para>The <methodname>ReceiveAndConvert</methodname> methods apply the
|
||||
templates message converter when receiving a message. 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>You can register a class that implements the
|
||||
<interfacename>IMessageListener</interfacename> interface. In the case
|
||||
of TIBCO EMS this interface is defined as</para>
|
||||
|
||||
<programlisting>public interface IMessageListener
|
||||
{
|
||||
void OnMessage(Message message);
|
||||
}</programlisting>
|
||||
|
||||
<para>Other vendors may provide a delegate based version of this
|
||||
interface.</para>
|
||||
|
||||
<para>You register you listener with a message listener container that
|
||||
specifies JMS configuration parameters and the number of concurrent
|
||||
consumers to create. There is an abstract base class for message
|
||||
listener containers,
|
||||
<classname>AbstractMessageListenerContainer</classname>, and one
|
||||
concrete implementation,
|
||||
<classname>SimpleMessageListenerContainer</classname>.
|
||||
<classname>SimpleMessageListenerContainer</classname> creates a fixed
|
||||
number of JMS Sessions/MessageConsumer pairs as set by the property
|
||||
<property>ConcurrentConsumers</property>. Here is a sample
|
||||
configuration</para>
|
||||
|
||||
<programlisting> <object id="connectionFactory" type="TIBCO.EMS.ConnectionFactory, TIBCO.EMS">
|
||||
<constructor-arg index="0" value="tcp://localhost:7222"/>
|
||||
</object>
|
||||
|
||||
<object id="messageListener" type="MyApp.MyMessageListener, MyApp"/>
|
||||
|
||||
<object id="jmsContainer" type="Spring.Messaging.Tibco.Ems.Listener.SimpleMessageListenerContainer, Spring.Messaging.Tibco.Ems">
|
||||
<property name="ConnectionFactory" ref="connectionFactory"/>
|
||||
<property name="DestinationName" value="APP.REQUEST"/>
|
||||
<property name="ConcurrentConsumers" value="10"/>
|
||||
<property name="MessageListener" ref="messageListener"/>
|
||||
</object></programlisting>
|
||||
|
||||
<para>The property <property>PubSubDomain</property> is by defalt false,
|
||||
meaning point-to-point/Queue delivery semantics. The above configuration
|
||||
will create 10 threads that process messages off of the queue named
|
||||
"APP.REQUEST". The threads are those owned by the JMS provider as a
|
||||
result of creating a JMS MessageConsumer. Other important properties are
|
||||
<property>ClientID</property>, used to set the ClientID of the JMS
|
||||
Connection and <property>MessageSelector</property> to specify the JMS
|
||||
'sql-like' message selector string. Durable subscriptions are supported
|
||||
via the properties <property>SubscriptionDurable</property> and
|
||||
<property>DurableSubscriptionName</property>. You may also register a
|
||||
listener using the property ExceptionListener.</para>
|
||||
|
||||
<section>
|
||||
<title>MessageListenerAdapater</title>
|
||||
|
||||
<para>The MessageListenerAdapter allows methods of a class that does
|
||||
not implement the IMessageListener interface to be invoked upon
|
||||
message delivery. Lets call this class the 'message handler' class. To
|
||||
achive this goal the MessageListenerAdapter implements the standard
|
||||
IMessageListener interface to recieve a message and then delegates the
|
||||
processing to the message handler class. Since the message handler
|
||||
class does not contain methods that refer to JMS artifacts such as
|
||||
Message,TextMessage etc, the MessageListenerAdapter uses a
|
||||
MessageConverter to bridge the JMS and 'plain object' worlds. As a
|
||||
reminder, the provided SimpleMessageConverter converts from
|
||||
TextMessage to string, BytesMessage to byte[], and MapMessage to
|
||||
IDictionary. Once the incoming message is converted to an IDictionary
|
||||
(for example) a method with the name 'Handle' is invoked via
|
||||
reflection passing in the IDictionary as an argument.</para>
|
||||
|
||||
<para>Using the SimpleMessageConverter, your "plain old object'
|
||||
messaging callback implementation would look like this.</para>
|
||||
|
||||
<programlisting>public class SimpleMessageHandler
|
||||
{
|
||||
public void HandleObject(IDictionary dict)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
public void HandleObject(string text)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
public void HandleObject(byte[] data)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
}</programlisting>
|
||||
|
||||
<para>Notice how the various message handling methods are strongly
|
||||
typed according to the contents of the various Message types that they
|
||||
can receive and handle. The following configuration shows how to hook
|
||||
up this class to process incoming JMS messages.</para>
|
||||
|
||||
<para><programlisting>
|
||||
<object name="simpleMessageHandler, type="MyApp.SimpleMessageHandler, MyApp"/>
|
||||
|
||||
<object name="simpleMessageConverter"
|
||||
type="Spring.Messaging.Tibco.Ems.Support.Converter.SimpleMessageConverter, Spring.Messaging.Tibco.Ems"/>
|
||||
|
||||
<object id="messageListenerAdapter" type="Spring.Messaging.Tibco.Ems.Listener.Adapter.MessageListenerAdapter, ">
|
||||
<property name="DelegateObject" ref="simpleMessageHandler"/>
|
||||
<property name="DefaultListenerMethod" value="HandleObject"/>
|
||||
<property name="MessageConverter" ref="simpleMessageConverter"/>
|
||||
</object>
|
||||
|
||||
|
||||
<object id="connectionFactory" type="TIBCO.EMS.ConnectionFactory, TIBCO.EMS">
|
||||
<constructor-arg index="0" value="tcp://localhost:7222"/>
|
||||
</object>
|
||||
|
||||
<object id="jmsContainer" type="Spring.Messaging.Tibco.Ems.Listener.SimpleMessageListenerContainer, Spring.Messaging.Tibco.Ems">
|
||||
<property name="ConnectionFactory" ref="connectionFactory"/>
|
||||
<property name="DestinationName" value="APP.REQUEST"/>
|
||||
<property name="ConcurrentConsumers" value="10"/>
|
||||
<property name="MessageListener" ref="messageListener"/>
|
||||
</object></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. Any non-null value that is
|
||||
returned from the execution of the handler method will (in the default
|
||||
configuration) be converted into a TextMessage. The resulting
|
||||
TextMessage will then be sent to the Destination (if one exists)
|
||||
defined in the JMS Reply-To property of the original Message, or the
|
||||
default Destination set on the MessageListenerAdapter (if one has been
|
||||
configured); if no Destination is found then an
|
||||
InvalidDestinationException will be thrown (and please note that this
|
||||
exception will not be swallowed and will propagate up the call
|
||||
stack).</para>
|
||||
|
||||
<para></para>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>TIBCO Specific Details</title>
|
||||
|
||||
<para>Caching of JMS resources is usually done by a wrapping the 'raw
|
||||
provider' JMS implementation with an implementation that will cache JMS
|
||||
resources. The resources that are candidates for caching are the JMS
|
||||
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 JMS Connection since the flow of
|
||||
events in JmsTemplate is to create/close a connection on each operation
|
||||
and this is an expensive operation. In the Java version of JmsTemplate a
|
||||
class, SingleConnectionFactory is provided 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 is to 'hard-code' the caching of these resources
|
||||
within JmsTemplate 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 integration with NMS (a set of common interfaces for
|
||||
.NET JMS providers) is completed this will not be necessary and we can use
|
||||
a wrapper implementation of the NMS API that performs caching of JMS
|
||||
resources in an appropriate manner for each vendor.</para>
|
||||
</section>
|
||||
</chapter>
|
||||
Reference in New Issue
Block a user