/*
* 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.cluster.failover;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.TransportConfiguration;
import org.apache.activemq.artemis.api.core.client.ActiveMQClient;
import org.apache.activemq.artemis.api.core.client.ClientMessage;
import org.apache.activemq.artemis.api.core.client.ClientSession;
import org.apache.activemq.artemis.api.core.client.ClusterTopologyListener;
import org.apache.activemq.artemis.api.core.client.ServerLocator;
import org.apache.activemq.artemis.api.core.client.TopologyMember;
import org.apache.activemq.artemis.core.client.impl.ClientSessionFactoryInternal;
import org.apache.activemq.artemis.core.client.impl.ServerLocatorInternal;
import org.apache.activemq.artemis.core.config.ClusterConnectionConfiguration;
import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.config.ha.ReplicaPolicyConfiguration;
import org.apache.activemq.artemis.core.config.ha.SharedStoreMasterPolicyConfiguration;
import org.apache.activemq.artemis.core.config.ha.SharedStoreSlavePolicyConfiguration;
import org.apache.activemq.artemis.core.remoting.impl.invm.InVMConnector;
import org.apache.activemq.artemis.core.remoting.impl.invm.InVMRegistry;
import org.apache.activemq.artemis.core.server.NodeManager;
import org.apache.activemq.artemis.core.server.cluster.ha.ReplicatedPolicy;
import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl;
import org.apache.activemq.artemis.core.server.impl.InVMNodeManager;
import org.apache.activemq.artemis.tests.integration.cluster.util.SameProcessActiveMQServer;
import org.apache.activemq.artemis.tests.integration.cluster.util.TestableServer;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.apache.activemq.artemis.tests.util.ReplicatedBackupUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
public abstract class FailoverTestBase extends ActiveMQTestBase {
// Constants -----------------------------------------------------
protected static final SimpleString ADDRESS = new SimpleString("FailoverTestAddress");
/*
* Used only by tests of large messages.
*/
protected static final int MIN_LARGE_MESSAGE = 1024;
private static final int LARGE_MESSAGE_SIZE = MIN_LARGE_MESSAGE * 3;
protected static final int PAGE_MAX = 2 * 1024;
protected static final int PAGE_SIZE = 1024;
// Attributes ----------------------------------------------------
protected TestableServer liveServer;
protected TestableServer backupServer;
protected Configuration backupConfig;
protected Configuration liveConfig;
protected NodeManager nodeManager;
protected boolean startBackupServer = true;
@Override
@Before
public void setUp() throws Exception {
super.setUp();
createConfigs();
setLiveIdentity();
liveServer.start();
waitForServerToStart(liveServer.getServer());
if (backupServer != null) {
setBackupIdentity();
if (startBackupServer) {
backupServer.start();
waitForBackup();
}
}
}
protected void waitForBackup() {
waitForRemoteBackupSynchronization(backupServer.getServer());
}
protected void setBackupIdentity() {
backupServer.setIdentity(this.getClass().getSimpleName() + "/backupServers");
}
protected void setLiveIdentity() {
liveServer.setIdentity(this.getClass().getSimpleName() + "/liveServer");
}
protected TestableServer createTestableServer(Configuration config) {
boolean isBackup = config.getHAPolicyConfiguration() instanceof ReplicaPolicyConfiguration || config.getHAPolicyConfiguration() instanceof SharedStoreSlavePolicyConfiguration;
return new SameProcessActiveMQServer(createInVMFailoverServer(true, config, nodeManager, isBackup ? 2 : 1));
}
protected TestableServer createColocatedTestableServer(Configuration config,
NodeManager liveNodeManager,
NodeManager backupNodeManager,
int id) {
return new SameProcessActiveMQServer(createColocatedInVMFailoverServer(true, config, liveNodeManager, backupNodeManager, id));
}
/**
* Large message version of {@link #setBody(int, ClientMessage)}.
*
* @param i
* @param message
*/
protected static void setLargeMessageBody(final int i, final ClientMessage message) {
try {
message.setBodyInputStream(ActiveMQTestBase.createFakeLargeStream(LARGE_MESSAGE_SIZE));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Large message version of {@link #assertMessageBody(int, ClientMessage)}.
*
* @param i
* @param message
*/
protected static void assertLargeMessageBody(final int i, final ClientMessage message) {
ActiveMQBuffer buffer = message.getBodyBuffer();
for (int j = 0; j < LARGE_MESSAGE_SIZE; j++) {
Assert.assertTrue("msg " + i + ", expecting " + LARGE_MESSAGE_SIZE + " bytes, got " + j, buffer.readable());
Assert.assertEquals("equal at " + j, ActiveMQTestBase.getSamplebyte(j), buffer.readByte());
}
}
protected void createConfigs() throws Exception {
nodeManager = new InVMNodeManager(false);
TransportConfiguration liveConnector = getConnectorTransportConfiguration(true);
TransportConfiguration backupConnector = getConnectorTransportConfiguration(false);
backupConfig = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(getAcceptorTransportConfiguration(false)).setHAPolicyConfiguration(new SharedStoreSlavePolicyConfiguration()).addConnectorConfiguration(liveConnector.getName(), liveConnector).addConnectorConfiguration(backupConnector.getName(), backupConnector).addClusterConfiguration(basicClusterConnectionConfig(backupConnector.getName(), liveConnector.getName()));
backupServer = createTestableServer(backupConfig);
liveConfig = super.createDefaultInVMConfig().clearAcceptorConfigurations().addAcceptorConfiguration(getAcceptorTransportConfiguration(true)).setHAPolicyConfiguration(new SharedStoreMasterPolicyConfiguration()).addClusterConfiguration(basicClusterConnectionConfig(liveConnector.getName())).addConnectorConfiguration(liveConnector.getName(), liveConnector);
liveServer = createTestableServer(liveConfig);
}
protected void createReplicatedConfigs() throws Exception {
final TransportConfiguration liveConnector = getConnectorTransportConfiguration(true);
final TransportConfiguration backupConnector = getConnectorTransportConfiguration(false);
final TransportConfiguration backupAcceptor = getAcceptorTransportConfiguration(false);
backupConfig = createDefaultInVMConfig();
liveConfig = createDefaultInVMConfig();
ReplicatedBackupUtils.configureReplicationPair(backupConfig, backupConnector, backupAcceptor, liveConfig, liveConnector, null);
backupConfig.setBindingsDirectory(getBindingsDir(0, true)).setJournalDirectory(getJournalDir(0, true)).setPagingDirectory(getPageDir(0, true)).setLargeMessagesDirectory(getLargeMessagesDir(0, true)).setSecurityEnabled(false);
setupHAPolicyConfiguration();
nodeManager = new InVMNodeManager(true, backupConfig.getJournalLocation());
backupServer = createTestableServer(backupConfig);
liveConfig.clearAcceptorConfigurations().addAcceptorConfiguration(getAcceptorTransportConfiguration(true));
liveServer = createTestableServer(liveConfig);
}
protected void setupHAPolicyConfiguration() {
((ReplicaPolicyConfiguration) backupConfig.getHAPolicyConfiguration()).setMaxSavedReplicatedJournalsSize(-1).setAllowFailBack(true);
((ReplicaPolicyConfiguration) backupConfig.getHAPolicyConfiguration()).setRestartBackup(false);
}
protected final void adaptLiveConfigForReplicatedFailBack(TestableServer server) {
Configuration configuration = server.getServer().getConfiguration();
final TransportConfiguration backupConnector = getConnectorTransportConfiguration(false);
if (server.getServer().getHAPolicy().isSharedStore()) {
ClusterConnectionConfiguration cc = configuration.getClusterConfigurations().get(0);
Assert.assertNotNull("cluster connection configuration", cc);
Assert.assertNotNull("static connectors", cc.getStaticConnectors());
cc.getStaticConnectors().add(backupConnector.getName());
// backupConnector is only necessary for fail-back tests
configuration.getConnectorConfigurations().put(backupConnector.getName(), backupConnector);
return;
}
ReplicatedPolicy haPolicy = (ReplicatedPolicy) server.getServer().getHAPolicy();
haPolicy.setCheckForLiveServer(true);
}
@Override
@After
public void tearDown() throws Exception {
logAndSystemOut("#test tearDown");
InVMConnector.failOnCreateConnection = false;
super.tearDown();
Assert.assertEquals(0, InVMRegistry.instance.size());
backupServer = null;
liveServer = null;
nodeManager = null;
try {
ServerSocket serverSocket = new ServerSocket(61616);
serverSocket.close();
} catch (IOException e) {
throw e;
}
try {
ServerSocket serverSocket = new ServerSocket(61617);
serverSocket.close();
} catch (IOException e) {
throw e;
}
}
protected ClientSessionFactoryInternal createSessionFactoryAndWaitForTopology(ServerLocator locator,
int topologyMembers) throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(topologyMembers);
locator.addClusterTopologyListener(new LatchClusterTopologyListener(countDownLatch));
ClientSessionFactoryInternal sf = (ClientSessionFactoryInternal) locator.createSessionFactory();
addSessionFactory(sf);
Assert.assertTrue("topology members expected " + topologyMembers, countDownLatch.await(5, TimeUnit.SECONDS));
return sf;
}
/**
* Waits for backup to be in the "started" state and to finish synchronization with its live.
*
* @param sessionFactory
* @param seconds
* @throws Exception
*/
protected void waitForBackup(ClientSessionFactoryInternal sessionFactory, int seconds) throws Exception {
final ActiveMQServerImpl actualServer = (ActiveMQServerImpl) backupServer.getServer();
if (actualServer.getHAPolicy().isSharedStore()) {
waitForServerToStart(actualServer);
} else {
waitForRemoteBackup(sessionFactory, seconds, true, actualServer);
}
}
protected abstract TransportConfiguration getAcceptorTransportConfiguration(boolean live);
protected abstract TransportConfiguration getConnectorTransportConfiguration(final boolean live);
protected ServerLocatorInternal getServerLocator() throws Exception {
return (ServerLocatorInternal) addServerLocator(ActiveMQClient.createServerLocatorWithHA(getConnectorTransportConfiguration(true), getConnectorTransportConfiguration(false))).setRetryInterval(50);
}
protected void crash(final ClientSession... sessions) throws Exception {
this.crash(true, sessions);
}
protected void crash(final boolean waitFailure, final ClientSession... sessions) throws Exception {
this.crash(true, waitFailure, sessions);
}
protected void crash(final boolean failover,
final boolean waitFailure,
final ClientSession... sessions) throws Exception {
liveServer.crash(failover, waitFailure, sessions);
}
// Private -------------------------------------------------------
// Inner classes -------------------------------------------------
public static final class LatchClusterTopologyListener implements ClusterTopologyListener {
final CountDownLatch latch;
List<String> liveNode = new ArrayList<>();
List<String> backupNode = new ArrayList<>();
public LatchClusterTopologyListener(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void nodeUP(TopologyMember topologyMember, boolean last) {
if (topologyMember.getLive() != null && !liveNode.contains(topologyMember.getLive().getName())) {
liveNode.add(topologyMember.getLive().getName());
latch.countDown();
}
if (topologyMember.getBackup() != null && !backupNode.contains(topologyMember.getBackup().getName())) {
backupNode.add(topologyMember.getBackup().getName());
latch.countDown();
}
}
@Override
public void nodeDown(final long uniqueEventID, String nodeID) {
//To change body of implemented methods use File | Settings | File Templates.
}
}
}