Should I cache JMS connections and JMS sessions?

This is a question that comes up again and again, and there seems to be a lot of confusion around it, mainly due, in my opinion by bad habits introduced by JEE and Spring.

 

Should I re-use JMS connections, sessions, consumers and producers or should I create them every time I want to send or consume a message?

 

The answer to this depends on where you're using the raw JMS API or you're using the JMS API from inside a JEE application server.

 

If you're using just raw JMS - i.e you have a simple JMS client which connects directly to a JMS server using the JMS API, then you should always re-use JMS objects.

 

JMS objects like connection, session, consumer and producer were designed to be re-used. In most implementations connection and session are pretty heavyweight to setup and consumer usually requires a network round trip to set up. Producer is often more lightweight, although there is often some overhead in creating it.

 

In a straightforward JMS deployment, it is an anti-pattern (in other words a very bad thing to do) to be creating these JMS objects every time you send receive messages. Don't do it!


What if my JMS code is running in a JEE application server?

 

What about if your code is in an application server - e.g. you have an EJB which needs to send a message to a messaging system?

 

In this case you've probably seen code in the EJB method which gets a connection, creates a session, creates a producer, sends the message then closes the connection?

 

Hang on a second, isn't this an anti-pattern like I mentioned before?

 

Well, the answer is, in this case, it's probably not!

 

So why isn't it anti-pattern in the EJB case?

 

The reason is, when you use the JMS API from inside an EJB (including an MDB), or a servlet, (or a MBean in JBoss), then when you call createConnection() or createSession() on your JMS connection factory or JMS connection, you're not actually making those calls on a real JMS connection factory or JMS connection.

 

The JEE specification mandates that the connection factory you use is actually a JCA managed connection factory (see the JCA specification). The JCA managed connection factory wraps the real underlying JMS connection factory and implements the same interface so to you, the user, you're just using a normal connection factory, right?

 

Wrong. The JCA layer intercepts the calls to createConnection() and createSession() and provides a caching layer (amongst other things). So when you call createConnection() or createSession(), then, in most cases it's not really calling the actual JMS implementation to actually create a new JMS connection or JMS session, it's just returning one from it's own internal cache - in other words the JCA layer pools JMS connections and JMS sessions.

 

This is why it is not an anti-pattern to call createConnection(), createSession(), createProducer(), send() and close connection every time your EJB method is executed, since it's not really creating a new connection and session each time.

 

However, it should be noted that the JCA layer does not cache JMS consumer and producer objects, only JMS connections and sessions. So a new JMS producer object will be created each time. This is not normally such a big deal since creating a JMS producer with most messaging systems normally just involves creating a Java object on the client side with no network round trip involved.

 

Note that create a JMS consumer will typically involve a network round trip and the JCA does not cache consumer objects!

 

In JBoss application server, the "special" JMS connection factory which provides the JCA caching is usually available at java:/JmsXA in jndi.

 

If you find yourself using any other JMS connection factories from within your EJB, MDB or servlet, e.g. java:/ConnectionFactory or java:/XAConnectiomFactory then it's almost certainly NOT the wrapped JCA connection factory, so your performance *will suck* if you're calling createConnection(), createSession() etc every time your method is invoked, since a raw JMS connection factory will provide *no caching*. You have been warned!

 

I am eternally ungrateful to the JEE authors for allowing these semantics, since IMHO it encourages poor JMS practices, and a poor understanding of the JMS API. EJB developers follow the same pattern when they are not running in an application server and are surprised when their application performs poorly.

 

I'm running outside the application server, does this mean I need to provide my own caching?

 

If you're running outside the JBoss application server, the JCA managed connection factory will not be available to you, so you will not have automatic caching of JMS connections and sessions.

 

In this case you need to follow sensible JMS application design, and design your application to re-use JMS connections, sessions, consumers and producers.