 |
Message acknowledgment and redelivery with message-driven beans
Posted by simongbrown on June 03, 2004 at 02:23 PM | Comments (3)
We've been doing some prototyping over the past week and wanted to put together a simple enterprise application to prove some of the characteristics of JMS message processing via message-driven beans. The application server we're using is WebSphere Application Server 5.1 and for the purposes of our testing, we've been using the embedded messaging server. Although WSAD isn't my favourite tool, it does allow us to develop/deploy/test components incredibly quickly and this is great for tweaking the properties of MDB's to see how they behave differently.
One of the features that we've been playing with is the transaction demarcation on MDBs. As with session beans, you can use container-managed transactions (CMT) or bean-managed transactions (BMT). However, unlike session beans, you don't seem to be able to easily implement a functionally identical bean with CMT and BMT. It comes down to message acknowledgment.
If you have a CMT message-driven bean with the transaction attribute defined as Required, receipt and acknowledgement of the message is included within the transaction that the container starts prior to calling onMessage(). In other words, if the onMessage() method returns successfully (i.e. without an exception being thrown) and the transaction hasn't been flagged to rollback, the container will acknowledge receipt of the message for you inside the transaction. On the other hand, if an exception is thrown or you call setRollbackOnly(), the container won't acknowledge the message. When this occurs, typically the container/JMS provider will attempt to redeliver (retry) the message a short time later.
Moving on to BMT message-driven beans and things are not the same. Of course, it's the responsibility of the bean developer to create and start a transaction, but a side-effect of this is that the acknowledgment of the JMS message isn't contained within this transaction. The following paragraph from section 15.4.7, Transaction context of message-driven bean methods, of the EJB 2.0 specification does in fact make this explicit.
When a message-driven bean using bean-managed transaction demarcation uses the javax.transaction.UserTransaction interface to demarcate transactions, the message receipt that causes the bean to be invoked is not part of the transaction. If the message receipt is to be part of the transaction, container-managed transaction demarcation with the Required transaction attribute must be used.
If you think about the sequence of events and when the transaction is started, this does make sense. Unfortunately, in the next section the spec goes on to say the following.
Message-driven beans should not attempt to use the JMS API for message acknowledgment. Message acknowledgment is automatically handled by the container. If the message-driven bean uses container managed transaction demarcation, message acknowledgment is handled automatically as a part of the transaction commit. If bean managed transaction demarcation is used, the message receipt cannot be part of the bean-managed transaction, and, in this case, the receipt is acknowledged by the container.
So then, message acknowledgment is handled by the container. With CMT, a commit or rollback will effectively acknowledge or not acknowledge the message, but with BMT the message will always be acknowledged. This is a subtle point but means with BMT that if the onMessage() doesn't return successfully, the message gets acknowledged anyway and redelivery won't occur. It seems that you have to be very careful about implementing BMT message-driven beans!
The reason that we discovered this is because we were trying to figure out the best way to implement logic like, "sorry, not quite ready to process this message yet". With CMT, rolling back the transaction puts the message back on the queue ready for it to be delivered to another MDB instance and if it wasn't for the listener ports in WebSphere Application Server blowing up, we might have had a solution. Now we're exploring other options, including the usual mechanism of forwarding on the message to another (or the same) queue. We're also looking into our WebSphere listener port problem and hopefully it's just a config issue. ;-)
Bookmark blog post: del.icio.us Digg DZone Furl Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment
-
Are Message-Driven Beans appropriate for that use case?
The reason that we discovered this is because we were trying
to figure out the best way to implement logic like, "sorry, not
quite ready to process this message yet".
Are you sure that a Message-Driven Bean is sound for that use case.
What you seem to need is a
polling consumer
rather than a event-driven
consumer.
Message-Driven Beans are clearly event-driven consumer and are not really made to
be polling customers...
Besides, why do you want to send such messages to the client. The client is
sending a JMS message, so he clearly know that he is in an async world and he
has to be prepared to delay in the processing of the message. The only important
thing for him is to know that the message has been acknowledged and will be processed
later.
Going back to you example, I'm not sure that using message acknowledgement
semantic to imply business processing semantic is a good thing.
What do you think?
jeff
Posted by: jmesnil on June 04, 2004 at 06:17 AM
-
Antipattern: Hot Potato
Simon,
You are describing pretty much to the letter the hot potato antipattern where noone wants to acknowlegde the receipt of a message. I think it's a mistake that many people make when applying CMT symantics to BMT.
In fact I think the CMT symantics are a little confusing. You sould not be doing business logic when acknowlodging the receipt of a message!
Have a look at the sample chapter provided by manning in Bruce Tate's excellant book Bitter EJB at http://www.manning-source.com/books/tate2/tate2_ch06.pdf
HTH ;)
Oz
Posted by: ozkologlu on June 14, 2004 at 11:35 PM
-
Regardless of antipatterns / blar, I think there are cases when this will occur, especially in very large and older systems.
In fact, I had a situation that is very similar to this. My issue was that I have a legacy application that is primarily DB driven and in normal operation can experience both SQLExceptions that are recoverable in that they are temporary but unavoidable deadlocks (which need to be retried, and recall that this is a legacy system so being told to just rewrite it is not realistic), and other SQLExceptions that are simply and correctly un-recoverable.
I had started out with BMT MDBs for the part of the app that listens to queues, but as detailed above it is not possible to stuff the message back in this case. This meant that the deadlocking situations would get dropped and not retried. Oops. Also, pure CMT with a call chain that was un-interrupted to the DB would just mean that I would poison myself with any message with bad data as it put it back to the Queue over and over (until the Listener Port stopped).
My solution, which is working well for me is to have CMT message beans (because you have to in order to put messages back) that call stateless session beans. The stateless session beans, then, grab their own UserTransaction and do BMT.
These report back to the MDB which calls setRollbackOnly() off the MessageContext as they determine is necessary. This allows me to not have to have a whole bunch of horrible SQL Exception error code inspection logic and to finely-tune the handling of which message call-chains are truly rollback-necessary and which ones can be dropped on the floor.
Posted by: nickzoss on July 08, 2005 at 12:09 PM
|