/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package javax.swing;
import java.lang.ref.
WeakReference;
import java.security.
AccessController;
import java.security.
PrivilegedAction;
import java.beans.
PropertyChangeListener;
import java.beans.
PropertyChangeSupport;
import java.beans.
PropertyChangeEvent;
import java.util.
List;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
import java.awt.event.*;
import javax.swing.
SwingUtilities;
import sun.awt.
AppContext;
import sun.swing.
AccumulativeRunnable;
/**
* An abstract class to perform lengthy GUI-interaction tasks in a
* background thread. Several background threads can be used to execute such
* tasks. However, the exact strategy of choosing a thread for any particular
* {@code SwingWorker} is unspecified and should not be relied on.
* <p>
* When writing a multi-threaded application using Swing, there are
* two constraints to keep in mind:
* (refer to
* <a href="https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html">
* Concurrency in Swing
* </a> for more details):
* <ul>
* <li> Time-consuming tasks should not be run on the <i>Event
* Dispatch Thread</i>. Otherwise the application becomes unresponsive.
* </li>
* <li> Swing components should be accessed on the <i>Event
* Dispatch Thread</i> only.
* </li>
* </ul>
*
*
* <p>
* These constraints mean that a GUI application with time intensive
* computing needs at least two threads: 1) a thread to perform the lengthy
* task and 2) the <i>Event Dispatch Thread</i> (EDT) for all GUI-related
* activities. This involves inter-thread communication which can be
* tricky to implement.
*
* <p>
* {@code SwingWorker} is designed for situations where you need to have a long
* running task run in a background thread and provide updates to the UI
* either when done, or while processing.
* Subclasses of {@code SwingWorker} must implement
* the {@link #doInBackground} method to perform the background computation.
*
*
* <p>
* <b>Workflow</b>
* <p>
* There are three threads involved in the life cycle of a
* {@code SwingWorker} :
* <ul>
* <li>
* <p>
* <i>Current</i> thread: The {@link #execute} method is
* called on this thread. It schedules {@code SwingWorker} for the execution on a
* <i>worker</i>
* thread and returns immediately. One can wait for the {@code SwingWorker} to
* complete using the {@link #get get} methods.
* <li>
* <p>
* <i>Worker</i> thread: The {@link #doInBackground}
* method is called on this thread.
* This is where all background activities should happen. To notify
* {@code PropertyChangeListeners} about bound properties changes use the
* {@link #firePropertyChange firePropertyChange} and
* {@link #getPropertyChangeSupport} methods. By default there are two bound
* properties available: {@code state} and {@code progress}.
* <li>
* <p>
* <i>Event Dispatch Thread</i>: All Swing related activities occur
* on this thread. {@code SwingWorker} invokes the
* {@link #process process} and {@link #done} methods and notifies
* any {@code PropertyChangeListeners} on this thread.
* </ul>
*
* <p>
* Often, the <i>Current</i> thread is the <i>Event Dispatch
* Thread</i>.
*
*
* <p>
* Before the {@code doInBackground} method is invoked on a <i>worker</i> thread,
* {@code SwingWorker} notifies any {@code PropertyChangeListeners} about the
* {@code state} property change to {@code StateValue.STARTED}. After the
* {@code doInBackground} method is finished the {@code done} method is
* executed. Then {@code SwingWorker} notifies any {@code PropertyChangeListeners}
* about the {@code state} property change to {@code StateValue.DONE}.
*
* <p>
* {@code SwingWorker} is only designed to be executed once. Executing a
* {@code SwingWorker} more than once will not result in invoking the
* {@code doInBackground} method twice.
*
* <p>
* <b>Sample Usage</b>
* <p>
* The following example illustrates the simplest use case. Some
* processing is done in the background and when done you update a Swing
* component.
*
* <p>
* Say we want to find the "Meaning of Life" and display the result in
* a {@code JLabel}.
*
* <pre>
* final JLabel label;
* class MeaningOfLifeFinder extends SwingWorker<String, Object> {
* {@code @Override}
* public String doInBackground() {
* return findTheMeaningOfLife();
* }
*
* {@code @Override}
* protected void done() {
* try {
* label.setText(get());
* } catch (Exception ignore) {
* }
* }
* }
*
* (new MeaningOfLifeFinder()).execute();
* </pre>
*
* <p>
* The next example is useful in situations where you wish to process data
* as it is ready on the <i>Event Dispatch Thread</i>.
*
* <p>
* Now we want to find the first N prime numbers and display the results in a
* {@code JTextArea}. While this is computing, we want to update our
* progress in a {@code JProgressBar}. Finally, we also want to print
* the prime numbers to {@code System.out}.
* <pre>
* class PrimeNumbersTask extends
* SwingWorker<List<Integer>, Integer> {
* PrimeNumbersTask(JTextArea textArea, int numbersToFind) {
* //initialize
* }
*
* {@code @Override}
* public List<Integer> doInBackground() {
* while (! enough && ! isCancelled()) {
* number = nextPrimeNumber();
* publish(number);
* setProgress(100 * numbers.size() / numbersToFind);
* }
* }
* return numbers;
* }
*
* {@code @Override}
* protected void process(List<Integer> chunks) {
* for (int number : chunks) {
* textArea.append(number + "\n");
* }
* }
* }
*
* JTextArea textArea = new JTextArea();
* final JProgressBar progressBar = new JProgressBar(0, 100);
* PrimeNumbersTask task = new PrimeNumbersTask(textArea, N);
* task.addPropertyChangeListener(
* new PropertyChangeListener() {
* public void propertyChange(PropertyChangeEvent evt) {
* if ("progress".equals(evt.getPropertyName())) {
* progressBar.setValue((Integer)evt.getNewValue());
* }
* }
* });
*
* task.execute();
* System.out.println(task.get()); //prints all prime numbers we have got
* </pre>
*
* <p>
* Because {@code SwingWorker} implements {@code Runnable}, a
* {@code SwingWorker} can be submitted to an
* {@link java.util.concurrent.Executor} for execution.
*
* @author Igor Kushnirskiy
*
* @param <T> the result type returned by this {@code SwingWorker's}
* {@code doInBackground} and {@code get} methods
* @param <V> the type used for carrying out intermediate results by this
* {@code SwingWorker's} {@code publish} and {@code process} methods
*
* @since 1.6
*/
public abstract class
SwingWorker<T, V> implements
RunnableFuture<T> {
/**
* number of worker threads.
*/
private static final int
MAX_WORKER_THREADS = 10;
/**
* current progress.
*/
private volatile int
progress;
/**
* current state.
*/
private volatile
StateValue state;
/**
* everything is run inside this FutureTask. Also it is used as
* a delegatee for the Future API.
*/
private final
FutureTask<T>
future;
/**
* all propertyChangeSupport goes through this.
*/
private final
PropertyChangeSupport propertyChangeSupport;
/**
* handler for {@code process} mehtod.
*/
private
AccumulativeRunnable<V>
doProcess;
/**
* handler for progress property change notifications.
*/
private
AccumulativeRunnable<
Integer>
doNotifyProgressChange;
private final
AccumulativeRunnable<
Runnable>
doSubmit =
getDoSubmit();
/**
* Values for the {@code state} bound property.
* @since 1.6
*/
public enum
StateValue {
/**
* Initial {@code SwingWorker} state.
*/
PENDING,
/**
* {@code SwingWorker} is {@code STARTED}
* before invoking {@code doInBackground}.
*/
STARTED,
/**
* {@code SwingWorker} is {@code DONE}
* after {@code doInBackground} method
* is finished.
*/
DONE
}
/**
* Constructs this {@code SwingWorker}.
*/
public
SwingWorker() {
Callable<T>
callable =
new
Callable<T>() {
public T
call() throws
Exception {
setState(
StateValue.
STARTED);
return
doInBackground();
}
};
future = new
FutureTask<T>(
callable) {
@
Override
protected void
done() {
doneEDT();
setState(
StateValue.
DONE);
}
};
state =
StateValue.
PENDING;
propertyChangeSupport = new
SwingWorkerPropertyChangeSupport(this);
doProcess = null;
doNotifyProgressChange = null;
}
/**
* Computes a result, or throws an exception if unable to do so.
*
* <p>
* Note that this method is executed only once.
*
* <p>
* Note: this method is executed in a background thread.
*
*
* @return the computed result
* @throws Exception if unable to compute a result
*
*/
protected abstract T
doInBackground() throws
Exception ;
/**
* Sets this {@code Future} to the result of computation unless
* it has been cancelled.
*/
public final void
run() {
future.
run();
}
/**
* Sends data chunks to the {@link #process} method. This method is to be
* used from inside the {@code doInBackground} method to deliver
* intermediate results
* for processing on the <i>Event Dispatch Thread</i> inside the
* {@code process} method.
*
* <p>
* Because the {@code process} method is invoked asynchronously on
* the <i>Event Dispatch Thread</i>
* multiple invocations to the {@code publish} method
* might occur before the {@code process} method is executed. For
* performance purposes all these invocations are coalesced into one
* invocation with concatenated arguments.
*
* <p>
* For example:
*
* <pre>
* publish("1");
* publish("2", "3");
* publish("4", "5", "6");
* </pre>
*
* might result in:
*
* <pre>
* process("1", "2", "3", "4", "5", "6")
* </pre>
*
* <p>
* <b>Sample Usage</b>. This code snippet loads some tabular data and
* updates {@code DefaultTableModel} with it. Note that it safe to mutate
* the tableModel from inside the {@code process} method because it is
* invoked on the <i>Event Dispatch Thread</i>.
*
* <pre>
* class TableSwingWorker extends
* SwingWorker<DefaultTableModel, Object[]> {
* private final DefaultTableModel tableModel;
*
* public TableSwingWorker(DefaultTableModel tableModel) {
* this.tableModel = tableModel;
* }
*
* {@code @Override}
* protected DefaultTableModel doInBackground() throws Exception {
* for (Object[] row = loadData();
* ! isCancelled() && row != null;
* row = loadData()) {
* publish((Object[]) row);
* }
* return tableModel;
* }
*
* {@code @Override}
* protected void process(List<Object[]> chunks) {
* for (Object[] row : chunks) {
* tableModel.addRow(row);
* }
* }
* }
* </pre>
*
* @param chunks intermediate results to process
*
* @see #process
*
*/
@
SafeVarargs
@
SuppressWarnings("varargs") // Passing chunks to add is safe
protected final void
publish(V...
chunks) {
synchronized (this) {
if (
doProcess == null) {
doProcess = new
AccumulativeRunnable<V>() {
@
Override
public void
run(
List<V>
args) {
process(
args);
}
@
Override
protected void
submit() {
doSubmit.
add(this);
}
};
}
}
doProcess.
add(
chunks);
}
/**
* Receives data chunks from the {@code publish} method asynchronously on the
* <i>Event Dispatch Thread</i>.
*
* <p>
* Please refer to the {@link #publish} method for more details.
*
* @param chunks intermediate results to process
*
* @see #publish
*
*/
protected void
process(
List<V>
chunks) {
}
/**
* Executed on the <i>Event Dispatch Thread</i> after the {@code doInBackground}
* method is finished. The default
* implementation does nothing. Subclasses may override this method to
* perform completion actions on the <i>Event Dispatch Thread</i>. Note
* that you can query status inside the implementation of this method to
* determine the result of this task or whether this task has been cancelled.
*
* @see #doInBackground
* @see #isCancelled()
* @see #get
*/
protected void
done() {
}
/**
* Sets the {@code progress} bound property.
* The value should be from 0 to 100.
*
* <p>
* Because {@code PropertyChangeListener}s are notified asynchronously on
* the <i>Event Dispatch Thread</i> multiple invocations to the
* {@code setProgress} method might occur before any
* {@code PropertyChangeListeners} are invoked. For performance purposes
* all these invocations are coalesced into one invocation with the last
* invocation argument only.
*
* <p>
* For example, the following invokations:
*
* <pre>
* setProgress(1);
* setProgress(2);
* setProgress(3);
* </pre>
*
* might result in a single {@code PropertyChangeListener} notification with
* the value {@code 3}.
*
* @param progress the progress value to set
* @throws IllegalArgumentException is value not from 0 to 100
*/
protected final void
setProgress(int
progress) {
if (
progress < 0 ||
progress > 100) {
throw new
IllegalArgumentException("the value should be from 0 to 100");
}
if (this.
progress ==
progress) {
return;
}
int
oldProgress = this.
progress;
this.
progress =
progress;
if (!
getPropertyChangeSupport().
hasListeners("progress")) {
return;
}
synchronized (this) {
if (
doNotifyProgressChange == null) {
doNotifyProgressChange =
new
AccumulativeRunnable<
Integer>() {
@
Override
public void
run(
List<
Integer>
args) {
firePropertyChange("progress",
args.
get(0),
args.
get(
args.
size() - 1));
}
@
Override
protected void
submit() {
doSubmit.
add(this);
}
};
}
}
doNotifyProgressChange.
add(
oldProgress,
progress);
}
/**
* Returns the {@code progress} bound property.
*
* @return the progress bound property.
*/
public final int
getProgress() {
return
progress;
}
/**
* Schedules this {@code SwingWorker} for execution on a <i>worker</i>
* thread. There are a number of <i>worker</i> threads available. In the
* event all <i>worker</i> threads are busy handling other
* {@code SwingWorkers} this {@code SwingWorker} is placed in a waiting
* queue.
*
* <p>
* Note:
* {@code SwingWorker} is only designed to be executed once. Executing a
* {@code SwingWorker} more than once will not result in invoking the
* {@code doInBackground} method twice.
*/
public final void
execute() {
getWorkersExecutorService().
execute(this);
}
// Future methods START
/**
* {@inheritDoc}
*/
public final boolean
cancel(boolean
mayInterruptIfRunning) {
return
future.
cancel(
mayInterruptIfRunning);
}
/**
* {@inheritDoc}
*/
public final boolean
isCancelled() {
return
future.
isCancelled();
}
/**
* {@inheritDoc}
*/
public final boolean
isDone() {
return
future.
isDone();
}
/**
* {@inheritDoc}
* <p>
* Note: calling {@code get} on the <i>Event Dispatch Thread</i> blocks
* <i>all</i> events, including repaints, from being processed until this
* {@code SwingWorker} is complete.
*
* <p>
* When you want the {@code SwingWorker} to block on the <i>Event
* Dispatch Thread</i> we recommend that you use a <i>modal dialog</i>.
*
* <p>
* For example:
*
* <pre>
* class SwingWorkerCompletionWaiter extends PropertyChangeListener {
* private JDialog dialog;
*
* public SwingWorkerCompletionWaiter(JDialog dialog) {
* this.dialog = dialog;
* }
*
* public void propertyChange(PropertyChangeEvent event) {
* if ("state".equals(event.getPropertyName())
* && SwingWorker.StateValue.DONE == event.getNewValue()) {
* dialog.setVisible(false);
* dialog.dispose();
* }
* }
* }
* JDialog dialog = new JDialog(owner, true);
* swingWorker.addPropertyChangeListener(
* new SwingWorkerCompletionWaiter(dialog));
* swingWorker.execute();
* //the dialog will be visible until the SwingWorker is done
* dialog.setVisible(true);
* </pre>
*/
public final T
get() throws
InterruptedException,
ExecutionException {
return
future.
get();
}
/**
* {@inheritDoc}
* <p>
* Please refer to {@link #get} for more details.
*/
public final T
get(long
timeout,
TimeUnit unit) throws
InterruptedException,
ExecutionException,
TimeoutException {
return
future.
get(
timeout,
unit);
}
// Future methods END
// PropertyChangeSupports methods START
/**
* Adds a {@code PropertyChangeListener} to the listener list. The listener
* is registered for all properties. The same listener object may be added
* more than once, and will be called as many times as it is added. If
* {@code listener} is {@code null}, no exception is thrown and no action is taken.
*
* <p>
* Note: This is merely a convenience wrapper. All work is delegated to
* {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
*
* @param listener the {@code PropertyChangeListener} to be added
*/
public final void
addPropertyChangeListener(
PropertyChangeListener listener) {
getPropertyChangeSupport().
addPropertyChangeListener(
listener);
}
/**
* Removes a {@code PropertyChangeListener} from the listener list. This
* removes a {@code PropertyChangeListener} that was registered for all
* properties. If {@code listener} was added more than once to the same
* event source, it will be notified one less time after being removed. If
* {@code listener} is {@code null}, or was never added, no exception is
* thrown and no action is taken.
*
* <p>
* Note: This is merely a convenience wrapper. All work is delegated to
* {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
*
* @param listener the {@code PropertyChangeListener} to be removed
*/
public final void
removePropertyChangeListener(
PropertyChangeListener listener) {
getPropertyChangeSupport().
removePropertyChangeListener(
listener);
}
/**
* Reports a bound property update to any registered listeners. No event is
* fired if {@code old} and {@code new} are equal and non-null.
*
* <p>
* This {@code SwingWorker} will be the source for
* any generated events.
*
* <p>
* When called off the <i>Event Dispatch Thread</i>
* {@code PropertyChangeListeners} are notified asynchronously on
* the <i>Event Dispatch Thread</i>.
* <p>
* Note: This is merely a convenience wrapper. All work is delegated to
* {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
*
*
* @param propertyName the programmatic name of the property that was
* changed
* @param oldValue the old value of the property
* @param newValue the new value of the property
*/
public final void
firePropertyChange(
String propertyName,
Object oldValue,
Object newValue) {
getPropertyChangeSupport().
firePropertyChange(
propertyName,
oldValue,
newValue);
}
/**
* Returns the {@code PropertyChangeSupport} for this {@code SwingWorker}.
* This method is used when flexible access to bound properties support is
* needed.
* <p>
* This {@code SwingWorker} will be the source for
* any generated events.
*
* <p>
* Note: The returned {@code PropertyChangeSupport} notifies any
* {@code PropertyChangeListener}s asynchronously on the <i>Event Dispatch
* Thread</i> in the event that {@code firePropertyChange} or
* {@code fireIndexedPropertyChange} are called off the <i>Event Dispatch
* Thread</i>.
*
* @return {@code PropertyChangeSupport} for this {@code SwingWorker}
*/
public final
PropertyChangeSupport getPropertyChangeSupport() {
return
propertyChangeSupport;
}
// PropertyChangeSupports methods END
/**
* Returns the {@code SwingWorker} state bound property.
*
* @return the current state
*/
public final
StateValue getState() {
/*
* DONE is a speacial case
* to keep getState and isDone is sync
*/
if (
isDone()) {
return
StateValue.
DONE;
} else {
return
state;
}
}
/**
* Sets this {@code SwingWorker} state bound property.
* @param state the state to set
*/
private void
setState(
StateValue state) {
StateValue old = this.
state;
this.
state =
state;
firePropertyChange("state",
old,
state);
}
/**
* Invokes {@code done} on the EDT.
*/
private void
doneEDT() {
Runnable doDone =
new
Runnable() {
public void
run() {
done();
}
};
if (
SwingUtilities.
isEventDispatchThread()) {
doDone.
run();
} else {
doSubmit.
add(
doDone);
}
}
/**
* returns workersExecutorService.
*
* returns the service stored in the appContext or creates it if
* necessary.
*
* @return ExecutorService for the {@code SwingWorkers}
*/
private static synchronized
ExecutorService getWorkersExecutorService() {
final
AppContext appContext =
AppContext.
getAppContext();
ExecutorService executorService =
(
ExecutorService)
appContext.
get(
SwingWorker.class);
if (
executorService == null) {
//this creates daemon threads.
ThreadFactory threadFactory =
new
ThreadFactory() {
final
ThreadFactory defaultFactory =
Executors.
defaultThreadFactory();
public
Thread newThread(final
Runnable r) {
Thread thread =
defaultFactory.
newThread(
r);
thread.
setName("SwingWorker-"
+
thread.
getName());
thread.
setDaemon(true);
return
thread;
}
};
executorService =
new
ThreadPoolExecutor(
MAX_WORKER_THREADS,
MAX_WORKER_THREADS,
10L,
TimeUnit.
MINUTES,
new
LinkedBlockingQueue<
Runnable>(),
threadFactory);
appContext.
put(
SwingWorker.class,
executorService);
// Don't use ShutdownHook here as it's not enough. We should track
// AppContext disposal instead of JVM shutdown, see 6799345 for details
final
ExecutorService es =
executorService;
appContext.
addPropertyChangeListener(
AppContext.
DISPOSED_PROPERTY_NAME,
new
PropertyChangeListener() {
@
Override
public void
propertyChange(
PropertyChangeEvent pce) {
boolean
disposed = (
Boolean)
pce.
getNewValue();
if (
disposed) {
final
WeakReference<
ExecutorService>
executorServiceRef =
new
WeakReference<
ExecutorService>(
es);
final
ExecutorService executorService =
executorServiceRef.
get();
if (
executorService != null) {
AccessController.
doPrivileged(
new
PrivilegedAction<
Void>() {
public
Void run() {
executorService.
shutdown();
return null;
}
}
);
}
}
}
}
);
}
return
executorService;
}
private static final
Object DO_SUBMIT_KEY = new
StringBuilder("doSubmit");
private static
AccumulativeRunnable<
Runnable>
getDoSubmit() {
synchronized (
DO_SUBMIT_KEY) {
final
AppContext appContext =
AppContext.
getAppContext();
Object doSubmit =
appContext.
get(
DO_SUBMIT_KEY);
if (
doSubmit == null) {
doSubmit = new
DoSubmitAccumulativeRunnable();
appContext.
put(
DO_SUBMIT_KEY,
doSubmit);
}
return (
AccumulativeRunnable<
Runnable>)
doSubmit;
}
}
private static class
DoSubmitAccumulativeRunnable
extends
AccumulativeRunnable<
Runnable> implements
ActionListener {
private final static int
DELAY = 1000 / 30;
@
Override
protected void
run(
List<
Runnable>
args) {
for (
Runnable runnable :
args) {
runnable.
run();
}
}
@
Override
protected void
submit() {
Timer timer = new
Timer(
DELAY, this);
timer.
setRepeats(false);
timer.
start();
}
public void
actionPerformed(
ActionEvent event) {
run();
}
}
private class
SwingWorkerPropertyChangeSupport
extends
PropertyChangeSupport {
SwingWorkerPropertyChangeSupport(
Object source) {
super(
source);
}
@
Override
public void
firePropertyChange(final
PropertyChangeEvent evt) {
if (
SwingUtilities.
isEventDispatchThread()) {
super.firePropertyChange(
evt);
} else {
doSubmit.
add(
new
Runnable() {
public void
run() {
SwingWorkerPropertyChangeSupport.this
.
firePropertyChange(
evt);
}
});
}
}
}
}