/*
* JBoss, Home of Professional Open Source
* Copyright 2006, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* (C) 2005-2006,
* @author JBoss Inc.
*/
/*
* Copyright (C) 1999-2001 by HP Bluestone Software, Inc. All rights Reserved.
*
* HP Arjuna Labs,
* Newcastle upon Tyne,
* Tyne and Wear,
* UK.
*
* $Id: RecoveryManagerImple.java 2342 2006-03-30 13:06:17Z $
*/
package com.arjuna.ats.internal.arjuna.recovery;
import java.io.IOException;
import java.util.Vector;
import com.arjuna.ats.arjuna.common.recoveryPropertyManager;
import com.arjuna.ats.arjuna.exceptions.FatalError;
import com.arjuna.ats.arjuna.logging.tsLogger;
import com.arjuna.ats.arjuna.recovery.RecoveryManager;
import com.arjuna.ats.arjuna.recovery.RecoveryModule;
/**
* The RecoveryManagerImple - does the real work. Currently we can have only one
* of these per node, so each instance checks it's the only one running. If it
* isn't it will kill itself before doing any work.
*/
public class RecoveryManagerImple
{
private PeriodicRecovery _periodicRecovery = null;
private RecActivatorLoader _recActivatorLoader = null;
/**
* Does the work of setting up crash recovery.
*
* @param threaded
* if <code>true</code> then the manager will start a separate
* thread to run recovery periodically.
*/
public RecoveryManagerImple (boolean threaded)
{
// by default we do not use a socket based listener,
// but it can be turned on if not required.
boolean useListener = recoveryPropertyManager.getRecoveryEnvironmentBean().isRecoveryListener();
/*
* Check whether there is a recovery daemon running - only allow one per
* object store
*
* Note: this does not actually check if a recovery manager is running for the same ObjectStore,
* only if one is on the same port as our configuration. Thus it's not particularly robust.
* TODO: add a lock file to the ObjectStore as a belt and braces approach?
*
* This check works by trying to bind the server socket, so don't do it if we are running local only
* (yup, that means there is a greater chance of winding up with more than one recovery manager if
* we are running without a listener. See comment on robustness and file locking.)
*/
if (useListener && isRecoveryManagerEndPointInUse())
{
try
{
tsLogger.i18NLogger.fatal_recovery_fail(RecoveryManager.getRecoveryManagerHost().getHostAddress(),
Integer.toString(RecoveryManager.getRecoveryManagerPort()));
}
catch (Throwable t)
{
tsLogger.i18NLogger.fatal_recovery_fail("unknown", "unknown");
}
throw new FatalError("Recovery manager already active (or recovery port and address are in use)!");
}
// start the activator recovery loader
_recActivatorLoader = new RecActivatorLoader();
_recActivatorLoader.startRecoveryActivators();
// start the periodic recovery thread
// (don't start this until just about to go on to the other stuff)
_periodicRecovery = new PeriodicRecovery(threaded, useListener);
/*
* Start the expiry scanner
*
* This has to happen after initiating periodic recovery, because periodic recovery registers record types used
* by the expiry scanner
*/
ExpiredEntryMonitor.startUp();
try
{
if (tsLogger.logger.isInfoEnabled())
{
if(useListener)
{
tsLogger.i18NLogger.info_recovery_socketready( Integer.toString(_periodicRecovery.getServerSocket().getLocalPort()));
}
else
{
tsLogger.logger.debug("RecoveryManagerImple is ready. Socket listener is turned off.");
}
}
}
catch (IOException ex) {
tsLogger.i18NLogger.warn_recovery_RecoveryManagerImple_2(ex);
}
}
public final void scan ()
{
_periodicRecovery.doWork();
}
public final void addModule (RecoveryModule module)
{
_periodicRecovery.addModule(module);
}
public final void removeModule (RecoveryModule module, boolean waitOnScan)
{
_periodicRecovery.removeModule(module, waitOnScan);
}
public final void removeAllModules (boolean waitOnScan)
{
_periodicRecovery.removeAllModules(waitOnScan);
}
public final Vector<RecoveryModule> getModules ()
{
return _periodicRecovery.getModules();
}
public void start ()
{
if (!_periodicRecovery.isAlive())
{
_periodicRecovery.start();
}
}
/**
* stop the recovery manager
* @param async false means wait for any recovery scan in progress to complete
*/
public void stop (boolean async)
{
// must ensure we clean up dependent threads
ExpiredEntryMonitor.shutdown();
_periodicRecovery.shutdown(async);
}
/**
* Suspend the recovery manager. If the recovery manager is in the process of
* doing recovery scans then it will be suspended afterwards, in order to
* preserve data integrity.
*
* @param async false means wait for the recovery manager to finish any scans before returning.
*/
public PeriodicRecovery.Mode trySuspendScan (boolean async)
{
return _periodicRecovery.suspendScan(async);
}
public void resumeScan ()
{
_periodicRecovery.resumeScan();
}
/**
* wait for the recovery implementation to be shut down.
*/
public void waitForTermination ()
{
try
{
_periodicRecovery.join();
}
catch (final Exception ex)
{
}
}
/**
* Test whether the recovery manager (RM) port and address are available - if not assume that another
* recovery manager is already active.
*
* Ideally this method needs to discover whether or not another RM is already monitoring the object store
*
* @return true if the RM port and address are in use
*/
private final boolean isRecoveryManagerEndPointInUse ()
{
/*
* attempt to create the server socket. If an exception is thrown then some other
* process is using the RM endpoint
*/
if(_periodicRecovery != null) {
return _periodicRecovery.getMode() != PeriodicRecovery.Mode.TERMINATED;
} else {
return false;
}
}
}