/** Vote using phase-1 of the 2-phase commit. */
  public int prepare(Xid xid) throws XAException {
    if (_endFlags != -1) {
      int endFlags = _endFlags;
      _endFlags = -1;

      if (_isXATransaction) endResource(xid, endFlags);
    }

    if (_isXATransaction) {
      try {
        if (log.isLoggable(Level.FINER)) log.finer("prepare-XA: " + xid + " " + _xaResource);

        int result = _xaResource.prepare(xid);

        if (result == XA_RDONLY) {
          if (_xaResource != null) _isXATransaction = true;

          clearXid();
        }

        return result;
      } catch (XAException e) {
        if (log.isLoggable(Level.FINER))
          log.finer("failed prepare-XA: " + xid + " " + _xaResource + " " + e);

        throw e;
      }
    } else return XA_OK;
  }
Esempio n. 2
0
 private boolean runXaResourcePrepare() throws SystemException {
   Collection<XAResource> resources = getEnlistedResources();
   for (XAResource res : resources) {
     try {
       res.prepare(xid);
     } catch (XAException e) {
       log.trace("The resource wants to rollback!", e);
       return false;
     } catch (Throwable th) {
       log.error("Unexpected error from resource manager!", th);
       throw new SystemException(th.getMessage());
     }
   }
   return true;
 }
Esempio n. 3
0
 @Override
 public int prepare(Xid xid) throws XAException {
   prepareResult = xaResource.prepare(xid);
   return prepareResult;
 }
