/*
* 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.remoting;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
import org.apache.activemq.artemis.api.core.client.FailoverEventListener;
import org.apache.activemq.artemis.api.core.client.FailoverEventType;
import org.apache.activemq.artemis.api.core.client.ServerLocator;
import org.apache.activemq.artemis.api.core.client.SessionFailureListener;
import org.apache.activemq.artemis.core.client.impl.ClientSessionFactoryInternal;
import org.apache.activemq.artemis.core.client.impl.ClientSessionInternal;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.ServerSession;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.junit.Assert;
import org.junit.Test;
public class ReconnectTest extends ActiveMQTestBase {
@Test
public void testReconnectNetty() throws Exception {
internalTestReconnect(true);
}
@Test
public void testReconnectInVM() throws Exception {
internalTestReconnect(false);
}
public void internalTestReconnect(final boolean isNetty) throws Exception {
final int pingPeriod = 1000;
ActiveMQServer server = createServer(false, isNetty);
server.start();
ClientSessionInternal session = null;
try {
ServerLocator locator = createFactory(isNetty).setClientFailureCheckPeriod(pingPeriod).setRetryInterval(500).setRetryIntervalMultiplier(1d).setReconnectAttempts(-1).setConfirmationWindowSize(1024 * 1024);
ClientSessionFactory factory = createSessionFactory(locator);
session = (ClientSessionInternal) factory.createSession();
final AtomicInteger count = new AtomicInteger(0);
final CountDownLatch latch = new CountDownLatch(1);
session.addFailureListener(new SessionFailureListener() {
@Override
public void connectionFailed(final ActiveMQException me, boolean failedOver) {
count.incrementAndGet();
latch.countDown();
}
@Override
public void connectionFailed(final ActiveMQException me, boolean failedOver, String scaleDownTargetNodeID) {
connectionFailed(me, failedOver);
}
@Override
public void beforeReconnect(final ActiveMQException exception) {
}
});
server.stop();
Thread.sleep((pingPeriod * 2));
server.start();
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
// Some time to let possible loops to occur
Thread.sleep(500);
Assert.assertEquals(1, count.get());
locator.close();
} finally {
try {
session.close();
} catch (Throwable e) {
}
server.stop();
}
}
@Test
public void testMetadataAfterReconnectionNetty() throws Exception {
internalMetadataAfterRetry(true);
}
@Test
public void testMetadataAfterReconnectionInVM() throws Exception {
internalMetadataAfterRetry(false);
}
public void internalMetadataAfterRetry(final boolean isNetty) throws Exception {
final int pingPeriod = 1000;
ActiveMQServer server = createServer(false, isNetty);
server.start();
ClientSessionInternal session = null;
try {
for (int i = 0; i < 100; i++) {
ServerLocator locator = createFactory(isNetty);
locator.setClientFailureCheckPeriod(pingPeriod);
locator.setRetryInterval(1);
locator.setRetryIntervalMultiplier(1d);
locator.setReconnectAttempts(-1);
locator.setConfirmationWindowSize(-1);
ClientSessionFactory factory = createSessionFactory(locator);
session = (ClientSessionInternal) factory.createSession();
session.addMetaData("meta1", "meta1");
ServerSession[] sessions = countMetadata(server, "meta1", 1);
Assert.assertEquals(1, sessions.length);
final AtomicInteger count = new AtomicInteger(0);
final CountDownLatch latch = new CountDownLatch(1);
session.addFailoverListener(new FailoverEventListener() {
@Override
public void failoverEvent(FailoverEventType eventType) {
if (eventType == FailoverEventType.FAILOVER_COMPLETED) {
latch.countDown();
}
}
});
sessions[0].getRemotingConnection().fail(new ActiveMQException("failure!"));
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
sessions = countMetadata(server, "meta1", 1);
Assert.assertEquals(1, sessions.length);
locator.close();
}
} finally {
try {
session.close();
} catch (Throwable e) {
}
server.stop();
}
}
private ServerSession[] countMetadata(ActiveMQServer server, String parameter, int expected) throws Exception {
List<ServerSession> sessionList = new LinkedList<>();
for (int i = 0; i < 10 && sessionList.size() != expected; i++) {
sessionList.clear();
for (ServerSession sess : server.getSessions()) {
if (sess.getMetaData(parameter) != null) {
sessionList.add(sess);
}
}
if (sessionList.size() != expected) {
Thread.sleep(100);
}
}
return sessionList.toArray(new ServerSession[sessionList.size()]);
}
@Test
public void testInterruptReconnectNetty() throws Exception {
internalTestInterruptReconnect(true, false);
}
@Test
public void testInterruptReconnectInVM() throws Exception {
internalTestInterruptReconnect(false, false);
}
@Test
public void testInterruptReconnectNettyInterruptMainThread() throws Exception {
internalTestInterruptReconnect(true, true);
}
@Test
public void testInterruptReconnectInVMInterruptMainThread() throws Exception {
internalTestInterruptReconnect(false, true);
}
public void internalTestInterruptReconnect(final boolean isNetty,
final boolean interruptMainThread) throws Exception {
final int pingPeriod = 1000;
ActiveMQServer server = createServer(false, isNetty);
server.start();
try {
ServerLocator locator = createFactory(isNetty).setClientFailureCheckPeriod(pingPeriod).setRetryInterval(500).setRetryIntervalMultiplier(1d).setReconnectAttempts(-1).setConfirmationWindowSize(1024 * 1024);
ClientSessionFactoryInternal factory = (ClientSessionFactoryInternal) locator.createSessionFactory();
// One for beforeReconnecto from the Factory, and one for the commit about to be done
final CountDownLatch latchCommit = new CountDownLatch(2);
final ArrayList<Thread> threadToBeInterrupted = new ArrayList<>();
factory.addFailureListener(new SessionFailureListener() {
@Override
public void connectionFailed(ActiveMQException exception, boolean failedOver) {
}
@Override
public void connectionFailed(final ActiveMQException me, boolean failedOver, String scaleDownTargetNodeID) {
connectionFailed(me, failedOver);
}
@Override
public void beforeReconnect(ActiveMQException exception) {
threadToBeInterrupted.add(Thread.currentThread());
System.out.println("Thread " + Thread.currentThread() + " reconnecting now");
latchCommit.countDown();
}
});
final ClientSessionInternal session = (ClientSessionInternal) factory.createSession();
final AtomicInteger count = new AtomicInteger(0);
final CountDownLatch latch = new CountDownLatch(1);
session.addFailureListener(new SessionFailureListener() {
@Override
public void connectionFailed(final ActiveMQException me, boolean failedOver) {
count.incrementAndGet();
latch.countDown();
}
@Override
public void connectionFailed(final ActiveMQException me, boolean failedOver, String scaleDownTargetNodeID) {
connectionFailed(me, failedOver);
}
@Override
public void beforeReconnect(final ActiveMQException exception) {
}
});
server.stop();
Thread tcommitt = new Thread() {
@Override
public void run() {
latchCommit.countDown();
try {
session.commit();
} catch (ActiveMQException e) {
e.printStackTrace();
}
}
};
tcommitt.start();
assertTrue(latchCommit.await(10, TimeUnit.SECONDS));
// There should be only one thread
assertEquals(1, threadToBeInterrupted.size());
if (interruptMainThread) {
tcommitt.interrupt();
} else {
for (Thread tint : threadToBeInterrupted) {
tint.interrupt();
}
}
tcommitt.join(5000);
assertFalse(tcommitt.isAlive());
locator.close();
} finally {
}
}
// Package protected ---------------------------------------------
// Protected -----------------------------------------------------
// Private -------------------------------------------------------
// Inner classes -------------------------------------------------
}