|
Wednesday, November 23, 2005 |
This class has bitten me in the past and I've seen quite a few folks on different mailing lists and forums hit issues recently so figured it was worth a little blog post.
Spring's JmsTemplate is a handy abstraction for working with JMS - its particularly useful for publishing messages in a simple way. However its got a few gotchas which I'll list here in gory detail; if you're short on time skip ahead to the recommendations section.
Gotchas with JmsTemplateThe thing to remember is JmsTemplate is designed for use in EJBs using the EJB containers JMS pooling abstraction. So every method will typically create a connection, session, producer or consumer, do something, then close them all down again. The idea being that this will use the J2EE containers pooling mechanism to pool the JMS resources under the covers. Without using a pooled JMS provider from the EJB container this is the worst possible way of working with JMS; since typically each create/close of a connection, producer/consumer results in a request-response with the JMS broker.
A quick aside; we had a bug once in ActiveMQ where if you created 65535 MessageProducer instances within the space of a few seconds, we'd get an exception thrown in the broker. Its a kinda silly thing to do, to create that many producers in a small space of time (one for each message to be sent) - JMS is designed for resources like producers and consumers to be created up front and reused across many message exchanges. The bug was highlighted by a user using JmsTemplate without a JMS pool underneath. Well at least it helped find our bug which I'm glad to say is now fixed, but it didn't help their performance too much :) So you should only use JmsTemplate with a pooled JMS provider. In J2EE 1.4 or later that typically means a JCA based JMS ConnectionFactory. So if you are not inside an EJB I'd recommend using Jencks to create the ConnectionFactory, then things will be nicely pooled & can take part in XA transactions if you like - otherwise if you are in an EJB then make sure you use your J2EE containers ConnectionFactory, never a plain-old-connection factory.
Another gotcha I've seen folks do is to create a MessageConsumer inside one of the SessionCallback methods then wonder why messages are not being received. After the SessionCallback is called, the session will be closed; which will close your consumers too :). So if you want to create a MessageConsumer you should create a connection, session and consumer yourself. (BTW it might be nice to make the createConsumer & createSession methods public on JmsTemplate).
Another problem I've seen is folks using the JmsTemplate.receive() method; as I've said above if you're not in an EJB using the J2EE containers ConnnectionFactory, a connection, session & consumer will be create and closed for each receive() method. This is all fine and well - if painfully slow unless you are using pooling - but be aware that this mechanism, without pooling, may well miss messages. If you are consuming on a topic which has messages sent with NON_PERSISTENT delivery mode then chances are you will miss messages, since each receive() call is a brand new consumer which will not receive any messages sent before the consumer existed.
Another aside; in ActiveMQ 4.x we have a new feature called Subscription Recovery which even in non-durable delivery mode allows a new consumer to go back in time and receive messages delivered within a window (a fixed amount of RAM or time window). e.g. if your broker dies you have 2 minutes to reconnect to another broker and not miss any messages - even without durable delivery. Recommendations for using JmsTemplate- Never use a regular ConnectionFactory unless you are totally sure it does all the pooling you need
- If using in an EJB ensure you use the EJB containers ConnectionFactory; otherwise consider using Jencks to create it for you
- If not using in an EJB then use Jencks to create the ConnectionFactory
- If you are only publishing messages and you are not in an EJB and you are using ActiveMQ then you can use the PooledConnectionFactory
- If you are consuming messages its probably simpler & more efficient & less likely to loose messages to avoid using the receive() method and use Message Driven POJOs instead - unless you absolutely must pull messages on demand from inside an EJB.
9:48:21 AM
|
|
Maybe there's something really clever under the covers I'm not seeing but so far Spring WS seems to be a case of NIH syndrome. I've yet to see anything its claiming to be able to do one day that XFire & ActiveSOAP haven't been able to do for some time. Worse it doesn't seem to play nice with any other SOAP stack and completely ignores JSR 181 and JAX-WS which are where its at these days for SOAP stacks. It looks spookily like a reinvention of the JBI interfaces for message exchange, but not done anywhere near as well & ignoring things like headers & attachments.
I've gotta say I'm pretty disappointed; up to now the spring folks have been great at building a kick ass IoC/AOP container, focussing on their core competency and reusing stuff & working with other OSS communities for stuff they are not experts in. I wonder if they are focussing too much on competing with JBoss and building their own versions of stuff rather than reusing the good stuff the OSS community creates just to get a tick in the box. Here's hoping the Spring guys don't get all NIH and reuse XBean in future Spring releases.
9:10:01 AM
|
|
© Copyright 2007 James Strachan.
|
|
|
|
|
|
November 2005 |
Sun |
Mon |
Tue |
Wed |
Thu |
Fri |
Sat |
|
|
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
|
|
|
Aug Dec |
|
|
|
|
|
|