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?
  • If you are using only JMS in your transactions, you can create a JMS transacted session. A JMS transacted session supports transactions that are located within the session. A JMS transacted session's transaction will not have any effects outside of the session. For example, rolling back a session will roll back all sends and receives on that session, but will not roll back any database updates. Enable to this way, simple set first parameter to true in createSession method.
  • If you are mixing other operations, such as EJB, JDBC, with JMS operations, you should use a Java Transaction API (JTA) user transaction in a non-transacted JMS session. In this case you should first get lookup UserTransaction using JNDI and then start, perform desired operations and commit all like in this snapshot:
          UserTransaction txt = (UserTransaction) ctx.lookup("UserTransaction");
          txt.begin();
          // make JDBC stuff and send message
          txt.commit();
       
  • Use message driven beans with CMT. In this case enable container manage transaction in ejb-jar.xml file. Define Container as type of the transaction in this file and use Required for trans-attributes. These values are setup by default for all MDB that are created in NetBeans. Then you can use the MDB context in onMessage method like this:
        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);
            }
        }
      
Comments:

Petr,

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:

  1. 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?)
  2. 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 27, 2006 at 09:09 PM CEST #

I have a question on transactions involving JMS message sends, and invoking method calls. Consider the following case:

BEGIN TXN

  • 1. ejb1.method1();
  • 2. ejb1.method2();
  • 3. sendJMSMessage();
  • 4. ejb1.method3();
  • 5. ejb1.method4();

  • 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 06:53 AM CEST #

    Post a Comment:
    • HTML Syntax: NOT allowed
    About

    pblaha

    Search

    Categories
    Archives
    « April 2015
    SunMonTueWedThuFriSat
       
    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
      
           
    Today
    Bookmarks