/*
* 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: ExpiredEntryMonitor.java 2342 2006-03-30 13:06:17Z $
*/
package com.arjuna.ats.internal.arjuna.recovery;
import java.text.*;
import java.util.Date;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Vector;
import com.arjuna.ats.arjuna.recovery.ExpiryScanner;
import com.arjuna.ats.arjuna.recovery.RecoveryEnvironment;
import com.arjuna.ats.arjuna.common.arjPropertyManager;
import com.arjuna.ats.arjuna.logging.tsLogger;
import com.arjuna.ats.arjuna.logging.FacilityCode;
import com.arjuna.common.util.logging.*;
/**
* Threaded object to run {@link ExpiryScanner} implementations to scan
* the action store to remove items deemed expired by some algorithm.
* Performs a scan at interval defined by the property
* com.arjuna.ats.arjuna.recovery.expiryScanInterval (hours).
* ExpiryScanner implementations are registered as properties beginning with
* "com.arjuna.ats.arjuna.recovery.expiryScanner".
* <P>
* Singleton, instantiated in the RecoveryManager.
* <P>
*
* @message com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_1 [com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_1] - Expiry scan interval set to {0} seconds
* @message com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_2 [com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_2] - Expiry scan zero - not scanning
* @message com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_3 [com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_3] - No Expiry scanners loaded - not scanning
* @message com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_4 [com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_4] - ExpiredEntryMonitor - constructed
* @message com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_5 [com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_5] - ExpiredEntryMonitor - no scans on first iteration
* @message com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_6 [com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_6] - Loading expiry scanner {0}
* @message com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_7 [com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_7] - Attempt to load expiry scanner with null class name!
* @message com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_8 [com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_8] - Loading expiry scanner {0}
* @message com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_9 [com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_9] - Expiry scanner {0} does not conform to ExpiryScanner interface
* @message com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_10 [com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_10] - Loading expiry scanner: could not find class {0}
* @message com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_11 [com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_11] - {0} has inappropriate value ({1})
*/
public class ExpiredEntryMonitor extends Thread
{
/**
* Start the monitor thread, if the properties make it appropriate
*/
public static boolean startUp()
{
// ensure singleton
if ( _started )
{
return false;
}
/*
* Read the system properties to set the configurable options
*/
String scanIntervalString =
arjPropertyManager.propertyManager.getProperty( RecoveryEnvironment.EXPIRY_SCAN_INTERVAL );
if ( scanIntervalString != null )
{
try
{
Integer scanIntervalInteger = new Integer(scanIntervalString);
// convert to seconds
_scanIntervalSeconds = scanIntervalInteger.intValue() * 60 * 60;
if (tsLogger.arjLoggerI18N.debugAllowed())
{
tsLogger.arjLoggerI18N.debug( DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
FacilityCode.FAC_CRASH_RECOVERY,
"com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_1",
new Object[]{Integer.toString(_scanIntervalSeconds)});
}
}
catch ( NumberFormatException e )
{
if (tsLogger.arjLoggerI18N.isWarnEnabled())
{
tsLogger.arjLoggerI18N.warn("com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_11",
new Object[]{RecoveryEnvironment.EXPIRY_SCAN_INTERVAL,
scanIntervalString});
}
}
}
if ( _scanIntervalSeconds == 0 )
{
// no scanning wanted
if (tsLogger.arjLoggerI18N.debugAllowed())
{
tsLogger.arjLoggerI18N.debug( DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
FacilityCode.FAC_CRASH_RECOVERY,
"com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_2");
}
return false;
}
// is it being used to skip the first time
if ( _scanIntervalSeconds < 0 )
{
notSkipping = false;
_scanIntervalSeconds = - _scanIntervalSeconds;
}
loadScanners();
if ( _expiryScanners.size() == 0 )
{
// nothing to do
if (tsLogger.arjLoggerI18N.isDebugEnabled())
{
tsLogger.arjLoggerI18N.debug( DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
FacilityCode.FAC_CRASH_RECOVERY,
"com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_3");
}
return false;
}
// create, and thus launch the monitor
_theInstance = new ExpiredEntryMonitor();
return _started;
}
public static void shutdown()
{
_started = false;
_expiryScanners = new Vector();
_scanIntervalSeconds = 12 * 60 * 60;
notSkipping = true;
_theInstance.interrupt();
_theInstance = null;
}
private ExpiredEntryMonitor()
{
if (tsLogger.arjLoggerI18N.isDebugEnabled())
{
tsLogger.arjLoggerI18N.debug( DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PRIVATE,
FacilityCode.FAC_CRASH_RECOVERY,
"com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_4");
}
_started = true;
this.setDaemon(true);
start();
}
/**
* Start the background thread to perform the periodic scans
*/
public void run()
{
while( true )
{
if (tsLogger.arjLogger.isInfoEnabled())
{
tsLogger.arjLogger.info("--- ExpiredEntryMonitor ----" +
_theTimestamper.format(new Date()) + "----" );
}
if ( notSkipping )
{
Enumeration scanners = _expiryScanners.elements();
while ( scanners.hasMoreElements() )
{
ExpiryScanner m = (ExpiryScanner)scanners.nextElement();
m.scan();
if (tsLogger.arjLogger.isDebugEnabled())
{
tsLogger.arjLogger.debug( DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
FacilityCode.FAC_CRASH_RECOVERY," ");
// bit of space if detailing
}
}
}
else
{
if (tsLogger.arjLoggerI18N.isInfoEnabled())
{
tsLogger.arjLoggerI18N.info("com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_5");
}
notSkipping = true;
}
// wait for a bit to avoid catching (too many) transactions etc. that
// are really progressing quite happily
try
{
Thread.sleep( _scanIntervalSeconds * 1000 );
}
catch ( InterruptedException e1 )
{
break;
}
if ( !_started )
return;
}
}
private static void loadScanners()
{
// search our properties
Properties properties = arjPropertyManager.propertyManager.getProperties();
if (properties != null)
{
Enumeration names = properties.propertyNames();
while (names.hasMoreElements())
{
String propertyName = (String) names.nextElement();
if ( propertyName.startsWith(RecoveryEnvironment.SCANNER_PROPERTY_PREFIX) )
{
loadScanner( properties.getProperty(propertyName) );
}
}
}
}
private static void loadScanner( String className )
{
if (tsLogger.arjLoggerI18N.isDebugEnabled())
{
tsLogger.arjLoggerI18N.debug( DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PRIVATE,
FacilityCode.FAC_CRASH_RECOVERY,
"com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_6",
new Object[]{className});
}
if (className == null)
{
if (tsLogger.arjLoggerI18N.isWarnEnabled())
tsLogger.arjLoggerI18N.warn("com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_7");
return;
}
else
{
try
{
Class c = Thread.currentThread().getContextClassLoader().loadClass( className );
try
{
ExpiryScanner m = (ExpiryScanner) c.newInstance();
if ( m.toBeUsed() )
{
_expiryScanners.add( m );
}
else
{
if (tsLogger.arjLoggerI18N.isDebugEnabled())
{
tsLogger.arjLoggerI18N.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PRIVATE,
FacilityCode.FAC_CRASH_RECOVERY,
"com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_8",
new Object[]{className});
}
}
}
catch (ClassCastException e)
{
if (tsLogger.arjLoggerI18N.isWarnEnabled())
{
tsLogger.arjLoggerI18N.warn("com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_9",
new Object[]{className});
}
}
catch (IllegalAccessException e1)
{
if (tsLogger.arjLoggerI18N.isWarnEnabled())
{
tsLogger.arjLoggerI18N.warn("com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_6",
new Object[]{e1});
}
}
catch (InstantiationException e2)
{
if (tsLogger.arjLoggerI18N.isWarnEnabled())
{
tsLogger.arjLoggerI18N.warn("com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_6",
new Object[]{e2});
}
}
c = null;
}
catch (ClassNotFoundException e)
{
if (tsLogger.arjLoggerI18N.isWarnEnabled())
{
tsLogger.arjLoggerI18N.warn("com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor_10",
new Object[]{className});
}
}
}
}
private static boolean _started = false;
private static Vector _expiryScanners = new Vector();
private static int _scanIntervalSeconds = 12 * 60 * 60;
private static SimpleDateFormat _theTimestamper = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss");
private static boolean notSkipping = true;
private static ExpiredEntryMonitor _theInstance = null;
}