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.
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
/@PreDestroy
methods. 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.
I see the accepted answer is recommending to create a new connection, a new session and a new producer every time. And it also states that Oracle says it is very cheap to create new connections. That is not really true. It depends on the application server being used.
The Weblogic documentation, for example, states clearly that it is expensive to create resources and sessions. I don't know if ApacheMQ does better.
I would recommend you create only one connection and keep it. Don't keep the reference in the EJB, but do keep it. In a singleton, per application scope, session scope, wherever you want.
If you did that, you'd have an additional problem that the connection may fail. You will want to know if your cached connection gets disconnected from the server. The auto-reconnect feature does not work great. Especially in case of auto-migratable JMS Servers.
You can implement an error listener using Connection.setExceptionListener(ExceptionListener).
When the onError(method) gets called, you can clear you cache.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With