/* * 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.jms.cluster; import javax.jms.BytesMessage; import javax.jms.Connection; import javax.jms.DeliveryMode; import javax.jms.Destination; import javax.jms.ExceptionListener; import javax.jms.JMSException; import javax.jms.MessageConsumer; import javax.jms.MessageProducer; import javax.jms.Queue; import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.Topic; import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException; import org.apache.activemq.artemis.api.core.SimpleString; 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.core.client.impl.ClientSessionInternal; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ActiveMQServers; import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; import org.apache.activemq.artemis.jms.client.ActiveMQSession; import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.apache.activemq.artemis.utils.RandomUtil; import org.junit.Assert; import org.junit.Before; import org.junit.Test; public class JMSReconnectTest extends ActiveMQTestBase { private ActiveMQServer server; //In this test we re-attach to the same node without restarting the server @Test public void testReattachSameNode() throws Exception { testReconnectOrReattachSameNode(true); } //In this test, we reconnect to the same node without restarting the server @Test public void testReconnectSameNode() throws Exception { testReconnectOrReattachSameNode(false); } private void testReconnectOrReattachSameNode(boolean reattach) throws Exception { ActiveMQConnectionFactory jbcf = ActiveMQJMSClient.createConnectionFactoryWithoutHA(JMSFactoryType.CF, new TransportConfiguration(INVM_CONNECTOR_FACTORY)); jbcf.setBlockOnDurableSend(true); jbcf.setBlockOnNonDurableSend(true); jbcf.setReconnectAttempts(-1); if (reattach) { jbcf.setConfirmationWindowSize(1024 * 1024); } // Note we set consumer window size to a value so we can verify that consumer credit re-sending // works properly on failover // The value is small enough that credits will have to be resent several time final int numMessages = 10; final int bodySize = 1000; jbcf.setConsumerWindowSize(numMessages * bodySize / 10); Connection conn = jbcf.createConnection(); MyExceptionListener listener = new MyExceptionListener(); conn.setExceptionListener(listener); Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); ClientSession coreSession = ((ActiveMQSession) sess).getCoreSession(); RemotingConnection coreConn = ((ClientSessionInternal) coreSession).getConnection(); SimpleString jmsQueueName = new SimpleString("myqueue"); coreSession.createQueue(jmsQueueName, RoutingType.ANYCAST, jmsQueueName, null, true); Queue queue = sess.createQueue("myqueue"); MessageProducer producer = sess.createProducer(queue); producer.setDeliveryMode(DeliveryMode.PERSISTENT); MessageConsumer consumer = sess.createConsumer(queue); byte[] body = RandomUtil.randomBytes(bodySize); for (int i = 0; i < numMessages; i++) { BytesMessage bm = sess.createBytesMessage(); bm.writeBytes(body); producer.send(bm); } conn.start(); Thread.sleep(2000); ActiveMQException me = new ActiveMQNotConnectedException(); coreConn.fail(me); //It should reconnect to the same node for (int i = 0; i < numMessages; i++) { BytesMessage bm = (BytesMessage) consumer.receive(1000); Assert.assertNotNull(bm); Assert.assertEquals(body.length, bm.getBodyLength()); } TextMessage tm = (TextMessage) consumer.receiveNoWait(); Assert.assertNull(tm); conn.close(); Assert.assertNotNull(listener.e); Assert.assertTrue(me == listener.e.getCause()); } @Test public void testReconnectSameNodeServerRestartedWithNonDurableSub() throws Exception { testReconnectSameNodeServerRestartedWithNonDurableSubOrTempQueue(true); } @Test public void testReconnectSameNodeServerRestartedWithTempQueue() throws Exception { testReconnectSameNodeServerRestartedWithNonDurableSubOrTempQueue(false); } //Test that non durable JMS sub gets recreated in auto reconnect private void testReconnectSameNodeServerRestartedWithNonDurableSubOrTempQueue(final boolean nonDurableSub) throws Exception { ActiveMQConnectionFactory jbcf = ActiveMQJMSClient.createConnectionFactoryWithoutHA(JMSFactoryType.CF, new TransportConfiguration(INVM_CONNECTOR_FACTORY)); jbcf.setReconnectAttempts(-1); Connection conn = jbcf.createConnection(); MyExceptionListener listener = new MyExceptionListener(); conn.setExceptionListener(listener); Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); ClientSession coreSession = ((ActiveMQSession) sess).getCoreSession(); Destination dest; if (nonDurableSub) { coreSession.createQueue("mytopic", "blahblah", null, false); dest = ActiveMQJMSClient.createTopic("mytopic"); } else { dest = sess.createTemporaryQueue(); } MessageProducer producer = sess.createProducer(dest); //Create a non durable subscriber MessageConsumer consumer = sess.createConsumer(dest); this.server.stop(); this.server.start(); //Allow client some time to reconnect Thread.sleep(3000); final int numMessages = 100; byte[] body = RandomUtil.randomBytes(1000); for (int i = 0; i < numMessages; i++) { BytesMessage bm = sess.createBytesMessage(); bm.writeBytes(body); producer.send(bm); } conn.start(); for (int i = 0; i < numMessages; i++) { BytesMessage bm = (BytesMessage) consumer.receive(1000); Assert.assertNotNull(bm); Assert.assertEquals(body.length, bm.getBodyLength()); } TextMessage tm = (TextMessage) consumer.receiveNoWait(); Assert.assertNull(tm); conn.close(); Assert.assertNotNull(listener.e); } //If the server is shutdown after a non durable sub is created, then close on the connection should proceed normally @Test public void testNoReconnectCloseAfterFailToReconnectWithTopicConsumer() throws Exception { ActiveMQConnectionFactory jbcf = ActiveMQJMSClient.createConnectionFactoryWithoutHA(JMSFactoryType.CF, new TransportConfiguration(INVM_CONNECTOR_FACTORY)); jbcf.setReconnectAttempts(0); Connection conn = jbcf.createConnection(); Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); ClientSession coreSession = ((ActiveMQSession) sess).getCoreSession(); coreSession.createQueue("mytopic", "blahblah", null, false); Topic topic = ActiveMQJMSClient.createTopic("mytopic"); //Create a non durable subscriber sess.createConsumer(topic); Thread.sleep(2000); this.server.stop(); this.server.start(); sess.close(); conn.close(); } //If server is shutdown, and then connection is closed, after a temp queue has been created, the close should complete normally @Test public void testNoReconnectCloseAfterFailToReconnectWithTempQueue() throws Exception { ActiveMQConnectionFactory jbcf = ActiveMQJMSClient.createConnectionFactoryWithoutHA(JMSFactoryType.CF, new TransportConfiguration(INVM_CONNECTOR_FACTORY)); jbcf.setReconnectAttempts(0); Connection conn = jbcf.createConnection(); Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); sess.createTemporaryQueue(); Thread.sleep(2000); this.server.stop(); this.server.start(); sess.close(); conn.close(); } // Package protected --------------------------------------------- // Protected ----------------------------------------------------- @Override @Before public void setUp() throws Exception { super.setUp(); server = addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig(), true)); server.start(); } // Private ------------------------------------------------------- // Inner classes ------------------------------------------------- private static class MyExceptionListener implements ExceptionListener { volatile JMSException e; @Override public void onException(final JMSException e) { this.e = e; } } }