/*
* 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) 2002,
*
* Arjuna Technologies Limited,
* Newcastle upon Tyne,
* Tyne and Wear,
* UK.
*
* $Id: ActivityReaper.java,v 1.6 2005/05/19 12:13:18 nmcl Exp $
*/
package com.arjuna.mwlabs.wsas.activity;
import com.arjuna.mw.wsas.logging.wsasLogger;
import com.arjuna.mw.wsas.UserActivityFactory;
import com.arjuna.mw.wsas.status.*;
import com.arjuna.mw.wsas.completionstatus.*;
import com.arjuna.mw.wsas.common.Environment;
import com.arjuna.ats.internal.arjuna.template.OrderedList;
import com.arjuna.ats.internal.arjuna.template.OrderedListIterator;
/**
* Class to record activities with non-zero timeout values, and
* class to implement a activity reaper thread which terminates
* these activities once their timeout elapses.
*
* @author Mark Little (mark@arjuna.com)
* @version $Id: ActivityReaper.java,v 1.6 2005/05/19 12:13:18 nmcl Exp $
* @since 1.0.
*/
public class ActivityReaper
{
public static final String NORMAL = "NORMAL";
public static final String DYNAMIC = "DYNAMIC";
public final long checkingPeriod ()
{
return _checkPeriod;
}
/*
* Should be no need to protect with a mutex since only one thread
* is ever doing the work.
*/
/**
* Only check for one at a time to prevent starvation.
*
* Timeout is given in milliseconds.
*
* @message com.arjuna.mwlabs.wsas.activity.ActivityReaper_1 [com.arjuna.mwlabs.wsas.activity.ActivityReaper_1] - ActivityReaper: could not terminate.
*/
public final boolean check (long timeout)
{
if (_list == null)
return true;
OrderedListIterator iter = new OrderedListIterator(_list);
ReaperElement e = null;
while ((e = (ReaperElement) iter.iterate()) != null)
{
if (timeout >= e._absoluteTimeout)
break;
else
{
iter = null;
return true;
}
}
iter = null;
if (e != null)
{
/*
* Only force rollback if the activity is still running.
*/
Status status = com.arjuna.mw.wsas.status.Unknown.instance();
try
{
status = e._activity.status();
}
catch (Exception ex)
{
}
if (status instanceof Active)
{
/*
* If this is a local activity, then we can roll it
* back completely. Otherwise, just mark it as rollback only.
*/
boolean problem = false;
try
{
try
{
/*
* TODO
*
* Everything works on thread-to-activity association
* so we can't just tell an activity to end: we have
* to resume it and then tell it to end. The reason
* is that all HLS-es assume that the invoking thread
* has the current context on it.
*/
// e._activity.end(Failure.instance());
UserActivityFactory.userActivity().resume(new ActivityHierarchyImple(e._activity));
UserActivityFactory.userActivity().end(Failure.instance());
UserActivityFactory.userActivity().suspend();
}
catch (Exception ex)
{
problem = true;
}
}
catch (Exception ex2)
{
problem = true;
}
if (problem)
{
boolean error = false;
try
{
e._activity.setCompletionStatus(FailureOnly.instance());
}
catch (Exception ex3)
{
error = true;
}
if (error)
{
wsasLogger.arjLoggerI18N.warn("com.arjuna.mwlabs.wsas.activity.ActivityReaper_1");
}
}
}
_list.remove(e);
}
System.gc(); // do some garbage collection while we're at it!
return true;
}
/**
* @return the number of items in the reaper's list.
*/
public final synchronized long numberOfActivities ()
{
return ((_list == null) ? 0 : _list.size());
}
/**
* timeout is given in seconds, but we work in milliseconds.
*/
public final synchronized boolean insert (ActivityImple activity, int timeout)
{
/*
* Ignore if the timeout is zero, since this means the activity
* should never timeout.
*/
if (timeout == 0)
return true;
ActivityReaper._lifetime += timeout;
/*
* If the timeout for this activity is less than the
* current timeout for the reaper thread (or one is not set for
* the reaper thread) then use that timeout and interrupt the thread
* to get it to recheck.
*/
if ((timeout < _checkPeriod) || (_checkPeriod == Long.MAX_VALUE))
{
_checkPeriod = timeout*1000; // convert to milliseconds!
ActivityReaper._reaperThread.interrupt();
}
ReaperElement e = new ReaperElement(activity, timeout);
if ((_list != null) && _list.insert(e))
return true;
else
{
e = null;
return false;
}
}
public final synchronized boolean remove (ActivityImple act)
{
if ((_list == null) || (act == null))
return false;
ReaperElement e = null;
OrderedListIterator iter = new OrderedListIterator(_list);
boolean result = false;
boolean found = false;
while (((e = (ReaperElement) iter.iterate()) != null) && !found)
{
try
{
found = e._activity.equals(act);
}
catch (Exception e2)
{
break;
}
}
iter = null;
if (found)
{
result = _list.remove(e);
e = null;
}
return result;
}
/**
* Currently we let the reaper thread run at same priority as other
* threads. Could get priority from environment.
*/
public static synchronized ActivityReaper create (long checkPeriod)
{
if (ActivityReaper._theReaper == null)
{
String mode = System.getProperty(Environment.REAPER_MODE);
if (mode != null)
{
if (mode.compareTo(ActivityReaper.DYNAMIC) == 0)
ActivityReaper._dynamic = true;
}
if (!ActivityReaper._dynamic)
{
String timeoutEnv = System.getProperty(Environment.REAPER_TIMEOUT);
if (timeoutEnv != null)
{
Long l = null;
try
{
l = new Long(timeoutEnv);
checkPeriod = l.longValue();
l = null;
}
catch (NumberFormatException e)
{
e.printStackTrace();
}
}
}
else
checkPeriod = Long.MAX_VALUE;
ActivityReaper._theReaper = new ActivityReaper(checkPeriod);
_reaperThread = new ReaperThread(ActivityReaper._theReaper);
// _reaperThread.setPriority(Thread.MIN_PRIORITY);
_reaperThread.setDaemon(true);
_reaperThread.start();
}
return ActivityReaper._theReaper;
}
public static synchronized ActivityReaper create ()
{
return create(ActivityReaper.defaultCheckPeriod);
}
public static synchronized ActivityReaper activityReaper ()
{
return activityReaper(false);
}
/*
* If parameter is true then do a create.
*/
public static synchronized ActivityReaper activityReaper (boolean createReaper)
{
if (createReaper)
return create();
else
return _theReaper;
}
/*
* Don't bother synchronizing as this is only an estimate anyway.
*/
public static final long activityLifetime ()
{
return ActivityReaper._lifetime;
}
public static final long defaultCheckPeriod = 120000; // in milliseconds
ActivityReaper (long checkPeriod)
{
_list = new OrderedList();
_checkPeriod = checkPeriod;
if (_list == null)
{
throw new OutOfMemoryError();
}
}
static final void reset ()
{
_theReaper = null;
}
private OrderedList _list;
private long _checkPeriod;
private static ActivityReaper _theReaper = null;
private static ReaperThread _reaperThread = null;
private static boolean _dynamic = false;
private static long _lifetime = 0;
}