Search This Blog

Saturday, January 24, 2009

Spring Jms Support

My year at Overstock.com has been just superb from a learning front. I have had the oppurtunity to work RESTful Web Services, Maven, Hibernate and in general be a part of a super architecture and development team at play.

I have also been able to contribute in the selecting of a JMS provider/architecture for reliable messaging at Overstock. The provider that we have selected is Oracle WebLogic.

As we enter the world of WebLogic JMS, I find that there are items that are best abstracted away such that developers of an application can easily utilize the provider without having to repeat code. Spring's JMS support feels ideal for the same.

Obtaining JMS Artifacts from JNDI:



Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
p.put(Context.PROVIDER_URL, "t3://localhost:8000,localhost:8002");
p.put(Context.SECURITY_PRINCIPAL, "weblogic");
p.put(Context.SECURITY_CREDENTIALS, "weblogic");

JndiTemplate template = new JndiTemplate(p);

QueueConnectionFactory connFactory = (QueueConnectionFactory)
template.execute(new JndiCallBack() {
public Object doInContext(Context ctx) {
return (QueueConnectionFactory) ctx.lookup("connectionFactory");
}
});
....




JmsTemplate:
Spring Framework provides a Template class called JmsTemplate much like HibernateTemplate for Hibernate DAO. The class provides many convinience methods for sending recieving messages by providing an abstraction layer over the JMS API. There are methods such as send/receive or methods that accept Call Backs. I like some of the safety these methods provide where Connections, Sessions, Producers and Consumers are gracefully closed after use of them. WebLogic documentation explicitly states that Connection objects must be closed after using them.

That said, I also fear that this class is very open for misuse. James Strachan has mentioned some of the PIT falls that one needs to be aware of when using JmsTemplate. The primary concern that surfaces with the JmsTemplate creating a Connection/Session/Producer or Consumer across every send/receive/callback. This can easily be misused and in the case of WebLogic, attempting to use the template with the default Connection Factory degrades the performance of operations. I fear that directly using the JmsTemplate can lead to code like:



jmsTemplate.send(new MessageCreator() {
public Message send(Session session) throws JMSException {
return session.createTextMessage("Order Submited");
}
});

... do some stuff..
jmsTemplate.send(new MessageCreator() {
public Message send(Session session) throws JMSException {
return session.createTextMessage("Order completed");
}
});



For each of the send operations above, a Connection, Session and Producer will be created. If the above sequence is being called many times over, this would present a performance problem.

With CallBacks like Connection/Session or Producer, re-use has more of chance as within a call back one could send multiple messages thus re-using the jms objects. However, if these operations are many, than these too can suffer misuse and cause performance degradation. JmsTemplate works I guess when you send a message once in a while.

James observation regarding the use of JmsTemplate is very astute and using JmsTemplate might not be best of options unless "Pooling" is introduced into the mix.

Spring Message Container:
Spring offers support for Message Driven POJOs (MDP) in the form of MessageListenerContainers. The Container is responsible for retrieving a message from a JMS destination and then notifying the listener associated with the Container. One nice feature of the Container is that it provides for re-connect logic in event of failure of a server in a Cluster by allowing transparent reconnect to different server in the cluster. Look at this POSTING about a Reconnecting Message Listener. The Spring Message ListenerContainer's tend to Pool Jms artifacts as well. In particular the DefaultMessageListenerContainer provides XA support as well. Using a Container to receive messages is as simple as:




SimpleMessageContainer container = new SimpleMessageContainer();
container.setConnectionFactory(connectionFactory);
container.setDestination(queue);
// Set how many concurrent consumers
container.setConcurrentConsumers(10);
consumer.setMessageListener(new javax.jms.MessageListener() {
public void onMessage(Message m) {
..do some stuff...
}
});
container.start();




Message Converters:
I feel that the SimpleMessageConverter class has some nice convinience methods for converting from POJO's to JMS Messages and vice versa. Message Converters are IMO a great concept for message translation. No whining here.

All in all, I think using Spring's MessageContainer is advisable considering it has all the logic for reconnect, multi-threading, XA etc all build in. Using the Converters has benefit and the JndiTemplate serves to abstract away boiler plate code. However, using Jms Template needs to be done with great care with Pooling almost a must. Jenks might be an option.

2 comments:

Anonymous said...

Have you tried using the Spring DefaultMessageListenerContainer within weblogic?

Sanjay Acharya said...

IMHO the DefaultMessageListenerContainer is rather heavy weight. In addition I do not like the fact that there are two methods start() and initialize(). If I call start() for example without initialize(), then listening will not start. Great that the afterPropertiesSet() method checks for the same. However, the reason I like Spring is that it can even be used outside the container. This however is a crank in the wheel IMO.