/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.openide.util;
import java.util.HashSet;
import java.util.Iterator;
/** A task that may be executed in a separate thread and permits examination of its status.
* Other threads can check if it is finished or wait for it
* to finish.
* <P>
* For example:
* <p><code><PRE>
* Runnable r = new Runnable () {
* public void run () {
* // do something
* }
* };
* Task task = new Task (r);
* RequestProcessor.postRequest (task);
* </PRE></code>
* <p>In a different thread one can then test <CODE>task.isFinished ()</CODE>
* or wait for it with <CODE>task.waitFinished ()</CODE>.
*
* @author Jaroslav Tulach
*/
public class Task extends Object implements Runnable {
/** Dummy task which is already finished. */
public static final Task EMPTY = new Task();
static {
EMPTY.finished = true;
}
/** what to run */
private Runnable run;
/** flag if we have finished */
private boolean finished;
/** listeners for the finish of task (TaskListener) */
private HashSet list;
/** Create a new task.
* The runnable should provide its own error-handling, as
* by default thrown exceptions are simply logged and not rethrown.
* @param run runnable to run that computes the task
*/
public Task(Runnable run) {
this.run = run;
if (run == null) {
finished = true;
}
}
/** Constructor for subclasses that wants to control whole execution
* itself.
* @since 1.5
*/
protected Task () {
}
/** Test whether the task has finished running.
* @return <code>true</code> if so
*/
public final boolean isFinished () {
return finished;
}
/** Wait until the task is finished.
* Changed not to be <code>final</code> in version 1.5
*/
public void waitFinished () {
synchronized (this) {
while (!finished) {
try {
wait ();
} catch (InterruptedException ex) {
}
}
}
}
/** Changes the state of the task to be running. Any call after this
* one and before notifyFinished to waitFinished blocks.
* @since 1.5
*/
protected final void notifyRunning () {
synchronized (this) {
this.finished = false;
notifyAll ();
}
}
/** Notify all waiters that this task has finished.
* @see #run
*/
protected final void notifyFinished () {
Iterator it;
synchronized (this) {
finished = true;
notifyAll ();
// fire the listeners
if (list == null) return;
it = ((HashSet)list.clone ()).iterator ();
}
while (it.hasNext ()) {
TaskListener l = (TaskListener)it.next ();
l.taskFinished (this);
}
}
/** Start the task.
* When it finishes (even with an exception) it calls
* {@link #notifyFinished}.
* Subclasses may override this method, but they
* then need to call {@link #notifyFinished} explicitly.
* <p>Note that this call runs synchronously, but typically the creator
* of the task will call this method in a separate thread.
*/
public void run () {
try {
notifyRunning ();
if (run != null) run.run ();
} finally {
notifyFinished ();
}
}
/** Add a listener to the task.
* @param l the listener to add
*/
public synchronized void addTaskListener (TaskListener l) {
if (list == null) list = new HashSet ();
list.add (l);
if (finished) {
l.taskFinished(this);
}
}
/** Remove a listener from the task.
* @param l the listener to remove
*/
public synchronized void removeTaskListener (TaskListener l) {
if (list == null) return;
list.remove (l);
}
public String toString () {
return "task " + run; // NOI18N
}
}