/* * @(#)EventLoop.java 1.5 2011-02-17 * * Copyright (c) 2001-2011 Werner Randelshofer, Goldau, Switzerland. * All rights reserved. * * You may not use, copy or modify this file, except in compliance with the * license agreement you entered into with Werner Randelshofer. * For details see accompanying license terms. */ package org.monte.media.concurrent; import java.util.*; /** * An EventLoop can process events on a separate worker thread. * It consists of two parts: the event collector and the event * processor. * <p> * The event collector collects all incoming events and puts them * into a queue.<br> * The event processor removes the events from the queue and * processes them. * <p> * The key feature of the EventLoop is, that clients don't have * to wait until an event has been processed. Clients are free * to proceed as soon as the collector has put the event into * the queue. * <p> * <b>Usage</b> * <p> * This is an abstract class. It does all the queue handling, but * does no processing. To use it, you have to create a subclass * which overrides the methods #collectEvent and #processEvent. * <p> * <b>Example</b> * <p> * An EventLoop, which outputs Strings on a background thread * could look like this: * <pre><tt> * public class AsyncDisplay * extends AbstractEventLoop { * public void display(String string) { * collectEvent(string); * } * protected void processEvent(Object event) { * System.out.println((String) event); * } * } * </tt></pre> * <p> * To use the class proceed like this: * <p><pre><tt> * AsyncDisplay a = new AsyncDisplay(); * a.display("Hello World"); * </tt></pre> * * @author Werner Randelshofer * @version 1.5 2011-02-17 Merged with CubeTwister. * <br>1.4 2008-01-02 Added method join. * <br>1.3 2002-05-15 Reworked. * <br>1.2 2001-09-24 Support for coalescing of events added. * <br>1.1 2001-08-24 Reworked for JDK 1.3. * <br>1.0.2 2000-03-03 * Catch SecurityException's (To make this class work in Netscape Navigator). * <br>1.0.1 1998-10-14 Adds version info to class comments. * <br>1.0 1998-10-01 Threads are not reused anymore. */ public abstract class EventLoop { /** * The event processor thread. */ protected Thread eventProcessor; /** * The priority of the processor thread. */ private int priority; /** * The queue stores the events until they * can be processed by a processor thread. */ private final Vector queue = new Vector(); /** * Indicates whether multiple events will be coalesced * by the event processor or not. */ private boolean isCoalesce; /** * Indicates whether events will be processed by the * event processor or not. */ private volatile boolean isAlive = true; /** * Creates a new EventLoop which processes events at Thread.NORM_PRORITY. */ public EventLoop() { this(Thread.NORM_PRIORITY); } /** * Creates a new EventLoop which processes events at the * desired thread priority. * * @param priority The Thread priority of the event processor. */ public EventLoop(int priority) { this.priority = priority; } /** * Collects an event and puts it into the event queue * for later processing. * * @param event The event to be put into the queue. */ protected void collectEvent(Object event) { synchronized(queue) { if (! isCoalesce || ! queue.contains(event)) { queue.addElement(event); if (isAlive) startProcessor(); } } } /** * Sets whether the EventLoop coalesces multiple pending events. * A busy application may not be able to keep up with event * generation, causing multiple events to be queued. * Coalescing is based on equality tests of event objects. * More formally, coalesces an event o if and only if the queue * contains at least one element e such that * <code>(o==null ? e==null : o.equals(e))</code>. * <p> * EventLoops do not coalesce events by default. * * @param b Specify true to turn on coalescing. */ public void setCoalesce(boolean b) { isCoalesce = b; } /** * Returns true if the EventLoop * coalesces multiple pending events. * * @see #setCoalesce(boolean) */ public boolean isCoalesce() { return isCoalesce; } /** * Starts the event processor. * <br>The event processor is started by default. */ public void start() { synchronized(queue) { isAlive = true; startProcessor(); } } /** * Stops the event processor. */ public void stop() { isAlive = false; } public void join() throws InterruptedException { Thread t = eventProcessor; if (t != null) { t.join(); } } /** * Clears the event queue. */ public void clear() { synchronized(queue) { queue.removeAllElements(); } } /** * This is the method which really starts the processor. */ private void startProcessor() { synchronized(queue) { if (eventProcessor == null) { eventProcessor = new Thread(this+" Event Processor") { public void run() { processEvents(); } }; try { // The event processor must not be a daemon. // If it was a daemon, the virtual machine would // stop before the event had been processed. eventProcessor.setDaemon(false); } catch (SecurityException e) {} try { eventProcessor.setPriority(priority); } catch (SecurityException e) {} eventProcessor.start(); } } } /** * This method processes a single event on the event processor thread. * * @param event An event from the queue. */ protected abstract void processEvent(Object event); /** * This method removes events from the event queue * and proceses them until the queue is empty or * until #stop is called. * <p> * This method must be called from the event processor * thread only. */ protected void processEvents() { Object event; while (isAlive) { synchronized(queue) { if (queue.isEmpty()) { eventProcessor = null; return; } event = queue.elementAt(0); queue.removeElementAt(0); } try { processEvent(event); } catch (Throwable e) { e.printStackTrace(); } } } }