package abbot.util; import java.util.Timer; import java.lang.reflect.Field; import java.util.TimerTask; import java.util.Date; import abbot.Log; /** Prevents misbehaving TimerTasks from canceling the timer thread by throwing exceptions and/or errors. Also extends the basic Timer to use a name for its thread. Naming the timer thread facilitates discerning different threads in a full stack dump.<p> */ public class NamedTimer extends Timer { /** Creates a non-daemon named timer. */ public NamedTimer(final String name) { this(name, false); } /** Creates a named timer, optionally running as a daemon thread. */ public NamedTimer(final String name, boolean isDaemon) { super(isDaemon); schedule(new TimerTask() { public void run() { Thread.currentThread().setName(name); } }, 0); } /** Handle an exception thrown by a TimerTask. The default does nothing. */ protected void handleException(Throwable thrown) { Log.warn(thrown); } // TODO: prevent scheduled tasks from throwing uncaught exceptions and // thus canceling the Timer. // We can easily wrap scheduled tasks with a catcher, but we can't readily // cancel the wrapper when private class ProtectingTimerTask extends TimerTask { private TimerTask task; public ProtectingTimerTask(TimerTask orig) { this.task = orig; } public void run() { if (isCanceled()) { cancel(); } else { try { task.run(); } catch(Throwable thrown) { handleException(thrown); } } } private boolean isCanceled() { boolean canceled = false; final int CANCELED = 3; try { Field f = TimerTask.class.getDeclaredField("state"); f.setAccessible(true); int state = ((Integer)f.get(task)).intValue(); canceled = state == CANCELED; } catch(Exception e) { Log.warn(e); } return canceled; } } public void schedule(TimerTask task, Date time) { super.schedule(new ProtectingTimerTask(task), time); } public void schedule(TimerTask task, Date firstTime, long period) { super.schedule(new ProtectingTimerTask(task), firstTime, period); } public void schedule(TimerTask task, long delay) { super.schedule(new ProtectingTimerTask(task), delay); } public void schedule(TimerTask task, long delay, long period) { super.schedule(new ProtectingTimerTask(task), delay, period); } public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) { super.scheduleAtFixedRate(new ProtectingTimerTask(task), firstTime, period); } public void scheduleAtFixedRate(TimerTask task, long delay, long period) { super.scheduleAtFixedRate(new ProtectingTimerTask(task), delay, period); } }