/* * 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.jms.bridge; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.RollbackException; import javax.transaction.Synchronization; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.xa.XAResource; import org.apache.activemq.artemis.api.core.TransportConfiguration; import org.apache.activemq.artemis.api.core.client.ClientSession; import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; import org.apache.activemq.artemis.api.jms.JMSFactoryType; import org.apache.activemq.artemis.jms.bridge.ConnectionFactoryFactory; import org.apache.activemq.artemis.jms.bridge.QualityOfServiceMode; import org.apache.activemq.artemis.jms.bridge.impl.JMSBridgeImpl; import org.apache.activemq.artemis.jms.client.ActiveMQXAConnectionFactory; import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; import org.apache.activemq.artemis.tests.integration.ra.DummyTransactionManager; import org.junit.Assert; import org.junit.Test; public class JMSBridgeReconnectionTest extends BridgeTestBase { /** * */ private static final int TIME_WAIT = 5000; private static final IntegrationTestLogger log = IntegrationTestLogger.LOGGER; // Crash and reconnect // Once and only once @Test public void testCrashAndReconnectDestBasic_OnceAndOnlyOnce_P() throws Exception { performCrashAndReconnectDestBasic(QualityOfServiceMode.ONCE_AND_ONLY_ONCE, true, false); } @Test public void testCrashAndReconnectDestBasic_OnceAndOnlyOnce_P_LargeMessage() throws Exception { performCrashAndReconnectDestBasic(QualityOfServiceMode.ONCE_AND_ONLY_ONCE, true, true); } @Test public void testCrashAndReconnectDestBasic_OnceAndOnlyOnce_NP() throws Exception { performCrashAndReconnectDestBasic(QualityOfServiceMode.ONCE_AND_ONLY_ONCE, false, false); } // dups ok @Test public void testCrashAndReconnectDestBasic_DuplicatesOk_P() throws Exception { performCrashAndReconnectDestBasic(QualityOfServiceMode.DUPLICATES_OK, true, false); } @Test public void testCrashAndReconnectDestBasic_DuplicatesOk_NP() throws Exception { performCrashAndReconnectDestBasic(QualityOfServiceMode.DUPLICATES_OK, false, false); } // At most once @Test public void testCrashAndReconnectDestBasic_AtMostOnce_P() throws Exception { performCrashAndReconnectDestBasic(QualityOfServiceMode.AT_MOST_ONCE, true, false); } @Test public void testCrashAndReconnectDestBasic_AtMostOnce_NP() throws Exception { performCrashAndReconnectDestBasic(QualityOfServiceMode.AT_MOST_ONCE, false, false); } // Crash tests specific to XA transactions @Test public void testCrashAndReconnectDestCrashBeforePrepare_P() throws Exception { performCrashAndReconnectDestCrashBeforePrepare(true); } @Test public void testCrashAndReconnectDestCrashBeforePrepare_NP() throws Exception { performCrashAndReconnectDestCrashBeforePrepare(false); } // Crash before bridge is started @Test public void testRetryConnectionOnStartup() throws Exception { jmsServer1.stop(); JMSBridgeImpl bridge = new JMSBridgeImpl(cff0, cff1, sourceQueueFactory, targetQueueFactory, null, null, null, null, null, 1000, -1, QualityOfServiceMode.DUPLICATES_OK, 10, -1, null, null, false).setBridgeName("test-bridge"); bridge.setTransactionManager(newTransactionManager()); addActiveMQComponent(bridge); bridge.start(); Assert.assertFalse(bridge.isStarted()); Assert.assertTrue(bridge.isFailed()); // Restart the server jmsServer1.start(); createQueue("targetQueue", 1); setUpAdministeredObjects(); Thread.sleep(3000); Assert.assertTrue(bridge.isStarted()); Assert.assertFalse(bridge.isFailed()); } /** * https://jira.jboss.org/jira/browse/HORNETQ-287 */ @Test public void testStopBridgeWithFailureWhenStarted() throws Exception { jmsServer1.stop(); JMSBridgeImpl bridge = new JMSBridgeImpl(cff0, cff1, sourceQueueFactory, targetQueueFactory, null, null, null, null, null, 500, -1, QualityOfServiceMode.DUPLICATES_OK, 10, -1, null, null, false).setBridgeName("test-bridge"); bridge.setTransactionManager(newTransactionManager()); bridge.start(); Assert.assertFalse(bridge.isStarted()); Assert.assertTrue(bridge.isFailed()); bridge.stop(); Assert.assertFalse(bridge.isStarted()); // we restart and setup the server for the test's tearDown checks jmsServer1.start(); createQueue("targetQueue", 1); setUpAdministeredObjects(); } /* * Send some messages * Crash the destination server * Bring the destination server back up * Send some more messages * Verify all messages are received */ private void performCrashAndReconnectDestBasic(final QualityOfServiceMode qosMode, final boolean persistent, final boolean largeMessage) throws Exception { JMSBridgeImpl bridge = null; ConnectionFactoryFactory factInUse0 = cff0; ConnectionFactoryFactory factInUse1 = cff1; if (qosMode.equals(QualityOfServiceMode.ONCE_AND_ONLY_ONCE)) { factInUse0 = cff0xa; factInUse1 = cff1xa; } bridge = new JMSBridgeImpl(factInUse0, factInUse1, sourceQueueFactory, targetQueueFactory, null, null, null, null, null, 1000, -1, qosMode, 10, -1, null, null, false).setBridgeName("test-bridge"); addActiveMQComponent(bridge); bridge.setTransactionManager(newTransactionManager()); bridge.start(); final int NUM_MESSAGES = 10; // Send some messages sendMessages(cf0, sourceQueue, 0, NUM_MESSAGES / 2, persistent, largeMessage); // Verify none are received checkEmpty(targetQueue, 1); // Now crash the dest server JMSBridgeReconnectionTest.log.info("About to crash server"); jmsServer1.stop(); // Wait a while before starting up to simulate the dest being down for a while JMSBridgeReconnectionTest.log.info("Waiting 5 secs before bringing server back up"); Thread.sleep(TIME_WAIT); JMSBridgeReconnectionTest.log.info("Done wait"); // Restart the server JMSBridgeReconnectionTest.log.info("Restarting server"); jmsServer1.start(); // jmsServer1.createQueue(false, "targetQueue", null, true, "queue/targetQueue"); createQueue("targetQueue", 1); setUpAdministeredObjects(); // Send some more messages JMSBridgeReconnectionTest.log.info("Sending more messages"); sendMessages(cf0, sourceQueue, NUM_MESSAGES / 2, NUM_MESSAGES / 2, persistent, largeMessage); JMSBridgeReconnectionTest.log.info("Sent messages"); jmsServer1.stop(); bridge.stop(); System.out.println("JMSBridgeReconnectionTest.performCrashAndReconnectDestBasic"); } @Test public void performCrashDestinationStopBridge() throws Exception { ConnectionFactoryFactory factInUse0 = cff0; ConnectionFactoryFactory factInUse1 = cff1; final JMSBridgeImpl bridge = new JMSBridgeImpl(factInUse0, factInUse1, sourceQueueFactory, targetQueueFactory, null, null, null, null, null, 1000, -1, QualityOfServiceMode.DUPLICATES_OK, 10, -1, null, null, false).setBridgeName("test-bridge"); addActiveMQComponent(bridge); bridge.setTransactionManager(newTransactionManager()); bridge.start(); Thread clientThread = new Thread(new Runnable() { @Override public void run() { while (bridge.isStarted()) { try { sendMessages(cf0, sourceQueue, 0, 1, false, false); } catch (Exception e) { e.printStackTrace(); } } } }); clientThread.start(); checkAllMessageReceivedInOrder(cf1, targetQueue, 0, 1, false); JMSBridgeReconnectionTest.log.info("About to crash server"); jmsServer1.stop(); // Wait a while before starting up to simulate the dest being down for a while JMSBridgeReconnectionTest.log.info("Waiting 5 secs before bringing server back up"); Thread.sleep(TIME_WAIT); JMSBridgeReconnectionTest.log.info("Done wait"); bridge.stop(); clientThread.join(5000); assertTrue(!clientThread.isAlive()); } @Test public void performCrashAndReconnect() throws Exception { performCrashAndReconnect(true); } @Test public void performCrashAndNoReconnect() throws Exception { performCrashAndReconnect(false); } private void performCrashAndReconnect(boolean restart) throws Exception { cff1xa = new ConnectionFactoryFactory() { @Override public Object createConnectionFactory() throws Exception { ActiveMQXAConnectionFactory cf = (ActiveMQXAConnectionFactory) ActiveMQJMSClient.createConnectionFactoryWithHA(JMSFactoryType.XA_CF, new TransportConfiguration(INVM_CONNECTOR_FACTORY, params1)); // Note! We disable automatic reconnection on the session factory. The bridge needs to do the reconnection cf.setReconnectAttempts(-1); cf.setBlockOnNonDurableSend(true); cf.setBlockOnDurableSend(true); cf.setCacheLargeMessagesClient(true); return cf; } }; DummyTransactionManager tm = new DummyTransactionManager(); DummyTransaction tx = new DummyTransaction(); tm.tx = tx; JMSBridgeImpl bridge = new JMSBridgeImpl(cff0xa, cff1xa, sourceQueueFactory, targetQueueFactory, null, null, null, null, null, 1000, -1, QualityOfServiceMode.ONCE_AND_ONLY_ONCE, 10, 5000, null, null, false).setBridgeName("test-bridge"); addActiveMQComponent(bridge); bridge.setTransactionManager(tm); bridge.start(); // Now crash the dest server JMSBridgeReconnectionTest.log.info("About to crash server"); jmsServer1.stop(); if (restart) { jmsServer1.start(); } // Wait a while before starting up to simulate the dest being down for a while JMSBridgeReconnectionTest.log.info("Waiting 5 secs before bringing server back up"); Thread.sleep(TIME_WAIT); JMSBridgeReconnectionTest.log.info("Done wait"); bridge.stop(); if (restart) { assertTrue(tx.rolledback); assertTrue(tx.targetConnected); } else { assertTrue(tx.rolledback); assertFalse(tx.targetConnected); } } private class DummyTransaction implements Transaction { boolean rolledback = false; ClientSession targetSession; boolean targetConnected = false; @Override public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, SystemException { } @Override public void rollback() throws IllegalStateException, SystemException { rolledback = true; targetConnected = !targetSession.isClosed(); } @Override public void setRollbackOnly() throws IllegalStateException, SystemException { } @Override public int getStatus() throws SystemException { return 0; } @Override public boolean enlistResource(XAResource xaResource) throws RollbackException, IllegalStateException, SystemException { targetSession = (ClientSession) xaResource; return false; } @Override public boolean delistResource(XAResource xaResource, int i) throws IllegalStateException, SystemException { return false; } @Override public void registerSynchronization(Synchronization synchronization) throws RollbackException, IllegalStateException, SystemException { } } /* * Send some messages * Crash the destination server * Set the max batch time such that it will attempt to send the batch while the dest server is down * Bring up the destination server * Send some more messages * Verify all messages are received */ private void performCrashAndReconnectDestCrashBeforePrepare(final boolean persistent) throws Exception { JMSBridgeImpl bridge = new JMSBridgeImpl(cff0xa, cff1xa, sourceQueueFactory, targetQueueFactory, null, null, null, null, null, 1000, -1, QualityOfServiceMode.ONCE_AND_ONLY_ONCE, 10, 5000, null, null, false).setBridgeName("test-bridge"); addActiveMQComponent(bridge); bridge.setTransactionManager(newTransactionManager()); bridge.start(); final int NUM_MESSAGES = 10; // Send some messages sendMessages(cf0, sourceQueue, 0, NUM_MESSAGES / 2, persistent, false); // verify none are received checkEmpty(targetQueue, 1); // Now crash the dest server JMSBridgeReconnectionTest.log.info("About to crash server"); jmsServer1.stop(); // Wait a while before starting up to simulate the dest being down for a while JMSBridgeReconnectionTest.log.info("Waiting 5 secs before bringing server back up"); Thread.sleep(TIME_WAIT); JMSBridgeReconnectionTest.log.info("Done wait"); // Restart the server jmsServer1.start(); createQueue("targetQueue", 1); setUpAdministeredObjects(); sendMessages(cf0, sourceQueue, NUM_MESSAGES / 2, NUM_MESSAGES / 2, persistent, false); checkMessagesReceived(cf1, targetQueue, QualityOfServiceMode.ONCE_AND_ONLY_ONCE, NUM_MESSAGES, false, false); } }