/* * 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.extras.byteman; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; import org.apache.activemq.artemis.api.core.ActiveMQTransactionOutcomeUnknownException; import org.apache.activemq.artemis.api.core.ActiveMQTransactionRolledBackException; import org.apache.activemq.artemis.api.core.ActiveMQUnBlockedException; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.TransportConfiguration; 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.ServerLocator; import org.apache.activemq.artemis.core.client.ActiveMQClientMessageBundle; import org.apache.activemq.artemis.core.client.impl.ClientMessageImpl; import org.apache.activemq.artemis.core.client.impl.ClientSessionFactoryInternal; import org.apache.activemq.artemis.core.client.impl.ClientSessionInternal; import org.apache.activemq.artemis.core.postoffice.Binding; import org.apache.activemq.artemis.core.server.Queue; import org.apache.activemq.artemis.core.transaction.impl.XidImpl; import org.apache.activemq.artemis.tests.integration.cluster.failover.FailoverTestBase; import org.apache.activemq.artemis.tests.integration.cluster.util.TestableServer; import org.apache.activemq.artemis.tests.util.RandomUtil; import org.apache.activemq.artemis.utils.UUIDGenerator; import org.jboss.byteman.contrib.bmunit.BMRule; import org.jboss.byteman.contrib.bmunit.BMRules; import org.jboss.byteman.contrib.bmunit.BMUnitRunner; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(BMUnitRunner.class) public class BMFailoverTest extends FailoverTestBase { private ServerLocator locator; private ClientSessionFactoryInternal sf; private ClientSessionFactoryInternal sf2; public static TestableServer serverToStop; @Before @Override public void setUp() throws Exception { super.setUp(); stopped = false; locator = getServerLocator(); } private static boolean stopped = false; public static void stopAndThrow() throws ActiveMQUnBlockedException { if (!stopped) { try { serverToStop.getServer().stop(true); } catch (Exception e) { e.printStackTrace(); } try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } stopped = true; throw ActiveMQClientMessageBundle.BUNDLE.unblockingACall(null); } } @Test @BMRules( rules = {@BMRule( name = "trace ActiveMQSessionContext xaEnd", targetClass = "org.apache.activemq.artemis.core.protocol.core.impl.ActiveMQSessionContext", targetMethod = "xaEnd", targetLocation = "AT EXIT", action = "org.apache.activemq.artemis.tests.extras.byteman.BMFailoverTest.stopAndThrow()")}) //https://bugzilla.redhat.com/show_bug.cgi?id=1152410 public void testFailOnEndAndRetry() throws Exception { serverToStop = liveServer; createSessionFactory(); ClientSession session = createSession(sf, true, false, false); session.createQueue(FailoverTestBase.ADDRESS, FailoverTestBase.ADDRESS, null, true); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); for (int i = 0; i < 100; i++) { producer.send(createMessage(session, i, true)); } ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); Xid xid = RandomUtil.randomXid(); session.start(xid, XAResource.TMNOFLAGS); session.start(); // Receive MSGs but don't ack! for (int i = 0; i < 100; i++) { ClientMessage message = consumer.receive(1000); Assert.assertNotNull(message); assertMessageBody(i, message); Assert.assertEquals(i, message.getIntProperty("counter").intValue()); } try { //top level prepare session.end(xid, XAResource.TMSUCCESS); } catch (XAException e) { try { //top level abort session.end(xid, XAResource.TMFAIL); } catch (XAException e1) { try { //rollback session.rollback(xid); } catch (XAException e2) { } } } xid = RandomUtil.randomXid(); session.start(xid, XAResource.TMNOFLAGS); for (int i = 0; i < 50; i++) { ClientMessage message = consumer.receive(1000); Assert.assertNotNull(message); assertMessageBody(i, message); Assert.assertEquals(i, message.getIntProperty("counter").intValue()); } session.end(xid, XAResource.TMSUCCESS); session.commit(xid, true); } @Test @BMRules( rules = {@BMRule( name = "trace clientsessionimpl commit", targetClass = "org.apache.activemq.artemis.core.client.impl.ClientSessionImpl", targetMethod = "start(javax.transaction.xa.Xid, int)", targetLocation = "AT EXIT", action = "org.apache.activemq.artemis.tests.extras.byteman.BMFailoverTest.serverToStop.getServer().stop(true)")}) public void testFailoverOnCommit2() throws Exception { serverToStop = liveServer; locator = getServerLocator().setFailoverOnInitialConnection(true); SimpleString inQueue = new SimpleString("inQueue"); SimpleString outQueue = new SimpleString("outQueue"); createSessionFactory(); createSessionFactory2(); // closeable will take care of closing it try (ClientSession session = sf.createSession(false, true, true); ClientProducer sendInitialProducer = session.createProducer();) { session.createQueue(inQueue, inQueue, null, true); session.createQueue(outQueue, outQueue, null, true); sendInitialProducer.send(inQueue, createMessage(session, 0, true)); } ClientSession xaSessionRec = addClientSession(sf.createSession(true, false, false)); ClientConsumer consumer = addClientConsumer(xaSessionRec.createConsumer(inQueue)); byte[] globalTransactionId = UUIDGenerator.getInstance().generateStringUUID().getBytes(); Xid xidRec = new XidImpl("xa2".getBytes(), 1, globalTransactionId); xaSessionRec.start(); xaSessionRec.getXAResource().start(xidRec, XAResource.TMNOFLAGS); //failover is now occurring, receive, ack and end will be called whilst this is happening. ClientMessageImpl m = (ClientMessageImpl) consumer.receive(5000); assertNotNull(m); System.out.println("********************" + m.getIntProperty("counter")); //the mdb would ack the message before calling onMessage() m.acknowledge(); try { //this may fail but thats ok, it depends on the race and when failover actually happens xaSessionRec.end(xidRec, XAResource.TMSUCCESS); } catch (XAException ignore) { } //we always reset the client on the RA ((ClientSessionInternal) xaSessionRec).resetIfNeeded(); // closeable will take care of closing it try (ClientSession session = sf.createSession(false, true, true); ClientProducer sendInitialProducer = session.createProducer();) { sendInitialProducer.send(inQueue, createMessage(session, 0, true)); } //now receive and send a message successfully globalTransactionId = UUIDGenerator.getInstance().generateStringUUID().getBytes(); xidRec = new XidImpl("xa4".getBytes(), 1, globalTransactionId); xaSessionRec.getXAResource().start(xidRec, XAResource.TMNOFLAGS); Binding binding = backupServer.getServer().getPostOffice().getBinding(inQueue); Queue inQ = (Queue) binding.getBindable(); m = (ClientMessageImpl) consumer.receive(5000); assertNotNull(m); //the mdb would ack the message before calling onMessage() m.acknowledge(); System.out.println("********************" + m.getIntProperty("counter")); xaSessionRec.getXAResource().end(xidRec, XAResource.TMSUCCESS); xaSessionRec.getXAResource().prepare(xidRec); xaSessionRec.getXAResource().commit(xidRec, false); //let's close the consumer so anything pending is handled consumer.close(); assertEquals(1, getMessageCount(inQ)); } @Test @BMRules( rules = {@BMRule( name = "trace clientsessionimpl commit", targetClass = "org.apache.activemq.artemis.core.client.impl.ClientSessionImpl", targetMethod = "commit", targetLocation = "ENTRY", action = "org.apache.activemq.artemis.tests.extras.byteman.BMFailoverTest.serverToStop.getServer().stop(true)")}) public void testFailoverOnCommit() throws Exception { serverToStop = liveServer; locator = getServerLocator().setFailoverOnInitialConnection(true); createSessionFactory(); ClientSession session = createSessionAndQueue(); ClientProducer producer = addClientProducer(session.createProducer(FailoverTestBase.ADDRESS)); sendMessages(session, producer, 10); try { session.commit(); fail("should have thrown an exception"); } catch (ActiveMQTransactionOutcomeUnknownException e) { //pass } sendMessages(session, producer, 10); session.commit(); Queue bindable = (Queue) backupServer.getServer().getPostOffice().getBinding(FailoverTestBase.ADDRESS).getBindable(); assertEquals(10, getMessageCount(bindable)); } @Test @BMRules( rules = {@BMRule( name = "trace clientsessionimpl commit", targetClass = "org.apache.activemq.artemis.core.client.impl.ClientSessionImpl", targetMethod = "commit", targetLocation = "ENTRY", action = "org.apache.activemq.artemis.tests.extras.byteman.BMFailoverTest.serverToStop.getServer().stop(true)")}) public void testFailoverOnReceiveCommit() throws Exception { serverToStop = liveServer; locator = getServerLocator().setFailoverOnInitialConnection(true); createSessionFactory(); ClientSession session = createSessionAndQueue(); ClientSession sendSession = createSession(sf, true, true); ClientProducer producer = addClientProducer(sendSession.createProducer(FailoverTestBase.ADDRESS)); sendMessages(sendSession, producer, 10); ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); for (int i = 0; i < 10; i++) { ClientMessage m = consumer.receive(500); assertNotNull(m); m.acknowledge(); } try { session.commit(); fail("should have thrown an exception"); } catch (ActiveMQTransactionOutcomeUnknownException e) { //pass } catch (ActiveMQTransactionRolledBackException e1) { //pass } Queue bindable = (Queue) backupServer.getServer().getPostOffice().getBinding(FailoverTestBase.ADDRESS).getBindable(); assertEquals(10, getMessageCount(bindable)); } @Override protected TransportConfiguration getAcceptorTransportConfiguration(final boolean live) { return getNettyAcceptorTransportConfiguration(live); } @Override protected TransportConfiguration getConnectorTransportConfiguration(final boolean live) { return getNettyConnectorTransportConfiguration(live); } private ClientSession createSessionAndQueue() throws Exception { ClientSession session = createSession(sf, false, false); session.createQueue(FailoverTestBase.ADDRESS, FailoverTestBase.ADDRESS, null, true); return session; } private ClientSession createXASessionAndQueue() throws Exception { ClientSession session = addClientSession(sf.createSession(true, true, true)); session.createQueue(FailoverTestBase.ADDRESS, FailoverTestBase.ADDRESS, null, true); return session; } protected ClientSession createSession(ClientSessionFactory sf1, boolean autoCommitSends, boolean autoCommitAcks) throws Exception { return addClientSession(sf1.createSession(autoCommitSends, autoCommitAcks)); } protected ClientSession createSession(ClientSessionFactory sf1, boolean xa, boolean autoCommitSends, boolean autoCommitAcks) throws Exception { return addClientSession(sf1.createSession(xa, autoCommitSends, autoCommitAcks)); } private void createSessionFactory() throws Exception { locator.setBlockOnNonDurableSend(true).setBlockOnDurableSend(true).setReconnectAttempts(-1); sf = createSessionFactoryAndWaitForTopology(locator, 2); } private void createSessionFactory2() throws Exception { sf2 = createSessionFactoryAndWaitForTopology(locator, 2); } }