Esempio n. 4
0
  private void checkXAHoldability() {
    System.out.println("START XA HOLDABILITY TEST");
    try {
      EmbeddedXADataSource dscsx = new EmbeddedXADataSource();
      dscsx.setDatabaseName("wombat");

      XAConnection xac = dscsx.getXAConnection("fred", "wilma");
      XAResource xr = xac.getXAResource();
      Xid xid = getXid(25, (byte) 21, (byte) 01);
      Connection conn1 = xac.getConnection();
      System.out.println(
          "By default, autocommit is " + conn1.getAutoCommit() + " for a connection");
      System.out.println("Default holdability for a connection is HOLD_CURSORS_OVER_COMMIT");
      System.out.println(
          "CONNECTION(not in xa transaction yet) HOLDABILITY "
              + (conn1.getHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      // start a global transaction and default holdability and autocommit will be switched to match
      // Derby XA restrictions
      xr.start(xid, XAResource.TMNOFLAGS);
      System.out.println(
          "Notice that autocommit now is "
              + conn1.getAutoCommit()
              + " for connection because it is part of the global transaction");
      System.out.println(
          "Notice that connection's holdability at this point is CLOSE_CURSORS_AT_COMMIT because it is part of the global transaction");
      System.out.println(
          "CONNECTION(in xa transaction) HOLDABILITY "
              + (conn1.getHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      xr.end(xid, XAResource.TMSUCCESS);
      conn1.commit();
      conn1.close();

      xid = getXid(27, (byte) 21, (byte) 01);
      xr.start(xid, XAResource.TMNOFLAGS);
      conn1 = xac.getConnection();
      System.out.println(
          "CONNECTION(in xa transaction) HOLDABILITY "
              + (conn1.getHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      System.out.println(
          "Autocommit on Connection inside global transaction has been set correctly to "
              + conn1.getAutoCommit());
      xr.end(xid, XAResource.TMSUCCESS);
      conn1.rollback();

      Connection conn = xac.getConnection();
      conn.setAutoCommit(false);
      conn.setHoldability(ResultSet.CLOSE_CURSORS_AT_COMMIT);
      System.out.println(
          "CONNECTION(non-xa) HOLDABILITY "
              + (conn.getHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT));

      Statement s = conn.createStatement();
      System.out.println(
          "STATEMENT HOLDABILITY "
              + (s.getResultSetHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT));

      s.executeUpdate("create table hold_30 (id int not null primary key, b char(30))");
      s.executeUpdate("insert into hold_30 values (1,'init2'), (2, 'init3'), (3,'init3')");
      s.executeUpdate("insert into hold_30 values (4,'init4'), (5, 'init5'), (6,'init6')");
      s.executeUpdate("insert into hold_30 values (7,'init7'), (8, 'init8'), (9,'init9')");

      System.out.println(
          "STATEMENT HOLDABILITY "
              + (s.getResultSetHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT));

      Statement sh =
          conn.createStatement(
              ResultSet.TYPE_FORWARD_ONLY,
              ResultSet.CONCUR_READ_ONLY,
              ResultSet.HOLD_CURSORS_OVER_COMMIT);
      PreparedStatement psh =
          conn.prepareStatement(
              "select id from hold_30 for update",
              ResultSet.TYPE_FORWARD_ONLY,
              ResultSet.CONCUR_READ_ONLY,
              ResultSet.HOLD_CURSORS_OVER_COMMIT);
      CallableStatement csh =
          conn.prepareCall(
              "select id from hold_30 for update",
              ResultSet.TYPE_FORWARD_ONLY,
              ResultSet.CONCUR_READ_ONLY,
              ResultSet.HOLD_CURSORS_OVER_COMMIT);

      System.out.println(
          "STATEMENT HOLDABILITY "
              + (sh.getResultSetHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      System.out.println(
          "PREPARED STATEMENT HOLDABILITY "
              + (psh.getResultSetHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      System.out.println(
          "CALLABLE STATEMENT HOLDABILITY "
              + (csh.getResultSetHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT));

      ResultSet rsh = sh.executeQuery("select id from hold_30 for update");
      rsh.next();
      System.out.println("H@1 id " + rsh.getInt(1));
      rsh.next();
      System.out.println("H@2 id " + rsh.getInt(1));
      conn.commit();
      rsh.next();
      System.out.println("H@3 id " + rsh.getInt(1));
      conn.commit();

      xid = getXid(23, (byte) 21, (byte) 01);
      xr.start(xid, XAResource.TMNOFLAGS);
      Statement stmtInsideGlobalTransaction = conn.createStatement();
      PreparedStatement prepstmtInsideGlobalTransaction =
          conn.prepareStatement("select id from hold_30");
      CallableStatement callablestmtInsideGlobalTransaction =
          conn.prepareCall("select id from hold_30");

      System.out.println(
          "CONNECTION(xa) HOLDABILITY "
              + (conn.getHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      System.out.println(
          "STATEMENT(this one was created with holdability false, outside the global transaction. Check it's holdability inside global transaction) HOLDABILITY "
              + (s.getResultSetHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      System.out.println(
          "STATEMENT(this one was created with holdability true, outside the global transaction. Check it's holdability inside global transaction) HOLDABILITY "
              + (sh.getResultSetHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      System.out.println(
          "STATEMENT(this one was created with default holdability inside this global transaction. Check it's holdability) HOLDABILITY "
              + (stmtInsideGlobalTransaction.getResultSetHoldability()
                  == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      System.out.println(
          "PREPAREDSTATEMENT(this one was created with default holdability inside this global transaction. Check it's holdability) HOLDABILITY "
              + (prepstmtInsideGlobalTransaction.getResultSetHoldability()
                  == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      System.out.println(
          "CALLABLESTATEMENT(this one was created with default holdability inside this global transaction. Check it's holdability) HOLDABILITY "
              + (callablestmtInsideGlobalTransaction.getResultSetHoldability()
                  == ResultSet.HOLD_CURSORS_OVER_COMMIT));

      ResultSet rsx = s.executeQuery("select id from hold_30 for update");
      rsx.next();
      System.out.println("X@1 id " + rsx.getInt(1));
      rsx.next();
      System.out.println("X@2 id " + rsx.getInt(1));
      xr.end(xid, XAResource.TMSUCCESS);

      // result set should not be useable, since it is part of a detached
      // XAConnection
      try {
        rsx.next();
        System.out.println("FAIL - rsx's connection not active id " + rsx.getInt(1));
      } catch (SQLException sqle) {
        System.out.println("Expected SQLException " + sqle.getMessage());
      }

      // result set should not be useable, it should have been closed by the xa start.
      try {
        rsh.next();
        System.out.println("FAIL - rsh's should be closed " + rsx.getInt(1));
      } catch (SQLException sqle) {
        System.out.println("Expected SQLException " + sqle.getMessage());
      }

      System.out.println("resume XA transaction and keep using rs");
      xr.start(xid, XAResource.TMJOIN);
      Statement stmtAfterGlobalTransactionResume = conn.createStatement();
      PreparedStatement prepstmtAfterGlobalTransactionResume =
          conn.prepareStatement("select id from hold_30");
      CallableStatement callablestmtAfterGlobalTransactionResume =
          conn.prepareCall("select id from hold_30");

      System.out.println("Check holdability of various jdbc objects after resuming XA transaction");
      System.out.println(
          "CONNECTION(xa) HOLDABILITY "
              + (conn.getHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      System.out.println(
          "STATEMENT(this one was created with holdability false, outside the global transaction. Check it's holdability inside global transaction) HOLDABILITY "
              + (s.getResultSetHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      System.out.println(
          "STATEMENT(this one was created with holdability true, outside the global transaction. Check it's holdability inside global transaction) HOLDABILITY "
              + (sh.getResultSetHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      System.out.println(
          "STATEMENT(this one was created with default holdability inside the global transaction when it was first started. Check it's holdability) HOLDABILITY "
              + (stmtInsideGlobalTransaction.getResultSetHoldability()
                  == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      System.out.println(
          "PREPAREDSTATEMENT(this one was created with default holdability inside the global transaction when it was first started. Check it's holdability) HOLDABILITY "
              + (prepstmtInsideGlobalTransaction.getResultSetHoldability()
                  == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      System.out.println(
          "CALLABLESTATEMENT(this one was created with default holdability inside the global transaction when it was first started. Check it's holdability) HOLDABILITY "
              + (callablestmtInsideGlobalTransaction.getResultSetHoldability()
                  == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      System.out.println(
          "STATEMENT(this one was created with default holdability after the global transaction was resumed. Check it's holdability) HOLDABILITY "
              + (stmtAfterGlobalTransactionResume.getResultSetHoldability()
                  == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      System.out.println(
          "PREPAREDSTATEMENT(this one was created with default holdability after the global transaction was resumed. Check it's holdability) HOLDABILITY "
              + (prepstmtAfterGlobalTransactionResume.getResultSetHoldability()
                  == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      System.out.println(
          "CALLABLESTATEMENT(this one was created with default holdability after the global transaction was resumed. Check it's holdability) HOLDABILITY "
              + (callablestmtAfterGlobalTransactionResume.getResultSetHoldability()
                  == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      rsx.next();
      System.out.println("X@3 id " + rsx.getInt(1));
      xr.end(xid, XAResource.TMSUCCESS);

      if (xr.prepare(xid) != XAResource.XA_RDONLY) xr.commit(xid, false);

      // try again once the xa transaction has been committed.
      try {
        rsx.next();
        System.out.println("FAIL - rsx's connection not active id (B)" + rsx.getInt(1));
      } catch (SQLException sqle) {
        System.out.println("Expected SQLException " + sqle.getMessage());
      }

      try {
        rsh.next();
        System.out.println("FAIL - rsh's should be closed (B) " + rsx.getInt(1));
      } catch (SQLException sqle) {
        System.out.println("Expected SQLException " + sqle.getMessage());
      }

      System.out.println("Set connection to hold ");
      conn.setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT);
      System.out.println(
          "CONNECTION(held) HOLDABILITY "
              + (conn.getHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT));

      xid = getXid(24, (byte) 21, (byte) 01);
      xr.start(xid, XAResource.TMNOFLAGS);
      System.out.println(
          "CONNECTION(xa) HOLDABILITY "
              + (conn.getHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      try {
        conn.setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT);
        System.out.println("FAIL allowed to set hold mode in xa transaction");
      } catch (SQLException sqle) {
        System.out.println("Expected SQLException(setHoldability) " + sqle.getMessage());
      }

      // try to create a statement with held attributes
      try {
        Statement shxa =
            conn.createStatement(
                ResultSet.TYPE_FORWARD_ONLY,
                ResultSet.CONCUR_READ_ONLY,
                ResultSet.HOLD_CURSORS_OVER_COMMIT);
        System.out.println(
            "FAIL opened statement with hold cursor attribute in global transaction");
      } catch (SQLException sqle) {
        System.out.println("Expected SQLException (Statement hold) " + sqle.getMessage());
      }
      try {
        Statement shxa =
            conn.prepareStatement(
                "select id from hold_30",
                ResultSet.TYPE_FORWARD_ONLY,
                ResultSet.CONCUR_READ_ONLY,
                ResultSet.HOLD_CURSORS_OVER_COMMIT);
        System.out.println(
            "FAIL opened statement with hold cursor attribute in global transaction");
      } catch (SQLException sqle) {
        System.out.println("Expected SQLException (PreparedStatement hold) " + sqle.getMessage());
      }
      try {
        Statement shxa =
            conn.prepareCall(
                "CALL XXX.TTT()",
                ResultSet.TYPE_FORWARD_ONLY,
                ResultSet.CONCUR_READ_ONLY,
                ResultSet.HOLD_CURSORS_OVER_COMMIT);
        System.out.println(
            "FAIL opened statement with hold cursor attribute in global transaction");
      } catch (SQLException sqle) {
        System.out.println("Expected SQLException (CallableStatement hold) " + sqle.getMessage());
      }

      // check we cannot use a holdable statement set up in local mode.
      System.out.println(
          "STATEMENT HOLDABILITY "
              + (sh.getResultSetHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      try {
        sh.executeQuery("select id from hold_30");
        System.out.println("FAIL used held statement in global transaction");
      } catch (SQLException sqle) {
        System.out.println("Expected SQLException (local Statement hold) " + sqle.getMessage());
      }
      try {
        sh.execute("select id from hold_30");
        System.out.println("FAIL used held statement in global transaction");
      } catch (SQLException sqle) {
        System.out.println("Expected SQLException (local Statement hold) " + sqle.getMessage());
      }
      System.out.println(
          "PREPARED STATEMENT HOLDABILITY "
              + (psh.getResultSetHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      try {
        psh.executeQuery();
        System.out.println("FAIL used held prepared statement in global transaction");
      } catch (SQLException sqle) {
        System.out.println(
            "Expected SQLException (local PreparedStatement hold) " + sqle.getMessage());
      }
      try {
        psh.execute();
        System.out.println("FAIL used held prepared statement in global transaction");
      } catch (SQLException sqle) {
        System.out.println(
            "Expected SQLException (local PreparedStatement hold) " + sqle.getMessage());
      }

      System.out.println(
          "CALLABLE STATEMENT HOLDABILITY "
              + (csh.getResultSetHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT));
      try {
        csh.executeQuery();
        System.out.println("FAIL used held callable statement in global transaction");
      } catch (SQLException sqle) {
        System.out.println(
            "Expected SQLException (local CallableStatement hold) " + sqle.getMessage());
      }
      try {
        csh.execute();
        System.out.println("FAIL used held callable statement in global transaction");
      } catch (SQLException sqle) {
        System.out.println(
            "Expected SQLException (local CallableStatement hold) " + sqle.getMessage());
      }

      // but an update works
      sh.executeUpdate("insert into hold_30 values(10, 'init10')");

      xr.end(xid, XAResource.TMSUCCESS);

      System.out.println(
          "CONNECTION(held) HOLDABILITY "
              + (conn.getHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT));

      conn.close();
      System.out.println("PASS XA HOLDABILITY TEST");

    } catch (XAException xae) {
      System.out.println("XAException error code " + xae.errorCode);
      xae.printStackTrace(System.out);
      Throwable t = xae.getCause();
      if (t instanceof SQLException) JDBCDisplayUtil.ShowSQLException(System.out, (SQLException) t);

    } catch (SQLException sqle) {
      JDBCDisplayUtil.ShowSQLException(System.out, sqle);
    } catch (Throwable t) {
      t.printStackTrace(System.out);
    }
    System.out.flush();
  }
Esempio n. 5
0
  /**
   * Tests that simple distributed transaction processing works as expected.
   *
   * @throws Exception if the test fails.
   */
  public void testCoordination() throws Exception {
    if (!versionMeetsMinimum(5, 0)) {
      return;
    }

    createTable("testCoordination", "(field1 int) ENGINE=InnoDB");

    Connection conn1 = null;
    Connection conn2 = null;
    XAConnection xaConn1 = null;
    XAConnection xaConn2 = null;

    try {
      xaConn1 = getXAConnection();
      XAResource xaRes1 = xaConn1.getXAResource();
      conn1 = xaConn1.getConnection();

      xaConn2 = getXAConnection();
      XAResource xaRes2 = xaConn2.getXAResource();
      conn2 = xaConn2.getConnection();

      Xid xid1 = createXid();
      Xid xid2 = createXid(xid1);

      xaRes1.start(xid1, XAResource.TMNOFLAGS);
      xaRes2.start(xid2, XAResource.TMNOFLAGS);
      conn1.createStatement().executeUpdate("INSERT INTO testCoordination VALUES (1)");
      conn2.createStatement().executeUpdate("INSERT INTO testCoordination VALUES (2)");
      xaRes1.end(xid1, XAResource.TMSUCCESS);
      xaRes2.end(xid2, XAResource.TMSUCCESS);

      xaRes1.prepare(xid1);
      xaRes2.prepare(xid2);

      xaRes1.commit(xid1, false);
      xaRes2.commit(xid2, false);

      this.rs = this.stmt.executeQuery("SELECT field1 FROM testCoordination ORDER BY field1");

      assertTrue(this.rs.next());
      assertEquals(1, this.rs.getInt(1));

      assertTrue(this.rs.next());
      assertEquals(2, this.rs.getInt(1));

      this.stmt.executeUpdate("TRUNCATE TABLE testCoordination");

      //
      // Now test rollback
      //

      xid1 = createXid();
      xid2 = createXid(xid1);

      xaRes1.start(xid1, XAResource.TMNOFLAGS);
      xaRes2.start(xid2, XAResource.TMNOFLAGS);
      conn1.createStatement().executeUpdate("INSERT INTO testCoordination VALUES (1)");

      // ensure visibility
      assertEquals(
          "1",
          getSingleIndexedValueWithQuery(
                  conn1, 1, "SELECT field1 FROM testCoordination WHERE field1=1")
              .toString());

      conn2.createStatement().executeUpdate("INSERT INTO testCoordination VALUES (2)");

      // ensure visibility
      assertEquals(
          "2",
          getSingleIndexedValueWithQuery(
                  conn2, 1, "SELECT field1 FROM testCoordination WHERE field1=2")
              .toString());

      xaRes1.end(xid1, XAResource.TMSUCCESS);
      xaRes2.end(xid2, XAResource.TMSUCCESS);

      xaRes1.prepare(xid1);
      xaRes2.prepare(xid2);

      xaRes1.rollback(xid1);
      xaRes2.rollback(xid2);

      this.rs = this.stmt.executeQuery("SELECT field1 FROM testCoordination ORDER BY field1");

      assertTrue(!this.rs.next());
    } finally {
      if (conn1 != null) {
        conn1.close();
      }

      if (conn2 != null) {
        conn2.close();
      }

      if (xaConn1 != null) {
        xaConn1.close();
      }

      if (xaConn2 != null) {
        xaConn2.close();
      }
    }
  }
Esempio n. 6
0
  /**
   * Tests that XA RECOVER works as expected.
   *
   * @throws Exception if test fails
   */
  public void testRecover() throws Exception {
    if (!versionMeetsMinimum(5, 0)) {
      return;
    }

    if (versionMeetsMinimum(5, 7) && !versionMeetsMinimum(5, 7, 5)) {
      // Test is broken in 5.7.0 - 5.7.4 after server bug#14670465 fix which changed the XA RECOVER
      // output format.
      // Fixed in 5.7.5 server version
      return;
    }

    // BUG#14670465 fix broke this functionality in 5.7.1 - 5.7.2
    if (versionMeetsMinimum(5, 7, 1) && !versionMeetsMinimum(5, 7, 3)) {
      return;
    }

    XAConnection xaConn = null, recoverConn = null;

    try {
      xaConn = getXAConnection();

      Connection c = xaConn.getConnection();
      Xid xid = createXid();

      XAResource xaRes = xaConn.getXAResource();
      xaRes.start(xid, XAResource.TMNOFLAGS);
      c.createStatement().executeQuery("SELECT 1");
      xaRes.end(xid, XAResource.TMSUCCESS);
      xaRes.prepare(xid);

      // Now try and recover
      recoverConn = getXAConnection();

      XAResource recoverRes = recoverConn.getXAResource();

      Xid[] recoveredXids = recoverRes.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN);

      assertTrue(recoveredXids != null);
      assertTrue(recoveredXids.length > 0);

      boolean xidFound = false;

      for (int i = 0; i < recoveredXids.length; i++) {
        if (recoveredXids[i] != null && recoveredXids[i].equals(xid)) {
          xidFound = true;

          break;
        }
      }

      assertTrue(xidFound);

      recoverRes = recoverConn.getXAResource();

      recoveredXids = recoverRes.recover(XAResource.TMSTARTRSCAN);

      assertTrue(recoveredXids != null);
      assertTrue(recoveredXids.length > 0);

      xidFound = false;

      for (int i = 0; i < recoveredXids.length; i++) {
        if (recoveredXids[i] != null && recoveredXids[i].equals(xid)) {
          xidFound = true;

          break;
        }
      }

      assertTrue(xidFound);

      // Test flags
      recoverRes.recover(XAResource.TMSTARTRSCAN);
      recoverRes.recover(XAResource.TMENDRSCAN);
      recoverRes.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN);

      // This should fail
      try {
        recoverRes.recover(XAResource.TMSUCCESS);
        fail("XAException should have been thrown");
      } catch (XAException xaEx) {
        assertEquals(XAException.XAER_INVAL, xaEx.errorCode);
      }
    } finally {
      if (xaConn != null) {
        xaConn.close();
      }

      if (recoverConn != null) {
        recoverConn.close();
      }
    }
  }
 @Override
 public int prepare(Xid xid) throws XAException {
   return resource.prepare(xid);
 }
Esempio n. 8
0
  @Override
  public boolean runExample() throws Exception {
    XAConnection connection = null;
    InitialContext initialContext = null;
    try {
      // Step 1. Create an initial context to perform the JNDI lookup.
      initialContext = getContext(0);

      // Step 2. Lookup on the queue
      Queue queue = (Queue) initialContext.lookup("/queue/exampleQueue");

      // Step 3. Perform a lookup on the XA Connection Factory
      XAConnectionFactory cf = (XAConnectionFactory) initialContext.lookup("/XAConnectionFactory");

      // Step 4.Create a JMS XAConnection
      connection = cf.createXAConnection();

      // Step 5. Start the connection
      connection.start();

      // Step 6. Create a JMS XASession
      XASession xaSession = connection.createXASession();

      // Step 7. Create a normal session
      Session normalSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

      // Step 8. Create a normal Message Consumer
      MessageConsumer normalConsumer = normalSession.createConsumer(queue);
      normalConsumer.setMessageListener(new SimpleMessageListener());

      // Step 9. Get the JMS Session
      Session session = xaSession.getSession();

      // Step 10. Create a message producer
      MessageProducer producer = session.createProducer(queue);

      // Step 11. Create two Text Messages
      TextMessage helloMessage = session.createTextMessage("hello");
      TextMessage worldMessage = session.createTextMessage("world");

      // Step 12. create a transaction
      Xid xid1 =
          new DummyXid(
              "xa-example1".getBytes(CharsetUtil.UTF_8),
              1,
              UUIDGenerator.getInstance().generateStringUUID().getBytes());

      // Step 13. Get the JMS XAResource
      XAResource xaRes = xaSession.getXAResource();

      // Step 14. Begin the Transaction work
      xaRes.start(xid1, XAResource.TMNOFLAGS);

      // Step 15. do work, sending two messages.
      producer.send(helloMessage);
      producer.send(worldMessage);

      Thread.sleep(2000);

      // Step 16. Check the result, it should receive none!
      checkNoMessageReceived();

      // Step 17. Stop the work
      xaRes.end(xid1, XAResource.TMSUCCESS);

      // Step 18. Prepare
      xaRes.prepare(xid1);

      // Step 19. Roll back the transaction
      xaRes.rollback(xid1);

      // Step 20. No messages should be received!
      checkNoMessageReceived();

      // Step 21. Create another transaction
      Xid xid2 =
          new DummyXid(
              "xa-example2".getBytes(),
              1,
              UUIDGenerator.getInstance().generateStringUUID().getBytes());

      // Step 22. Start the transaction
      xaRes.start(xid2, XAResource.TMNOFLAGS);

      // Step 23. Re-send those messages
      producer.send(helloMessage);
      producer.send(worldMessage);

      // Step 24. Stop the work
      xaRes.end(xid2, XAResource.TMSUCCESS);

      // Step 25. Prepare
      xaRes.prepare(xid2);

      // Step 26. No messages should be received at this moment
      checkNoMessageReceived();

      // Step 27. Commit!
      xaRes.commit(xid2, false);

      Thread.sleep(2000);

      // Step 28. Check the result, all message received
      checkAllMessageReceived();

      return result;
    } finally {
      // Step 29. Be sure to close our JMS resources!
      if (initialContext != null) {
        initialContext.close();
      }
      if (connection != null) {
        connection.close();
      }
    }
  }