/*
* 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.core.server.impl;
import java.nio.channels.ClosedChannelException;
import java.util.concurrent.TimeUnit;
import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.TransportConfiguration;
import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.paging.PagingManager;
import org.apache.activemq.artemis.core.persistence.StorageManager;
import org.apache.activemq.artemis.core.postoffice.PostOffice;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
import org.apache.activemq.artemis.core.server.NodeManager;
import org.apache.activemq.artemis.core.server.QueueFactory;
import org.apache.activemq.artemis.core.server.cluster.ha.ScaleDownPolicy;
import org.apache.activemq.artemis.core.server.cluster.ha.SharedStoreSlavePolicy;
import org.apache.activemq.artemis.core.server.group.GroupingHandler;
import org.apache.activemq.artemis.core.server.management.ManagementService;
import org.jboss.logging.Logger;
public final class SharedStoreBackupActivation extends Activation {
private static final Logger logger = Logger.getLogger(SharedStoreBackupActivation.class);
//this is how we act as a backup
private SharedStoreSlavePolicy sharedStoreSlavePolicy;
private ActiveMQServerImpl activeMQServer;
private final Object failbackCheckerGuard = new Object();
private boolean cancelFailBackChecker;
public SharedStoreBackupActivation(ActiveMQServerImpl server, SharedStoreSlavePolicy sharedStoreSlavePolicy) {
this.activeMQServer = server;
this.sharedStoreSlavePolicy = sharedStoreSlavePolicy;
synchronized (failbackCheckerGuard) {
cancelFailBackChecker = false;
}
}
@Override
public void run() {
try {
activeMQServer.getNodeManager().startBackup();
ScaleDownPolicy scaleDownPolicy = sharedStoreSlavePolicy.getScaleDownPolicy();
boolean scalingDown = scaleDownPolicy != null && scaleDownPolicy.isEnabled();
if (!activeMQServer.initialisePart1(scalingDown))
return;
activeMQServer.getBackupManager().start();
activeMQServer.setState(ActiveMQServerImpl.SERVER_STATE.STARTED);
ActiveMQServerLogger.LOGGER.backupServerStarted(activeMQServer.getVersion().getFullVersion(), activeMQServer.getNodeManager().getNodeId());
activeMQServer.getNodeManager().awaitLiveNode();
sharedStoreSlavePolicy.getSharedStoreMasterPolicy().setSharedStoreSlavePolicy(sharedStoreSlavePolicy);
activeMQServer.setHAPolicy(sharedStoreSlavePolicy.getSharedStoreMasterPolicy());
//activeMQServer.configuration.getHAPolicy().setPolicyType(HAPolicy.POLICY_TYPE.SHARED_STORE);
activeMQServer.getBackupManager().activated();
if (activeMQServer.getState() != ActiveMQServerImpl.SERVER_STATE.STARTED) {
return;
}
activeMQServer.initialisePart2(scalingDown);
activeMQServer.completeActivation();
if (scalingDown) {
ActiveMQServerLogger.LOGGER.backupServerScaledDown();
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
activeMQServer.stop();
//we are shared store but if we were started by a parent server then we shouldn't restart
if (sharedStoreSlavePolicy.isRestartBackup()) {
activeMQServer.start();
}
} catch (Exception e) {
ActiveMQServerLogger.LOGGER.serverRestartWarning();
}
}
});
t.start();
return;
} else {
ActiveMQServerLogger.LOGGER.backupServerIsLive();
activeMQServer.getNodeManager().releaseBackup();
}
if (sharedStoreSlavePolicy.isAllowAutoFailBack()) {
startFailbackChecker();
}
} catch (ClosedChannelException | InterruptedException e) {
// these are ok, we are being stopped
} catch (Exception e) {
if (!(e.getCause() instanceof InterruptedException)) {
ActiveMQServerLogger.LOGGER.initializationError(e);
}
} catch (Throwable e) {
ActiveMQServerLogger.LOGGER.initializationError(e);
}
}
@Override
public void close(boolean permanently, boolean restarting) throws Exception {
if (!restarting) {
synchronized (failbackCheckerGuard) {
cancelFailBackChecker = true;
}
}
// To avoid a NPE cause by the stop
NodeManager nodeManagerInUse = activeMQServer.getNodeManager();
//we need to check as the servers policy may have changed
if (activeMQServer.getHAPolicy().isBackup()) {
activeMQServer.interruptActivationThread(nodeManagerInUse);
if (nodeManagerInUse != null) {
nodeManagerInUse.stopBackup();
}
} else {
if (nodeManagerInUse != null) {
// if we are now live, behave as live
// We need to delete the file too, otherwise the backup will failover when we shutdown or if the backup is
// started before the live
if (sharedStoreSlavePolicy.isFailoverOnServerShutdown() || permanently) {
nodeManagerInUse.crashLiveServer();
} else {
nodeManagerInUse.pauseLiveServer();
}
}
}
}
@Override
public JournalLoader createJournalLoader(PostOffice postOffice,
PagingManager pagingManager,
StorageManager storageManager,
QueueFactory queueFactory,
NodeManager nodeManager,
ManagementService managementService,
GroupingHandler groupingHandler,
Configuration configuration,
ActiveMQServer parentServer) throws ActiveMQException {
if (sharedStoreSlavePolicy.getScaleDownPolicy() != null && sharedStoreSlavePolicy.getScaleDownPolicy().isEnabled()) {
return new BackupRecoveryJournalLoader(postOffice, pagingManager, storageManager, queueFactory, nodeManager, managementService, groupingHandler, configuration, parentServer, ScaleDownPolicy.getScaleDownConnector(sharedStoreSlavePolicy.getScaleDownPolicy(), activeMQServer), activeMQServer.getClusterManager().getClusterController());
} else {
return super.createJournalLoader(postOffice, pagingManager, storageManager, queueFactory, nodeManager, managementService, groupingHandler, configuration, parentServer);
}
}
/**
* To be called by backup trying to fail back the server
*/
private void startFailbackChecker() {
activeMQServer.getScheduledPool().scheduleAtFixedRate(new FailbackChecker(), 1000L, 1000L, TimeUnit.MILLISECONDS);
}
private class FailbackChecker implements Runnable {
BackupTopologyListener backupListener;
FailbackChecker() {
TransportConfiguration connector = activeMQServer.getClusterManager().getDefaultConnection(null).getConnector();
backupListener = new BackupTopologyListener(activeMQServer.getNodeID().toString(), connector);
activeMQServer.getClusterManager().getDefaultConnection(null).addClusterTopologyListener(backupListener);
}
private boolean restarting = false;
@Override
public void run() {
try {
if (!restarting && activeMQServer.getNodeManager().isAwaitingFailback()) {
if (backupListener.waitForBackup()) {
ActiveMQServerLogger.LOGGER.awaitFailBack();
restarting = true;
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
logger.debug(activeMQServer + "::Stopping live node in favor of failback");
NodeManager nodeManager = activeMQServer.getNodeManager();
activeMQServer.stop(true, false, true);
// ensure that the server to which we are failing back actually starts fully before we restart
nodeManager.start();
nodeManager.awaitLiveStatus();
nodeManager.stop();
synchronized (failbackCheckerGuard) {
if (cancelFailBackChecker || !sharedStoreSlavePolicy.isRestartBackup())
return;
activeMQServer.setHAPolicy(sharedStoreSlavePolicy);
logger.debug(activeMQServer + "::Starting backup node now after failback");
activeMQServer.start();
}
} catch (Exception e) {
ActiveMQServerLogger.LOGGER.warn(e.getMessage(), e);
ActiveMQServerLogger.LOGGER.serverRestartWarning();
}
}
});
t.start();
}
}
} catch (Exception e) {
ActiveMQServerLogger.LOGGER.serverRestartWarning(e);
}
}
}
}