Chapter 30. Message Oriented Middleware - TIBCO EMS

30.1. Introduction

The bulk of the documentation for Spring's JMS support , independent of vendor, is described in the chapter Chapter 29, Message Oriented Middleware - Apache ActiveMQ and TIBCO EMS. 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.

[Note]Note

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.

30.2. Interface based APIs

The 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.

30.3. Using Spring's EMS based Messaging

30.3.1. Overivew

In 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.

30.3.2. Connections

To 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.

30.3.3. Caching Messaging Resources

While 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 of

IConnectionFactory->IConnection->ISession->IMessageProducer->Send

30.3.3.1. SingleConnectionFactory

Spring.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.

30.3.3.2. CachingConnectionFactory

Spring.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.

[Note]Note

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.

[Note]Note

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>

30.3.4. Dynamic Destination Management

The section in the ActiveMQ documentation covers the use of Dynamic Destination mangement for TIBCO as well.

30.3.5. Accessing Admistrated objects via JNDI

TIBCO 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 IFactoryObject 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.

[Note]Note

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 are

  • JndiContextType : 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>
    [Note]Note

    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.

30.3.6. MessageListenerContainers

Spring's MessageListenerContainer's are used to process messages asynchronously and concurrently. MessageListenerContainers are described more in this section.

30.3.7. Transaction Management

Spring 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.

30.3.8. Sending a Message

The 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>

30.4. Using MessageConverters

In 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's ConvertAndSendWithDelegate, 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;
           });
} 

30.5. Session and Producer Callback

Please refer to this section for more information on Session and Producer Callbacks.

30.6. Receiving a messages

There are two ways to receive messages, synchronously and asynchronously. To recieve messages synchronously use EmsTemplate, to recieve asynchronously use a MessageListenerContainer.

30.6.1. Synchronous Reception

Please refer to this section for using EmsTemplate's overloaded Recieve methods.

30.6.2. Asynchronous Reception

Please 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>

30.6.3. The ISessionAwareMessageListener interface

Refer to this section for more information on the use of this interface.

30.6.4. MessageListenerAdapter

Refer to this section for more information on this feature and change code/XML references of 'Nms' to 'Ems'.

30.6.5. Processing messages within a messaging transaction

Refer to this section for more information about this type of message processing.

30.6.6. Messaging Namespace support

To 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.