3 Replies Latest reply on Mar 18, 2019 11:23 PM by mullaikani

    Publish messages to remote JMS provider in transaction

    sai.pabbathi

      I have the following code which sends messages to a remote jms provider(Tibco) in this case, I am looking for a solution to put the sending code into a Bean managed transaction or the usertransaction, but not sure how to do that. I have the generic resource adapter setup already for consuming the messages from the tibco.

       

       

      Tech stack/background:

      Wildfly, CDI, Tibco ems 8.2 JMS provider, Java8, Using Standalone-full.xml, Added genericra.rar resource adapter file for consuming messages and that works. Message driven beans for consuming messages.

       

      import java.util.Hashtable;
      import javax.annotation.Resource;
      import javax.inject.Singleton;
      import javax.jms.Queue;
      import javax.jms.QueueConnection;
      import javax.jms.QueueConnectionFactory;
      import javax.jms.QueueSender;
      import javax.jms.QueueSession;
      import javax.jms.Session;
      import javax.jms.TextMessage;
      import javax.naming.Context;
      import javax.naming.InitialContext;
      import javax.transaction.Transactional;
      import javax.transaction.Transactional.TxType;
      import javax.transaction.UserTransaction;
      @Singleton
      public class ServiceLayer implements IServiceLayer{
          public final static String JNDI_FACTORY = "com.tibco.tibjms.naming.TibjmsInitialContextFactory";
          public final static String PROVIDER_URL = "tcp://serverurl:7225";
          public final static String JMS_FACTORY = "XAQueueConnectionFactory";
          public final static String QUEUE = "Queue";
          @Resource 
          public UserTransaction utx;
          public void sendMessage(String message) throws Exception {
              Hashtable env = new Hashtable();
              env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
              env.put(Context.PROVIDER_URL, PROVIDER_URL);
              for (int i = 0; i < 1; i++) {
                  // Define queue
                  QueueSender qsender = null;
                  QueueSession qsession = null;
                  QueueConnection qcon = null;
                  try {
                      utx.begin();
                      InitialContext ctx = new InitialContext(env);
                      QueueConnectionFactory qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
                      qcon = qconFactory.createQueueConnection();
                      qsession = qcon.createQueueSession(true, -1);
                      Queue queue = (Queue) ctx.lookup(QUEUE);
                      TextMessage msg = qsession.createTextMessage();
                      msg.setText(message);
                      qsender = qsession.createSender(queue);
                      qsender.send(msg);
                      System.out.println("sleep 5 secs.." + message.toString());
                      Thread.sleep(5000);
                      System.out.println("Message [" + msg.getText() + "] sent to Queue: " + QUEUE);
      //              qsession.commit();
                      utx.commit();
                  } catch (Exception ex) {
                      ex.printStackTrace();
                  } finally {
                      if (qsender != null)
                          qsender.close();
                      if (qsession != null)
                          qsession.close();
                      if (qcon != null)
                          qcon.close();
                  }
              }
          }
      }
      
        • 1. Re: Publish messages to remote JMS provider in transaction
          jbertram

          Your code currently is using standard JMS conventions and components.  It connects directly to Tibco and uses a standard JMS XA connection factory.  Since it's not using a JCA RA then you don't get features like automatic transaction enlisting, pooling, etc.  Furthermore, this code follows an anti-pattern in that it creates and closes a connection for every message sent.  This will be a huge drain on resources and significantly decrease performance.

           

          You can remedy all these short-comings by using an outbound connection factory provided by the Generic JMS JCA RA.  To configure an outbound connector you simply add a bit of XML to your server config and supply the Tibco connection details (e.g. JNDI parameters, connection factory name, etc.).  Then your code will use (ideally via injection) this new outbound connector instead.  That will provide you with automatic transaction enlistment and connection pooling.  Due to the connection pooling you can leave your code the way it is since when you create/close a connection in your code it actually uses the pool behind the scenes.

           

          You can see a simple example of the XML configuration in the documentation.  Here's what it would like in Wildfly 10.1.0.Final with the details from your code:

           

          <subsystem xmlns="urn:jboss:domain:resource-adapters:4.0">
              <resource-adapters>
                  <resource-adapter>
                      <archive>
                          generic-jms-ra.rar
                      </archive>
                      <transaction-support>NoTransaction</transaction-support>
                      <connection-definitions>
                          <connection-definition class-name="org.jboss.resource.adapter.jms.JmsManagedConnectionFactory" jndi-name="java:/GenericJmsXA" enabled="true" use-java-context="true" pool-name="GenericJmsXA" use-ccm="true">
                              <config-property name="JndiParameters">
                                  java.naming.factory.initial=com.tibco.tibjms.naming.TibjmsInitialContextFactory;java.naming.provider.url=tcp://serverurl:7225
                              </config-property>
                              <config-property name="ConnectionFactory">
                                  XAQueueConnectionFactory
                              </config-property>
                              <pool>
                                  <min-pool-size>0</min-pool-size>
                                  <max-pool-size>10</max-pool-size>
                                  <prefill>false</prefill>
                                  <use-strict-min>false</use-strict-min>
                                  <flush-strategy>FailingConnectionOnly</flush-strategy>
                              </pool>
                              <security>
                                  <application></application>
                              </security>
                          </connection-definition>
                      </connection-definitions>
                  </resource-adapter>
              </resource-adapters>
          </subsystem>
          

           

          In your code you would just lookup java:/GenericJmsXA from the local JNDI context (i.e. not from Tibco) or just inject it (which is what I'd recommend).  If you do end up looking it up manually then don't look it up every time you send a message.  Just look it up once and then cache it.

           

          Lastly, it's not clear to me why you would actually need an XA transaction for this use-case.  You are simply sending a single message to a single broker.  In other words, you're only involving a single resource manager in your transaction (i.e. Tibco).  XA transactions employ a 2-phase commit protocol which is really only necessary when you involve more than one resource manager (e.g. a JMS broker and a database).

          2 of 2 people found this helpful
          • 2. Re: Publish messages to remote JMS provider in transaction
            sai.pabbathi

            Thanks Justin! that was easier than I thought.

             

            I understand what you are trying to say about anti pattern, this is just a POC that I'm working on to prove that XA works, all this research would be put in the actual application which uses Databases and web-services etc.,

            I did the following and it worked, except that I get the following error on the logs

             

             

            2017-03-03 11:28:46,700 ERROR [org.jboss.jca.core.connectionmanager.listener.TxConnectionListener] (default task-7) IJ000315: Pool GenericJmsXA has 1 active handles

            Code that worked

            @Resource(mappedName="GenericJmsXA")

                public ConnectionFactory queueFactory;

             

                public void sendMessage(String message) throws Exception {

                    MessageProducer qsender = null;

                    Session qsession = null;

                    Connection qcon = null;

                    try{

                        qcon = this.queueFactory.createConnection();

                        qsession = qcon.createSession(true, QueueSession.AUTO_ACKNOWLEDGE);

                        Queue q = qsession.createQueue(QUEUE);

                        qsender = qsession.createProducer(q);

                        TextMessage tm = qsession.createTextMessage(message);

                        System.out.println("Before sending to Queue: Waiting for 5 seconds. " + QUEUE);

                        qsender.send(tm);

                        Thread.sleep(5000);

                        System.out.println("Message [" + tm.getText() + "] sent to Queue: " + QUEUE);

                       

                    }catch(Exception e){

                        System.out.println("Exception : "+e.getMessage());

                        e.printStackTrace();

                    }

            Thanks Again! Not much about this is provided in the internet.

            • 3. Re: Publish messages to remote JMS provider in transaction
              mullaikani

              Hi

                Am trying to do the same way for wmqresource adaptor which is configured in standalone.xml. I am not able to see any log that jndi is connected.

              Could you please give me steps to follow to send message to ibmmq using wmq resourceadaptor.

               

              Regards

              Mullai