14 Replies Latest reply on Nov 27, 2012 11:32 PM by jbertram

    JMS Transaction Confusion

    cpuffalt

      I'm using JBoss v7.1.2 and have been having grief getting JMS messaging to work.  Messages were seemingly being posted to a Queue without error but nothing was appearing in the corresponding consuming MDB.

       

      Here's my setup:

       

      {code}

      $ ./bin/jboss-cli.sh

      You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.

      [disconnected /] connect

      [standalone@localhost:9999 /] jms-queue add --queue-address=MailServiceQ --entries=jms/queue/MailServiceQ

      [standalone@localhost:9999 /] exit

      {code}

       

      A stateless session bean which posts messages to the queue:

       

      {code}

      @Stateless

      @TransactionAttribute(TransactionAttributeType.REQUIRED)

      public class JmsMailService implements MailServiceRemote

      {

        @Resource(mappedName = "java:/ConnectionFactory")

        private ConnectionFactory connectionFactory;

       

        @Resource(mappedName = "java:/jms/queue/MailServiceQ")

        private Queue queue;

       

        @Override

        public void sendMessage(EmailMessage msg)

        {

          Connection connection = null;

          try

          {

            connection = connectionFactory.createConnection();

      // This does not seem to work??

            Session session =

                connection.createSession(true, Session.SESSION_TRANSACTED);

      // This works??

      //          connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

            MessageProducer messageProducer = session.createProducer(queue);

            connection.start();

       

            ObjectMessage jmsMsg = session.createObjectMessage(msg);

       

            messageProducer.send(jmsMsg);

          }

          catch (Exception e)

          { throw new RuntimeException(e); }

          finally

          {

            if (connection != null) { try { connection.close(); } catch (JMSException e) { } }

          }

        }

      }

      {code}

       

      And a MDB to consume the messages:

       

      {code}

      @MessageDriven(name = "MailServiceMDB", activationConfig = {

                          @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),

                          @ActivationConfigProperty(propertyName = "destination", propertyValue = "java:/jms/queue/MailServiceQ"),

                          @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge") })

      @Singleton

      @Startup

      @TransactionAttribute(TransactionAttributeType.REQUIRED)

      public class MailProcessor implements MessageListener

      {

        public void onMessage(Message m)

        {

          jmsMsg = (EmailMessage) ((ObjectMessage) m).getObject();

          handleMessage(jmsMsg);

        }

      }

      {code}

       

      Can someone explain why messages are not received by the MDB at all when using a transacted JMS Session.  I get no errors but the MDB never receives messages.  If I change the Session to non-transacted in the connection.createSession() call then messages are delivered?  Is it a bug?  My understanding is that the parameters to the createSession() method are basically ignored when called from a session bean using container-managed transactions?

       

      Thanks,

      Corey

        • 1. Re: JMS Transaction Confusion
          jbertram

          If you use a transacted session then you actually have to commit the session for the message to be sent.  Since your code never calls javax.jms.Session.commit() your message is never sent.

           

          Furthermore, I'm not sure I see the point of using a transacted session when you're only sending a single message.

           

          Lastly, there's no need to invoke javax.jms.Connection.start() since you're not consuming any messages.

          • 2. Re: JMS Transaction Confusion
            cpuffalt

            Justin,

             

            Thanks for your response.  The JEE6 tutorial seems to be misleading in this respect:

             

            When you create a session in an enterprise bean, the container ignores the arguments you specify, because it manages all transactional properties for enterprise beans. It is still a good idea to specify arguments of true and 0 to the createSessionmethod to make this situation clear:

            session = connection.createSession(true, 0);

            When you use container-managed transactions, you normally use the Required transaction attribute (the default) for your enterprise bean’s business methods.

             

             

            My interpretation of the above was that I should NOT be explicitly calling Session.commit() since I'm using CMT?

             

            My code is using a transacted session because I don't want a message posted to the queue unless the whole transaction completes (this would include separate calls to persist an Entity bean for example.)

             

            Thanks,

            Corey

            • 3. Re: JMS Transaction Confusion
              sfcoy

              The last time I looked at this you had to use:

               

              {code:java}

                @Resource(mappedName = "java:/JmsXA")

                private ConnectionFactory connectionFactory;

              {code}

               

              to get the transactional behaviour that you're looking for.

              • 4. Re: JMS Transaction Confusion
                jbertram

                The first thing to note here is that if you are in a JTA context (which you are since you are in an EJB with @TransactionAttribute(TransactionAttributeType.REQUIRED)) and you want your JMS operations to be part of the JTA transaction then you need to use a JCA based JMS connection factory.  The connection factory that you are using is not JCA based.  It is a plain JMS connection factory which means it will not be enlisted into the ongoing JTA transaction.

                 

                The "java:/JmsXA" connection factory is JCA based.  Based on your expectations, you should be using this connection factory (as Stephen already mentioned).

                 

                When you use a JCA based JMS connection factory then the JCA layer takes care of enlisting the session into the JTA transaction which then gets committed automatically when the EJB container calls commit() on the JTA transaction.

                 

                Remember, JMS transacted sessions have nothing to do with JTA, and IMO it is misleading to suggest they do.  I do not think the tutorial you cited fully describes behavior which is defined by the Java EE 6 specification.

                1 of 1 people found this helpful
                • 5. Re: JMS Transaction Confusion
                  cpuffalt

                  Thanks a lot Stephen!  That indeed resolves the issue.

                   

                  Would it be fair to ask for the container to log a warning when injecting a non-XA ConnectionFactory into a session bean using container-managed transactions?  It seems to me that's unlikely to ever be what users are intending.  (In an ideal world the container would inject the appropriate ConnectionFactory for me but I don't know enough about the specs in general to know whether that should be expected or not.)

                   

                  Corey

                  • 6. Re: JMS Transaction Confusion
                    jbertram

                    I don't think that would be fair.  It's perfectly valid to use a non-JCA and/or non-XA connection factory in a JTA context.

                    • 7. Re: JMS Transaction Confusion
                      sfcoy

                      Personally, I think the main problem here is that as recently as JBoss 4.3EAP (when I last used it, but I think JBoss 5.1.x EAP is the same) the connection factory at "java:/ConnectionFactory" would return connections that behaved transactionally, and it no longer does this.

                      • 8. Re: JMS Transaction Confusion
                        cpuffalt

                        Thanks for the explanation Justin.

                         

                        A non-XA connection factory may certainly be valid (although I think it should be an explicit annotation) but non-JCA?  To me, if you want a non-JCA connection factory then the session bean should be explicitly annotated to use BEAN-managed transactions.  Making things implicit via JNDI strings is just asking for trouble.

                         

                        Corey

                        • 9. Re: JMS Transaction Confusion
                          jbertram

                          I've not observed that behavior before, Stephen.  The connection factory implementation bound at "java:/ConnectionFactory" has always been a plain JMS connection factory in JBoss Messaging and now in HornetQ as well.

                          • 10. Re: JMS Transaction Confusion
                            jbertram

                            There are different connection factories for different use-cases.  Once you know the difference, choosing the right one is simple. 

                             

                            There's no need to throw away valid and productive use-cases for a user education issue.  Why should an application not be able to inject a non-JCA JMS connection factory into a CMT SLSB and send (or receive) a message outside the confines of its JTA transaction if it so desires?

                            • 11. Re: JMS Transaction Confusion
                              cpuffalt

                              Justin,

                               

                              The behaviour is not intuitive regardless.  Certainly the "Principal of Least Astonishment" is not at play here.  Perhaps you don't see this when looking at it through the lens of an expert living and breathing JTA/JCA/JMS daily.  I see I'm not the first one to run into this issue: AS7-3143

                               

                              Corey

                              • 12. Re: JMS Transaction Confusion
                                jbertram

                                Don't get me wrong.  It almost certainly could be more intuitive, and you're definitely not the first one to run into this.  But I think the cure you've suggested is worse than the disease.  Creating additional annotations to inject specific kinds of JMS connection factories and then limiting the use of those annotations based on what kind of transaction management the bean is using is overly complex, IMO.

                                 

                                As far as logging a WARN, lots of users don't like spurious WARN messages, and spurious it would be since it is valid and useful to use a non JCA JMS connection factory in an EJB using CMT.

                                 

                                The JCA, JTA, and JMS specifications are, of course, independent of each other.  When they are combined in Java EE they support flexible and powerful functionality, but with that flexibility comes some complexity.  At this point, I see this as simply a user-education issue and not something that should be solved by additional code/rules within the container.  There is already a discussion of the different kinds of connections factories in the documentation.  I'll add some notes about this scenario to be more clear.

                                 

                                For what it's worth, the same basic semantics apply to JDBC connections.  If you create your own JDBC connection (e.g. http://docs.oracle.com/javase/tutorial/jdbc/basics/connecting.html) rather than getting it from a JDBC datasource (which uses a JCA adapter under the covers) then that connection will not be automatically enlisted in the ongoing JTA transaction.

                                • 13. Re: JMS Transaction Confusion
                                  cpuffalt

                                  Actually, I wasn't suggesting additional annotations.  I was suggesting the container should honour the existing annotations.  Namely, @TransactionManagementType and related @TransactionAttributeType.  This would be least surprising to users.

                                   

                                  Hopefully a future JMS spec will bring better alignment with JEE. 

                                   

                                  Corey

                                  • 14. Re: JMS Transaction Confusion
                                    jbertram

                                    Hopefully a future JMS spec will bring better alignment with JEE.

                                    I believe your hope may be realized in JMS 2.0.  For example, see http://jms-spec.java.net/2.0-SNAPSHOT/apidocs/javax/jms/Connection.html#createSession().  Note - the JMS 2.0 specification is still an early draft.

                                     

                                    That said, while JMS 2.0 looks to address the enlistment of the session into the current JTA transaction it doesn't provide any pooling so the developer will still need to decide between a JCA-based connection factory and a normal one.