The great Interval revamp. Cleaner, better, easier to use.

There may be chapter 17 issues, but I'm fairly certain that if there are we can iron those out without changing the API.


git-svn-id: https://samskivert.googlecode.com/svn/trunk@1554 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
ray
2004-12-22 19:18:23 +00:00
parent 15bff32e71
commit 77f9c0fa11
4 changed files with 174 additions and 225 deletions
@@ -32,7 +32,6 @@ import com.samskivert.servlet.RedirectException;
import com.samskivert.servlet.util.CookieUtil;
import com.samskivert.servlet.util.RequestUtils;
import com.samskivert.util.Interval;
import com.samskivert.util.IntervalManager;
import com.samskivert.util.StringUtil;
/**
@@ -130,8 +129,8 @@ public class UserManager
}
// register a cron job to prune the session table every hour
Interval pruner = new Interval() {
public void intervalExpired (int id, Object arg)
_pruner = new Interval() {
public void expired ()
{
try {
_repository.pruneSessions();
@@ -141,8 +140,7 @@ public class UserManager
}
}
};
_prunerid = IntervalManager.register(pruner, SESSION_PRUNE_INTERVAL,
null, true);
_pruner.schedule(SESSION_PRUNE_INTERVAL, true);
}
/**
@@ -159,7 +157,7 @@ public class UserManager
public void shutdown ()
{
// cancel our session table pruning thread
IntervalManager.remove(_prunerid);
_pruner.cancel();
}
/**
@@ -321,8 +319,8 @@ public class UserManager
/** The user repository. */
protected UserRepository _repository;
/** The interval id for the user session pruning interval. */
protected int _prunerid = -1;
/** The interval for user session pruning. */
protected Interval _pruner;
/** The URL for the user login page. */
protected String _loginURL;
@@ -23,7 +23,6 @@ import com.samskivert.Log;
* collection, processing and possible archiving of the logs.
*/
public class AuditLogger
implements Interval
{
/**
* Creates an audit logger that logs to the specified file.
@@ -119,8 +118,10 @@ public class AuditLogger
}
}
// documentation inherited from interface Interval
public synchronized void intervalExpired (int id, Object arg)
/**
* Check to see if it's time to roll over the log file.
*/
protected synchronized void checkRollOver ()
{
// check to see if we should roll over the log
String newDayStamp = _dayFormat.format(new Date());
@@ -160,7 +161,7 @@ public class AuditLogger
(59L - cal.get(Calendar.SECOND)) * 1000L +
(59L - cal.get(Calendar.MINUTE)) * (1000L * 60L);
IntervalManager.register(this, nextCheck, null, false);
_rollover.schedule(nextCheck);
}
/** The path to our log file. */
@@ -169,6 +170,13 @@ public class AuditLogger
/** We actually write to this feller here. */
protected PrintWriter _logWriter;
/** The interval that rolls over the log file. */
protected Interval _rollover = new Interval() {
public void expired () {
checkRollOver();
}
};
/** Suppress freakouts if our log file becomes hosed. */
protected Throttle _throttle = new Throttle(2, 5*60*1000L);
@@ -20,14 +20,163 @@
package com.samskivert.util;
import java.util.Timer;
import java.util.TimerTask;
import com.samskivert.Log;
/**
* An interface for doing operations after some delay. Normally,
* <code>intervalExpired()</code> should not do anything that will take
* very long.
*
* @see IntervalManager
* An interface for doing operations after some delay.
*/
public interface Interval
public abstract class Interval
implements Runnable
{
public void intervalExpired (int id, Object arg);
/**
* Create a simple interval that does not use a RunQueue to run
* the expire() method.
*/
public Interval ()
{
}
/**
* Create an Interval that uses the specified RunQueue to run
* the expire() method.
*/
public Interval (RunQueue runQueue)
{
if (runQueue == null) {
throw new NullPointerException("RunQueue cannot be null, " +
"use other constructor if you want a simple Interval.");
}
_runQueue = runQueue;
}
/**
*
* The main method where your interval should do its work.
*
*/
public abstract void expired ();
/**
* Schedule the interval to execute once, after the specified delay.
*/
public final void schedule (long delay)
{
schedule(delay, 0L);
}
/**
* Schedule the interval to execute repeatedly, with the same delay.
*/
public final void schedule (long delay, boolean repeat)
{
schedule(delay, repeat ? delay : 0L);
}
/**
* Schedule the interval to execute repeatedly with the specified
* initial delay and repeat delay.
*/
public final void schedule (long initialDelay, long repeatDelay)
{
cancel();
_task = new TimerTask() {
public void run () {
if (this != _task) {
return;
}
// if the Interval is operating in simple mode,
// just expire here
if (_runQueue == null) {
safelyExpire();
return;
}
// else we need to make sure we want to run
// and post the Runnable
synchronized (this) {
if (_fired != -1) {
_fired++;
_runQueue.postRunnable(Interval.this);
}
}
}
};
synchronized (_task) {
_fired = _expired = 0;
if (repeatDelay == 0L) {
_timer.schedule(_task, initialDelay);
} else {
_timer.scheduleAtFixedRate(_task, initialDelay, repeatDelay);
}
}
}
/**
* Cancel the Interval, and ensure that any expirations that are queued
* up but have not yet run do not run.
*/
public final void cancel ()
{
if (_task != null) { // task can only be null if we were never scheduled
synchronized (_task) {
_task.cancel();
_fired = -1;
}
}
}
// documentation inherited from interface Runnable
public final void run ()
{
if (_expired >= _fired) {
// we are a dead interval. We were queued up prior to cancel()
// being called, but we are now running after cancel()
return;
}
// increment expired and scoot everything back if we're getting too big
_expired++;
if (_expired > Integer.MAX_VALUE/2) {
synchronized (_task) {
_expired -= Integer.MAX_VALUE/2;
_fired -= Integer.MAX_VALUE/2;
}
}
safelyExpire();
}
/**
* Safely expire the interval.
*/
protected final void safelyExpire ()
{
try {
expired();
} catch (Throwable t) {
Log.warning("Interval broken in expired(): " + t);
Log.logStackTrace(t);
}
}
/** Counters used to guarantee that we don't fuck up. */
protected int _fired = -1, _expired = 0;
/** If non-null, the RunQueue used to run the expired() method for each
* Interval. */
protected RunQueue _runQueue;
/** The task that actually schedules our execution with the static Timer.
* Also the object that we synchronize upon when dealing with those issues.
*/
protected TimerTask _task;
/** The daemon timer used to schedule all Intervals. */
protected static final Timer _timer =
new Timer(/*JDK1.5 "samskivert Interval Timer",*/ true);
}
@@ -1,206 +0,0 @@
//
// $Id: IntervalManager.java,v 1.10 2003/08/13 21:04:12 mdb Exp $
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001 Michael Bayne
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY 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 along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.util;
import java.util.Timer;
import java.util.TimerTask;
import com.samskivert.Log;
/**
* Can be used to register an object that is to be called once or
* repeatedly after an interval has expired. This now uses {@link Timer}
* to do all the actual scheduling, but we keep this front end because it
* is static (accessible anywhere), provides a single thread where
* disparate simple intervals can all run happily, and because {@link
* Interval} is an interface, unlike {@link TimerTask}, which is an
* abstract class.
*/
public class IntervalManager
{
/**
* Schedules the intervaled object to get called after an interval.
*
* @param i the intervaled object
* @param delay the milliseconds until the interval expires.
* @param arg the object to be passed to the interval when the
* interval delay expires.
* @param recur if true, interval gets called every timeout until
* removed.
*
* @return an ID number that will passed to the {@link
* Interval#intervalExpired} method of i along with arg
*/
public static int register (
Interval i, long delay, Object arg, boolean recur)
{
IntervalTask task = new IntervalTask(i, arg, recur);
_hash.put(task.id, task);
if (recur) {
_timer.scheduleAtFixedRate(task, delay, delay);
} else {
_timer.schedule(task, delay);
}
return task.id;
}
/**
* Schedule the interval to be called repeatedly, but at a fixed delay
* instead of fixed rate. This means that the delay between executions
* will be the delay specified below plus the actual execution time it
* takes to run the interval's expire method.
*/
public static int registerAtFixedDelay (Interval i, long delay, Object arg)
{
IntervalTask task = new IntervalTask(i, arg, true);
_hash.put(task.id, task);
_timer.schedule(task, delay, delay);
return task.id;
}
/**
* Non-recurring intervals are removed automatically after they are
* run! This method is only useful if you want to remove a recurring
* {@link Interval} or if you want to remove a non-recurring {@link
* Interval} before it gets run.
*/
public static void remove (int id)
{
IntervalTask task = (IntervalTask) _hash.remove(id);
if (task != null) {
task.cancel();
} else {
Log.warning("remove() called on non-registered " +
"interval [id=" + id + "].");
}
}
/**
* Turn on or off logging of all interval firings.
*/
public static void setLogIntervals (boolean log)
{
_logging = log;
}
/**
* Returns the total number of intervals registered.
*/
public static int registeredIntervalCount ()
{
return _hash.size();
}
/**
* Returns the number of intervals fired since the last call to this
* method.
*/
public static int getAndClearFiredIntervals ()
{
int fired;
synchronized (_hash) {
fired = _firedIntervals;
_firedIntervals = 0;
}
return fired;
}
/** The timer we use to schedule everything. */
protected static Timer _timer = new Timer(true);
/** Our registered intervals, indexed by id. */
protected static IntMap _hash =
Collections.synchronizedIntMap(new HashIntMap());
/** Whether or not we're logging interval fires. */
protected static boolean _logging = false;
/** Used by {@link #getAndClearFiredIntervals}. */
protected static int _firedIntervals;
/**
* A class to adapt {@link TimerTask} to the smooth action of {@link
* Interval}.
*/
static class IntervalTask extends TimerTask
{
public int id;
protected Interval _i;
protected Object _arg;
protected boolean _onetime;
protected String _source;
public IntervalTask (Interval i, Object arg, boolean recur)
{
_i = i;
_arg = arg;
_onetime = !recur;
id = nextID();
try {
_source = new Throwable().getStackTrace()[2].toString();
} catch (Throwable oopsie) {
_source = "<unknown>";
}
}
/**
* Run the interval. It's synchronized so that if we someday have
* multiple threads, the same interval won't be called multiple
* times.
*/
public void run ()
{
if (_logging) {
System.err.println("Interval fired [source=" + _source + "]");
}
// note that we fired another interval
synchronized (_hash) {
_firedIntervals++;
}
// protect our ass.
try {
_i.intervalExpired(id, _arg);
} catch (Exception e) {
Log.warning("Exception while expiring interval: " + e);
Log.logStackTrace(e);
}
// if we're not recurring, be sure to remove from the mgr's hash
if (_onetime) {
_hash.remove(id);
}
}
/**
* Unique ids for each interval item.
*/
private static synchronized int nextID ()
{
return _idseq++;
}
private static int _idseq = 0;
}
}