1151 lines
59 KiB
XML
1151 lines
59 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<chapter id="msmq">
|
|
<title>Message Oriented Middleware - MSMQ</title>
|
|
|
|
<section>
|
|
<title>Introduction</title>
|
|
|
|
<para>The goals of Spring's MSMQ 3.0 messaging support is to raise the
|
|
level of abstraction when writing MSMQ applications. The
|
|
<literal>System.Messaging</literal> API is a low-level API that provides
|
|
the basis for creating a messaging application. However, 'Out-of-the-box',
|
|
<literal>System.Messaging</literal> leaves the act of creating
|
|
sophisticated multi-threaded messaging servers and clients as an
|
|
infrastructure activity for the developer. Spring fills this gap by
|
|
proving easy to use helper classes that makes creating an enterprise
|
|
messaging application easy. These helper classes take into account the
|
|
nuances of the <literal>System.Messaging</literal> API, such as its lack
|
|
of thread-safety in many cases, the handling of so-called 'poison
|
|
messages' (messages that are endlessly redelivered due to an unrecoverable
|
|
exception during message processing), and combining database transactions
|
|
with message transactions. Other goals of Spring's MSMQ messaging support
|
|
are to support messaging best practices, in particular encouraging a clean
|
|
architectural layering that separates the messaging middleware specifics
|
|
from the core business processing.</para>
|
|
|
|
<para>Spring's approach to distributed computing has always been to
|
|
promote a plain old .NET object approach or a PONO programming model. In
|
|
this approach plain .NET objects are those that are devoid of any
|
|
reference to a particular middleware technology. Spring provides the
|
|
'adapter' classes that converts between the middleware world, in this case
|
|
MSMQ, and the oo-world of your business processing. This is done through
|
|
the use of Spring's <classname>MessageListenerAdapter</classname> class
|
|
and <classname>IMessageConverters</classname>.</para>
|
|
|
|
<para>The namespace <literal>Spring.Messaging</literal> provides the core
|
|
functionality for messaging. It contains the class
|
|
<classname>MessageQueueTemplate</classname> that simplifies the use of
|
|
<classname>System.Messaging.MessageQueue</classname> by handling the lack
|
|
of thread-safety in most of
|
|
<literal>System.Messaging.MessageQueue's</literal> methods (for example
|
|
<literal>Send</literal>). A single instance of
|
|
<classname>MessageQueueTemplate</classname> can be used throughout your
|
|
application and Spring will ensure that a different instance of a
|
|
<classname>MessageQueue</classname> class is used per thread when using
|
|
<classname>MessageQueueTemplate's</classname> methods. This per-thread
|
|
instance of a <classname>System.Messaging.MessageQueue</classname> is also
|
|
available via its property <classname>MessageQueue</classname>. The
|
|
<classname>MessageQueueTemplate</classname> class is also aware of the
|
|
presence of either an 'ambient' <literal>System.Transaction's</literal>
|
|
transaction or a local
|
|
<classname>System.Messaging.MessageQueueTransaction</classname>. As such
|
|
if you use <classname>MessageQueueTemplate's</classname> send and receive
|
|
methods, unlike with plain use of
|
|
<classname>System.Messaging.MessageQueue</classname>, you do not need to
|
|
keep track of this information yourself and call the correct overloaded
|
|
<literal>System.Messaging.MessageQueue</literal> method for a specific
|
|
transaction environment. When using a
|
|
<literal>System.Messaging.MessageQueueTransaction</literal> this would
|
|
usually require you as a developer to come up with your own mechanism for
|
|
passing around a <literal>MessageQueueTransaction</literal> to multiple
|
|
classes and layers in your application.
|
|
<literal>MessageQueueTemplate</literal> manages this for you, so you don't
|
|
have to do so yourself. These resource management and transaction features
|
|
of <literal>MessageQueueTemplate</literal> are quite analogous to the
|
|
transactional features of Spring's <literal>AdoTemplate</literal> in case
|
|
you are already familiar with that functionality.</para>
|
|
|
|
<para>For asynchronous reception Spring provides several multi-threaded
|
|
message listener containers. You can pick and configure the container that
|
|
matches your message transactional processing needs and configure
|
|
poison-message handling policies. The message listener container leverages
|
|
Spring's support for managing transactions. Both DTC, local messaging
|
|
transactions, and local database transactions are supported. In
|
|
particular, you can easily coordinate the commit and rollback of a local
|
|
MessageQueueTransaction and a local database transaction when they are
|
|
used together.</para>
|
|
|
|
<para>From a programming perspective, Spring's MSMQ support involves you
|
|
<emphasis>configuring</emphasis> message listener containers and
|
|
<emphasis>writing a callback function</emphasis> for message processing.
|
|
On the sending side, it involves you learning how to use
|
|
<classname>MessageQueueTemplate</classname>. In both cases you will quite
|
|
likely want to take advantage of using
|
|
<classname>MessageListenerConverters</classname> so you can better
|
|
structure the translation from the System.Messaging.Message data structure
|
|
to your business objects. After the initial learning hurdle, you should
|
|
find that you will be much more productive leveraging Spring's helper
|
|
classes to write enterprise MSMQ applications than rolling your own
|
|
infrastructure. Feedback and new feature requests are always
|
|
welcome.</para>
|
|
|
|
<para>The Spring.MsmqQuickstart application located in the examples
|
|
directory of the distribution shows this functionality in action.</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>A quick tour for the impatient</title>
|
|
|
|
<para>Here is a quick example of how to use Spring's MSMQ support to
|
|
create a client that sends a message and a multi-threaded server
|
|
application that receives the message. (The client code could also be used
|
|
as-is in a multi-threaded environment but this is not
|
|
demonstrated).</para>
|
|
|
|
<para>On the client side you create an instance of the
|
|
<classname>MessageQueueTemplate</classname> class and configure it to use
|
|
a <classname>MessageQueue</classname>. This can be done programmatically
|
|
but it is common to use dependency injection and Spring's XML
|
|
configuration file to configure your client class as shown below.</para>
|
|
|
|
<programlisting> <object id='questionTxQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
|
|
<property name='Path' value='.\Private$\questionTxQueue'/>
|
|
<property name='MessageReadPropertyFilterSetAll' value='true'/>
|
|
</object>
|
|
|
|
<object id="messageQueueTemplate" type="Spring.Messaging.Core.MessageQueueTemplate, Spring.Messaging">
|
|
<property name="MessageQueueObjectName" value="questionTxQueue"/>
|
|
</object>
|
|
|
|
<<emphasis role="bold">!-- Class you write --></emphasis>
|
|
<object id="questionService" type="MyNamespace.QuestionService, MyAssembly">
|
|
<property name="MessageQueueTemplate" ref="messageQueueTemplate"/>
|
|
<object>
|
|
|
|
</programlisting>
|
|
|
|
<para>The <classname>MessageQueue</classname> object is created via an
|
|
instance of <classname>MessageQueueFactoryObject</classname> and the
|
|
<classname>MessageQueueTemplate</classname> refers to this factory object
|
|
by name and not by reference. The <classname>SimpleSender</classname>
|
|
class looks like this</para>
|
|
|
|
<programlisting>public class QuestionService : IQuestionService
|
|
{
|
|
private MessageQueueTemplate messageQueueTemplate;
|
|
|
|
public MessageQueueTemplate {
|
|
get { return messageQueueTemplate; }
|
|
set { messageQueueTemplate = value; }
|
|
}
|
|
|
|
public void SendQuestion(string question)
|
|
{
|
|
MessageQueueTemplate.ConvertAndSend(question);
|
|
}
|
|
}</programlisting>
|
|
|
|
<para>This class can be shared across multiple threads and the
|
|
<classname>MessageQueueTemplate</classname> will take care of managing
|
|
thread local access to a
|
|
<classname>System.Messaging.MessageQueue</classname> as well as any
|
|
<classname>System.Messaging.IMessageFormatter</classname>
|
|
instances.</para>
|
|
|
|
<para>Furthermore, since this is a transactional queue (only the name
|
|
gives it away), the message will be sent using a single local messaging
|
|
transaction. The conversion from the string to the underling message is
|
|
managed by an instance of the <classname>IMessageConverter</classname>
|
|
class. By default an implementation that uses an
|
|
<classname>XmlMessageFormatter</classname> with a
|
|
<literal>TargetType</literal> of <classname>System.String</classname> is
|
|
used. You can configure the <literal>MessageQueueTemplate</literal> to use
|
|
other <classname>IMessageConveter</classname> implementations that do
|
|
conversions above and beyond what the 'stock'
|
|
<classname>IMessageFormatters</classname> do. See the section on
|
|
MessageConverters for more details.</para>
|
|
|
|
<para>On the receiving side we would like to consume the messages
|
|
transactionally from the queue. Since no other database operations are
|
|
being performed in our server side processing, we select the
|
|
<classname>TransactionMessageListenerContainer</classname> and configure
|
|
it to use the <classname>MessageQueueTransactionManager</classname>. The
|
|
<classname>MessageQueueTransactionManager</classname> an implementation of
|
|
Spring's <classname>IPlatformTransactionManager</classname> abstraction
|
|
that provides a uniform API on top of various transaction manager
|
|
(ADO.NET,NHibernate, MSMQ, etc). Spring's
|
|
<classname>MessageQueueTransactionManager</classname> is responsible for
|
|
createing, committing, and rolling back a MSMQ
|
|
<classname>MessageQueueTransaction</classname>.</para>
|
|
|
|
<para>While you can create the message listener container
|
|
programmatically, we will show the declarative configuration approach
|
|
below</para>
|
|
|
|
<programlisting> <emphasis role="bold"><!-- Queue to receive from --></emphasis>
|
|
<object id='questionTxQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
|
|
<property name='Path' value='.\Private$\questionTxQueue'/>
|
|
<property name='MessageReadPropertyFilterSetAll' value='true'/>
|
|
</object>
|
|
|
|
<emphasis role="bold"><!-- MSMQ Transaction Manager --></emphasis>
|
|
<object id="messageQueueTransactionManager" type="Spring.Messaging.Core.MessageQueueTransactionManager, Spring.Messaging"/>
|
|
|
|
<emphasis role="bold"> <!-- Message Listener Container that uses MSMQ transactional for receives --></emphasis>
|
|
<object id="transactionalMessageListenerContainer" type="Spring.Messaging.Listener.TransactionalMessageListenerContainer, Spring.Messaging">
|
|
<property name="MessageQueueObjectName" value="questionTxQueue"/>
|
|
<property name="PlatformTransactionManager" ref="messageQueueTransactionManager"/>
|
|
<property name="MaxConcurrentListeners" value="10"/>
|
|
<property name="MessageListener" ref="messageListenerAdapter"/>
|
|
</object>
|
|
|
|
<emphasis role="bold"> <!-- Adapter to call a PONO as a messaging callback --></emphasis>
|
|
<object id="messageListenerAdapter" type="Spring.Messaging.Listener.MessageListenerAdapter, Spring.Messaging">
|
|
<property name="HandlerObject" ref="questionHandler"/>
|
|
</object>
|
|
|
|
<emphasis role="bold"><!-- The PONO class that you write --></emphasis>
|
|
<object id="questionHandler" type="MyNamespace.QuestionHandler, MyAssembly"/>
|
|
</programlisting>
|
|
|
|
<para>We have specified the queue to listen, that we want to consume the
|
|
messages transactionally, process messages from the queue using 10
|
|
threads, and that our plain object that will handle the business
|
|
processing is of the type <classname>QuestionHandler</classname>. The only
|
|
class you need to write, <classname>QuestionHandler</classname>, looks
|
|
like</para>
|
|
|
|
<programlisting>public class QuestionHandler : IQuestionHandler
|
|
{
|
|
public void HandleObject(string question)
|
|
{
|
|
// perform message processing here
|
|
|
|
Console.WriteLine("Received question: " + question);
|
|
|
|
// use an instance of MessageQueueTemplate and have other MSQM send operations
|
|
// partake in the same local message transaction used to receive
|
|
}
|
|
|
|
}</programlisting>
|
|
|
|
<para>That is general idea. You write the sender class using
|
|
<classname>MessageQueueTemplate</classname> and the consumer class which
|
|
does not refer to any messaging specific class. The rest is configuration
|
|
of Spring provided helper classes.</para>
|
|
|
|
<para>Note that if the <literal>HandleObject</literal> method has returned
|
|
a string value a reply message would be sent to a response queue. The
|
|
response queue would be taken from the Message's own
|
|
<literal>ResponseQueue</literal> property or can be specified explicitly
|
|
using MessageListenerAdapter's <literal>DefaultResponseQueueName</literal>
|
|
property.</para>
|
|
|
|
<para>If an exception is thrown inside the QuestionHandler, then the MSMQ
|
|
transaction is rolled back, putting the message back on the queue for
|
|
redelivery. If the exception is not due to a transient error in the
|
|
system, but a logical processing exception, then one would get endless
|
|
redelivery of the message - clearly not a desirable situation. These
|
|
messages are so called 'poison messages' and a strategy needs to be
|
|
developed to deal with them. This is left as a development task if you
|
|
when using the System.Messaging APIs but Spring provides a strategy for
|
|
handling poison messages, both for DTC based message reception as well as
|
|
for local messaging transactions.</para>
|
|
|
|
<para>In the last part this 'quick tour' we will configure the message
|
|
listener container to handle poison messages. This is done by creating an
|
|
instance of <classname>SendToQueueExceptionHandler</classname> and setting
|
|
the property <literal>MaxRetry</literal> to be the number of exceptions or
|
|
retry attempts we are willing to tolerate before taking corrective
|
|
actions. In this case, the corrective action is to send the message to
|
|
another queue. We can then create other message listener containers to
|
|
read from those queues and handle the messages appropriately or perhaps
|
|
you will avoid automated processing of these messages and take manual
|
|
corrective actions.</para>
|
|
|
|
<programlisting>
|
|
<emphasis role="bold"> <!-- The 'error' queue to send poison messages --></emphasis>
|
|
<object id='errorQuestionTxQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
|
|
<property name='Path' value='.\Private$\errorQuestionTxQueue'/>
|
|
<property name='MessageReadPropertyFilterSetAll' value='true'/>
|
|
</object>
|
|
|
|
<emphasis role="bold"> <!-- Message Listener Container that uses MSMQ transactional for receives --></emphasis>
|
|
<object id="transactionalMessageListenerContainer" type="Spring.Messaging.Listener.TransactionalMessageListenerContainer, Spring.Messaging">
|
|
|
|
<emphasis role="bold"><!-- as before but adding -->
|
|
</emphasis>
|
|
<property name="MessageTransactionExceptionHandler" ref="messageTransactionExceptionHandler"/>
|
|
</object>
|
|
|
|
<emphasis role="bold"> <!-- Poison message handling policy --></emphasis>
|
|
<object id="messageTransactionExceptionHandler" type="Spring.Messaging.Listener.SendToQueueExceptionHandler, Spring.Messaging">
|
|
<property name="MaxRetry" value="5"/>
|
|
<property name="MessageQueueObjectName" value="errorQuestionTxQueue"/>
|
|
</object></programlisting>
|
|
|
|
<para>In the event of an exception while processing the message, the
|
|
message transaction will be rolled back (putting the message back on the
|
|
queue questionTxQueue for redelivery). If the same message causes an
|
|
exception in processing 5 times ,then it will be sent transactionally to
|
|
the errorQuestionTxQueue and the message transaction will commit (removing
|
|
it from the queue questionTxQueue). You can also specify that certain
|
|
exceptions should commit the transaction (remove from the queue) but this
|
|
is not shown here ,see below for more informatio non this functionality
|
|
The <classname>SendToQueueExceptionHandler</classname> implements the
|
|
interface <classname>IMessageTransactionExceptionHandler</classname>
|
|
(discussed below) so you can write your own implementations should the
|
|
provided ones not meet your needs.</para>
|
|
|
|
<para>That's the quick tour folks. Hopefully you got a general feel for
|
|
how things work, what requires configuration, and what is the code you
|
|
need to write. The following sections describe each of Spring's helper
|
|
classes in more detail. The sample application that ships with Spring is
|
|
also a good place to get started.</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Using Spring MSMQ</title>
|
|
|
|
<section>
|
|
<title>MessageQueueTemplate</title>
|
|
|
|
<para>The <literal>MessageQueueTemplate</literal> is used for
|
|
synchronously sending and receiving messages. A single instance can be
|
|
shared across multiple threads, unlike the standard
|
|
<literal>System.Messaging.MessageQueue</literal> class. (One less
|
|
resource management issue to worry about!) A thread-local instance of
|
|
the <literal>MessageQueue</literal> class is available via
|
|
<literal>MessageQueueTemplate's</literal> property
|
|
<literal>MessageQueue</literal>. A
|
|
<classname>MessageQueueTemplate</classname> is created by passing a
|
|
reference to the name of a
|
|
<classname>MessageQueueFactoryObject</classname>, you can think of it as
|
|
a friendly name for your <classname>MessagingQueue</classname> and the
|
|
recipe of how to create an instance of it. See the following section on
|
|
<classname>MessageQueueFactoryObject</classname> for more
|
|
information.</para>
|
|
|
|
<para>The <literal>MessageQueueTemplate</literal> also provides several
|
|
convenience methods for sending and receiving messages. A family of
|
|
overloaded <literal>ConvertAndSend</literal> and
|
|
<literal>ReceiveAndConvert</literal> methods allow you to send and
|
|
receive an object. The default message queue to send and receive from is
|
|
specified using the <literal>MessageQueueTemplate's</literal> property
|
|
<literal>MessageQueueObjectName</literal>. The responsibility of
|
|
converting the object to a <literal>Message</literal> and vice versa is
|
|
given to the template's associated <literal>IMessageConverter</literal>
|
|
implementation. This can be set using the property
|
|
<literal>MessageConverter</literal>. The default implementation,
|
|
<classname>XmlMessageConverter</classname>, uses an
|
|
<classname>XmlMessageFormatter</classname> with its
|
|
<literal>TargetType</literal> set to
|
|
<classname>System.String</classname>. Note that
|
|
<classname>System.Messaging.IMessageFormatter</classname> classes are
|
|
also not thread safe, so <classname>MessageQueueTemplate</classname>
|
|
ensures that thread-local instances of
|
|
<classname>IMessageConverter</classname> are used (as they generally
|
|
wrap <classname>IMessageFormatter's</classname> that are not
|
|
thread-safe).</para>
|
|
|
|
<para>You can use the <literal>MessageQueueTemplate</literal> to send
|
|
messages to other MessageQueues by specifying their queue 'object name',
|
|
the name of the <classname>MessageQueueFactoryObject</classname>.</para>
|
|
|
|
<para>The family of overloaded <literal>ConvertAndSend</literal> and
|
|
<literal>ReceiveAndConvert</literal> methods are shown below</para>
|
|
|
|
<programlisting>void ConvertAndSend(object obj);
|
|
|
|
void ConvertAndSend(object obj, MessagePostProcessorDelegate messagePostProcessorDelegate);
|
|
|
|
void ConvertAndSend(string messageQueueObjectName, object message);
|
|
|
|
void ConvertAndSend(string messageQueueObjectName, object obj, MessagePostProcessorDelegate messagePostProcessorDelegate);
|
|
|
|
object ReceiveAndConvert();
|
|
|
|
object ReceiveAndConvert(string messageQueueObjectName);</programlisting>
|
|
|
|
<para>The transactional settings of the underlying overloaded
|
|
<classname>System.Messaging.MessageQueue</classname> Send method that
|
|
are used are based on the following algorithm. If the message queue is
|
|
transactional and there is an ambient
|
|
<classname>MessageQueueTransaction</classname> in thread local storage
|
|
(put there via the use of Spring's
|
|
<classname>MessageQueueTransactionManager</classname> or
|
|
<classname>TransactionalMessageListenerContainer</classname>), the
|
|
message will be sent transactionally using the
|
|
<classname>MessageQueueTransaction</classname> object in thread local
|
|
storage. This lets you group together multiple messaging operations
|
|
within the same transaction without having to explicitly pass around the
|
|
<classname>MessageQueueTransaction</classname> object. If the message
|
|
queue is transactional but there is no ambient
|
|
<classname>MessageQueueTransaction</classname>, then a single message
|
|
transaction is created on each messaging operation.
|
|
(MessageQueueTransactionType = Single). If there is an ambient
|
|
System.Transactions transaction then that transaction will be used
|
|
(MessageQueueTransactionType = Automatic). Finally, if the queue is not
|
|
transactional, then a non-transactional send
|
|
(MessageQueueTransactionType = None) is used.</para>
|
|
|
|
<para>The delegate <classname>MessagePostProcessorDelegate</classname>
|
|
has the following signature</para>
|
|
|
|
<programlisting>public delegate Message MessagePostProcessorDelegate(Message message);</programlisting>
|
|
|
|
<para>This lets you modify the message after it has been converted from
|
|
and object to a message using the
|
|
<classname>IMessageConverter</classname> but before it is sent. This is
|
|
useful for setting <classname>Message</classname> properties (e.g.
|
|
<literal>CorrelationId</literal>, <literal>AppSpecific</literal>,
|
|
<literal>TimeToReachQueue</literal>). Using anonymous delegates in .NET
|
|
2.0 makes this a very succinct coding task. If you have elaborate
|
|
properties that need to be set, perhaps creating a custom
|
|
<classname>IMessageConverter</classname> would be appropriate.</para>
|
|
|
|
<para>Overloaded <literal>Send</literal> and <literal>Receive</literal>
|
|
operations that use the algorithm listed above to set transactional
|
|
delivery options are also available. These are listed below</para>
|
|
|
|
<programlisting>Message Receive();
|
|
|
|
Message Receive(string messageQueueObjectName);
|
|
|
|
void Send(Message message);
|
|
|
|
void Send(string messageQueueObjectName, Message message);
|
|
|
|
void Send(MessageQueue messageQueue, Message message);</programlisting>
|
|
|
|
<para>Note that in the last <literal>Send</literal> method that takes a
|
|
<classname>MessageQueue</classname> instance, it is the callers
|
|
responsibility to ensure that this instance is not accessed from
|
|
multiple threads. This <literal>Send</literal> method is commonly used
|
|
when getting the <classname>MessageQueue</classname> from the
|
|
<literal>ResponseQueue</literal> property of a
|
|
<classname>Message</classname> during an asynchronous receive process.
|
|
The receive timeout of the <literal>Receive</literal> operations is set
|
|
using the <literal>ReceiveTimeout</literal> property of
|
|
<classname>MessageQueueTemplate</classname>. The default value is
|
|
<classname>MessageQueue.InfiniteTimeout </classname>(which is actually
|
|
~3 months).</para>
|
|
|
|
<para>The XML configuration snippit for defining a MessageQueueTemplate
|
|
is shown in the previous section and also is located in the MSMQ
|
|
quickstart application configuraiton file Messaging.xml</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>MessageQueueFactoryObject</title>
|
|
|
|
<para>The <classname>MessageQueueFactoryObject</classname> is
|
|
responsible for creating <classname>MessageQueue</classname> instances.
|
|
You configure the factory with some basic information, namely the
|
|
constructor parameters you are familiar with already when creating a
|
|
standard <classname>MessageQueue</classname> instance, and then setting
|
|
<classname>MessageQueue</classname> properties, such a Label etc. Some
|
|
configuration tasks of a <classname>MessageQueue</classname> involve
|
|
calling methods, for example to set which properties of the message to
|
|
read. These available as properties to set on the
|
|
<classname>MessageQueueFactoryObject</classname>. An example declarative
|
|
configuration is shown below</para>
|
|
|
|
<programlisting> <object id='testqueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
|
|
<emphasis role="bold"> <!-- propeties passed to the MessageQueue constructor --></emphasis>
|
|
<property name='Path' value='.\Private$\testqueue'/>
|
|
<property name='DenySharedReceive' value='true'/>
|
|
<property name='AccessMode' value='Receive'/>
|
|
<property name='EnableCache' value='true'/>
|
|
<emphasis role="bold"><!-- properties that call configuration methods on the MessageQueue --></emphasis>
|
|
<property name='MessageReadPropertyFilterSetAll' value='true'/>
|
|
<property name='ProductTemplate'>
|
|
<object>
|
|
<property name='Label' value='MyLabel'/>
|
|
<emphasis role="bold"><!-- other MessageQueue properties can be set here --></emphasis>
|
|
</object>
|
|
</property>
|
|
</object></programlisting>
|
|
|
|
<para>Whenever an object reference is made to 'testqueue' an new
|
|
instance of the <classname>MessageQueue</classname> class is created.
|
|
This Spring's so-called 'prototype' model, which differs from
|
|
'singleton' mode. In the singleton creation mode whenever an object
|
|
reference is made to a 'testqueue' the same
|
|
<classname>MessageQueue</classname> instance would be used. So that a
|
|
new instance can be retrieved based on need, the message listener
|
|
containers take as an argument the name of the
|
|
<classname>MessageQueueFactoryObject</classname> and not a reference.
|
|
(i.e. use of 'value' instead of 'ref' in the XML).</para>
|
|
|
|
<note>
|
|
<para>The <classname>MessageQueueFactoryObject</classname> class is an
|
|
ideal candidate for use of a custom namespace. This will be provided
|
|
in the future. This will allow you to use VS.NET IntelliSense to
|
|
configure this commonly used object. An example of the potential
|
|
syntax is shown below</para>
|
|
|
|
<programlisting><mq:messageQueue id="testqueue" path=".\Private$\testqueue" MessageReadPropertyFilterSetAll="true">
|
|
<mq:properties label="MyLabel"/>
|
|
</mq:messageQueue></programlisting>
|
|
</note>
|
|
</section>
|
|
|
|
<section>
|
|
<title>MessageQueue and IMessageConverter resource management</title>
|
|
|
|
<para><classname>MessageQueues</classname> and
|
|
<classname>IMessageFormatters</classname> (commonly used in
|
|
<classname>IMessageConverter</classname> implementations) are not
|
|
thread-safe. For example, only the following methods on
|
|
<classname>MessageQueue</classname> are thread-safe,
|
|
<literal>BeginPeek</literal>, <literal>BeginReceive</literal>,
|
|
<literal>EndPeek</literal>, <literal>EndReceive</literal>,
|
|
<literal>GetAllMessages</literal>, <literal>Peek</literal>, and
|
|
<literal>Receive</literal>.</para>
|
|
|
|
<para>To isolate the creation logic of these classes, the factory
|
|
interface <classname>IMessageQueueFactory</classname> is used. The
|
|
interface is shown below</para>
|
|
|
|
<programlisting> public interface IMessageQueueFactory
|
|
{
|
|
MessageQueue CreateMessageQueue(string messageQueueObjectName);
|
|
|
|
IMessageConverter CreateMessageConverter(string messageConverterObjectName);
|
|
}</programlisting>
|
|
|
|
<para>A provided implementation,
|
|
<classname>DefaultMessageQueueFactory</classname> will create an
|
|
instance of each class per-thread. It delegates the creation of the
|
|
<classname>MessageQueue</classname> instance to the Spring container.
|
|
The argument, messageConverterObjectName, must be the id/name of a
|
|
<classname>MessageQueueFactoryObject</classname> defined in the Spring
|
|
container.</para>
|
|
|
|
<para><classname>DefaultMessageQueueFactory</classname> leverages
|
|
Spring's local thread storage support so it will work correctly in stand
|
|
alone and web applications.</para>
|
|
|
|
<para>You can use the <classname>DefaultMessageQueueFactory</classname>
|
|
independent of the rest of Spring's MSMQ support should you need only
|
|
the functionality it offers. <classname>MessageQueueTemplate</classname>
|
|
and the listener containers create an instance of
|
|
<classname>DefaultMessageQueueFactory</classname> by default. Should you
|
|
want to share the same instance across these two classes, or provide
|
|
your own custom implementation, use the property
|
|
<classname>MessageQueueFactory</classname> on either
|
|
<classname>MessageQueueTemplate</classname> or the message listener
|
|
classe.s</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Message Listener Containers</title>
|
|
|
|
<para>One of the most common uses of MSMQ is to concurrently process
|
|
messages delivered asynchronously. This support is provided in Spring by
|
|
message listener containers. A message listener container is the
|
|
intermediary between an <classname>IMessageListener</classname> and a
|
|
<classname>MessageQueue</classname>. (Note, message listener containers
|
|
are conceptually different than Spring's Inversion of Control container,
|
|
though it integrates and leverages the IoC container.) The message
|
|
listener container 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 (possibly complex) business logic associated with
|
|
receiving a message (and possibly responding to it), and delegate
|
|
boilerplate MSMQ infrastructure concerns to the framework.</para>
|
|
|
|
<para>A subclass of
|
|
<classname>AbstractMessageListenerContainer</classname> is used to
|
|
receive messages from a <classname>MessageQueue</classname>. Which
|
|
subclass you pick depends on your transaction processing requirements.
|
|
The following subclasses are available in the namespace
|
|
<literal>Spring.Messaging.Listener</literal></para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><classname>NonTransactionalMessageListenerContainer</classname>
|
|
- does not surround the receive operation with a transaction</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><classname>TransactionalMessageListenerContainer</classname> -
|
|
surrounds the receive operation with local (non-DTC) based
|
|
transaction(s).</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><classname>DistributedTxMessageListenerContainer</classname> -
|
|
surrounds the receive operation with a distributed (DTC)
|
|
transaction</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>Each of these containers use an implementation in which is based
|
|
on Peeking for messages on a <literal>MessageQueue</literal>. Peeking is
|
|
the only resource efficient approach that can be used in order to have
|
|
<literal>MessageQueue</literal> receipt in conjunction with
|
|
transactions, either local MSMQ transactions, local ADO.NET based
|
|
transactions, or DTC transactions. Each container can specify the number
|
|
of threads that will be created for processing messages after the Peek
|
|
occurs via the property <literal>MaxConcurrentListeners</literal>. Each
|
|
processing thread will continue to listen for messages up until the
|
|
timeout value specified by <literal>ListenerTimeLimit</literal> or until
|
|
there are no more messages on the queue (whichever comes first). The
|
|
default value of <literal>ListenerTimeLimit</literal> is
|
|
<literal>TimeSpan.Zero</literal>, meaning that only one attempt to
|
|
receive a message from the queue will be performed by each listener
|
|
thread. The current implementation uses the standard .NET thread pool.
|
|
Future implementations will use a custom (and pluggable) thread
|
|
pool.</para>
|
|
|
|
<section>
|
|
<title>NonTransactionalMessageListenerContainer</title>
|
|
|
|
<para>This container performs a Receive operation on the
|
|
<classname>MessageQueue</classname> without any transactional
|
|
settings. As such messages will not be redelivered if an exception is
|
|
thrown during message processing. Exceptions during message processing
|
|
can be handled via an implementation of the interface
|
|
<classname>IExceptionHandler</classname>. This can be set via the
|
|
property <literal>ExceptionHandler</literal> on the listener. The
|
|
<literal>IExceptionHandler</literal> interface is shown below</para>
|
|
|
|
<programlisting> public interface IExceptionHandler
|
|
{
|
|
void OnException(Exception exception, Message message);
|
|
}</programlisting>
|
|
|
|
<para>An example of configuring a
|
|
<classname>NonTransactionalMessageListenerContainer</classname> with
|
|
an <classname>IExceptionHandler</classname> is shown below</para>
|
|
|
|
<programlisting>
|
|
<emphasis role="bold"><!-- Queue to receive from --></emphasis>
|
|
<object id='msmqTestQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
|
|
<property name='Path' value='.\Private$\testqueue'/>
|
|
<property name='MessageReadPropertyFilterSetAll' value='true'/>
|
|
<property name='ProductTemplate'>
|
|
<object>
|
|
<property name='Label' value='MyTestQueueLabel'/>
|
|
</object>
|
|
</property>
|
|
</object>
|
|
|
|
<emphasis role="bold"><!-- Queue to respond to --></emphasis>
|
|
<object id='msmqTestResponseQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
|
|
<property name='Path' value='.\Private$\testresponsequeue'/>
|
|
<property name='MessageReadPropertyFilterSetAll' value='true'/>
|
|
<property name='ProductTemplate'>
|
|
<object>
|
|
<property name='Label' value='MyTestResponseQueueLabel'/>
|
|
</object>
|
|
</property>
|
|
</object>
|
|
|
|
<emphasis role="bold"><!-- Listener container --></emphasis>
|
|
<object id="nonTransactionalMessageListenerContainer" type="Spring.Messaging.Listener.NonTransactionalMessageListenerContainer, Spring.Messaging">
|
|
<property name="MessageQueueObjectName" value="msmqTestQueue"/>
|
|
<property name="MaxConcurrentListeners" value="2"/>
|
|
<property name="ListenerTimeLimit" value="20s"/> <!-- 20 seconds -->
|
|
<property name="MessageListener" ref="messageListenerAdapter"/>
|
|
<property name="ExceptionHandler" ref="exceptionHandler"/>
|
|
</object>
|
|
|
|
<emphasis role="bold"><!-- Delegate to plain .NET object for message handling --></emphasis>
|
|
<object id="messageListenerAdapter" type="Spring.Messaging.Listener.MessageListenerAdapter, Spring.Messaging">
|
|
<property name="DefaultResponseQueueName" value="msmqTestResponseQueue"/>
|
|
<property name="HandlerObject" ref="simpleHandler"/>
|
|
</object>
|
|
|
|
<emphasis role="bold"><!-- Classes you need to write --></emphasis>
|
|
<object id="simpleHandler" type="MyNamespace.SimpleHandler, MyAssembly"/>
|
|
|
|
<object id="exceptionHandler" type="MyNamespace.SimpleExceptionHandler, MyAssembly"/>
|
|
</programlisting>
|
|
|
|
<para>The SimpleHandler class would look something like this</para>
|
|
|
|
<programlisting>public class SimpleHandler : ISimpleHandler
|
|
{
|
|
public void HandleObject(string txt)
|
|
{
|
|
// perform message processing...
|
|
Console.WriteLine("Received text: " + txt);
|
|
}
|
|
}</programlisting>
|
|
</section>
|
|
|
|
<section>
|
|
<title>TransactionalMessageListenerContainer</title>
|
|
|
|
<para>This message listener container performs receive operations
|
|
within the context of local transaction. This class requires an
|
|
instance of Spring's
|
|
<classname>IPlatformTransactionManager</classname>, either
|
|
<classname>AdoPlatformTransactionManager</classname>,
|
|
<classname>HibernateTransactionManager</classname>, or
|
|
<classname>MessageQueueTransactionManager</classname>.</para>
|
|
|
|
<para>If you specify a
|
|
<classname>MessageQueueTransactionManager</classname> then a
|
|
<classname>MessageQueueTransaction</classname> will be started before
|
|
receiving the message and used as part of the container's receive
|
|
operation. As with other
|
|
<classname>IPlatformTransactionManager</classname> implementation's,
|
|
the transactional resources (in this case an instance of the
|
|
<classname>MessageQueueTransaction</classname> class) is bound to
|
|
thread local storage. <classname>MessageQueueTemplate</classname> will
|
|
look in thread-local storage and use this 'ambient' transaction if
|
|
found for its send and receive operations. The message listener is
|
|
invoked and if no exception occurs, then the
|
|
<classname>MessageQueueTransactionManager</classname> will commit the
|
|
<classname>MessageQueueTransaction</classname>.</para>
|
|
|
|
<para>The message listener implementation can call into service layer
|
|
classes that are made transactional using standard Spring declarative
|
|
transactional techniques. In case of exceptions in the service layer,
|
|
the database operation will be rolled back (nothing new here), and the
|
|
<classname>TransactionalMessageListenerContainer</classname> will call
|
|
it's <classname>IMessageTransactionExceptionHandler</classname>
|
|
implementation to determine if the
|
|
<classname>MessageQueueTransaction</classname> should commit (removing
|
|
the message from the queue) or rollback (leaving the message on the
|
|
queue for redelivery).<note>
|
|
<para>The use of a transactional service layer in combination with
|
|
a <classname>MessageQueueTransactionManager</classname> is a
|
|
powerful combination that can be used to achieve "exactly one"
|
|
transaction message processing with database operations. This
|
|
requires a little extra programming effort and is a more efficient
|
|
alternative than using distributed transactions which are commonly
|
|
associated with this functionality since both the database and the
|
|
message transaction commit or rollback together.</para>
|
|
|
|
<para>The additional programming logic needed to achieve this is
|
|
to keep track of the <literal>Message.Id</literal> that has been
|
|
processed successfully within the transactional service layer.
|
|
This is needed as there may be a system failure (e.g. power goes
|
|
off) between the 'inner' database commit and the 'outer' messaging
|
|
commit, resulting in message redelivery. The transactional service
|
|
layer needs logic to detect if incoming message was processed
|
|
successfully. It can do this by checking the database for an
|
|
indication of successful processing, perhaps by recording the
|
|
<literal>Message.Id</literal> itself in a status table. If the
|
|
transactional service layer determines that the message has
|
|
already been processed, it can throw a specific exception for this
|
|
case. The container's exception handler will recognize this
|
|
exception type and vote to commit (remove from the queue) the
|
|
'outer' messaging transaction. Spring provides an exception
|
|
handler with this functionality, see
|
|
<classname>SendToQueueExceptionHandler</classname> described
|
|
below.</para>
|
|
</note></para>
|
|
|
|
<para>An example of configuring the
|
|
<classname>TransactionalMessageListenerContainer</classname> using a
|
|
<literal>MessageQueueTransactionManager</literal> is shown
|
|
below</para>
|
|
|
|
<programlisting> <emphasis role="bold"><!-- Queue to receive from --></emphasis>
|
|
<object id='msmqTestQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
|
|
<property name='Path' value='.\Private$\testqueue'/>
|
|
<property name='MessageReadPropertyFilterSetAll' value='true'/>
|
|
<property name='ProductTemplate'>
|
|
<object>
|
|
<property name='Label' value='MyTestQueueLabel'/>
|
|
</object>
|
|
</property>
|
|
</object>
|
|
|
|
<emphasis role="bold"><!-- Queue to respond to --></emphasis>
|
|
<object id='msmqTestResponseQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
|
|
<property name='Path' value='.\Private$\testresponsequeue'/>
|
|
<property name='MessageReadPropertyFilterSetAll' value='true'/>
|
|
<property name='ProductTemplate'>
|
|
<object>
|
|
<property name='Label' value='MyTestResponseQueueLabel'/>
|
|
</object>
|
|
</property>
|
|
</object>
|
|
|
|
<emphasis role="bold"><!-- Transaction Manager for MSMQ Messaging --></emphasis>
|
|
<object id="messageQueueTransactionManager" type="Spring.Messaging.Core.MessageQueueTransactionManager, Spring.Messaging"/>
|
|
|
|
<!-- The transaction message listener container -->
|
|
<object id="transactionalMessageListenerContainer" type="Spring.Messaging.Listener.TransactionalMessageListenerContainer, Spring.Messaging">
|
|
<property name="MessageQueueObjectName" value="msmqTestQueue"/>
|
|
<property name="PlatformTransactionManager" ref="messageQueueTransactionManager"/>
|
|
<property name="MaxConcurrentListeners" value="5"/>
|
|
<property name="ListenerTimeLimitIn" value="20s"/>
|
|
<property name="MessageListener" ref="messageListenerAdapter"/>
|
|
<property name="MessageTransactionExceptionHandler" ref="messageTransactionExceptionHandler"/>
|
|
</object>
|
|
|
|
<emphasis role="bold"><!-- Delegate to plain .NET object for message handling --></emphasis>
|
|
<object id="messageListenerAdapter" type="Spring.Messaging.Listener.MessageListenerAdapter, Spring.Messaging">
|
|
<property name="DefaultResponseQueueName" value="msmqTestResponseQueue"/>
|
|
<property name="HandlerObject" ref="simpleHandler"/>
|
|
</object>
|
|
|
|
<emphasis role="bold"><!-- Poison message handling --></emphasis>
|
|
<object id="messageTransactionExceptionHandler" type="Spring.Messaging.Listener.SendToQueueExceptionHandler, Spring.Messaging">
|
|
<property name="MaxRetry" value="5"/>
|
|
<property name="MessageQueueObjectName" value="testTxErrorQueue"/>
|
|
</object>
|
|
|
|
<emphasis role="bold"><!-- Classes you need to write --></emphasis>
|
|
<object id="simpleHandler" type="MyNamespace.SimpleHandler, MyAssembly"/>
|
|
|
|
|
|
</programlisting>
|
|
|
|
<para>If you specify either
|
|
<classname>AdoPlatformTransactionManager</classname> or
|
|
<classname>HibernateTransactionManager</classname> then a local
|
|
database transaction will be started before the receiving the message.
|
|
By default, the container will also start a local
|
|
<classname>MessageQueueTransaction</classname> after the local
|
|
database transaction has started, but before the receiving the
|
|
message. This <classname>MessageQueueTransaction</classname> will be
|
|
used to receive the message. By default the
|
|
<classname>MessageQueueTransaction</classname> will be bound to thread
|
|
local storage so that any <classname>MessageQueueTemplate</classname>
|
|
send or receive operations will participate transparently in the same
|
|
<classname>MessageQueueTransaction</classname>. If you do not want
|
|
this behavior set the property
|
|
<literal>ExposeContainerManagedMessageQueueTransaction</literal> to
|
|
false.</para>
|
|
|
|
<para>In case of exceptions during <literal>IMessageListener</literal>
|
|
processing when using either either
|
|
<classname>AdoPlatformTransactionManager</classname> or
|
|
<classname>HibernateTransactionManager</classname> the container's
|
|
<classname>IMessageTransactionExceptionHandler</classname> will
|
|
determine if the <classname>MessageQueueTransaction</classname> should
|
|
commit (removing it from the queue) or rollback (placing it back on
|
|
the queue for redelivery). The listener exception will always trigger
|
|
a rollback in the 'outer' database transaction.</para>
|
|
|
|
<para>Poison message handing, that is, the endless redelivery of a
|
|
message due to exceptions during processing, can be detected using
|
|
implementations of the
|
|
<classname>IMessageTransactionExceptionHandler</classname>. This
|
|
interface is shown below</para>
|
|
|
|
<programlisting>public interface IMessageTransactionExceptionHandler
|
|
{
|
|
TransactionAction OnException(Exception exception, Message message, MessageQueueTransaction messageQueueTransaction);
|
|
}</programlisting>
|
|
|
|
<para>The return value is an enumeration with the values
|
|
<literal>Commit</literal> and <literal>Rollback</literal>. A specific
|
|
implementation is provided that will move the poison message to
|
|
another queue after a maximum number of redelivery attempts. See
|
|
<classname>SendToQueueExceptionHandler</classname> described below.
|
|
You can set a specific implementation to by setting
|
|
<classname>TransactionalMessageListenerContainer's</classname>
|
|
property
|
|
<classname>MessageTransactionExceptionHandler</classname></para>
|
|
|
|
<para>The <literal>IMessageTransactionExceptionHandler</literal>
|
|
implementation <classname>SendToQueueExceptionHandler</classname>
|
|
keeps track of the Message's <literal>Id</literal> property in memory
|
|
with a count of how many times an exception has occurred. If that
|
|
count is greater than the handler's <literal>MaxRetry</literal> count
|
|
it will be sent to another queue using the provided
|
|
<classname>MessageQueueTransaction</classname>. The queue to send the
|
|
message to is specified via the property
|
|
<classname>MessageQueueObjectName</classname>.</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>DistributedTxMessageListenerContainer</title>
|
|
|
|
<para>This message listener container performs receive operations
|
|
within the context of distributed transaction. A distributed
|
|
transaction is started before a message is received. The receive
|
|
operation participates in this transaction using by specifying
|
|
MessageQueueTransactionType = Automatic. The transaction that is
|
|
started is automatically promoted to two-phase-commit to avoid the
|
|
default behavior of transaction promotion since the only reason to use
|
|
this container is to use two different resource managers (messaging
|
|
and database typically).</para>
|
|
|
|
<para>The commit and rollback semantics are simple, if the message
|
|
listener does not throw an exception the transaction is committed,
|
|
otherwise it is rolled back.</para>
|
|
|
|
<para>Exceptions in message listener processing are handled by
|
|
implementations of the
|
|
<classname>IDistributedTransactionExceptionHandler</classname>
|
|
interface. This interface is shown below</para>
|
|
|
|
<programlisting> public interface IDistributedTransactionExceptionHandler
|
|
{
|
|
bool IsPoisonMessage(Message message);
|
|
|
|
void HandlePoisonMessage(Message poisonMessage);
|
|
|
|
void OnException(Exception exception, Message message);
|
|
}</programlisting>
|
|
|
|
<para>the <literal>IsPoisonMessage</literal> method determines whether
|
|
the incoming message is a poison message. This method is called before
|
|
the <literal>IMessageListener</literal> is invoked. The container will
|
|
call <literal>HandlePoisonMessage</literal> is
|
|
<literal>IsPoisonMessage</literal> returns true and will then commit
|
|
the distributed transaction (removing the message from the queue.
|
|
Typical implementations of <literal>HandlePoisonMessage</literal> will
|
|
move the poison message to another queue (under the same distributed
|
|
transaction used to receive the message). The class
|
|
<classname>SendToQueueDistributedTransactionExceptionHandler</classname>
|
|
detects poison messages by tracking the Message <literal>Id</literal>
|
|
property in memory with a count of how many times an exception has
|
|
occurred. If that count is greater than the handler's
|
|
<literal>MaxRetry</literal> count it will be sent to another queue.
|
|
The queue to send the message to is specified via the property
|
|
<classname>MessageQueueObjectName</classname>.</para>
|
|
</section>
|
|
</section>
|
|
</section>
|
|
|
|
<section>
|
|
<title>MessageConverters</title>
|
|
|
|
<section>
|
|
<title>Using MessageConverters</title>
|
|
|
|
<para>In order to facilitate the sending of business model objects, the
|
|
<classname>MessageQueueTemplate</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>MessageQueue</classname> delegate the conversion process to
|
|
an instance of the <interfacename>IMessageConverter</interfacename>
|
|
interface. This interface defines a simple contract to convert between
|
|
.NET objects and JMS messages. The interface is shown below</para>
|
|
|
|
<programlisting> public interface IMessageConverter : ICloneable
|
|
{
|
|
Message ToMessage(object obj);
|
|
|
|
object FromMessage(Message message);
|
|
}</programlisting>
|
|
|
|
<para>There are a standard implementations provided the simply wrap
|
|
existing <classname>IMessageFormatter</classname>
|
|
implementations.</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><classname>XmlMessageConverter</classname> - uses a
|
|
XmlMessageFormatter.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><classname>BinaryMessageConverter</classname> - uses a
|
|
BinaryMessageFormatter</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><classname>ActiveXMessageConverter</classname> - uses a
|
|
ActiveXMessageFormatter</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>The default implementation used in
|
|
<classname>MessageQueueTemplate</classname> and the message listener
|
|
containers is an instance of XmlMessageConverter configured with a
|
|
TargetType to be System.String. You specify the types that the
|
|
XmlMessageConverter can convert though either the array property
|
|
<classname>TargetTypes</classname> or
|
|
<classname>TargetTypeNames</classname>. Here is an example taken from
|
|
the QuickStart application</para>
|
|
|
|
<programlisting> <object id="xmlMessageConverter" type="Spring.Messaging.Support.Converters.XmlMessageConverter, Spring.Messaging">
|
|
<property name="TargetTypes">
|
|
<list>
|
|
<value>Spring.MsmqQuickStart.Common.Data.TradeRequest, Spring.MsmqQuickStart.Common</value>
|
|
<value>Spring.MsmqQuickStart.Common.Data.TradeResponse, Spring.MsmqQuickStart.Common</value>
|
|
<value>System.String, mscorlib</value>
|
|
</list>
|
|
</property>
|
|
</object></programlisting>
|
|
|
|
<para>You can specify other <classname>IMessageConverter</classname>
|
|
implementations using the
|
|
<classname>MessageConverterObjectName</classname> property on the
|
|
<classname>MessageQueueTemplate</classname> and
|
|
<classname>MessageListenerAdapter</classname>. </para>
|
|
|
|
<para>Other implementations provided are</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><classname>XmlDocumentConverter</classname> - loads and saves
|
|
an XmlDocument to the message BodyStream. This lets you manipulate
|
|
directly the XML data independent of type serialization issues. This
|
|
is quite useful if you use XPath expressions to pick out the
|
|
relevant information to construct your business objects.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>Other potential implementations:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>RawBytesMessageConverter - directly write raw bytes to the
|
|
message stream, compress</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>CompressedMessageConverter - compresses the message
|
|
payload</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>EncryptedMessageConverter - encrypt the message (standard MSMQ
|
|
encryptiong has several limitations)</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>SoapMessageConverter - use soap formatting.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</section>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Interface based message processing</title>
|
|
|
|
<section>
|
|
<section>
|
|
<title>MessageListenerAdapater</title>
|
|
|
|
<para>The <classname>MessageListenerAdapter</classname> allows methods
|
|
of a class that does not implement the
|
|
<classname>IMessageListener</classname> interface to be invoked upon
|
|
message delivery. Lets call this class the 'message handler' class. To
|
|
achieve this goal the <classname>MessageListenerAdapter</classname>
|
|
implements the standard <classname>IMessageListener</classname>
|
|
interface to receive a message and then delegates the processing to
|
|
the message handler class. Since the message handler class does not
|
|
contain methods that refer to MSMQ artifacts such as Message, the
|
|
<classname>MessageListenerAdapter</classname> uses a
|
|
<classname>IMessageConverter</classname> to bridge the MSMQ and 'plain
|
|
object' worlds. As a reminder, the default
|
|
<classname>XmlMessageConverter</classname> used in
|
|
<classname>MessageQueueTemplate</classname> and the message listener
|
|
containers converts from Message to string. Once the incoming message
|
|
is converted to an object (string for example) a method with the name
|
|
'HandleMessage' is invoked via reflection passing in the string as an
|
|
argument.</para>
|
|
|
|
<para>Using the default configuration of XmlMessageConverter in the
|
|
message listeners, a simple string based message handler would look
|
|
like this.</para>
|
|
|
|
<programlisting>public class MyHandler
|
|
{
|
|
|
|
public void HandleMessage(string text)
|
|
{
|
|
...
|
|
}
|
|
|
|
}</programlisting>
|
|
|
|
<para>The next example has a similar method signature but the name of
|
|
the handler method name has been changed to "DoWork", by setting the
|
|
adapter's property DefaultHandlerMethod.</para>
|
|
|
|
<programlisting>public interface IMyHandler
|
|
{
|
|
void DoWork(string text);
|
|
}</programlisting>
|
|
|
|
<para>If your IMessageConverter 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.</para>
|
|
|
|
<programlisting>public interface IMyHandler
|
|
{
|
|
void DoWork(string text);
|
|
void DoWork(OrderRequest orderRequest);
|
|
void DoWork(InvoiceRequest invoiceRequest);
|
|
void DoWork(object obj);
|
|
}</programlisting>
|
|
|
|
<para>Another of the capabilities of the
|
|
<classname>MessageListenerAdapter</classname> class is the ability to
|
|
automatically send back a response <classname>Message</classname> 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 to a string. The resulting string will
|
|
then be sent to the <literal>ResponseQueue</literal> defined in the
|
|
Message's <literal>ResponseQueue</literal> property of the original
|
|
Message, or the <literal>DefaultResponseQueueName</literal> on the
|
|
<classname>MessageListenerAdapter</classname> (if one has been
|
|
configured) will be used. If not <literal>ResponseQueue</literal> is
|
|
found then an Spring <classname>MessagingException</classname> will be
|
|
thrown. Please note that this exception will not be swallowed and will
|
|
propagate up the call stack.</para>
|
|
|
|
<para>Here is an example of Handler signatures that have various
|
|
return types.</para>
|
|
|
|
<programlisting>public interface IMyHandler
|
|
{
|
|
string DoWork(string text);
|
|
OrderResponse DoWork(OrderRequest orderRequest);
|
|
InvoiceResponse DoWork(InvoiceRequest invoiceRequest);
|
|
void DoWork(object obj);
|
|
}</programlisting>
|
|
|
|
<para>The following configuration shows how to hook up the adapter to
|
|
process incoming MSMQ messages using the default message converter.
|
|
</para>
|
|
|
|
<programlisting> <emphasis role="bold"><!-- Delegate to plain .NET object for message handling --></emphasis>
|
|
<object id="messageListenerAdapter" type="Spring.Messaging.Listener.MessageListenerAdapter, Spring.Messaging">
|
|
<property name="DefaultResponseQueueName" value="msmqTestResponseQueue"/>
|
|
<property name="HandlerObject" ref="myHandler"/>
|
|
</object></programlisting>
|
|
</section>
|
|
</section>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Comparison with using WCF</title>
|
|
|
|
<para>The goals of Spring's MSMQ messaging support are quite similar to
|
|
those of WCF with its MSMQ related bindings, in as much as a WCF service
|
|
contract is a PONO (minus the attributes if you really picky about what
|
|
you call a PONO). Spring's messaging support can give you the programming
|
|
convenience of dealing with PONO contracts for message receiving but does
|
|
not (at the moment) provide a similar PONO contract for sending, instead
|
|
relying on explicit use of the MessageQueueTemplate class. This feature
|
|
exists - some question whether it should for messaging - in the Java
|
|
version of the Spring framework, see JmsInvokerServiceExporter and
|
|
JmsInvokerProxyFactoryBean.</para>
|
|
|
|
<para>The good news is that if and when it comes time to move from a
|
|
Spring MSMQ solution to WCF, you will be in a great position as the PONO
|
|
interface used for business processing when receiving in a Spring based
|
|
MSMQ application can easily be adapted to a WCF environment. There may
|
|
also be some features unique to MSMQ and/or Spring's MSMQ support that you
|
|
may find appealing over WCF. Many messaging applications still need to be
|
|
'closer to the metal' and this is not possible using the WCF bindings, for
|
|
example Peeking and Label, AppSpecific properties, multicast.. An
|
|
interesting recent quote by Yoel Arnon (MSMQ guru) <emphasis>"With all the
|
|
respect to WCF, System.Messaging is still the major programming model for
|
|
MSMQ programmers, and is probably going to remain significant for the
|
|
foreseeable future. The message-oriented programming model is different
|
|
from the service-oriented model of WCF, and many real-world solutions
|
|
would always prefer it."</emphasis></para>
|
|
|
|
<para></para>
|
|
</section>
|
|
</chapter> |