/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.activemq.artemis.tests.integration.xa; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.client.ClientConsumer; import org.apache.activemq.artemis.api.core.client.ClientMessage; import org.apache.activemq.artemis.api.core.client.ClientProducer; import org.apache.activemq.artemis.api.core.client.ClientSession; import org.apache.activemq.artemis.api.core.client.ClientSessionFactory; import org.apache.activemq.artemis.api.core.client.MessageHandler; import org.apache.activemq.artemis.api.core.client.ServerLocator; import org.apache.activemq.artemis.core.config.Configuration; import org.apache.activemq.artemis.core.config.StoreConfiguration; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; import org.apache.activemq.artemis.core.transaction.impl.XidImpl; import org.apache.activemq.artemis.ra.ActiveMQRAXAResource; import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.apache.activemq.artemis.utils.UUIDGenerator; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @RunWith(Parameterized.class) public class BasicXaTest extends ActiveMQTestBase { private static IntegrationTestLogger log = IntegrationTestLogger.LOGGER; private final Map<String, AddressSettings> addressSettings = new HashMap<>(); private ActiveMQServer messagingService; private ClientSession clientSession; private ClientSessionFactory sessionFactory; private Configuration configuration; private final SimpleString atestq = new SimpleString("BasicXaTestq"); private ServerLocator locator; private StoreConfiguration.StoreType storeType; public BasicXaTest(StoreConfiguration.StoreType storeType) { this.storeType = storeType; } @Parameterized.Parameters(name = "storeType={0}") public static Collection<Object[]> data() { Object[][] params = new Object[][]{{StoreConfiguration.StoreType.FILE}, {StoreConfiguration.StoreType.DATABASE}}; return Arrays.asList(params); } @Override @Before public void setUp() throws Exception { super.setUp(); addressSettings.clear(); if (storeType == StoreConfiguration.StoreType.DATABASE) { configuration = createDefaultJDBCConfig(true); } else { configuration = createDefaultNettyConfig(); } messagingService = createServer(true, configuration, -1, -1, addressSettings); // start the server messagingService.start(); locator = createInVMNonHALocator(); sessionFactory = createSessionFactory(locator); clientSession = addClientSession(sessionFactory.createSession(true, false, false)); clientSession.createQueue(atestq, atestq, null, true); } @Test public void testSendWithoutXID() throws Exception { // Since both resources have same RM, TM will probably use 1PC optimization ServerLocator locator = createInVMNonHALocator(); ClientSessionFactory factory = createSessionFactory(locator); ClientSession session = addClientSession(factory.createSession(true, false, false)); session.createQueue("Test", RoutingType.ANYCAST, "Test"); ClientProducer prod = session.createProducer("Test"); prod.send(session.createMessage(true)); session.start(); ClientConsumer cons = session.createConsumer("Test"); assertNotNull("Send went through an invalid XA Session", cons.receiveImmediate()); } @Test public void testACKWithoutXID() throws Exception { // Since both resources have same RM, TM will probably use 1PC optimization ClientSessionFactory factory = createSessionFactory(locator); ClientSession session = addClientSession(factory.createSession(false, true, true)); session.createQueue("Test", RoutingType.ANYCAST, "Test"); ClientProducer prod = session.createProducer("Test"); prod.send(session.createMessage(true)); session.close(); session = addClientSession(factory.createSession(true, false, false)); session.start(); ClientConsumer cons = session.createConsumer("Test"); ClientMessage msg = cons.receive(5000); assertNotNull(msg); msg.acknowledge(); session.close(); session = addClientSession(factory.createSession(false, false, false)); session.start(); cons = session.createConsumer("Test"); msg = cons.receiveImmediate(); assertNull("Acknowledge went through invalid XA Session", msg); } @Test public void testIsSameRM() throws Exception { try (ServerLocator locator = createNettyNonHALocator(); ServerLocator locator2 = createNettyNonHALocator()) { ClientSessionFactory nettyFactory = createSessionFactory(locator); ClientSessionFactory nettyFactory2 = createSessionFactory(locator2); ClientSession session1 = nettyFactory.createSession(true, false, false); ClientSession session2 = nettyFactory2.createSession(true, false, false); assertTrue(session1.isSameRM(session2)); ActiveMQRAXAResource activeMQRAXAResource = new ActiveMQRAXAResource(null, session2); assertTrue(session1.isSameRM(activeMQRAXAResource)); } } @Test public void testXAInterleaveResourceSuspendWorkCommit() throws Exception { Xid xid = newXID(); Xid xid2 = newXID(); ClientProducer clientProducer = clientSession.createProducer(atestq); ClientSession recSession = sessionFactory.createSession(); recSession.start(); ClientConsumer clientConsumer = recSession.createConsumer(atestq); ClientMessage m1 = createTextMessage(clientSession, "m1"); ClientMessage m2 = createTextMessage(clientSession, "m2"); clientSession.start(xid, XAResource.TMNOFLAGS); clientProducer.send(m1); clientSession.end(xid, XAResource.TMSUSPEND); clientSession.start(xid2, XAResource.TMNOFLAGS); clientProducer.send(m2); clientSession.end(xid, XAResource.TMSUCCESS); clientSession.commit(xid, true); ClientMessage message = clientConsumer.receiveImmediate(); assertNotNull(message); message = clientConsumer.receiveImmediate(); assertNull(message); clientSession.end(xid2, XAResource.TMSUCCESS); clientSession.commit(xid2, true); message = clientConsumer.receiveImmediate(); assertNotNull(message); } @Test public void testXAInterleaveResourceRollbackAfterPrepare() throws Exception { Xid xid = newXID(); Xid xid2 = newXID(); Xid xid3 = newXID(); ClientProducer clientProducer = clientSession.createProducer(atestq); ClientConsumer clientConsumer = clientSession.createConsumer(atestq); ClientMessage m1 = createTextMessage(clientSession, "m1"); clientSession.start(xid, XAResource.TMNOFLAGS); clientProducer.send(m1); clientSession.end(xid, XAResource.TMSUCCESS); clientSession.prepare(xid); clientSession.commit(xid, false); clientSession.start(); clientSession.start(xid2, XAResource.TMNOFLAGS); ClientMessage m2 = clientConsumer.receiveImmediate(); assertNotNull(m2); clientSession.end(xid2, XAResource.TMSUCCESS); clientSession.prepare(xid2); clientSession.rollback(xid2); clientSession.start(xid3, XAResource.TMNOFLAGS); m2 = clientConsumer.receiveImmediate(); assertNotNull(m2); clientSession.end(xid3, XAResource.TMSUCCESS); clientSession.prepare(xid3); clientSession.commit(xid3, false); } @Test public void testSendPrepareDoesntRollbackOnClose() throws Exception { Xid xid = newXID(); ClientMessage m1 = createTextMessage(clientSession, "m1"); ClientMessage m2 = createTextMessage(clientSession, "m2"); ClientMessage m3 = createTextMessage(clientSession, "m3"); ClientMessage m4 = createTextMessage(clientSession, "m4"); ClientProducer clientProducer = clientSession.createProducer(atestq); clientSession.start(xid, XAResource.TMNOFLAGS); clientProducer.send(m1); clientProducer.send(m2); clientProducer.send(m3); clientProducer.send(m4); clientSession.end(xid, XAResource.TMSUCCESS); clientSession.prepare(xid); clientSession.close(); clientSession = sessionFactory.createSession(true, false, false); log.info("committing"); clientSession.commit(xid, false); clientSession.start(); ClientConsumer clientConsumer = clientSession.createConsumer(atestq); ClientMessage m = clientConsumer.receive(1000); Assert.assertNotNull(m); Assert.assertEquals(m.getBodyBuffer().readString(), "m1"); m = clientConsumer.receive(1000); Assert.assertNotNull(m); Assert.assertEquals(m.getBodyBuffer().readString(), "m2"); m = clientConsumer.receive(1000); Assert.assertNotNull(m); Assert.assertEquals(m.getBodyBuffer().readString(), "m3"); m = clientConsumer.receive(1000); Assert.assertNotNull(m); Assert.assertEquals(m.getBodyBuffer().readString(), "m4"); } @Test public void testReceivePrepareDoesntRollbackOnClose() throws Exception { Xid xid = newXID(); ClientSession clientSession2 = sessionFactory.createSession(false, true, true); ClientProducer clientProducer = clientSession2.createProducer(atestq); ClientMessage m1 = createTextMessage(clientSession2, "m1"); ClientMessage m2 = createTextMessage(clientSession2, "m2"); ClientMessage m3 = createTextMessage(clientSession2, "m3"); ClientMessage m4 = createTextMessage(clientSession2, "m4"); clientProducer.send(m1); clientProducer.send(m2); clientProducer.send(m3); clientProducer.send(m4); clientSession.start(xid, XAResource.TMNOFLAGS); clientSession.start(); ClientConsumer clientConsumer = clientSession.createConsumer(atestq); ClientMessage m = clientConsumer.receive(1000); Assert.assertNotNull(m); m.acknowledge(); Assert.assertEquals(m.getBodyBuffer().readString(), "m1"); m = clientConsumer.receive(1000); Assert.assertNotNull(m); m.acknowledge(); Assert.assertEquals(m.getBodyBuffer().readString(), "m2"); m = clientConsumer.receive(1000); Assert.assertNotNull(m); m.acknowledge(); Assert.assertEquals(m.getBodyBuffer().readString(), "m3"); m = clientConsumer.receive(1000); Assert.assertNotNull(m); m.acknowledge(); Assert.assertEquals(m.getBodyBuffer().readString(), "m4"); clientSession.end(xid, XAResource.TMSUCCESS); clientSession.prepare(xid); clientSession.close(); clientSession = sessionFactory.createSession(true, false, false); clientSession.commit(xid, false); clientSession.start(); clientConsumer = clientSession.createConsumer(atestq); m = clientConsumer.receiveImmediate(); Assert.assertNull(m); clientSession2.close(); } @Test public void testReceiveRollback() throws Exception { int numSessions = 100; ClientSession clientSession2 = sessionFactory.createSession(false, true, true); ClientProducer clientProducer = clientSession2.createProducer(atestq); for (int i = 0; i < numSessions; i++) { clientProducer.send(createTextMessage(clientSession2, "m" + i)); } ClientSession[] clientSessions = new ClientSession[numSessions]; ClientConsumer[] clientConsumers = new ClientConsumer[numSessions]; TxMessageHandler[] handlers = new TxMessageHandler[numSessions]; CountDownLatch latch = new CountDownLatch(numSessions * AddressSettings.DEFAULT_MAX_DELIVERY_ATTEMPTS); for (int i = 0; i < clientSessions.length; i++) { clientSessions[i] = sessionFactory.createSession(true, false, false); clientConsumers[i] = clientSessions[i].createConsumer(atestq); handlers[i] = new TxMessageHandler(clientSessions[i], latch); clientConsumers[i].setMessageHandler(handlers[i]); } for (ClientSession session : clientSessions) { session.start(); } boolean ok = latch.await(10, TimeUnit.SECONDS); Assert.assertTrue(ok); for (TxMessageHandler messageHandler : handlers) { Assert.assertFalse(messageHandler.failedToAck); } clientSession2.close(); for (ClientSession session : clientSessions) { session.stop(); session.close(); } } @Test public void testSendMultipleQueues() throws Exception { multipleQueuesInternalTest(true, false, false, false, false); } @Test public void testSendMultipleQueuesOnePhase() throws Exception { multipleQueuesInternalTest(true, false, false, false, true); multipleQueuesInternalTest(false, false, true, false, true); } @Test public void testSendMultipleQueuesOnePhaseJoin() throws Exception { multipleQueuesInternalTest(true, false, false, true, true); multipleQueuesInternalTest(false, false, true, true, true); } @Test public void testSendMultipleQueuesTwoPhaseJoin() throws Exception { multipleQueuesInternalTest(true, false, false, true, false); multipleQueuesInternalTest(false, false, true, true, false); } @Test public void testSendMultipleQueuesRecreate() throws Exception { multipleQueuesInternalTest(true, false, true, false, false); } @Test public void testSendMultipleSuspend() throws Exception { multipleQueuesInternalTest(true, true, false, false, false); } @Test public void testSendMultipleSuspendRecreate() throws Exception { multipleQueuesInternalTest(true, true, true, false, false); } @Test public void testSendMultipleSuspendErrorCheck() throws Exception { ClientSession session = null; session = sessionFactory.createSession(true, false, false); Xid xid = newXID(); session.start(xid, XAResource.TMNOFLAGS); try { session.start(xid, XAResource.TMRESUME); Assert.fail("XAException expected"); } catch (XAException e) { Assert.assertEquals(XAException.XAER_PROTO, e.errorCode); } session.close(); } @Test public void testEmptyXID() throws Exception { Xid xid = newXID(); ClientSession session = sessionFactory.createSession(true, false, false); session.start(xid, XAResource.TMNOFLAGS); session.end(xid, XAResource.TMSUCCESS); session.rollback(xid); session.close(); messagingService.stop(); // do the same test with a file persistence now messagingService = createServer(true, configuration, -1, -1, addressSettings); messagingService.start(); sessionFactory = createSessionFactory(locator); xid = newXID(); session = sessionFactory.createSession(true, false, false); session.start(xid, XAResource.TMNOFLAGS); session.end(xid, XAResource.TMSUCCESS); session.rollback(xid); xid = newXID(); session.start(xid, XAResource.TMNOFLAGS); session.end(xid, XAResource.TMSUCCESS); session.prepare(xid); session.commit(xid, false); session.close(); xid = newXID(); session = sessionFactory.createSession(true, false, false); session.start(xid, XAResource.TMNOFLAGS); session.end(xid, XAResource.TMSUCCESS); session.prepare(xid); session.rollback(xid); session.close(); messagingService.start(); sessionFactory = createSessionFactory(locator); xid = newXID(); session = sessionFactory.createSession(true, false, false); session.start(xid, XAResource.TMNOFLAGS); session.end(xid, XAResource.TMSUCCESS); session.rollback(xid); session.close(); messagingService.stop(); messagingService.start(); // This is not really necessary... But since the server has stopped, I would prefer to keep recreating the factory sessionFactory = createSessionFactory(locator); session = sessionFactory.createSession(true, false, false); Xid[] xids = session.recover(XAResource.TMSTARTRSCAN); Assert.assertEquals(0, xids.length); session.close(); } @Test public void testFailXID() throws Exception { Xid xid = newXID(); ClientSession session = sessionFactory.createSession(true, false, false); session.start(xid, XAResource.TMNOFLAGS); session.end(xid, XAResource.TMFAIL); session.rollback(xid); session.close(); } @Test public void testForgetUnknownXID() throws Exception { try { clientSession.forget(newXID()); Assert.fail("should throw a XAERR_NOTA XAException"); } catch (XAException e) { Assert.assertEquals(XAException.XAER_NOTA, e.errorCode); } } @Test public void testForgetHeuristicallyCommittedXID() throws Exception { Xid xid = newXID(); clientSession.start(xid, XAResource.TMNOFLAGS); clientSession.end(xid, XAResource.TMSUCCESS); clientSession.prepare(xid); String[] preparedTransactions = messagingService.getActiveMQServerControl().listPreparedTransactions(); Assert.assertEquals(1, preparedTransactions.length); System.out.println(preparedTransactions[0]); Assert.assertTrue(messagingService.getActiveMQServerControl().commitPreparedTransaction(XidImpl.toBase64String(xid))); Assert.assertEquals(1, messagingService.getActiveMQServerControl().listHeuristicCommittedTransactions().length); clientSession.forget(xid); Assert.assertEquals(0, messagingService.getActiveMQServerControl().listHeuristicCommittedTransactions().length); } @Test public void testForgetHeuristicallyRolledBackXID() throws Exception { Xid xid = newXID(); clientSession.start(xid, XAResource.TMNOFLAGS); clientSession.end(xid, XAResource.TMSUCCESS); clientSession.prepare(xid); String[] preparedTransactions = messagingService.getActiveMQServerControl().listPreparedTransactions(); Assert.assertEquals(1, preparedTransactions.length); System.out.println(preparedTransactions[0]); Assert.assertTrue(messagingService.getActiveMQServerControl().rollbackPreparedTransaction(XidImpl.toBase64String(xid))); Assert.assertEquals(1, messagingService.getActiveMQServerControl().listHeuristicRolledBackTransactions().length); clientSession.forget(xid); Assert.assertEquals(0, messagingService.getActiveMQServerControl().listHeuristicRolledBackTransactions().length); } @Test public void testCommitHeuristicallyCommittedXID() throws Exception { doCompleteHeuristicallyCompletedXID(true, true); } @Test public void testCommitHeuristicallyRolledBackXID() throws Exception { doCompleteHeuristicallyCompletedXID(true, false); } @Test public void testRollbacktHeuristicallyCommittedXID() throws Exception { doCompleteHeuristicallyCompletedXID(false, true); } @Test public void testRollbackHeuristicallyRolledBackXID() throws Exception { doCompleteHeuristicallyCompletedXID(false, false); } @Test public void testSimpleJoin() throws Exception { SimpleString ADDRESS1 = new SimpleString("Address-1"); SimpleString ADDRESS2 = new SimpleString("Address-2"); clientSession.createQueue(ADDRESS1, ADDRESS1, true); clientSession.createQueue(ADDRESS2, ADDRESS2, true); Xid xid = newXID(); ClientSession sessionA = sessionFactory.createSession(true, false, false); sessionA.start(xid, XAResource.TMNOFLAGS); ClientSession sessionB = sessionFactory.createSession(true, false, false); sessionB.start(xid, XAResource.TMJOIN); ClientProducer prodA = sessionA.createProducer(ADDRESS1); ClientProducer prodB = sessionB.createProducer(ADDRESS2); for (int i = 0; i < 100; i++) { prodA.send(createTextMessage(sessionA, "A" + i)); prodB.send(createTextMessage(sessionB, "B" + i)); } sessionA.end(xid, XAResource.TMSUCCESS); sessionB.end(xid, XAResource.TMSUCCESS); sessionB.close(); sessionA.commit(xid, true); sessionA.close(); xid = newXID(); clientSession.start(xid, XAResource.TMNOFLAGS); ClientConsumer cons1 = clientSession.createConsumer(ADDRESS1); ClientConsumer cons2 = clientSession.createConsumer(ADDRESS2); clientSession.start(); for (int i = 0; i < 100; i++) { ClientMessage msg = cons1.receive(1000); Assert.assertNotNull(msg); Assert.assertEquals("A" + i, getTextMessage(msg)); msg.acknowledge(); msg = cons2.receive(1000); Assert.assertNotNull(msg); Assert.assertEquals("B" + i, getTextMessage(msg)); msg.acknowledge(); } Assert.assertNull(cons1.receiveImmediate()); Assert.assertNull(cons2.receiveImmediate()); clientSession.end(xid, XAResource.TMSUCCESS); clientSession.commit(xid, true); clientSession.close(); } /** * @throws ActiveMQException * @throws XAException */ protected void multipleQueuesInternalTest(final boolean createQueues, final boolean suspend, final boolean recreateSession, final boolean isJoinSession, final boolean onePhase) throws Exception { int NUMBER_OF_MSGS = 100; int NUMBER_OF_QUEUES = 10; ClientSession session = null; SimpleString ADDRESS = new SimpleString("Address"); ClientSession newJoinSession = null; try { session = sessionFactory.createSession(true, false, false); if (createQueues) { for (int i = 0; i < NUMBER_OF_QUEUES; i++) { session.createQueue(ADDRESS, ADDRESS.concat(Integer.toString(i)), true); if (isJoinSession) { clientSession.createQueue(ADDRESS.concat("-join"), ADDRESS.concat("-join." + i), true); } } } for (int tr = 0; tr < 2; tr++) { Xid xid = newXID(); session.start(xid, XAResource.TMNOFLAGS); ClientProducer prod = session.createProducer(ADDRESS); for (int nmsg = 0; nmsg < NUMBER_OF_MSGS; nmsg++) { ClientMessage msg = createTextMessage(session, "SimpleMessage" + nmsg); prod.send(msg); } if (suspend) { session.end(xid, XAResource.TMSUSPEND); session.start(xid, XAResource.TMRESUME); } prod.send(createTextMessage(session, "one more")); prod.close(); if (isJoinSession) { newJoinSession = sessionFactory.createSession(true, false, false); // This is a basic condition, or a real TM wouldn't be able to join both sessions in a single // transactions Assert.assertTrue(session.isSameRM(newJoinSession)); newJoinSession.start(xid, XAResource.TMJOIN); // The Join Session will have its own queue, as it's not possible to guarantee ordering since this // producer will be using a different session ClientProducer newProd = newJoinSession.createProducer(ADDRESS.concat("-join")); newProd.send(createTextMessage(newJoinSession, "After Join")); } session.end(xid, XAResource.TMSUCCESS); if (isJoinSession) { newJoinSession.end(xid, XAResource.TMSUCCESS); newJoinSession.close(); } if (!onePhase) { session.prepare(xid); } if (recreateSession) { session.close(); session = sessionFactory.createSession(true, false, false); } if (tr == 0) { session.rollback(xid); } else { session.commit(xid, onePhase); } } for (int i = 0; i < 2; i++) { Xid xid = newXID(); session.start(xid, XAResource.TMNOFLAGS); for (int nqueues = 0; nqueues < NUMBER_OF_QUEUES; nqueues++) { ClientConsumer consumer = session.createConsumer(ADDRESS.concat(Integer.toString(nqueues))); session.start(); for (int nmsg = 0; nmsg < NUMBER_OF_MSGS; nmsg++) { ClientMessage msg = consumer.receive(1000); Assert.assertNotNull(msg); Assert.assertEquals("SimpleMessage" + nmsg, getTextMessage(msg)); msg.acknowledge(); } ClientMessage msg = consumer.receive(1000); Assert.assertNotNull(msg); Assert.assertEquals("one more", getTextMessage(msg)); msg.acknowledge(); if (suspend) { session.end(xid, XAResource.TMSUSPEND); session.start(xid, XAResource.TMRESUME); } Assert.assertEquals("one more", getTextMessage(msg)); if (isJoinSession) { ClientSession newSession = sessionFactory.createSession(true, false, false); newSession.start(xid, XAResource.TMJOIN); newSession.start(); ClientConsumer newConsumer = newSession.createConsumer(ADDRESS.concat("-join." + nqueues)); msg = newConsumer.receive(1000); Assert.assertNotNull(msg); Assert.assertEquals("After Join", getTextMessage(msg)); msg.acknowledge(); newSession.end(xid, XAResource.TMSUCCESS); newSession.close(); } Assert.assertNull(consumer.receiveImmediate()); consumer.close(); } session.end(xid, XAResource.TMSUCCESS); session.prepare(xid); if (recreateSession) { session.close(); session = sessionFactory.createSession(true, false, false); } if (i == 0) { session.rollback(xid); } else { session.commit(xid, false); } } } finally { if (session != null) { session.close(); } } } private void doCompleteHeuristicallyCompletedXID(final boolean isCommit, final boolean heuristicCommit) throws Exception { Xid xid = newXID(); clientSession.start(xid, XAResource.TMNOFLAGS); clientSession.end(xid, XAResource.TMSUCCESS); clientSession.prepare(xid); String[] preparedTransactions = messagingService.getActiveMQServerControl().listPreparedTransactions(); Assert.assertEquals(1, preparedTransactions.length); if (heuristicCommit) { Assert.assertTrue(messagingService.getActiveMQServerControl().commitPreparedTransaction(XidImpl.toBase64String(xid))); Assert.assertEquals(1, messagingService.getActiveMQServerControl().listHeuristicCommittedTransactions().length); } else { Assert.assertTrue(messagingService.getActiveMQServerControl().rollbackPreparedTransaction(XidImpl.toBase64String(xid))); Assert.assertEquals(1, messagingService.getActiveMQServerControl().listHeuristicRolledBackTransactions().length); } Assert.assertEquals(0, messagingService.getActiveMQServerControl().listPreparedTransactions().length); try { if (isCommit) { clientSession.commit(xid, false); } else { clientSession.rollback(xid); } Assert.fail("neither commit not rollback must succeed on a heuristically completed tx"); } catch (XAException e) { if (heuristicCommit) { Assert.assertEquals(XAException.XA_HEURCOM, e.errorCode); } else { Assert.assertEquals(XAException.XA_HEURRB, e.errorCode); } } if (heuristicCommit) { Assert.assertEquals(1, messagingService.getActiveMQServerControl().listHeuristicCommittedTransactions().length); } else { Assert.assertEquals(1, messagingService.getActiveMQServerControl().listHeuristicRolledBackTransactions().length); } } class TxMessageHandler implements MessageHandler { boolean failedToAck = false; final ClientSession session; private final CountDownLatch latch; TxMessageHandler(final ClientSession session, final CountDownLatch latch) { this.latch = latch; this.session = session; } @Override public void onMessage(final ClientMessage message) { Xid xid = new XidImpl(UUIDGenerator.getInstance().generateStringUUID().getBytes(), 1, UUIDGenerator.getInstance().generateStringUUID().getBytes()); try { session.start(xid, XAResource.TMNOFLAGS); } catch (XAException e) { e.printStackTrace(); } try { message.acknowledge(); } catch (ActiveMQException e) { BasicXaTest.log.error("Failed to process message", e); } try { session.end(xid, XAResource.TMSUCCESS); session.rollback(xid); } catch (Exception e) { e.printStackTrace(); failedToAck = true; try { session.close(); } catch (ActiveMQException e1) { // } } latch.countDown(); } } }