5 Replies Latest reply on Jul 1, 2003 2:11 PM by halldori

    XATransaction outside of JBoss VM

    halldori

      Hello

      I am writing a java application that gets messages of a JBoss Message Queue, and writes data from the messages to the database.

      This should be done logically in a single transaction , so two phase commit/XATransaction is needed. I am a bit confused on how to do it after having looked at the JTA stuff in JBoss.

      Basically I am wondering if I should interact directly with the TransactionManager/Transaction by enlisting the XAResources, or if it is enough to do it only through the UserTransaction.

      If the latter case is true is it simply enough to get the ClientUserTransaction from JNDI and to begin and commit on that. Does that really handle the two phase commit? Also in order to make sure that the resources participate in the global transaction should I get the connections from a DataSource, or through the XAConnectionFactory?

      I am also pondering on the description of the TxManager in JBoss Admin book:

      "While it does support propagating transaction contexts with remote calls, it does not support prop-agating transaction contexts to other virtual machines, so all transactional work must be done in the same virtual machine as the JBoss"

      Does this mean that I am allowed to use the UserTransaction in a standalone client?

      Regards, Halldor

        • 1. Re: XATransaction outside of JBoss VM

          Is there a reason you're not using the EJB container for transaction demarcation? Write a message and entity bean and set the transaction attributes accordingly?

          Otherwise, you can get a transaction from the JMS server by looking up the XAConnectionFactory and creating a transacted session, JBoss datasources are XA aware as long as your JDBC drivers supports XA.

          • 2. Re: XATransaction outside of JBoss VM
            halldori

            The reason is that a lot of code has been written and it would be too much effort to bring it into an application server.

            Anyway... would I then simply look up the user transaction and begin a global transaction? Is the Transaction manager in that case aware of operations on the XAConnection?

            • 3. Re: XATransaction outside of JBoss VM
              halldori

              Or should I perhaps use the JMS resource adapter? JMSXA?

              • 4. Re: XATransaction outside of JBoss VM
                ioparra

                I had this similar situation when I was building a trading engine for my old company(accessed a ejb and JMS) in a single transaction. This is what I did.
                Bare in mind, I did this about 1 year ago(and its still in production).

                1)Use UserTransaction when you begin your work
                2)create a seperate JMSSession(i think transacted)
                3)do any work with your datasource/jms
                4)acknowledge your jms messages
                -- you may need to check this, you'll end up with dup messages if I'm wrong.
                5)just commit your user transaction(not your jms session)
                ---this step should iterate through all the XAResource(ie, DB and JMS) and commit each one.

                6)close your session

                Hope that helps you out. I remember that this confused me, I've always questioned how the JMS Session level transaction worked with the Thread Context Based Transaction.

                G/L
                -Ivan

                • 5. Re: XATransaction outside of JBoss VM
                  halldori

                  Thanks for your reply. I actually figured out a similar approach, but I can't even seem to get a XATransaction to work just for one queue. This is a message I posted in the JMS forum.

                  I also seem to have problem with getting XATransactions to work for a simple Java Application. I simply get a UserTransaction, start transaction, get the XAConnectionFactory, create the sessions/senders and send the message. At that point I get a JMS Exception with the message "Invalid transaction ID". What seems to be the problem is that when the addMessage method of SpyXAResourceManager is called there is no state for the transaction. So in effect the method startTx is never called in SpyXAResourceManager. That is because by default the SpyXASession does not do so:

                  SpySession( Connection conn, boolean trans, int acknowledge, boolean xaSession )
                  {
                  connection = conn;
                  transacted = trans;
                  acknowledgeMode = acknowledge;
                  if ( xaSession )
                  {
                  spyXAResource = new SpyXAResource( this );
                  }

                  running = true;
                  closed = false;
                  consumers = new HashSet();

                  //Have a TX ready with the resource manager.
                  if ( spyXAResource == null && transacted )
                  {
                  currentTransactionId = connection.spyXAResourceManager.startTx();
                  if (log.isTraceEnabled())
                  {
                  log.trace("Current transaction id: " + currentTransactionId);
                  }
                  }
                  }

                  Anyway has anybody got a clue. I appended the code:

                  public static void main(String[] args)
                  {
                  UserTransaction tx = null;
                  try
                  {
                  TestBookingFactory.initializeForTest();
                  log.info("JMSConnectionHelper.JMSConnectionHelper");
                  log.debug("JMSConnectionHelper.JMSConnectionHelper");
                  Hashtable env = new Hashtable();
                  String timeout = System.getProperties().getProperty("jnp.timeout");
                  if (timeout == null)
                  {
                  log.fatal("JMSConnectionHelper:JMSConnectionHelper: jnp.timeout property missing");
                  }
                  String sotimeout = System.getProperties().getProperty("jnp.sotimeout");
                  if (sotimeout == null)
                  {
                  log.fatal("JMSConnectionHelper:JMSConnectionHelper: jnp.sotimeout property missing");
                  }
                  String host = System.getProperties().getProperty("JMS.PROVIDER_URL");
                  if (host == null)
                  {
                  log.fatal("JMSConnectionHelper:JMSConnectionHelper: JMS.PROVIDER_URL property missing");
                  }
                  String contextFactory = System.getProperties().getProperty("JMS.INITIAL_CONTEXT_FACTORY");
                  if (contextFactory == null)
                  {
                  log.fatal("JMSConnectionHelper:JMSConnectionHelper: JMS.INITIAL_CONTEXT_FACTORY property missing");
                  }
                  String queueFactory = System.getProperties().getProperty("JMS.ConnectionFactoryName");
                  log.debug(queueFactory);
                  if (queueFactory == null)
                  {
                  log.fatal("JMSConnectionHelper:JMSConnectionHelper: JMS.ConnectionFactoryName property missing");
                  }
                  String queueName = System.getProperties().getProperty("JMS.CalidrisQueue");
                  if (queueName == null)
                  {
                  log.fatal("JMSConnectionHelper:JMSConnectionHelper: JMS.CalidrisQueue property missing");
                  }
                  env.put("jnp.timeout", timeout);
                  env.put("jnp.sotimeout", sotimeout);
                  env.put(Context.PROVIDER_URL, host);
                  env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory);
                  InitialContext iniCtx = new InitialContext(env);
                  tx = (UserTransaction) iniCtx.lookup("UserTransaction");

                  tx.begin();

                  Object tmp = iniCtx.lookup("XAConnectionFactory");
                  XAQueueConnectionFactory qcf = (XAQueueConnectionFactory) tmp;
                  JMSHashMapEntry defaultJMSQueue = new JMSHashMapEntry();
                  defaultJMSQueue.connection = qcf.createXAQueueConnection();
                  defaultJMSQueue.queue = (Queue) iniCtx.lookup(queueName);
                  defaultJMSQueue.session = defaultJMSQueue.connection.createXAQueueSession();
                  defaultJMSQueue.sender = defaultJMSQueue.session.getQueueSession().createSender(defaultJMSQueue.queue);
                  defaultJMSQueue.connection.start();

                  Message m = defaultJMSQueue.session.createTextMessage("Kalli Halli");
                  defaultJMSQueue.sender.send(m);

                  }
                  catch (Exception ne)
                  {
                  log.error("TestClass.main:", ne);
                  ne.printStackTrace();
                  }
                  System.out.println("success!");
                  try
                  {
                  tx.commit();
                  }
                  catch (RollbackException e)
                  {
                  log.error("TestClass.main:", e);
                  }
                  catch (HeuristicMixedException e)
                  {
                  log.error("TestClass.main:", e);
                  }
                  catch (HeuristicRollbackException e)
                  {
                  log.error("TestClass.main:", e);
                  }
                  catch (SecurityException e)
                  {
                  log.error("TestClass.main:", e);
                  }
                  catch (IllegalStateException e)
                  {
                  log.error("TestClass.main:", e);
                  }
                  catch (SystemException e)
                  {
                  log.error("TestClass.main:", e);
                  }
                  }