msmq doc updates
This commit is contained in:
@@ -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> <object id='questionTxQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
|
||||
<property name='Path' value='.\Private$\questionTxQueue'/>
|
||||
@@ -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> <object id='questionTxQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
|
||||
<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"><!-- Class that you write --></emphasis>
|
||||
<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 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> <object id='retryQuestionTxQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
|
||||
<programlisting>
|
||||
<emphasis role="bold"> <!-- The 'retry' queue to send poison messages --></emphasis>
|
||||
<object id='retryQuestionTxQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
|
||||
<property name='Path' value='.\Private$\retryQuestionTxQueue'/>
|
||||
<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">
|
||||
|
||||
<!-- as before but adding -->
|
||||
|
||||
<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="retryQuestionTxQueue"/>
|
||||
@@ -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> <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>
|
||||
|
||||
@@ -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"><!-- Queue to receive from --></emphasis>
|
||||
@@ -479,33 +653,44 @@ void Send(MessageQueue messageQueue, Message message);</programlisting>
|
||||
|
||||
<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 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>
|
||||
|
||||
Reference in New Issue
Block a user