UpAllNight UpAllNight - 18 days ago 6
Java Question

When should I close a JMS connection that was created in a stateless session bean?

I have a general question about when to close connections that have been created within a stateless session bean (EJB). The connections are to ActiveMQ and they are created within the bean's constructor. The connection is then used within a method, and I'm wondering when the appropriate time/place to close this connection is.

Would it be appropriate to have a separate method for closing the connection, that must be called by the class using the bean? Or should I simply close the connection within the method using it? I am worried that I may close a connection then re-use that bean with a now-closed connection since the connection is opened in the constructor. Here's some code to bat around:

@Stateless
@LocalBean
public class SendEventsBean {


private static String brokerURL = ".......";
private static transient ConnectionFactory factory;
private transient Connection connection;
private transient Session session;
private transient MessageProducer producer;

public SendEventsBean() {
factory = new ActiveMQConnectionFactory(brokerURL);
try {
connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(null);
} catch (JMSException e) {
e.printStackTrace();
}
}

public void sendEvent(String id, String description, String area) {

Destination destination;
try {
destination = session.createQueue("FWT." + "events");
TextMessage message = session.createTextMessage(id + " " + description + " " + area);
producer.send(destination, message);
} catch (JMSException e) {
e.printStackTrace();
}
}

public void close() throws JMSException {
if (connection != null) {
connection.close();
}
}
}


As you can see, I currently have a separate close method that should be called by the class using the bean after sending an event. Is this legitimate, or asking for trouble? I am inexperienced with EJB and am open to any suggestions. The bean is injected into the calling class using the @EJB annotation.

NBW NBW
Answer

I would not recommend the accepted answer. Let me address this and several points raised by other answers to this question.

A better approach would look something like this example (JavaEE6 JMS style) which sends an ObjectMessage to a Queue from within a stateless EJB:

@Stateless
public class SendEventsBean {

  private static final Logger log = Logger.getLogger(SendEventsBean.class);

  @Resource(mappedName = "jms/MyConnectionFactory")
  private ConnectionFactory jmsConnectionFactory;

  @Resource(mappedName = "jms/myApp/MyQueue"
  private Queue queue;

  public void sendEvent() {
    Connection jmsConnection = null;
    try {
        connection = jmsConnectionFactory.createConnection();
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        MessageProducer producer = session.createProducer(queue);
        MyObj obj = new MyObj(1, "Foo");
        ObjectMessage myObjMsg = session.createObjectMessage(obj);
        producer.send(myObjMsg);
    } catch (JMSException jmxEx) {
        log.error("Couldn't send JMS message: ", jmsEx);
    }finally{
        if (jmsConnection != null) {
            try {
                jmsConnection.close();
            }catch(JMSException ex) {
               log.warn("Couldn't close JMSConnection: ", ex);
            }
        }
    }
  }

}

JMS resources should be administered by your Application server (unless you need to use dynamic ones) and as such will be exposed to your application for @Resource injection.

Caching JMS resources in your stateless session bean is not recommended by Oracle. It is very very cheap to create a JMSConnection object as it is only a thin wrapper around the physical connection. As such its OK not to cache it, or create and tear it down in @PostConstruct/@PreDestroymethods. In fact I've tested out the approach of leveraging the EJB lifecycle methods for this and only run into problems.

Also the Session and Producer objects are not thread safe, they need to be created once per thread. You could run into threading issues by storing/caching them in the SLSB as properties.

All these are reasons to keep it simple and leverage your resources locally in your producer method.

There are other details/considerations you will need to factor in, for example the level and type of transaction support you might need. Or the handling of 'poison messages', but with my simple example I am trying to address some of the basic issues I see with many of the answers here. Hope this helps.

Comments