Files
spring-net/doc/reference/src/msmq-quickstart.xml
sbohlen 9788fd3580 SPRNET-1407
Updated copyright notices in docs.
2010-12-11 20:35:49 +00:00

276 lines
13 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!--
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-->
<chapter version="5" xml:id="msmq-quickstart"
xmlns="http://docbook.org/ns/docbook"
xmlns:ns6="http://www.w3.org/1999/xlink"
xmlns:ns5="http://www.w3.org/1999/xhtml"
xmlns:ns4="http://www.w3.org/2000/svg"
xmlns:ns3="http://www.w3.org/1998/Math/MathML"
xmlns:ns="http://docbook.org/ns/docbook">
<title>MSMQ QuickStart</title>
<section>
<title>Introduction</title>
<para>The MSMQ quick start application demonstrates how to use
asynchronous messaging to implement a system for purchasing a stock. Is
follows the same basic approach as in the <link
linkend="nms-quickstart">NMS QuickStart</link> but is adapted as need for
use with MSMQ. Please read the introduction in that chapter to get an
overview of the system.</para>
<para>When there is direct overlap in functionality between the MSMQ and
NMS quickstart a reference to the appropriate section in the NMS
QuickStart documentation is given.</para>
<note>
<para>To follow this MSMQ QuickStart load the solution file found in the
directory
<literal>&lt;spring-install-dir&gt;\examples\Spring\Spring.MsmqQuickStart</literal></para>
</note>
</section>
<section>
<title>Message Destinations</title>
<para>To communicate between th client and server a pair of queues will be
used. Messages sent from the client to the server will use the
transactional queue named <literal>.\Private$\request.txqueue</literal>.
Messages sent from the server to the client will use the transactional
queue <literal>.\Private$\response.joe.txqueue</literal>. The queue for
messages that cannot be processed, so called 'poison messages' will be
sent to the queue <literal>.\Private$\dead.txqueue</literal>. You can
create these queues using the computer management administration console.
Private queues are used to simplify the application setup
requirements.</para>
<note>
<para>You must create the queues mentioned previously using standard
Windows Computer Management console to manage MSMQ. This <link
ns6:href="http://www.worldofasp.net/tut/MSMQ/Basic_Introduction_about_MSMQ_in_NET_Framework_98.aspx">article</link>
covers the basics of creating the queus in the management console.
</para>
</note>
<para>Since MSMQ does not natively support the publish-subscribe messaging
style as in other messaging systems, Apache MQ, IBM Websphere MQ, TIBCO
EMS, the market data information is sent on the same queue as the
responses from the server to the client for trade requests..</para>
</section>
<section>
<title>Gateways</title>
<para>The gateway interfaces are the same as those described in the NMS
QuickStart <link linkend="nms-gateways">here</link>.</para>
</section>
<section>
<title>Message Data</title>
<para>TradeRequest and TradeResponse messages are defined using XML Schema
and classes are generated from that schema. This is the same approach as
described in more details in the NMS QuickStart <link
linkend="nms-messagedata">here</link>.</para>
<para>An important difference in the types of message data formats
supported 'out-of-the-box' with Apache, IBM, TIBCO as compared to
Microsoft MSMQ is the latter support sending a hashtable data structure.
As a result, the hashtable that was used to send market data information
from the server to the client was changed to be of type System.String in
the MSMQ example.</para>
</section>
<section>
<title>Message Handlers</title>
<para>The message handlers are the same as used in the NMS QuickStart
<link linkend="nms-handlers">here</link>, aside from the change of the
hashtable data structure to a string. This is an important benefit of
enforcing a separation between the messaging specific classes and the
business processing layer.</para>
</section>
<section>
<title>MessageConverters</title>
<para>The message converter used is
Spring.Messaging.Support.Converters.XmlMessageConverter. It is configured
by specifying the data types that will be send and received. Here is a
configuration example for types generated from the XML Schema and a plain
string.</para>
<programlisting language="myxml">&lt;object id="xmlMessageConverter" type="Spring.Messaging.Support.Converters.XmlMessageConverter, Spring.Messaging"&gt;
&lt;property name="TargetTypes"&gt;
&lt;list&gt;
&lt;value&gt;Spring.MsmqQuickStart.Common.Data.TradeRequest, Spring.MsmqQuickStart.Common&lt;/value&gt;
&lt;value&gt;Spring.MsmqQuickStart.Common.Data.TradeResponse, Spring.MsmqQuickStart.Common&lt;/value&gt;
&lt;value&gt;System.String, mscorlib&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
</section>
<section>
<title>Messaging Infrastructure</title>
<para>The implementations of the gateway interfaces inherit from Spring's
helper class <literal>MessageQueueGatewaySupport</literal> in order to get
easy access to a <literal>MessageQueueTemplate</literal> for sending. The
implementation of the <literal>IStockService</literal> interface is shown
below</para>
<programlisting language="csharp">public class <classname>MsmqStockServiceGateway</classname> : <classname>MessageQueueGatewaySupport</classname>, <classname>IStockService</classname>
{
private <classname>Random</classname> random = new <classname>Random</classname>();
private string defaultResponseQueueObjectName;
public string DefaultResponseQueueObjectName
{
set { defaultResponseQueueObjectName = value; }
}
public void Send(<classname>TradeRequest</classname> tradeRequest)
{
<classname>MessageQueueTemplate</classname>.ConvertAndSend(tradeRequest, delegate(<classname>Message</classname> message)
{
message.ResponseQueue = GetResponseQueue();
message.AppSpecific = random.Next();
return message;
});
}
private <classname>MessageQueue</classname> GetResponseQueue()
{
return <classname>MessageQueueFactory</classname>.CreateMessageQueue(defaultResponseQueueObjectName);
}
}</programlisting>
<para>The <literal>Send</literal> method is using MessageQueueTemplate's
<literal>ConvertAndSend(object obj, MessagePostProcessorDelegate
messagePostProcessorDelegate)</literal> method. The anonymous delegate
allows you to modify the message properties, such as ResponseQueue and
AppSpecific after the message has been converted from an object but before
it has been sent. The use of an anonymous delegate allows makes it very
easy to apply any post processing logic to the converted message.</para>
<para>The configuration for <literal>MsmqStockServiceGateway</literal> and
all its dependencies is shown below, highlighting important dependency
links.</para>
<programlisting language="myxml">&lt;object name="stockServiceGateway" type="Spring.MsmqQuickStart.Client.Gateways.MsmqStockServiceGateway, Spring.MsmqQuickStart.Client"&gt;
&lt;property name="MessageQueueTemplate" ref="<emphasis role="bold">messageQueueTemplate</emphasis>"/&gt;
&lt;property name="DefaultResponseQueueObjectName" value="responseTxQueue"/&gt;
&lt;/object&gt;
&lt;object id="<emphasis role="bold">messageQueueTemplate</emphasis>" type="Spring.Messaging.Core.MessageQueueTemplate, Spring.Messaging"&gt;
&lt;property name="DefaultMessageQueueObjectName" value="requestTxQueue"/&gt;
&lt;property name="MessageConverterObjectName" value="<emphasis role="bold">xmlMessageConverter</emphasis>"/&gt;
&lt;/object&gt;
&lt;object id="<emphasis role="bold">xmlMessageConverter</emphasis>" type="Spring.Messaging.Support.Converters.XmlMessageConverter, Spring.Messaging"&gt;
&lt;property name="TargetTypes"&gt;
&lt;list&gt;
&lt;value&gt;Spring.MsmqQuickStart.Common.Data.TradeRequest, Spring.MsmqQuickStart.Common&lt;/value&gt;
&lt;value&gt;Spring.MsmqQuickStart.Common.Data.TradeResponse, Spring.MsmqQuickStart.Common&lt;/value&gt;
&lt;value&gt;System.String, mscorlib&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;object id="requestTxQueue" type="Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging"&gt;
&lt;property name="Path" value=".\Private$\request.txqueue"/&gt;
&lt;property name="MessageReadPropertyFilterSetAll" value="true"/&gt;
&lt;/object&gt;
&lt;object id="responseTxQueue" type="Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging"&gt;
&lt;property name="Path" value=".\Private$\response.joe.txqueue"/&gt;
&lt;property name="MessageReadPropertyFilterSetAll" value="true"/&gt;
&lt;/object&gt;</programlisting>
<para>Since the client also needs to listen to incoming messages on the
responseTxQueue, a
<literal>TransactionalMessageListenerContainer</literal> is configured.
The configuration for the message listener container and all its
dependencies is shown below, highlighting important dependency
links.</para>
<programlisting language="myxml">&lt;!-- MSMQ Transaction Manager --&gt;
&lt;object id="messageQueueTransactionManager" type="Spring.Messaging.Core.MessageQueueTransactionManager, Spring.Messaging"/&gt;
&lt;!-- Message Listener Container that uses MSMQ transactional for receives --&gt;
&lt;object id="transactionalMessageListenerContainer" type="Spring.Messaging.Listener.TransactionalMessageListenerContainer, Spring.Messaging"&gt;
&lt;property name="MessageQueueObjectName" value="responseTxQueue"/&gt;
&lt;property name="PlatformTransactionManager" ref="messageQueueTransactionManager"/&gt;
&lt;property name="MessageListener" ref="<emphasis role="bold">messageListenerAdapter</emphasis>"/&gt;
&lt;property name="MessageTransactionExceptionHandler" ref="<emphasis
role="bold">sendToQueueExceptionHandler</emphasis>"/&gt;
&lt;/object&gt;
&lt;!-- Delegate to plain CLR object for message handling --&gt;
&lt;object id="<emphasis role="bold">messageListenerAdapter</emphasis>" type="Spring.Messaging.Listener.MessageListenerAdapter, Spring.Messaging"&gt;
&lt;property name="HandlerObject" ref="stockAppHandler"/&gt;
&lt;property name="DefaultHandlerMethod" value="Handle"/&gt;
&lt;property name="MessageConverterObjectName" value="xmlMessageConverter"/&gt;
&lt;/object&gt;
&lt;object id="<emphasis role="bold">sendToQueueExceptionHandler</emphasis>" type="Spring.Messaging.Listener.SendToQueueExceptionHandler, Spring.Messaging"&gt;
&lt;property name="MessageQueueObjectName" value="<emphasis role="bold">deadTxQueue</emphasis>"/&gt;
&lt;/object&gt;
&lt;object id="<emphasis role="bold">deadTxQueue</emphasis>" type="Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging"&gt;
&lt;property name="Path" value=".\Private$\dead.queue"/&gt;
&lt;property name="MessageReadPropertyFilterSetAll" value="true"/&gt;
&lt;/object&gt;</programlisting>
<para>A similar configuration is used on the server to configure the class
<literal>Spring.MsmqQuickStart.Server.Gateways.MarketDataServiceGateway
</literal>that implements the <literal>IMarketDataService</literal>
interface and a <literal>TransactionalMessageListenerContainer</literal>
to process messages on the requestTxQueue. You can increase the number of
processing thread in the
<literal>TransactionalMessageListenerContainer</literal> by setting the
property <literal>MaxConcurrentListeners</literal>, the default value is
1.</para>
</section>
<section>
<title>Running the application</title>
<para>To run both the client and server make sure that you select
'Multiple Startup Projects' within VS.NET. The GUI has a button to make a
hard coded trade request and show confirmation in a text box. A text area
is used to display the market data. There is a 'Get Portfolio' button that
is not implemented at the moment. A picture of the GUI after it has been
running for a while and trade has been sent and responded to is shown
below.</para>
<mediaobject>
<imageobject>
<imagedata fileref="images/nms-quickstart-gui.png"></imagedata>
</imageobject>
</mediaobject>
<para></para>
</section>
</chapter>