Message Oriented Middleware - TIBCO EMSIntroductionThe bulk of the documentation for Spring's JMS support , independent
of vendor, is described in the chapter . While
that chapter refers to classes that are part of Spring's ActiveMQ
integration, those classes have counter parts as part of Spring's TIBCO
EMS integration. For example,
Spring.Messaging.Nms.Core.NmsTemplate and
Spring.Messaging.Ems.Core.EmsTemplate. This chapter
fills in some of the gaps in taking that approach by describing Spring.NET
features that are specific to its integration with TIBCO EMS and showing
some examples using the TIBCO EMS integration.A complete sample application using Spring's EMS integration
classes is in the distribution under the directory
examples\Spring\Spring.EmsQuickStart. Documentation
for the Quickstart is available here.Interface based APIsThe TIBCO EMS APIs are not interface based. What this means is that
the class TIBCO.EMS.Session does not inherit from
an ISession interface. The lack of interfaces makes
it impossible to apply traditional approahces to support caching of
Connections, Sessions, MessageProducers, and MessageProducers. Also, in
some cases Java like setter methods were used instead of standard .NET
properties making it difficult to configure those classes using dependency
injection. (For example, see
EmssslSystemStoreInfo.SetCertificateStoreLocation()).
For these reasons it was decided to create a 'mirror' API of the TIBCO EMS
API that is interface based. In the namespace
Spring.Messaging.Ems.Common are interfaces such as
IConnectionFactory,
IConnection,
ISession,
IMessageProducer, etc as well as their
implementation classes EmsConnectionFactory,
EmsConnection, EmsSession,
etc. The interfaces mirror all the operations that are on the standard
TIBCO EMS classes so you should feel right as home when programming
against these classes.Typically users of Spring.NET do not need to programmatically
interact with these classes, instead using methods of
EmsTemplate to syncrhonously send and consume
messages and a SimpleMessageListenerContainer to
asynchronously consume messages. It will be common to configure an
Spring.Messaging.Ems.Common.ConnectionFactory using
dependency injection. The following sections show some example usage. You
can also set or get the underlying 'native' TIBCO EMS object, such as the
TIBCO.EMS.ConnectionFactory using a property 'NativeConnectionFactory'
Each class in the Spring.Messaging.Ems.Common namespace has a similar
'Native' property, for example NativeSession, NativeMessageProducer if you
need access the raw TIBCO EMS class.Using Spring's EMS based MessagingOverivewIn the namespace Spring.Messaging.Ems.Core is the class
EmsTemplate. This is the main class you will use to send messages and to
receive messages synchronously. In the namespace
Spring.Messaging.Ems.Listener is the class
SimpleMessageListenerContainer. This is the main class you will use to
recieve messages asynchronously.ConnectionsTo create a
Spring.Messaging.Ems.Common.ConnectionFactory use
the following object definition <object id="emsConnectionFactory" type="Spring.Messaging.Ems.Common.EmsConnectionFactory, Spring.Messaging.Ems">
<constructor-arg name="serverUrl" value="tcp://localhost:7222"/>
<constructor-arg name="clientId" value="SpringEMSClient"/>
<property name="ConnAttemptCount" value="10" />
<property name="ConnAttemptDelay" value="100" />
<property name="ConnAttemptTimeout" value="1000" />
</object>Please refer to the API documentation for other properties you way
want to set, in particular for those relating to SSL.Caching Messaging ResourcesWhile TIBCO EMS provides thread safe access to EMS Sessions (above
and beyond what is specified in the JMS specification), Spring provides
two implementations of the IConnectionFactory infrastructure to manage
the use of intermediate objects when following the 'standard' API walk
ofIConnectionFactory->IConnection->ISession->IMessageProducer->SendSingleConnectionFactorySpring.Messaging.Ems.Connections.SingleConnectionFactory
will return the same connection on all calls to
CreateConnection and ignore calls to
Close.You can configure a SingleConnectionFactory as you would an
EmsConnectionFactory.CachingConnectionFactorySpring.Messaging.Ems.Connections.CachingConnectionFactory
extends the functionality of SingleConnectionFactory and adds the
caching of Sessions, MessageProducers, and MessageConsumers. See the
documentation for ActiveMQ CachingConnectionFactory for some
additional information here.An example configuration is shown below <object id="connectionFactory" type="Spring.Messaging.Ems.Connections.CachingConnectionFactory, Spring.Messaging.Ems">
<property name="SessionCacheSize" value="10" />
<property name="TargetConnectionFactory" ref="emsConnectionFactory" />
</object>Notice that the property TargetConnectionFactory refers to
'emsConnectionFactory' defined in the previous section. This
connection factory implementation also set the ReconnectOnException
property to true by default allowing for automatic recovery of the
underlying Connection.The CachingConnectionFactory requires explicit closing of all
Sessions obtained from its shared Connection. This is the usual
recommendation for native EMS access code anyway and Spring EMS code
follows this recommendation. However, with the
CachingConnectionFactory, its use is mandatory in order to actually
allow for Session reuse.MessageConsumers obtained from a cached Session won't get
closed until the Session will eventually be removed from the pool.
This may lead to semantic side effects in some cases. For a durable
subscriber, the logical Session.Close() call will also close the
subscription. Re-registering a durable consumer for the same
subscription on the same Session handle is not supported; close and
reobtain a cached Session first.To avoid accidentally referring to the ConnectionFactory that
does not support caching, (emsConnectionFactory), you should use an
inner object definition as shown below. <object id="connectionFactory" type="Spring.Messaging.Ems.Connections.CachingConnectionFactory, Spring.Messaging.Ems">
<property name="SessionCacheSize" value="10" />
<property name="TargetConnectionFactory">
<object type="Spring.Messaging.Ems.Common.EmsConnectionFactory, Spring.Messaging.Ems">
<constructor-arg name="serverUrl" value="tcp://localhost:7222"/>
<constructor-arg name="clientId" value="SpringEMSClient"/>
<property name="ConnAttemptCount" value="10" />
<property name="ConnAttemptDelay" value="100" />
<property name="ConnAttemptTimeout" value="1000" />
</object>
</property>
</object>Dynamic Destination ManagementThe section
in the ActiveMQ documentation covers the use of Dynamic Destination
mangement for TIBCO as well.Accessing Admistrated objects via JNDITIBCO provides an implementation of JNDI to retrieve admistrive
objects in .NET. You can retrieve TIBCO
Destinations and
ConnectionFactories from the JNDI registry. To
provide ease of access to these JNDI managed objects in a Spring
application context the class JndiFactoryObject
is used. This allows you look configure the location of the JNDI
registry and to retrieve objects by name. The objects are retrieved from
JNDI at application startup.These retrieved objetcts from JNDI in turn can be dependency
injected into other collaborating objects such as Spring's
CachingConnectionFactory (for connections) or
EmsTemplate (for destinations). Here is an example to retrieve a TIBCO
ConnectionFactory object from the JNDI registry. <object id="jndiEmsConnectionFactory" type="Spring.Messaging.Ems.Jndi.JndiLookupFactoryObject, Spring.Messaging.Ems">
<property name="JndiName" value="TopicConnectionFactory"/>
<property name="JndiProperties[LookupContext.PROVIDER_URL]" value="tibjmsnaming://localhost:7222"/>
</object>JndiLookupFactory object implements the
IConfigurableFactoryObject interface, so
the type that is associated with the name 'jndiConnectionFactory' is not
JndiLookupFactoryObject, but the type returned from this factory's
'GetType' method, in this case the type of what was retrieved from JNDI.
The IConfigurableFactoryObject interface
also allows for the object that was returned to be dependency injected.
Please refer to the documentation on
IConfigurableFactoryObject for more
information.The dictionary JndiProperties is set using Spring Expression
language syntax for the property name. This provides a shortcut to the
more verbose <dictionary/> element. To enable this functionality
a the TIBCO.EMS.LookupContext was registered under the name
'LookupContext' in Spring's TypeRegistry.The use of this object retrieved from JNDI to configure Spring's
CachingConnectionFactory set the property TargetConnectionFactory as
shown below <object id="cachingJndiConnectionFactory" type="Spring.Messaging.Ems.Connections.CachingConnectionFactory, Spring.Messaging.Ems">
<property name="SessionCacheSize" value="10" />
<property name="TargetConnectionFactory">
<object type="Spring.Messaging.Ems.Common.EmsConnectionFactory, Spring.Messaging.Ems">
<constructor-arg ref="jndiEmsConnectionFactory"/>
</object>
</property>
</object>Other useful properties and features of JndiLookupFactoryObject
areJndiContextType : This is an enumeration
that can have either the value JMS or LDAP. These translate to
configuring JNDI context with the constants
LookupContextFactory.TIBJMS_NAMING_CONT or
LookupContextFactory.LDAP_CONTEXT for use with EMS's own JNDI
registry or an LDAP directory respectively. The default is set use
LookupContextFactory.TIBJMS_NAMING_CONT. The type JndiContextType is
also registered in Spring's TypeRegistry so that you can use a SpEL
expression to set the value as shown below. <object id="jndiEmsConnectionFactory" type="Spring.Messaging.Ems.Jndi.JndiLookupFactoryObject, Spring.Messaging.Ems">
<property name="JndiName" value="TopicConnectionFactory"/>
<property name="JndiProperties[LookupContext.PROVIDER_URL]" value="tibjmsnaming://localhost:7222"/>
<property name="JndiContextType" expression="JndiContextType.JMS"/>
<property name="ExpectedType" value="TIBCO.EMS.ConnectionFactory"/>
</object>The TargetConnectionFactory is of the
Spring wrapper type
Spring.Messaging.Ems.Common.IConnectionFactory.
You can pass into Spring's implementation of that interface,
Spring.Messaging.Ems.Common.EmsConnectionFactory,
the 'raw' TIBCO EMS type,
TIBCO.EMS.ConnectionFactory.ExpectedType: This is a property of the
type System.Type. You can set the type that the located JNDI object
is supposed to be assignable to, if any. It's use is shown in the
previous XML configuraiton listing.JndiLookupContext: This is a property of
the type TIBCO.EMS.ILookupContext. If you
create a custom implementation of
ILookupContext (for example one that
performs lazy caching), assign this property instead of configuring
the property JndiContextType.DefaultObject: Sets a reference to an
instance of an object to fall back to if the JNDI lookup fails. The
default is not to have a fallback object.MessageListenerContainersSpring's MessageListenerContainer's are used to process messages
asynchronously and concurrently. MessageListenerContainers are described
more in this
section.Transaction ManagementSpring provides an implementation of the
IPlatformTransactionManager interface for managing TiBCO messaging
transactions. The class is EmsTransactionManager and
it manages transactions for a single ConnectionFactory. Please refer to
this section for addtional
information on messaging based transaction managers.Sending a MessageThe class Spring.Messaging.Ems.Core.EmsTemplate contains several
convenience methods to send a message. These methods are identical to
those described in the ActiveMQ documentation section aside from the use of
type destination type TIBCO.EMS.Destination instead of
Apache.NMS.IDestination and switching of the namespace from Apache.NMS
to Spring.Messaging.Ems.Common.Shown below is the code example for a SimplePublisher using
Spring's TIBCO EMS classes. This does now show the 'one-liner' send
methods but one that gives you direct access to the ISession to create
the message however you wish.using Spring.Messaging.Ems.Common;
using TIBCO.EMS;
namespace Spring.Messaging.Ems.Core
{
public class SimplePublisher
{
private EmsTemplate emsTemplate;
public SimplePublisher()
{
emsTemplate = new EmsTemplate(new EmsConnectionFactory("tcp://localhost:7222"));
}
public void Publish(string ticker, double price)
{
emsTemplate.SendWithDelegate("APP.STOCK.MARKETDATA",
delegate(ISession session)
{
MapMessage message = session.CreateMapMessage();
message.SetString("TICKER", ticker);
message.SetDouble("PRICE", price);
message.Priority = 5;
return message;
});
}
}
}A more DI friendly implementation would be to expose a EmsTemplate
property or to inherit from Spring's EmsGatewaySupport base class which
provides a IConnectionFactory property that will instantiate a
EmsTemplate instance that is made available via the property
EmsTemplate.using Spring.Messaging.Ems.Common;
using TIBCO.EMS;
namespace Spring.Messaging.Ems.Core
{
public class SimpleGateway : EmsGatewaySupport
{
public void Publish(string ticker, double price)
{
EmsTemplate.SendWithDelegate("APP.STOCK.MARKETDATA",
delegate(ISession session)
{
MapMessage message = session.CreateMapMessage();
message.SetString("TICKER", ticker);
message.SetDouble("PRICE", price);
message.Priority = 5;
return message;
});
}
}
}Where the ConnectionFactory is injected using the
configuration. <object id="simpleGateway" type="Spring.Messaging.Ems.Core.SimpleGateway, Spring.Messaging.Ems.Integration.Tests">
<property name="ConnectionFactory" ref="connectionFactory" />
</object>Using MessageConvertersIn order to facilitate the sending of domain model objects, the
EmsTemplate has various send methods that take a .NET
object as an argument for a message's data content. The overloaded methods
ConvertAndSend and ReceiveAndConvert
in NmsTemplate delegate the conversion process to an
instance of the IMessageConverter interface. Please
refer to this section for
more information on MessageConverters.Example code that uses the EmsTemplate'sConvertAndSendWithDelegate, which allows access to the
message after it has been converted but before it has been sent is shown
below. For examples of using other ConvertAndSend
methods see the section referred to in the previous paragraph.public void PublishUsingDict(string ticker, double price)
{
IDictionary marketData = new Hashtable();
marketData.Add("TICKER", ticker);
marketData.Add("PRICE", price);
EmsTemplate.ConvertAndSendWithDelegate("APP.STOCK.MARKETDATA", marketData,
delegate(Message message)
{
message.Priority = 5;
message.CorrelationID = new Guid().ToString();
return message;
});
} Session and Producer CallbackPlease refer to this section for more
information on Session and Producer Callbacks.Receiving a messagesThere are two ways to receive messages, synchronously and
asynchronously. To recieve messages synchronously use EmsTemplate, to
recieve asynchronously use a MessageListenerContainer.Synchronous ReceptionPlease refer to this section for using
EmsTemplate's overloaded Recieve methods.Asynchronous ReceptionPlease refer to this section for an
introduction to Spring's MessageListenerContainers. The TIBCO EMS
namespace to create an instance of a message listener container is shown
below.using Common.Logging;
using TIBCO.EMS;
namespace Spring.Messaging.Ems.Core
{
public class SimpleMessageListener : IMessageListener
{
private static readonly ILog LOG = LogManager.GetLogger(typeof(SimpleMessageListener));
private int messageCount;
public int MessageCount
{
get { return messageCount; }
}
public void OnMessage(Message message)
{
messageCount++;
LOG.Debug("Message listener count = " + messageCount);
TextMessage textMessage = message as TextMessage;
if (textMessage != null)
{
LOG.Info("Message Text = " + textMessage.Text);
} else
{
LOG.Warn("Can not process message of type " message.GetType());
}
}
}
}And the configuration to create 10 threads that process
message off the queue named "APP.STOCK.REQUEST". See this section for
more details about the message listener container.<objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ems="http://www.springframework.net/ems">
<object id="connectionFactory" type="Spring.Messaging.Ems.Connections.CachingConnectionFactory, Spring.Messaging.Ems">
<property name="SessionCacheSize" value="10" />
<property name="TargetConnectionFactory">
<object type="Spring.Messaging.Ems.Common.EmsConnectionFactory, Spring.Messaging.Ems">
<constructor-arg name="serverUrl" value="tcp://localhost:7222"/>
<constructor-arg name="clientId" value="SpringEMSClient"/>
</object>
</property>
</object>
<object name="simpleMessageListener"
type="Spring.Messaging.Ems.Core.SimpleMessageListener, Spring.Messaging.Ems.Integration.Tests"/>
<ems:listener-container connection-factory="connectionFactory" concurrency="10">
<ems:listener ref="simpleMessageListener" destination="APP.STOCK.REQUEST" />
</ems:listener-container>
</objects>The ISessionAwareMessageListener interfaceRefer to this section for more information
on the use of this interface.MessageListenerAdapterRefer to this
section for more information on this feature and change code/XML
references of 'Nms' to 'Ems'.Processing messages within a messaging transactionRefer to this section for
more information about this type of message processing.Messaging Namespace supportTo use the EMS namespace you will need to reference the Ems
schema. Please refer to this
section for more information on configuring message listener containers.
Change references of 'Nms' to 'Ems' in that section.