Tuesday November 29, 2005
Transactions and JMS I started to talk about JMS thence I might write a few sentences about transactions. Very often use case is that you deliver message and then this message is stored in database. How we can solve this. Should we use JTA for this?
I hope that reader knows that you mustn't use global transaction for consumer and producers. Why? Because, having all producers and all consumers participate in one global transaction would defeat the purpose of using a loosely coupled asynchronous messaging environment. JMS transactions follow the convention os separating the send operations from the receive operations. Which ways do we have for transactions with JMS?
UserTransaction txt = (UserTransaction) ctx.lookup("UserTransaction");
txt.begin();
// make JDBC stuff and send message
txt.commit();
public void onMessage(Message aMessage) {
try{
if (aMessage instanceof ObjectMessage){
// process message
Order order = (Order)((ObjectMessage) aMessage).getObject();
Connection conn = getDS().getConnection();
// insert Order in DB
...................
}else{
logger.log(Level.SEVERE,"Only object messages are supported.");
}
}catch(SQLException ex){
context.setRollbackOnly();
}
}
I encountered one issue with this approach in Sun Aplication server. Messages are redelivered again and again. It means that you can flood your server with many messages. BTW, it's good test of Sun App server for DOS attack :-). I know that Jboss allows to setup of number of redelivering but I can't find it for Sun App server. Therefore, I suggest to use a little bit approach, throw EJB exception. What's happened when the database will be offline? Under container-managed transaction demarcation, upon receiving a runtime exception (EJBException extends Runtime exception) from a MDB the container roll-backs the container-started transaction and the message is delivered in dead queue. Below is same sample that throws EJBException.
public void onMessage(Message aMessage) {
try{
if (aMessage instanceof ObjectMessage){
// process message
Order order = (Order)((ObjectMessage) aMessage).getObject();
Connection conn = getDS().getConnection();
// insert Order in DB
...................
}else{
logger.log(Level.SEVERE,"Only object messages are supported.");
}
}catch(SQLException ex){
throw new EJBException(ex);
}
}
Thank you for posting this blog. I am having a similar 'database transaction rolls back and then message gets redelivered for ever' problem.
Can I just clarify:
- You are saying that JMS will redeliver the message if the database rolls back even if you catch the Exception and stop it bubbling out of onMessage (how does it know the Exception happened in this case?)
- If you throw an EJBException from onMessage, JMS will never try to resend the message? Is this a side-effect or is this in the spec?
Thanks,Richard.
Posted by Richard Kennard on March 28, 2006 at 05:09 AM CEST #
BEGIN TXN
END TXN My questions are: 1. What happens to the JMS messages already sent if the transaction needs to be rolled back at step 5? 2. If the JMS Messages are not sent until the transaction commits, isn't that similar to putting step 3 after all the ejb calls? Thanks, Nitin
Posted by Nitin on April 27, 2006 at 01:53 PM CEST #