| /* |
| * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| 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="http://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 implements 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); |
| } |
| @SuppressWarnings("unchecked") |
| AccumulativeRunnable<Runnable> tmp = (AccumulativeRunnable<Runnable>) doSubmit; |
| return tmp; |
| } |
| } |
| private static class DoSubmitAccumulativeRunnable |
| extends AccumulativeRunnable<Runnable> implements ActionListener { |
| private static final 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(); |
| } |
| } |
| |
| @SuppressWarnings("serial") // JDK-implementation class |
| 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); |
| } |
| }); |
| } |
| } |
| } |
| } |