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) and 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. I have seen lot of bad programs starting connections unnecessarily where the connection has only being used to send messages. A connection represents a physical link to the JMS provider.

The JMS specification is a bit ambiguous on the "state" of the JMSContext. The specification doesn't expect JMS providers to offer a 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 (I'm going to discuss this behavior with HornetQ later). You could invoke JMSContext.getAutoStart() to obtain the status of the context.

Connection sharing with 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. The JMSContext cannot be accessed concurrently. However you could still maintain a single connection and share it among multiple JMS client threads by issuing each with a JMSContext by calling JMSContext.createContext(int sessionMode). Each JMSContext.createContext() invocation would not create a new connection but a new JMSContext and each of these JMS contexts would use the same physical connection to communicate with the JMS broker. 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);
//The physical connection still active although the initial JMSContext was closed.
Queue queue = jmsContext2.createQueue("jms.queue.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 JMSConsumers and JMSProducers. 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