Friday 8 November 2013

JMSContext in action

One of the key changes in JMS 2.0 (simplified) API is the introduction of JMSContext Interface. JMSContext encapsulates the functionality of both Connection and a Session. This eliminates the need to create a JMS session explicitly, thus it reduces the number of JMS objects needed to send a message. A JMSContext can be used to create JMSConsumer(s), JMSProducer(s) or more JMSContext(s) as preferred.


Starting a JMSContext
With the JMS 1.1 specification, you need to start a connection to receive messages explicitly by invoking connection.start(). The connection objects are in "stopped" mode so you need to start the connection to consume messages. Please mind, you *only* required to start a connection if you're planning to consume messages via the created connection object, else it is *not* required to start a connection. 

The JMS specification is a bit ambiguous on the "state" of the JMSContext. The specification doesn't expect JMS providers to offer a JMSContext at "started" state by default. Instead, it expects JMS providers to auto start the JMSContext at the creation of the first consumer. However, HornetQ offers a JMSContext at started state irrespective to that. You could invoke JMSContext.getAutoStart() to obtain the status of the context.


Connection sharing with JMSContext (How to cache a JMSContext)
On my previous blog post, I explained it's an anti-pattern to create a connection at each message send() when sending messages from a plain JMS client. How could that be possible with JMSContext; since it represents both, session and a connection? 

Yes, this is still doable with JMSContext on an application client. A JMSContext is not recommended to be accessed concurrently to ensure thread safety. However you could still maintain a single physical connection and share it among multiple JMS client threads by issuing each JMS client with a JMSContext. You need to invoke JMSContext.createContext(int sessionMode) for each JMS client; each JMSContext.createContext() invocation would not create a new physical connection but a new JMSContext. When you invoke JMSContext.close() on a JMSContext, it would close just the JMSContext not the physical connection if it's being used by another (active) JMSContext(s). Here's an illustration :

JMSContext jmsContext = resourceFactory.getJMSContext(JMSContext.AUTO_ACKNOWLEDGE);
JMSContext jmsContext2 = jmsContext.createContext(0);
JMSContext jmsContext3 = jmsContext.createContext(0);
jmsContext.close();   
jmsContext3.close();
//The physical connection still active although the initial JMSContext was closed.
Queue queue = jmsContext2.createQueue("A"); 
JMSConsumer jmsConsumer = jmsContext2.createConsumer(queue);


Closing a JMSContext
Invoking JMSContext.close() is suffice to close a connection. The client code does *not* need to close its associated resources, such as JMS consumers and JMS producers. The JMS provider closes all the associated resources when you invoke JMSContext.close().


The invocation of JMSContext.createContext(int sessionMode) is prohibited inside a JEE container as it violates JEE 7 specification. The WEB, EJB applications inside a JEE container are not allowed to have more than an active JMSContext at any given moment. The container would throw JMSRuntimeException if attempted to create more than a single active JMS context. (Please refer JEE 7 Spec, section 6:7)

No comments:

Post a Comment