ch.randelshofer.quaqua.util.EventLoop Maven / Gradle / Ivy
Show all versions of Quaqua Show documentation
/*
* @(#)EventLoop.java 1.4 2009-06-01
*
* Copyright (c) 2001-2010 Werner Randelshofer, Immensee, 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 ch.randelshofer.util;
package ch.randelshofer.quaqua.util;
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.
*
* The event collector collects all incoming events and puts them
* into a queue.
* The event processor removes the events from the queue and
* processes them.
*
* 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.
*
* Other important features are that events are processed in
* the same sequence as they have been collected and that only one
* thread is used to process the events.
*
* Usage
*
* 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.
*
* Example
*
* An EventLoop, which outputs Strings on a background thread
* could look like this:
*
* public class AsyncDisplay
* extends AbstractEventLoop {
* public void display(String string) {
* collectEvent(string);
* }
* protected void processEvent(Object event) {
* System.out.println((String) event);
* }
* }
*
*
* To use the class proceed like this:
*
* AsyncDisplay a = new AsyncDisplay();
* a.display("Hello World");
*
*
* @author Werner Randelshofer
* @version 1.4 2009-06-01 Added method dispose.
*
1.3 2006-04-07 Fixed bug in processEvents method. Support for
* FIFO sequence added. Replaced Vector by LinkedList.
*
1.2AWT 2001-09-24 Support for coalescing of events added.
*
1.1 2001-08-24 Reworked for JDK 1.3.
*
1.0.2 2000-03-03
* Catch SecurityException's (To make this class work in Netscape Navigator).
*
history 1.0.1 14.10.1998
* Versionskennung in Klassenkommentar eingef�gt.
*
history 1.0 Datum ?
* Threads werden nicht mehr wiederverwendet.
*/
public abstract class EventLoop {
private Thread eventProcessor;
private int threadPriority;
private final LinkedList eventQueue = new LinkedList();
/**
* 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;
/**
* Indicates whether events shall be enqueued add the beginning
* of the queue.
*/
private volatile boolean isLIFO = false;
/**
* 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.threadPriority = 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(eventQueue) {
if (! isCoalesce || ! eventQueue.contains(event)) {
if (isLIFO) {
eventQueue.addFirst(event);
} else {
eventQueue.addLast(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
* (o==null ? e==null : o.equals(e))
.
*
* 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;
}
/**
* Sets whether the EventLoop shall enqueue events at the beginning of the
* queue instead of at the end of the queue.
*
* @param b Specify true to enqueue events at the beginning of the queue.
*/
public void setLIFO(boolean b) {
isLIFO = b;
}
/**
* Returns true if the EventLoop
* coalesces multiple pending events.
*
* @see #setCoalesce(boolean)
*/
public boolean isLIFO() {
return isLIFO;
}
/**
* Starts the event processor.
*
The event processor is started by default.
*/
public void start() {
synchronized(eventQueue) {
isAlive = true;
startProcessor();
}
}
/**
* Stops the event processor.
*/
public void stop() {
isAlive = false;
}
/**
* Clears the event queue.
*/
public void clear() {
synchronized(eventQueue) {
eventQueue.clear();
}
}
public void dispose() {
stop();
clear();
}
/**
* This is the method which really starts the processor.
*/
private void startProcessor() {
synchronized(eventQueue) {
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(threadPriority);
} catch (SecurityException e) {}
eventProcessor.start();
}
}
}
/**
* This method processes an 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.
*
* This method must be called from the event processor
* thread only.
*/
protected void processEvents() {
Object event;
while (true) {
synchronized(eventQueue) {
if (eventQueue.isEmpty() || ! isAlive) {
eventProcessor = null;
return;
}
event = eventQueue.removeFirst();
}
try {
processEvent(event);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
}