msmq doc updates

This commit is contained in:
markpollack
2008-07-05 11:14:19 +00:00
parent b0ec233aca
commit 074a212357

View File

@@ -1,70 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter>
<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 System.Messaging
API is a low-level API that provides the basis for creating messaging an
application. 'Out-of-the-box', System.Messaging leaves the act of creating
a sophisticated mult-threaded messaging servers and clients as an
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 System.Messaging 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 middlware specifics
from the core business processing. </para>
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 proramming model. In
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 MessageListenerAdapter class and
IMessageConverters.</para>
the use of Spring's <classname>MessageListenerAdapter</classname> class
and <classname>IMessageConverters</classname>.</para>
<para>The namespace Spring.Messaging provides the core functionality for
messaging. It contains the class MessageQueueTemplate that simplifies the
use of the the System.Messaging.MessageQueue by handling the lack of
thread-safety in most of System.Messaging.MessageQueue's methods (for
example Send). A single instance of MessageQueueTemplate can be used
througout your application and Spring will ensure that a different
instance of a MessageQueue class is used per thread. The
MessageQueueTemplate class is aware of the present of either an 'ambient'
System.Transaction's transaction or a local
System.Messaging.MessageQueueTransaction. As such you do not need to code
your messaing operations to a specific transaction enviornment or come up
with your own mechanism for passing around a MessageQueueTransaction to
multiple classes. The transaction features of MessageQueueTemplate are
quite analogous to the transactional features of Spring's AdoTemplate (in
case you are already familiar with that functionality).</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 multie-threaded
<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.</para>
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 coordinating 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>
</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 simple client that sends a message and a multi-threaded server
application that receives the message.</para>
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
MessageQueueTemplate class and configure it to use a MessageQueue. 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>
<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> &lt;object id='questionTxQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'&gt;
&lt;property name='Path' value='.\Private$\questionTxQueue'/&gt;
@@ -82,10 +121,11 @@
</programlisting>
<para>The MessageQueue object is created via an instance of
MessageQueueFactoryObject and the MessageQueueTemplate refers to this
factory object by name and not by reference. The SimpleSender class looks
like this</para>
<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
{
@@ -103,53 +143,71 @@
}</programlisting>
<para>This class can be shared across multiple threads and the
MessageQueueTemplate will take care of managing thread local access to
System.Messaging.MessageQueue and underlying
System.Messaging.IMessageFormatter instances appropriately. Futhermore,
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 IMessageConverter class. By default an implementation that
uses an XmlMessageFormatter with a TargetType of System.String is used.
You can configure the MessageQueueTemplate to use other implementations
that do conversions above and beyond what the 'stock' IMessageFormatters
do. See the section on MessageConverters for more details.</para>
<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
TransactionMessageListenerContainer and configure it to use the
MessageQueueTransactionManager (an implementation of Spring's
IPlatformTransactionManager abstraction). The configuration is shown
<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). </para>
<para>While you can create the message listener container
programmatically, we will show the declarative configuration approach
below</para>
<programlisting> &lt;object id='questionTxQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'&gt;
<programlisting> <emphasis role="bold">&lt;!-- Queue to receive from --&gt;</emphasis>
&lt;object id='questionTxQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'&gt;
&lt;property name='Path' value='.\Private$\questionTxQueue'/&gt;
&lt;property name='MessageReadPropertyFilterSetAll' value='true'/&gt;
&lt;/object&gt;
<emphasis role="bold">&lt;!-- MSMQ Transaction Manager --&gt;</emphasis>
&lt;object id="messageQueueTransactionManager" type="Spring.Messaging.Core.MessageQueueTransactionManager, Spring.Messaging"/&gt;
<emphasis role="bold"> &lt;!-- Message Listener Container that uses MSMQ transactional for receives --&gt;</emphasis>
&lt;object id="transactionalMessageListenerContainer" type="Spring.Messaging.Listener.TransactionalMessageListenerContainer, Spring.Messaging"&gt;
&lt;property name="MessageQueueObjectName" value="questionTxQueue"/&gt;
&lt;property name="PlatformTransactionManager" ref="messageQueueTransactionManager"/&gt;
&lt;property name="MaxConcurrentListeners" value="10"/&gt;
&lt;property name="MessageListener" ref="messageListenerAdapter"/&gt;
&lt;/object&gt;
<emphasis role="bold"> &lt;!-- Adapter to call a PONO as a messaging callback --&gt;</emphasis>
&lt;object id="messageListenerAdapter" type="Spring.Messaging.Listener.MessageListenerAdapter, Spring.Messaging"&gt;
&lt;property name="HandlerObject" ref="questionHandler"/&gt;
&lt;/object&gt;
<emphasis role="bold">&lt;!-- Class that you write --&gt;</emphasis>
<emphasis role="bold">&lt;!-- The PONO class that you write --&gt;</emphasis>
&lt;object id="questionHandler" type="MyNamespace.QuestionHandler, MyAssembly"/&gt;
</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 QuestionHandler. The only class you need to
write, QuestionHandler, looks like</para>
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
{
@@ -170,30 +228,51 @@
does not refer to any messaging specific class. The rest is configuration
of Spring provided helper classes.</para>
<para>Note that if the HandleObject 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>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>As a last part of this 'quick tour' we will configure the message
<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 <literal>MaxRetry</literal> count and the queue to send the message to
should that retry count be exeeded.</para>
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> &lt;object id='retryQuestionTxQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'&gt;
<programlisting>
<emphasis role="bold"> &lt;!-- The 'retry' queue to send poison messages --&gt;</emphasis>
&lt;object id='retryQuestionTxQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'&gt;
&lt;property name='Path' value='.\Private$\retryQuestionTxQueue'/&gt;
&lt;property name='MessageReadPropertyFilterSetAll' value='true'/&gt;
&lt;/object&gt;
<emphasis role="bold"> &lt;!-- Message Listener Container that uses MSMQ transactional for receives --&gt;</emphasis>
&lt;object id="transactionalMessageListenerContainer" type="Spring.Messaging.Listener.TransactionalMessageListenerContainer, Spring.Messaging"&gt;
&lt;!-- as before but adding --&gt;
<emphasis role="bold">&lt;!-- as before but adding --&gt;
</emphasis>
&lt;property name="MessageTransactionExceptionHandler" ref="messageTransactionExceptionHandler"/&gt;
&lt;/object&gt;
<emphasis role="bold"> &lt;!-- Poison message handling policy --&gt;</emphasis>
&lt;object id="messageTransactionExceptionHandler" type="Spring.Messaging.Listener.SendToQueueExceptionHandler, Spring.Messaging"&gt;
&lt;property name="MaxRetry" value="5"/&gt;
&lt;property name="MessageQueueObjectName" value="retryQuestionTxQueue"/&gt;
@@ -204,9 +283,17 @@
queue questionTxQueue for redelivery). If the same message causes an
exception in processing 5 times ,then it will be sent transactionally to
the retryQuestionTxQueue and the message transaction will commit (removing
it from the queue questionTxQueue).</para>
it from the queue questionTxQueue). The SendToQueueExceptionHandler
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></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>
@@ -219,23 +306,30 @@
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 managment issue to worry about!) A thread-local instance of the
<literal>MessageQueue</literal> class is available via
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>.</para>
<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 recieving messages. A family of
convenience methods for sending and receiving messages. A family of
overloaded <literal>ConvertAndSend</literal> and
<literal>ReceiveAndConvert</literal> methods allow you to send and
recieve an object. The default message queue to send and recieve from is
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
the responsibility of 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
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
@@ -243,10 +337,12 @@
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>)</para>
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 name.</para>
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>
@@ -271,11 +367,12 @@ object ReceiveAndConvert(string messageQueueObjectName);</programlisting>
(put there via the use of Spring's
<classname>MessageQueueTransactionManager</classname> or
<classname>TransactionalMessageListenerContainer</classname>), the
message will be sent transactionally using the transaction 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
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
@@ -284,8 +381,8 @@ object ReceiveAndConvert(string messageQueueObjectName);</programlisting>
transactional, then a non-transactional send
(MessageQueueTransactionType = None) is used.</para>
<para>The delegate MessagePostProcessorDelegate has the following
signature</para>
<para>The delegate <classname>MessagePostProcessorDelegate</classname>
has the following signature</para>
<programlisting>public delegate Message MessagePostProcessorDelegate(Message message);</programlisting>
@@ -295,7 +392,7 @@ object ReceiveAndConvert(string messageQueueObjectName);</programlisting>
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 suscint coding task. If you have elaborate
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>
@@ -315,18 +412,73 @@ 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 access from multipe
threads. This <literal>Send</literal> method is commonly used when
getting the <classname>MessageQueue</classname> from the
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>. The recieve timeout of the
<literal>Receive</literal> operations is set using the
<literal>ReceiveTimeout</literal> property of
<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>
</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> &lt;object id='testqueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'&gt;
<emphasis role="bold"> &lt;!-- propeties passed to the MessageQueue constructor --&gt;</emphasis>
&lt;property name='Path' value='.\Private$\testqueue'/&gt;
&lt;property name='DenySharedReceive' value='true'/&gt;
&lt;property name='AccessMode' value='Receive'/&gt;
&lt;property name='EnableCache' value='true'/&gt;
<emphasis role="bold">&lt;!-- properties that call configuration methods on the MessageQueue --&gt;</emphasis>
&lt;property name='MessageReadPropertyFilterSetAll' value='true'/&gt;
&lt;property name='ProductTemplate'&gt;
&lt;object&gt;
&lt;property name='Label' value='MyLabel'/&gt;
<emphasis role="bold">&lt;!-- other MessageQueue properties can be set here --&gt;</emphasis>
&lt;/object&gt;
&lt;/property&gt;
&lt;/object&gt;</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>&lt;mq:messageQueue id="testqueue" path=".\Private$\testqueue" MessageReadPropertyFilterSetAll="true"&gt;
&lt;mq:properties label="MyLabel"/&gt;
&lt;/mq:messageQueue&gt;</programlisting>
</note>
</section>
<section>
<title>MessageQueue and IMessageConverter resource management</title>
@@ -341,19 +493,38 @@ void Send(MessageQueue messageQueue, Message message);</programlisting>
<literal>Receive</literal>.</para>
<para>To isolate the creation logic of these classes, the factory
interface <classname>IMessageQueueFactory</classname> is used. A
provided implementation,
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. This leverages Spring's local thread
storage support so it will work correctly in stand alone and web
applications. You can use the
<classname>DefaultMessageQueueFactory</classname> independent of the
rest of Sprng's MSMQ support should you need only this functionality.
<classname>MessageQueueTemplate</classname> and the listener containers
create this implementation by default, but should you want to share the
same instance across these two classes, or provide your own custom
implementation, use the property
<classname>MessageQueueFactory</classname>.</para>
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>
@@ -363,13 +534,15 @@ void Send(MessageQueue messageQueue, Message message);</programlisting>
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>. It 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 MSMQ infrastructure concerns to the framework.
</para>
<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
@@ -386,13 +559,13 @@ void Send(MessageQueue messageQueue, Message message);</programlisting>
<listitem>
<para><classname>TransactionalMessageListenerContainer</classname> -
surrounds the receive operation with a local (non-DTC) based
transaction</para>
surrounds the receive operation with local (non-DTC) based
transaction(s).</para>
</listitem>
<listitem>
<para><classname>DistributedTxMessageListenerContainer</classname> -
surrounds the receive operation with a distribued (DTC)
surrounds the receive operation with a distributed (DTC)
transaction</para>
</listitem>
</itemizedlist>
@@ -404,13 +577,13 @@ void Send(MessageQueue messageQueue, Message message);</programlisting>
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 the
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
recieve a message from the queue will be performed by each listener
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>
@@ -418,13 +591,14 @@ void Send(MessageQueue messageQueue, Message message);</programlisting>
<section>
<title>NonTransactionalMessageListenerContainer</title>
<para>This container performs a Recieve operation on the MessageQueue
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 IExceptionHandler. This can be set via
the property ExceptionHandler on the listener. The IExceptionHandler
interface is shown below</para>
<para>This container performs a Recieve 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
{
@@ -432,8 +606,8 @@ void Send(MessageQueue messageQueue, Message message);</programlisting>
}</programlisting>
<para>An example of configuring a
NonTransactionalMessageListenerContainer with an IExceptionHandler is
shown below</para>
<classname>NonTransactionalMessageListenerContainer</classname> with
an <classname>IExceptionHandler</classname> is shown below</para>
<programlisting>
<emphasis role="bold">&lt;!-- Queue to receive from --&gt;</emphasis>
@@ -479,33 +653,44 @@ void Send(MessageQueue messageQueue, Message message);</programlisting>
&lt;object id="exceptionHandler" type="MyNamespace.SimpleExceptionHandler, MyAssembly"/&gt;
</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 peforms receive operations
<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>
<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 recieve
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 recieve operations. The message listener is
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>
<classname>MessageQueueTransaction</classname>.</para>
<para>The message listener implementation can call into service layer
classes that are made transactional using standard Spring declarative
@@ -521,7 +706,7 @@ void Send(MessageQueue messageQueue, Message message);</programlisting>
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 efficinet
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>
@@ -534,7 +719,7 @@ void Send(MessageQueue messageQueue, Message message);</programlisting>
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 successfull processing, perhaps by recording the
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
@@ -623,7 +808,7 @@ void Send(MessageQueue messageQueue, Message message);</programlisting>
false.</para>
<para>In case of exceptions during <literal>IMessageListener</literal>
processing when using either ither
processing when using either either
<classname>AdoPlatformTransactionManager</classname> or
<classname>HibernateTransactionManager</classname> the container's
<classname>IMessageTransactionExceptionHandler</classname> will
@@ -634,7 +819,7 @@ void Send(MessageQueue messageQueue, Message message);</programlisting>
<para>Poison message handing, that is, the endless redelivery of a
message due to exceptions during processing, can be detected using
implementatons of the
implementations of the
<classname>IMessageTransactionExceptionHandler</classname>. This
interface is shown below</para>
@@ -653,9 +838,9 @@ void Send(MessageQueue messageQueue, Message message);</programlisting>
<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 occured. if that count
is greater than the handler's <literal>MaxRetry</literal> count it
will be sent to another queue using the provided
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>
@@ -664,15 +849,15 @@ void Send(MessageQueue messageQueue, Message message);</programlisting>
<section>
<title>DistributedTxMessageListenerContainer</title>
<para>This message listener container peforms receive operations
<para>This message listener container performs receive operations
within the context of distributed transaction. A distributed
transaction is started before a message is recieved. The recieve
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>
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,
@@ -696,15 +881,15 @@ void Send(MessageQueue messageQueue, Message message);</programlisting>
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 distibuted transaction (removing the message from the queue.
<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
occured. if that count is greater than the handler's
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>
@@ -735,7 +920,8 @@ void Send(MessageQueue messageQueue, Message message);</programlisting>
}</programlisting>
<para>There are a standard implementations provided the simply wrap
existing IMessageFormatter implementations.</para>
existing <classname>IMessageFormatter</classname>
implementations.</para>
<itemizedlist>
<listitem>
@@ -763,15 +949,15 @@ void Send(MessageQueue messageQueue, Message message);</programlisting>
<itemizedlist>
<listitem>
<para>XmlDocumentConverter - loads and saves an XmlDocument to the
messgae BodyStream. This lets you manipulate direclty 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>
<para><classname>XmlDocumentConverter</classname> - loads and saves
an XmlDocument to the messgae 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 implementationsRaw</para>
<para>Other potential implementations:</para>
<itemizedlist>
<listitem>
@@ -807,9 +993,9 @@ void Send(MessageQueue messageQueue, Message message);</programlisting>
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
achive this goal the <classname>MessageListenerAdapter</classname>
achieve this goal the <classname>MessageListenerAdapter</classname>
implements the standard <classname>IMessageListener</classname>
interface to recieve a message and then delegates the processing to
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
@@ -846,7 +1032,7 @@ void Send(MessageQueue messageQueue, Message message);</programlisting>
}</programlisting>
<para>If your IMessageConverter implementation will return multiple
object types, overloading the handler method is perfectly acceptible,
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>
@@ -905,9 +1091,9 @@ void Send(MessageQueue messageQueue, Message message);</programlisting>
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
conveninece of dealing with PONO contracts for message receiving but does
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 explict use of the MessageQueueTemplate class. This feature
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>