3 Replies Latest reply on Jul 3, 2013 2:53 AM by Miroslav Novak

    XA Enlisting for non-MDB JMS Consumer

    Alan Feng Novice

      I've been struggling with this for a while and read lots of posting but haven't found a solution yet.

       

      My setup is a J2EE app that needs to publish / consume messages to queues in a remote HornetQ instance.

       

      From the following doc and post:

      https://docs.jboss.org/author/display/AS72/Messaging+configuration

      https://community.jboss.org/thread/213629

       

      I see that the pooled-connection-factory can be used for sending messages to the queue with XA support, and when I try it out, I see the transaction is properly enlisted. The problem is there doesn't seem to be an easy way to consume messages with XA support if I'm not using MDB annotations. The reason I'm not using it is we are using Spring to create the message listener container and consumer (DefaultMessageListenerContainer) so we can create consumers to different queues via Spring context configuration instead of creating queue specific MDB classes with annotation.

       

      As such, is there any easy way to achieve this?

       

      Thank you,

        • 1. Re: XA Enlisting for non-MDB JMS Consumer
          Alan Feng Novice

          Some more details:

           

          I tried to specify the factory type XA_QUEUE when define a JMS Connection Factory in standalone.xml but that doesn't seem to help:

           

                    <connection-factory name="QueueConnectionFactory">
                      <factory-type>XA_QUEUE</factory-type>
                      <ha>true</ha>
                    <connectors>
                        <connector-ref connector-name="netty"/>
                    </connectors>
                    <entries>
                        <entry name="java:/QueueConnectionFactory"/>
                      </entries>
                    </connection-factory>
          

           

           

          The Spring context I used to create the Container / Listener is like this :

           

            <bean id="jmsQueueConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean" depends-on="jmsContainerMgrHelper">
              <property name="jndiTemplate" ref="jndiTemplate"/>
              <property name="jndiName" value="/QueueConnectionFactory"/>
            </bean>
          
            <bean id="baseJmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer" scope="prototype">
              <property name="connectionFactory" ref="jmsQueueConnectionFactory"/>
              <property name="sessionTransacted" value="true"/>
              <property name="transactionManager" ref="transactionManager"/>
              <property name="autoStartup" value="false"/>
              <property name="destinationName" value="my_queue"/>
              <property name="messageListener" ref="queueConsumer"/>
            </bean>
          
            <bean id="queueConsumer" class="com.company.app.MyQueueListener">
            </bean>
          

           

          It consumes the message fine, but when I examine the XA logs, it's not enlisted as XA transaction.

           

           

           

          • 2. Re: XA Enlisting for non-MDB JMS Consumer
            Alan Feng Novice

            The transaction manager referenced above is defined as the standard Spring JTA transaction manager that delegates to JBoss.

             

              <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
            

             

            And according the Spring doc:

             

            To configure a message listener container for XA transaction participation, you'll want to configure a JtaTransactionManager (which, by default, delegates to the Java EE server's transaction subsystem). Note that the underlying JMS ConnectionFactory needs to be XA-capable and properly registered with your JTA transaction coordinator! (Check your Java EE server's configuration of JNDI resources.) This allows message reception as well as e.g. database access to be part of the same transaction (with unified commit semantics, at the expense of XA transaction log overhead).

             

            So what's really missing is obtaining a XA-capable ConnectionFactory from the JBoss JNDI context. And the question boils down to: How can I define a XA ConnectionFactory in JBoss standalone.xml for the remote JMS queue?

             

            I tried to create another instance of the pooled-connection-factory and point the consumer to it, it throws out exception:

             

            13:01:19,283 ERROR [hornetq.ra.HornetQRASessionFactoryImpl] Could not create session: javax.jms.IllegalStateException: Only allowed one se
            ssion per connection. See the J2EE spec, e.g. J2EE1.4 Section 6.6
                    at org.hornetq.ra.HornetQRASessionFactoryImpl.allocateConnection(HornetQRASessionFactoryImpl.java:816)
                    at org.hornetq.ra.HornetQRASessionFactoryImpl.createSession(HornetQRASessionFactoryImpl.java:470)
                    at org.springframework.jms.support.JmsAccessor.createSession(JmsAccessor.java:196) [spring-jms-3.1.0.RELEASE.jar:3.1.0.RELEASE]
                    at org.springframework.jms.listener.DefaultMessageListenerContainer.access$12(DefaultMessageListenerContainer.java:1) [spring-jms-3.1.0.RELEASE.jar:3.1.
            0.RELEASE]
                    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.initResourcesIfNecessary(DefaultMessageListenerContainer
            .java:1079) [spring-jms-3.1.0.RELEASE.jar:3.1.0.RELEASE]
                    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1058
            ) [spring-jms-3.1.0.RELEASE.jar:3.1.0.RELEASE]
                    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:
            1051) [spring-jms-3.1.0.RELEASE.jar:3.1.0.RELEASE]
                    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:948) [spring-jm
            s-3.1.0.RELEASE.jar:3.1.0.RELEASE]
                    at java.lang.Thread.run(Thread.java:662) [rt.jar:1.6.0_29]
            
            • 3. Re: XA Enlisting for non-MDB JMS Consumer
              Miroslav Novak Master

              Hi,

               

              if you need XA transactions then you need to use pooled connection factory in your application. As I understand this connection factory should be connected to remote AS7(HornetQ) server. So:

              <jms-connection-factories>

              ...

              <!-- configuration for pooled connection factory -->

              <pooled-connection-factory name="hornetq-ra">

                                      <transaction mode="xa"/>

                                      <connectors>

                                          <connector-ref connector-name="netty-remote"/>

                                      </connectors>

                                      <entries>

                                          <entry name="java:/RemoteJmsXA"/>

                                      </entries>

                                      <reconnect-attempts>-1</reconnect-attempts>

              </pooled-connection-factory>

              ...

              </jms-connection-factories>

               

              where connectory netty-remote is using socket binding messaging-remote defined in <socket-binding-group>:

              <connectors>

                                  <netty-connector name="netty" socket-binding="messaging"/>

                                  <netty-connector name="netty-throughput" socket-binding="messaging-throughput">

                                      <param key="batch-delay" value="50"/>

                                  </netty-connector>

                                  <netty-connector name="netty-remote" socket-binding="messaging-remote"/>

                                  <in-vm-connector name="in-vm" server-id="0"/>

              </connectors>

              ...

              <socket-binding-group...

              <outbound-socket-binding name="messaging-remote">

                          <remote-destination host="192.168.40.1" port="5445"/>

              </outbound-socket-binding>

              ...

              </socket-binding-group>

               

              In all beans you should use this pooled connection factory with JNDI name "java:/RemoteJmsXA".

               

              I hope it helps.

               

              Cheers,

               

              Mirek