/*
* 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: PeriodicRecovery.java 2342 2006-03-30 13:06:17Z $
*/
package com.arjuna.ats.internal.arjuna.recovery;
import java.io.IOException;
import java.net.ServerSocket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Vector;
import com.arjuna.ats.arjuna.common.recoveryPropertyManager;
import com.arjuna.ats.arjuna.logging.tsLogger;
import com.arjuna.ats.arjuna.recovery.RecoveryManager;
import com.arjuna.ats.arjuna.recovery.RecoveryModule;
import com.arjuna.ats.arjuna.utils.Utility;
/**
* Threaded object to perform the periodic recovery. Instantiated in
* the RecoveryManager. The work is actually completed by the recovery
* modules. These modules are dynamically loaded. The modules to load
* are specified by properties beginning with "RecoveryExtension"
* <P>
* n.b. recovery scans may be performed by this object (it is a thread and may be started as a background task)
* and by other ad hoc threads
* @author
* @version $Id: PeriodicRecovery.java 2342 2006-03-30 13:06:17Z $
*/
public class PeriodicRecovery extends Thread
{
/***** public API *****/
/**
* state values indicating whether or not some thread is currently scanning. used to define values of field
* {@link PeriodicRecovery#_currentStatus}
*/
public static enum Status
{
/**
* state value indicating that no thread is scanning
*/
INACTIVE,
/**
* state value indicating that some thread is scanning.
* n.b. the scanning thread may not be the singleton PeriodicRecovery thread instance
*/
SCANNING
}
/**
* state values indicating operating mode of scanning process for ad hoc threads and controlling behaviour of
* singleton periodic recovery thread. used to define values of field {@link PeriodicRecovery#_currentMode}
*
* n.b. {@link PeriodicRecovery#_currentStatus} may not transition to state SCANNING when
* {@link PeriodicRecovery#_currentStatus} is in state SUSPENDED or TERMINATED. However, if a scan is in
* progress when {@link PeriodicRecovery#_currentMode} transitions to state SUSPENDED or TERMINATED
* {@link PeriodicRecovery#_currentStatus} may (temporarily) remain in state SCANNING before transitioning
* to state INACTIVE.
*/
public static enum Mode
{
/**
* state value indicating that new scans may proceed
*/
ENABLED,
/**
* state value indicating that new scans may not proceed and the periodic recovery thread should suspend
*/
SUSPENDED,
/**
* state value indicating that new scans may not proceed and that the singleton
* PeriodicRecovery thread instance should exit if it is still running
*/
TERMINATED
}
/**
*
*
* @param threaded
* @param useListener if true, start a socket based listener.
*/
public PeriodicRecovery (boolean threaded, boolean useListener)
{
super("Periodic Recovery");
initialise();
// Load the recovery modules that actually do the work.
loadModules();
if (useListener)
{
try
{
_workerService = new WorkerService(this);
_listener = new Listener(getServerSocket(), _workerService);
_listener.setDaemon(true);
tsLogger.i18NLogger.info_recovery_PeriodicRecovery_13(_socket.getInetAddress().getHostAddress(),
Integer.toString(_socket.getLocalPort()));
}
catch (Exception ex) {
tsLogger.i18NLogger.warn_recovery_PeriodicRecovery_9(ex);
}
}
if (threaded)
{
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: starting background scanner thread");
}
start();
}
if(useListener && _listener != null)
{
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: starting listener worker thread");
}
_listener.start();
}
}
/**
* initiate termination of the periodic recovery thread and stop any subsequent scan requests from proceeding.
*
* this switches the recovery operation mode to TERMINATED. if a scan is in progress when this method is called
* and has not yet started phase 2 of its scan it will be forced to return before completing phase 2.
*
* @param async false if the calling thread should wait for any in-progress scan to complete before returning
*/
public void shutdown (boolean async)
{
// stop the lsitener from adding threads which can exercise the worker
if (_listener != null) {
_listener.stopListener();
}
synchronized (_stateLock) {
if (getMode() != Mode.TERMINATED) {
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: Mode <== TERMINATED");
}
setMode(Mode.TERMINATED);
_stateLock.notifyAll();
}
if (!async) {
// synchronous, so we keep waiting until the currently active scan stops or scanning
// changes to TERMINATED
while (getStatus() == Status.SCANNING) {
try {
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: shutdown waiting for scan to end");
}
_stateLock.wait();
} catch(InterruptedException ie) {
// just ignore and retest condition
}
}
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: shutdown scan wait complete");
}
}
}
// if the shutdown is synchronous then make sure the periodic recovery thread really has stopped running
if (!async && this.isAlive()) {
try {
this.join();
} catch (InterruptedException e) {
// ignore
}
}
}
/**
* make all scanning operations suspend.
*
* This switches the recovery operation mode to SUSPENDED. Any attempt to start a new scan either by an ad hoc
* threads or by the periodic recovery thread will suspend its thread until the mode changes. If a scan is in
* progress when this method is called it will complete its scan without suspending.
*
* @param async false if the calling thread should wait for any in-progress scan to complete before returning
* @return the previous mode before attempting the suspend
*/
public Mode suspendScan (boolean async)
{
synchronized (_stateLock)
{
// only switch and kick everyone if we are currently ENABLED
Mode currentMode = getMode();
if (currentMode == Mode.ENABLED) {
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: Mode <== SUSPENDED");
}
setMode(Mode.SUSPENDED);
_stateLock.notifyAll();
}
if (!async) {
// synchronous, so we keep waiting until the currently active scan stops
while (getStatus() == Status.SCANNING) {
try {
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: suspendScan waiting for scan to end");
}
_stateLock.wait();
} catch(InterruptedException ie) {
// just ignore and retest condition
}
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: suspendScan scan wait compelete");
}
}
}
return currentMode;
}
}
/**
* resume scanning operations
*
* This switches the recovery operation mode from SUSPENDED to RESUMED. Any threads which suspended when
* they tried to start a scan will be woken up by this transition.
*/
public void resumeScan ()
{
synchronized (_stateLock)
{
if (getMode() == Mode.SUSPENDED) {
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: Mode <== ENABLED");
}
setMode(Mode.ENABLED);
_stateLock.notifyAll();
}
}
}
/**
*
* @return a bound server socket corresponding to the recovery manager
* @throws IOException if the host name is unknown or the endpoint has already been bound
*/
public ServerSocket getServerSocket () throws IOException
{
synchronized (_socketLock)
{
if (_socket == null)
{
_socket = new ServerSocket(RecoveryManager.getRecoveryManagerPort(), Utility.BACKLOG, RecoveryManager.getRecoveryManagerHost());
recoveryPropertyManager.getRecoveryEnvironmentBean().setRecoveryPort(_socket.getLocalPort());
}
return _socket;
}
}
/**
* Implements the background thread which performs the periodic recovery
*/
public void run ()
{
doInitialWait();
boolean finished = false;
do
{
boolean workToDo = false;
// ok, get to the point where we are ready to start a scan
synchronized(_stateLock) {
if (getStatus() == Status.SCANNING) {
// need to wait for some other scan to finish
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: background thread waiting on other scan");
}
doScanningWait();
// we don't wait around if a worker scan request has just come in
if (getMode() == Mode.ENABLED && !_workerScanRequested) {
// the last guy just finished scanning so we ought to wait a bit rather than just
// pile straight in to do some work
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: background thread backing off");
}
doPeriodicWait();
// if we got told to stop then do so
finished = (getMode() == Mode.TERMINATED);
}
} else {
// status == INACTIVE so we can go ahead and scan if scanning is enabled
switch (getMode()) {
case ENABLED:
// ok grab our chance to be the scanning thread
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: background thread Status <== SCANNING");
}
setStatus(Status.SCANNING);
// must kick any other waiting threads
_stateLock.notifyAll();
workToDo = true;
break;
case SUSPENDED:
// we need to wait while we are suspended
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: background thread wait while SUSPENDED");
}
doSuspendedWait();
// we come out of here with the lock and either ENABLED or TERMINATED
finished = (getMode() == Mode.TERMINATED);
break;
case TERMINATED:
finished = true;
break;
}
}
}
// its ok to start work if requested -- we cannot be stopped now by a mode change to SUSPEND
// or TERMINATE until we get through phase 1 and maybe phase 2 if we are lucky
if (workToDo) {
// ok it is now this thread's turn to run a scan. before starting we check if there is a
// worker waiting and reset the waiting flag. we will check again after the scan has
// completed to see if a worker request has come in after starting this scan.
// if so we avoid notifying the worker ensuring a requst is only confirmed when a
// full scan has happened afetr the request was made
boolean notifyRequired;
synchronized(_stateLock) {
notifyRequired = _workerScanRequested;
_workerScanRequested = false;
}
// we are in state SCANNING so actually do the scan
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: background thread scanning");
}
doWorkInternal();
// clear the SCANNING state now we have done
synchronized(_stateLock) {
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: background thread Status <== INACTIVE");
}
setStatus(Status.INACTIVE);
// must kick any other waiting threads
_stateLock.notifyAll();
// check if we need to notify a listener worker that we just finished a scan
if (notifyRequired && !_workerScanRequested) {
notifyWorker();
}
if (getMode() == Mode.ENABLED && !_workerScanRequested) {
// we managed a full scan and scanning is still enabled
// so wait a bit before the next attempt
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: background thread backing off");
}
doPeriodicWait();
}
finished = (getMode() == Mode.TERMINATED);
}
}
} while (!finished);
// make sure the worker thread is not wedged waiting for a scan to complete
synchronized(_stateLock) {
if (_workerScanRequested) {
notifyWorker();
}
}
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: background thread exiting");
}
}
/**
* Perform a recovery scan on all registered modules.
*
* <b>Caveats:</b> if a scan is already in progress this method will wait for it to complete otherwise it will
* perform its own scan before returning. If scanning is suspended this will require waiting for scanning
* to resume.
*/
public final void doWork ()
{
boolean workToDo = false;
synchronized(_stateLock) {
if (getMode() == Mode.SUSPENDED) {
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: ad hoc thread wait while SUSPENDED");
}
doSuspendedWait();
}
// no longer SUSPENDED -- retest in case we got TERMINATED
if (getMode() == Mode.TERMINATED) {
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: ad hoc thread scan TERMINATED");
}
} else {
// ok scanning must be enabled -- see if we can start a scan or whether we have to wait on another one
if (getStatus() == Status.SCANNING) {
// just wait for the other scan to finish
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: ad hoc thread waiting on other scan");
}
doScanningWait();
} else {
// ok grab our chance to start a scan
setStatus(Status.SCANNING);
// must kick any other waiting threads
_stateLock.notifyAll();
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: ad hoc thread Status <== SCANNING");
}
workToDo = true;
}
}
}
if (workToDo) {
// ok it is now this thread's turn to run a scan. before starting we check if there is a
// worker waiting and reset the waiting flag. we will check again after the scan has
// completed to see if a worker request has come in after starting this scan.
// if so we avoid notifying the worker ensuring a request is only confirmed when a
// full scan has happened after the request was made
boolean notifyRequired;
synchronized(_stateLock) {
notifyRequired = _workerScanRequested;
_workerScanRequested = false;
}
// ok to start work -- we cannot be stopped now by a mode change to SUSPEND or TERMINATE
// until we get through phase 1 and maybe phase 2 if we are lucky
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: ad hoc thread scanning");
}
doWorkInternal();
// clear the scan for some other thread to have a go
synchronized(_stateLock) {
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: ad hoc thread Status <== INACTIVE");
}
setStatus(Status.INACTIVE);
// must kick any other waiting threads
_stateLock.notifyAll();
// notify the worker if it was waiting before we started the scan otherwise just leave it to
// be notified when the next scan finishes.
if (notifyRequired && !_workerScanRequested) {
notifyWorker();
}
}
}
}
/**
* called by the listener worker to wake the periodic recovery thread and get it to start a scan if one
* is not already in progress
*/
public void wakeUp()
{
synchronized (_stateLock) {
_workerScanRequested = true;
// wake up the periodic recovery thread if no scan is in progress
if (getStatus() != Status.SCANNING) {
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: listener worker interrupts background thread");
}
_stateLock.notifyAll();
}
}
}
/**
* Add the specified module to the end of the recovery module list.
* There is no way to specify relative ordering of recovery modules
* with respect to modules loaded via the property file.
*
* @param module The module to append.
*/
public final void addModule (RecoveryModule module)
{
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: adding module " + module.getClass().getName());
}
_recoveryModules.add(module);
}
/**
* remove a recovery module from the recovery modules list
* @param module the module to be removed
* @param waitOnScan true if the remove operation should wait for any in-progress scan to complete
*/
public final void removeModule (RecoveryModule module, boolean waitOnScan)
{
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: removing module " + module.getClass().getName());
}
if (waitOnScan) {
// make sure any scan which might be using the module has completed
synchronized (_stateLock) {
doScanningWait();
}
}
// now remove it.
_recoveryModules.remove(module);
}
/**
* Remove all modules.
*
* @param waitOnScan true if the remove operation should wait for any in-progress scan to complete.
*/
public final void removeAllModules (boolean waitOnScan)
{
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: removing all modules.");
}
if (waitOnScan) {
// make sure any scan which might be using the module has completed
synchronized (_stateLock) {
doScanningWait();
}
}
_recoveryModules.clear();
}
/**
* return a copy of the current recovery modules list
*
* @return a copy of the the recovery modules list.
*/
public final Vector<RecoveryModule> getModules ()
{
// return a copy of the modules list so that clients are not affected by dynamic modifications to the list
// synchronize so that we don't copy in the middle of an add or remove
synchronized (_recoveryModules) {
return new Vector<RecoveryModule>(_recoveryModules);
}
}
/*
* debugging aid
*/
public Listener getListener()
{
return _listener;
}
/***** private implementation *****/
/**
* fetch the current activity status either INACTIVE or SCANNING
*
* <b>Caveats:</b> must only be called while synchronized on {@link PeriodicRecovery#_stateLock}
* @return INACTIVE if no scan is in progress or SCANNING if some thread is performing a scan
*/
private Status getStatus ()
{
return _currentStatus;
}
/**
* fetch the current recovery operation mode either ENABLED, SUSPENDED or TERMINATED
*
* <b>Caveats:</b> must only be called while synchronized on {@link PeriodicRecovery#_stateLock}
* @return the current recovery operation mode
*/
public Mode getMode ()
{
return _currentMode;
}
/**
* set the current activity status
* @param status the new status to be used
*/
private void setStatus (Status status)
{
_currentStatus = status;
}
/**
* set the current recovery operation mode
* @param mode the new mode to be used
*/
private void setMode (Mode mode)
{
_currentMode = mode;
}
/**
* wait for the required backoff period or less if the scanning status or scan mode changes
*
* <b>Caveats:</b> this must only be called when synchronized on {@link PeriodicRecovery#_stateLock} and when
* _currentStatus is SCANNING and _currentMode is ENABLED
*/
private void doBackoffWait()
{
try {
_stateLock.wait(_backoffPeriod * 1000L);
} catch (InterruptedException e) {
// we can ignore this exception
}
}
/**
* wait for the required recovery period or less if the scanning status or scan mode changes
*
* <b>Caveats:</b> this must only be called when synchronized on {@link PeriodicRecovery#_stateLock} and when
* _currentStatus is INACTIVE and _currentMode is ENABLED
*/
private void doPeriodicWait()
{
try {
_stateLock.wait(_recoveryPeriod * 1000L);
} catch (InterruptedException e) {
// we can ignore this exception
}
}
/**
* wait for the initial required recovery delay or less if the scanning status or scan mode changes
*/
private void doInitialWait()
{
if (_periodicRecoveryInitilizationOffset > 0) {
try {
synchronized (_stateLock) {
_stateLock.wait(_periodicRecoveryInitilizationOffset * 1000L);
}
} catch (InterruptedException e) {
// we can ignore this exception
}
}
}
/**
* wait until the we move out of SUSPENDED mode
*
* <b>Caveats:</b> this must only be called when synchronized on {@link PeriodicRecovery#_stateLock}
*/
private void doSuspendedWait()
{
while (getMode() == Mode.SUSPENDED) {
try {
_stateLock.wait();
} catch (InterruptedException e) {
// we can ignore this exception
}
}
}
/**
* wait until some other thread stops scanning
*
* <b>Caveats:</b> this must only be called when synchronized on {@link PeriodicRecovery#_stateLock} and when
* _currentStatus is SCANNING
*/
private void doScanningWait()
{
while (getStatus() == Status.SCANNING) {
try {
_stateLock.wait();
} catch (InterruptedException e) {
// we can ignore this exception
}
}
}
/**
* start performing a scan continuing to completion unless we are terminating
*
* <b>Caveats:</b> this must only be called when _currentStatus is SCANNING. on return _currentStatus is always
* still SCANNING
*/
private void doWorkInternal()
{
// n.b. we only get here if status is SCANNING
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("Periodic recovery first pass at "+_theTimestamper.format(new Date()));
}
// n.b. this works on a copy of the modules list so it is not affected by
// dynamic updates in the middle of a scan, ensuring first+second pass happen
// for the same stable set of modules.
Vector copyOfModules = getModules();
Enumeration modules = copyOfModules.elements();
while (modules.hasMoreElements())
{
RecoveryModule m = (RecoveryModule) modules.nextElement();
// we need to ensure we use the class loader context of the recovery module while we are executing
// its methods
ClassLoader cl = switchClassLoader(m);
try {
m.periodicWorkFirstPass();
} finally {
restoreClassLoader(cl);
}
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug(" ");
}
}
// take the lock again so we can do a backoff wait on it
synchronized (_stateLock) {
// we have to wait for a bit to avoid catching (too many)
// transactions etc. that are really progressing quite happily
doBackoffWait();
// we carry on scanning even if scanning is SUSPENDED because the suspending thread
// might be waiting on us to complete and we don't want to risk deadlocking it by waiting
// here for a resume.
// if we have been TERMINATED we bail out now
// n.b. if we give up here the caller is responsible for clearing the active scan
if (getMode() == Mode.TERMINATED) {
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: scan TERMINATED at phase 1");
}
return;
}
}
// move on to phase 2
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("Periodic recovery second pass at "+_theTimestamper.format(new Date()));
}
modules = copyOfModules.elements();
while (modules.hasMoreElements())
{
RecoveryModule m = (RecoveryModule) modules.nextElement();
ClassLoader cl = switchClassLoader(m);
try {
m.periodicWorkSecondPass();
} finally {
restoreClassLoader(cl);
}
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug(" ");
}
}
// n.b. the caller is responsible for clearing the active scan
}
/**
* notify the listener worker that a scan has completed
*
* <b>Caveats:</b> this must only be called when synchronized on {@link PeriodicRecovery#_stateLock} at the point
* where Status transitions from SCANNING to INACTIVE
*/
private void notifyWorker()
{
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery: scan thread signals listener worker");
}
if(_workerService != null)
{
_workerService.notifyDone();
}
_workerScanRequested = false;
}
/**
* install the classloader associated with some specific recovery module as the current thread's class loader
*
* this avoids a problem where the background periodic recovery thread can see the same class as the recovery
* module's class loader, specifically where a the recovery module resides in a sar (e.g. the XTS code).
* If class with name "A" is loaded via the background thread class loader as A' and used to create instance
* a' then a cast expression in th erecovery code of the form (A)a' will try to resolve a' against version
* A'' loaded via the sar loader and get a class cast exception.
*
* @param rm the recovery module whose class loader is to be installed as the new thread class loader
* @return the class loader currently installed as the thread class loader
*/
private ClassLoader switchClassLoader(RecoveryModule rm)
{
Thread currentThread = Thread.currentThread();
ClassLoader cl = currentThread.getContextClassLoader();
currentThread.setContextClassLoader(rm.getClass().getClassLoader());
return cl;
}
/**
* restore the current thread's classloader
*
* @param cl the class loader to be set as the current thread class loader
*/
private void restoreClassLoader(ClassLoader cl)
{
Thread currentThread = Thread.currentThread();
currentThread.setContextClassLoader(cl);
}
/**
* Load recovery modules prior to starting to recovery. These are loaded in list iteration order.
*/
private void loadModules ()
{
_recoveryModules.addAll(recoveryPropertyManager.getRecoveryEnvironmentBean().getRecoveryModules());
}
/**
* initialise the periodic recovery instance to a suitable initial state
*/
private void initialise ()
{
setStatus(Status.INACTIVE);
setMode(Mode.ENABLED);
_recoveryPeriod = recoveryPropertyManager.getRecoveryEnvironmentBean().getPeriodicRecoveryPeriod();
if (_recoveryPeriod != _defaultRecoveryPeriod && tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("com.arjuna.ats.arjuna.recovery.PeriodicRecovery" +
": Recovery period set to " + _recoveryPeriod + " seconds");
}
_backoffPeriod = recoveryPropertyManager.getRecoveryEnvironmentBean().getRecoveryBackoffPeriod();
if (_backoffPeriod != _defaultBackoffPeriod && tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("PeriodicRecovery" +
": Backoff period set to " + _backoffPeriod + " seconds");
}
_periodicRecoveryInitilizationOffset = recoveryPropertyManager.getRecoveryEnvironmentBean().getPeriodicRecoveryInitilizationOffset();
}
// this refers to the modules specified in the recovery manager
// property file which are dynamically loaded.
/**
* list of instances of RecoiveryModule either loaded during startup as specified in the recovery manager
* property file or added dynamically by calls to addModule
*/
private final Vector<RecoveryModule> _recoveryModules = new Vector<RecoveryModule>();
/**
* time in seconds between the first and second pass in any given scan
*/
private int _backoffPeriod = 0;
/**
* time in seconds for which the periodic recovery thread waits between scan attempts
*/
private int _recoveryPeriod = 0;
/**
* Time in seconds for which the periodic recovery thread waits to start initial scan.
*/
private int _periodicRecoveryInitilizationOffset = 0;
/**
* default value for _backoffPeriod if not specified via RecoveryEnvironmentBean
*/
public static final int _defaultBackoffPeriod = 10;
/**
* default value for _recoveryPeriod if not specified via RecoveryEnvironmentBean
*/
public static final int _defaultRecoveryPeriod = 120;
/**
* lock controlling access to {@link PeriodicRecovery#_currentStatus}, {@link PeriodicRecovery#_currentMode} and
* {@link PeriodicRecovery#_workerScanRequested}
*/
private final Object _stateLock = new Object();
/**
* activity status indicating whether we IDLING or some thread is SCANNING
*/
private Status _currentStatus;
/**
* operating mode indicating whether scanning is ENABLED, SUSPENDED or TERMINATED
*/
private Mode _currentMode;
/**
* flag indicating whether the listener has prodded the recovery thread
*/
private boolean _workerScanRequested = false;
/**
* format for printing dates in log messages
*/
private SimpleDateFormat _theTimestamper = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss");
/**
* socket used by listener worker thread
*/
private ServerSocket _socket = null;
private final Object _socketLock = new Object();
/**
* listener thread running worker service
*/
private Listener _listener = null;
/**
* the worker service which handles requests via the listener socket
*/
private WorkerService _workerService = null;
/*
* Read the system properties to set the configurable options
*
* Note: if we start and stop the service then changes to the timeouts
* won't be reflected. We will need to modify this eventually.
*/
static
{
}
}