// BlogBridge -- RSS feed reader, manager, and web based service // Copyright (C) 2002-2006 by R. Pito Salas // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software Foundation; // either version 2 of the License, or (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License along with this program; // if not, write to the Free Software Foundation, Inc., 59 Temple Place, // Suite 330, Boston, MA 02111-1307 USA // // Contact: R. Pito Salas // mailto:pitosalas@users.sourceforge.net // More information: about BlogBridge // http://www.blogbridge.com // http://sourceforge.net/projects/blogbridge // // $Id: EventQueueWithWD.java,v 1.13 2006/05/30 08:25:28 spyromus Exp $ // package com.salas.bb.utils.watchdogs; import com.salas.bb.utils.i18n.Strings; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.TimerTask; import java.util.logging.Logger; import java.util.logging.Level; /** * Alternative events dispatching queue. The benefit over the default Event Dispatch queue is that * you can add as many watchdog timers as you need and they will trigger arbitrary actions. * <p/> * Timers can be of two types: * <ul> * <li><b>Repetitive </b>- action can be triggered multiple times for the same "lengthy" event. * </li> * <li><b>Non-repetitive </b>- action can be triggered only once per event.</li> * </ul> * <p/> * Queue records the time of the event dispatching start. This time is used by the timers to * check if the event is being dispatched for too long. If so the expired timers trigger associated * actions. * <p/> * In order to use this queue application should call <code>install()</code> method. This method * will create, initialize and register the alternative queue as appropriate. It also will return * the instance of the queue for further interractions. Here's an example of how it can be done: * <p/> * <pre> * * EventQueueWithWD queue = EventQueueWithWD.install(); * Action edtOverloadReport = ...; * * // install single-shot wg to report EDT overload after 10-seconds timeout * queue.addWatchdog(10000, edtOverloadReport, false); * * </pre> */ public final class EventQueueWithWD extends EventQueue { private static final Logger LOG = Logger.getLogger(EventQueueWithWD.class.getName()); private static final String MSG_EXCEPTION = Strings.error("failed.to.dispatch.event"); // Main timer private java.util.Timer timer; // Group of informational fields for describing the event private Object eventChangeLock; private long eventDispatchingStart; private AWTEvent event; /** * Hidden utility constructor. */ private EventQueueWithWD() { timer = new java.util.Timer(true); eventDispatchingStart = -1; event = null; eventChangeLock = new Object(); } /** * Install alternative queue. * * @return instance of queue installed. */ public static EventQueueWithWD install() { EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue(); EventQueueWithWD newEventQueue = new EventQueueWithWD(); eventQueue.push(newEventQueue); return newEventQueue; } /** * Record the event and continue with usual dispatching. * * @param anEvent event to dispatch. */ protected void dispatchEvent(AWTEvent anEvent) { setEventDispatchingStart(anEvent, System.currentTimeMillis()); try { super.dispatchEvent(anEvent); } catch (Throwable e) { LOG.log(Level.SEVERE, MSG_EXCEPTION, e); } setEventDispatchingStart(null, -1); } /** * Register event and dispatching start time. * * @param anEvent event. * @param timestamp dispatching start time. */ private void setEventDispatchingStart(AWTEvent anEvent, long timestamp) { synchronized (eventChangeLock) { event = anEvent; eventDispatchingStart = timestamp; } } /** * Add watchdog timer. Timer will trigger <code>listener</code> if the queue dispatching * event longer than specified <code>maxProcessingTime</code>. If the timer is * <code>repetitive</code> then it will trigger additional events if the processing 2x, 3x and * further longer than <code>maxProcessingTime</code>. * * @param maxProcessingTime maximum processing time. * @param listener listener for events. The listener will receive <code>AWTEvent</code> * as source of event. * @param repetitive TRUE to trigger consequent events for 2x, 3x and further periods. */ public void addWatchdog(long maxProcessingTime, ActionListener listener, boolean repetitive) { Watchdog checker = new Watchdog(maxProcessingTime, listener, repetitive); timer.schedule(checker, maxProcessingTime, maxProcessingTime); } /** * Checks if the processing of the event is longer than the specified * <code>maxProcessingTime</code>. If so then listener is notified. */ private class Watchdog extends TimerTask { // Settings private long maxProcessingTime; private ActionListener listener; private boolean repetitive; // Event reported as "lengthy" for the last time. Used to prevent // repeatitive behaviour in non-repeatitive timers. private int lastReportedEventHashCode; /** * Creates timer. * * @param aMaxProcessingTime maximum event processing time before listener is notified. * @param aListener listener to notify. * @param aRepeatitive TRUE to allow consequent notifications for the same event. */ public Watchdog(long aMaxProcessingTime, ActionListener aListener, boolean aRepeatitive) throws IllegalArgumentException { if (aListener == null) throw new IllegalArgumentException(Strings.error("unspecified.listener")); if (aMaxProcessingTime < 0) throw new IllegalArgumentException(Strings.error("max.locking.period.should.be.greater.than.zero")); maxProcessingTime = aMaxProcessingTime; listener = aListener; lastReportedEventHashCode = -1; repetitive = aRepeatitive; } public void run() { long time; AWTEvent currentEvent; // Get current event requisites synchronized (eventChangeLock) { time = eventDispatchingStart; currentEvent = event; } long currentTime = System.currentTimeMillis(); // Check if event is being processed longer than it's allowed if (time != -1 && (currentTime - time > maxProcessingTime) && (repetitive || System.identityHashCode(currentEvent) != lastReportedEventHashCode)) { listener.actionPerformed(new ActionEvent(currentEvent, -1, null)); lastReportedEventHashCode = System.identityHashCode(currentEvent); } } } }