org.jdesktop.application.TaskMonitor Maven / Gradle / Ivy
Show all versions of tink-app Show documentation
package org.jdesktop.application;
import java.awt.EventQueue;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.jdesktop.swingworker.SwingWorker.StateValue;
/**
* This class is intended to serve as the model for GUI components,
* like status bars, that display the state of an application's
* background tasks. {@code TaskMonitor} provides an overview of all
* the ApplicationContext's Tasks, as well as the state of a single
* {@code foreground} Task.
*
*
* The value of {@link #getTasks getTasks()} is a list of all of the
* {@code Tasks} whose state is not {@link
* Task#isDone DONE} for all of the
* ApplicationContext's {@code TaskServices}. In other words: all of
* the ApplicationContext's background tasks that haven't finished
* executing. Each time a new TaskService Task is executed it's added
* to the list; when the Task finishes it's removed. Each time the
* list changes {@code PropertyChangeListeners} are fired.
* Applications that wish to create a detailed visualization of all
* Tasks should monitor the TaskMonitor {@code "tasks"} property.
*
*
* Users are often only interested in the status of a single
* foreground task, typically the one associated with GUI
* element they're working with, or with the most recent command
* they've issued. The TaskMonitor's PropertyChangeListener is
* notified each time a property of the {@link #setForegroundTask
* foregroundTask} changes. Additionally the TaskMonitor fires
* synthetic PropertyChangeEvents for properties named "pending",
* "started", and "done" when the corresponding Task {@code state}
* property changes occur.
*
*
* TaskMonitor manages a queue of new Tasks. The
* foregroundTask is automatically set to the first new Task, and when
* that Task finishes, the next Task in the queue, and so on.
* Applications can set the foregroundTask explicitly, to better
* reflect what the user is doing. For example, a tabbed browsing GUI
* that launched one Task per tab might set the foreground Task each
* time the user selected a tab. To prevent the foregroundTask
* property from (ever) being reset automatically, one must set {@link
* #setAutoUpdateForegroundTask autoUpdateForegroundTask} to false.
*
*
* This class is not thread-safe. All of its methods must be called
* on the event dispatching thread (EDT) and all of its listeners will
* run on the EDT.
*
*
* @author Hans Muller ([email protected])
* @see ApplicationContext#getTaskServices
* @see TaskService#getTasks
* @see TaskService#execute
*/
public class TaskMonitor extends AbstractBean {
private final PropertyChangeListener applicationPCL;
private final PropertyChangeListener taskServicePCL;
private final PropertyChangeListener taskPCL;
private final LinkedList taskQueue;
private boolean autoUpdateForegroundTask = true;
private Task foregroundTask = null;
/**
* Construct a TaskMonitor.
*/
public TaskMonitor(ApplicationContext context) {
applicationPCL = new ApplicationPCL();
taskServicePCL = new TaskServicePCL();
taskPCL = new TaskPCL();
taskQueue = new LinkedList();
context.addPropertyChangeListener(applicationPCL);
for(TaskService taskService : context.getTaskServices()) {
taskService.addPropertyChangeListener(taskServicePCL);
}
}
/**
* The TaskMonitor's PropertyChangeListeners are fired each time
* any property of the the {@code foregroundTask} changes. By
* default this property is set to the first Task to be executed
* and then, when that Task finishes, reset to the next most
* recently executed Task. If the {@code
* autoUpdateForegroundTask} is false, then the foregroundTask
* property is not reset automatically.
*
* @param foregroundTask the task whose properties are reflected by this class
* @see #setAutoUpdateForegroundTask
* @see #getForegroundTask
*/
public void setForegroundTask(Task foregroundTask) {
final Task oldTask = this.foregroundTask;
if (oldTask != null) {
oldTask.removePropertyChangeListener(taskPCL);
}
this.foregroundTask = foregroundTask;
Task newTask = this.foregroundTask;
if (newTask != null) {
newTask.addPropertyChangeListener(taskPCL);
}
firePropertyChange("foregroundTask", oldTask, newTask);
}
/**
* Indicates the {@code Task} whose status the ApplicationContext's GUI wants
* to be displayed, typically in the main window's status bar.
*
*
* @return the value of the foregroundTask property.
* @see #setForegroundTask
*/
public Task getForegroundTask() {
return foregroundTask;
}
/**
* True if the {@code foregroundTask} property should be automatically
* reset to the oldest Task in the queue when it finishes running.
*
* This property is true by default.
*
* @return true if the foregroundTask should be set automatically.
* @see #setAutoUpdateForegroundTask
* @see #setForegroundTask
*/
public boolean getAutoUpdateForegroundTask() {
return autoUpdateForegroundTask;
}
/**
* True if the {@code foregroundTask} property should be automatically
* reset to the oldest Task in the queue when it finishes running. An
* application that wants explicit control over the Task being monitored
* can set this property to false.
*
* This property is true by default.
*
* @param autoUpdateForegroundTask true if the foregroundTask should be set automatically
* @see #getAutoUpdateForegroundTask
*/
public void setAutoUpdateForegroundTask(boolean autoUpdateForegroundTask) {
boolean oldValue = this.autoUpdateForegroundTask;
this.autoUpdateForegroundTask = autoUpdateForegroundTask;
firePropertyChange("autoUpdateForegroundTask", oldValue, this.autoUpdateForegroundTask);
}
private List copyTaskQueue() {
synchronized(taskQueue) {
if (taskQueue.isEmpty()) {
return Collections.emptyList();
}
else {
return new ArrayList(taskQueue);
}
}
}
/**
* All of the Application Tasks whose {@code state} is not {@code DONE}.
*
* Each time the list of Tasks changes, a PropertyChangeEvent for the
* property named "tasks" is fired. Applications that want to monitor all
* background Tasks should monitor the tasks property.
*
* @return a list of all Tasks that aren't {@code DONE}
*/
public List getTasks() {
return copyTaskQueue();
}
/* Called on the EDT, each time a TaskService's list of tasks changes,
* i.e. each time a new Task is executed and each time a Task's
* state changes to DONE.
*/
private void updateTasks(List oldTasks, List newTasks) {
boolean tasksChanged = false; // has the "tasks" property changed?
List oldTaskQueue = copyTaskQueue();
// Remove each oldTask that's not in the newTasks list from taskQueue
for(Task oldTask : oldTasks) {
if (!(newTasks.contains(oldTask))) {
if (taskQueue.remove(oldTask)) {
tasksChanged = true;
}
}
}
// Add each newTask that's not in the oldTasks list to the taskQueue
for(Task newTask : newTasks) {
if (!(taskQueue.contains(newTask))) {
taskQueue.addLast(newTask);
tasksChanged = true;
}
}
// Remove any tasks that are DONE for the sake of tasksChanged
Iterator tasks = taskQueue.iterator();
while(tasks.hasNext()) {
Task task = tasks.next();
if (task.isDone()) {
tasks.remove();
tasksChanged = true;
}
}
// Maybe fire the "tasks" PCLs
if (tasksChanged) {
List newTaskQueue = copyTaskQueue();
firePropertyChange("tasks", oldTaskQueue, newTaskQueue);
}
if (autoUpdateForegroundTask && (getForegroundTask() == null)) {
setForegroundTask(taskQueue.isEmpty() ? null : taskQueue.getLast());
}
}
/* Each time an ApplicationContext TaskService is added or removed, we
* remove our taskServicePCL from the old ones, add it to the new
* ones. In a typical application, this will happen infrequently
* and the number of TaskServices will be small, often just one.
* This listener runs on the EDT.
*/
private class ApplicationPCL implements PropertyChangeListener {
public void propertyChange(PropertyChangeEvent e) {
String propertyName = e.getPropertyName();
if ("taskServices".equals(propertyName)) {
List oldList = (List)e.getOldValue();
List newList = (List)e.getNewValue();
for(TaskService oldTaskService : oldList) {
oldTaskService.removePropertyChangeListener(taskServicePCL);
}
for(TaskService newTaskService : newList) {
newTaskService.addPropertyChangeListener(taskServicePCL);
}
}
}
}
/* Each time a TaskService's list of Tasks (the "tasks" property) changes,
* update the taskQueue (the "tasks" property) and possibly the
* foregroundTask property. See updateTasks().
* This listener runs on the EDT.
*/
private class TaskServicePCL implements PropertyChangeListener {
public void propertyChange(PropertyChangeEvent e) {
String propertyName = e.getPropertyName();
if ("tasks".equals(propertyName)) {
List oldList = (List)e.getOldValue();
List newList = (List)e.getNewValue();
updateTasks(oldList, newList);
}
}
}
/* Each time a property of the foregroundTask that's also a
* TaskMonitor property changes, update the TaskMonitor's state
* and fire a TaskMonitor ProprtyChangeEvent.
* This listener runs on the EDT.
*/
private class TaskPCL implements PropertyChangeListener {
private void fireStateChange(Task task, String propertyName) {
firePropertyChange(new PropertyChangeEvent(task, propertyName, false, true));
}
public void propertyChange(PropertyChangeEvent e) {
String propertyName = e.getPropertyName();
Task task = (Task)(e.getSource());
Object newValue = e.getNewValue();
if ((task != null) && (task == getForegroundTask())) {
firePropertyChange(e);
if ("state".equals(propertyName)) {
StateValue newState = (StateValue)(e.getNewValue());
switch(newState) {
case PENDING: fireStateChange(task, "pending"); break;
case STARTED: fireStateChange(task, "started"); break;
case DONE:
fireStateChange(task, "done");
setForegroundTask(null);
}
}
}
}
}
}