1 /*
   2  * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.swing;
  27 
  28 import java.awt.event.ActionEvent;
  29 import java.awt.event.ActionListener;
  30 import java.beans.PropertyChangeEvent;
  31 import java.beans.PropertyChangeListener;
  32 import java.beans.PropertyChangeSupport;
  33 import java.lang.ref.WeakReference;
  34 import java.security.AccessController;
  35 import java.security.PrivilegedAction;
  36 import java.util.List;
  37 import java.util.concurrent.Callable;
  38 import java.util.concurrent.CancellationException;
  39 import java.util.concurrent.ExecutionException;
  40 import java.util.concurrent.ExecutorService;
  41 import java.util.concurrent.Executors;
  42 import java.util.concurrent.FutureTask;
  43 import java.util.concurrent.LinkedBlockingQueue;
  44 import java.util.concurrent.RunnableFuture;
  45 import java.util.concurrent.ThreadFactory;
  46 import java.util.concurrent.ThreadPoolExecutor;
  47 import java.util.concurrent.TimeUnit;
  48 import java.util.concurrent.TimeoutException;
  49 
  50 import sun.awt.AppContext;
  51 import sun.swing.AccumulativeRunnable;
  52 
  53 /**
  54  * An abstract class to perform lengthy GUI-interaction tasks in a
  55  * background thread. Several background threads can be used to execute such
  56  * tasks. However, the exact strategy of choosing a thread for any particular
  57  * {@code SwingWorker} is unspecified and should not be relied on.
  58  * <p>
  59  * When writing a multi-threaded application using Swing, there are
  60  * two constraints to keep in mind:
  61  * (refer to
  62  * <a href="http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html">
  63  *   Concurrency in Swing
  64  * </a> for more details):
  65  * <ul>
  66  *   <li> Time-consuming tasks should not be run on the <i>Event
  67  *        Dispatch Thread</i>. Otherwise the application becomes unresponsive.
  68  *   </li>
  69  *   <li> Swing components should be accessed  on the <i>Event
  70  *        Dispatch Thread</i> only.
  71  *   </li>
  72  * </ul>
  73  *
  74  *
  75  * <p>
  76  * These constraints mean that a GUI application with time intensive
  77  * computing needs at least two threads:  1) a thread to perform the lengthy
  78  * task and 2) the <i>Event Dispatch Thread</i> (EDT) for all GUI-related
  79  * activities.  This involves inter-thread communication which can be
  80  * tricky to implement.
  81  *
  82  * <p>
  83  * {@code SwingWorker} is designed for situations where you need to have a long
  84  * running task run in a background thread and provide updates to the UI
  85  * either when done, or while processing.
  86  * Subclasses of {@code SwingWorker} must implement
  87  * the {@link #doInBackground} method to perform the background computation.
  88  *
  89  *
  90  * <p>
  91  * <b>Workflow</b>
  92  * <p>
  93  * There are three threads involved in the life cycle of a
  94  * {@code SwingWorker} :
  95  * <ul>
  96  * <li>
  97  * <p>
  98  * <i>Current</i> thread: The {@link #execute} method is
  99  * called on this thread. It schedules {@code SwingWorker} for the execution on a
 100  * <i>worker</i>
 101  * thread and returns immediately. One can wait for the {@code SwingWorker} to
 102  * complete using the {@link #get get} methods.
 103  * <li>
 104  * <p>
 105  * <i>Worker</i> thread: The {@link #doInBackground}
 106  * method is called on this thread.
 107  * This is where all background activities should happen. To notify
 108  * {@code PropertyChangeListeners} about bound properties changes use the
 109  * {@link #firePropertyChange firePropertyChange} and
 110  * {@link #getPropertyChangeSupport} methods. By default there are two bound
 111  * properties available: {@code state} and {@code progress}.
 112  * <li>
 113  * <p>
 114  * <i>Event Dispatch Thread</i>:  All Swing related activities occur
 115  * on this thread. {@code SwingWorker} invokes the
 116  * {@link #process process} and {@link #done} methods and notifies
 117  * any {@code PropertyChangeListeners} on this thread.
 118  * </ul>
 119  *
 120  * <p>
 121  * Often, the <i>Current</i> thread is the <i>Event Dispatch
 122  * Thread</i>.
 123  *
 124  *
 125  * <p>
 126  * Before the {@code doInBackground} method is invoked on a <i>worker</i> thread,
 127  * {@code SwingWorker} notifies any {@code PropertyChangeListeners} about the
 128  * {@code state} property change to {@code StateValue.STARTED}.  After the
 129  * {@code doInBackground} method is finished the {@code done} method is
 130  * executed.  Then {@code SwingWorker} notifies any {@code PropertyChangeListeners}
 131  * about the {@code state} property change to {@code StateValue.DONE}.
 132  *
 133  * <p>
 134  * {@code SwingWorker} is only designed to be executed once.  Executing a
 135  * {@code SwingWorker} more than once will not result in invoking the
 136  * {@code doInBackground} method twice.
 137  *
 138  * <p>
 139  * <b>Sample Usage</b>
 140  * <p>
 141  * The following example illustrates the simplest use case.  Some
 142  * processing is done in the background and when done you update a Swing
 143  * component.
 144  *
 145  * <p>
 146  * Say we want to find the "Meaning of Life" and display the result in
 147  * a {@code JLabel}.
 148  *
 149  * <pre>
 150  *   final JLabel label;
 151  *   class MeaningOfLifeFinder extends SwingWorker&lt;String, Object&gt; {
 152  *       {@code @Override}
 153  *       public String doInBackground() {
 154  *           return findTheMeaningOfLife();
 155  *       }
 156  *
 157  *       {@code @Override}
 158  *       protected void done() {
 159  *           try {
 160  *               label.setText(get());
 161  *           } catch (Exception ignore) {
 162  *           }
 163  *       }
 164  *   }
 165  *
 166  *   (new MeaningOfLifeFinder()).execute();
 167  * </pre>
 168  *
 169  * <p>
 170  * The next example is useful in situations where you wish to process data
 171  * as it is ready on the <i>Event Dispatch Thread</i>.
 172  *
 173  * <p>
 174  * Now we want to find the first N prime numbers and display the results in a
 175  * {@code JTextArea}.  While this is computing, we want to update our
 176  * progress in a {@code JProgressBar}.  Finally, we also want to print
 177  * the prime numbers to {@code System.out}.
 178  * <pre>
 179  * class PrimeNumbersTask extends
 180  *         SwingWorker&lt;List&lt;Integer&gt;, Integer&gt; {
 181  *     PrimeNumbersTask(JTextArea textArea, int numbersToFind) {
 182  *         //initialize
 183  *     }
 184  *
 185  *     {@code @Override}
 186  *     public List&lt;Integer&gt; doInBackground() {
 187  *         while (! enough &amp;&amp; ! isCancelled()) {
 188  *                 number = nextPrimeNumber();
 189  *                 publish(number);
 190  *                 setProgress(100 * numbers.size() / numbersToFind);
 191  *             }
 192  *         }
 193  *         return numbers;
 194  *     }
 195  *
 196  *     {@code @Override}
 197  *     protected void process(List&lt;Integer&gt; chunks) {
 198  *         for (int number : chunks) {
 199  *             textArea.append(number + &quot;\n&quot;);
 200  *         }
 201  *     }
 202  * }
 203  *
 204  * JTextArea textArea = new JTextArea();
 205  * final JProgressBar progressBar = new JProgressBar(0, 100);
 206  * PrimeNumbersTask task = new PrimeNumbersTask(textArea, N);
 207  * task.addPropertyChangeListener(
 208  *     new PropertyChangeListener() {
 209  *         public  void propertyChange(PropertyChangeEvent evt) {
 210  *             if (&quot;progress&quot;.equals(evt.getPropertyName())) {
 211  *                 progressBar.setValue((Integer)evt.getNewValue());
 212  *             }
 213  *         }
 214  *     });
 215  *
 216  * task.execute();
 217  * System.out.println(task.get()); //prints all prime numbers we have got
 218  * </pre>
 219  *
 220  * <p>
 221  * Because {@code SwingWorker} implements {@code Runnable}, a
 222  * {@code SwingWorker} can be submitted to an
 223  * {@link java.util.concurrent.Executor} for execution.
 224  *
 225  * @author Igor Kushnirskiy
 226  *
 227  * @param <T> the result type returned by this {@code SwingWorker's}
 228  *        {@code doInBackground} and {@code get} methods
 229  * @param <V> the type used for carrying out intermediate results by this
 230  *        {@code SwingWorker's} {@code publish} and {@code process} methods
 231  *
 232  * @since 1.6
 233  */
 234 public abstract class SwingWorker<T, V> implements RunnableFuture<T> {
 235     /**
 236      * number of worker threads.
 237      */
 238     private static final int MAX_WORKER_THREADS = 10;
 239 
 240     /**
 241      * current progress.
 242      */
 243     private volatile int progress;
 244 
 245     /**
 246      * current state.
 247      */
 248     private volatile StateValue state;
 249 
 250     /**
 251      * everything is run inside this FutureTask. Also it is used as
 252      * a delegatee for the Future API.
 253      */
 254     private final FutureTask<T> future;
 255 
 256     /**
 257      * all propertyChangeSupport goes through this.
 258      */
 259     private final PropertyChangeSupport propertyChangeSupport;
 260 
 261     /**
 262      * handler for {@code process} mehtod.
 263      */
 264     private AccumulativeRunnable<V> doProcess;
 265 
 266     /**
 267      * handler for progress property change notifications.
 268      */
 269     private AccumulativeRunnable<Integer> doNotifyProgressChange;
 270 
 271     private final AccumulativeRunnable<Runnable> doSubmit = getDoSubmit();
 272 
 273     /**
 274      * Values for the {@code state} bound property.
 275      * @since 1.6
 276      */
 277     public enum StateValue {
 278         /**
 279          * Initial {@code SwingWorker} state.
 280          */
 281         PENDING,
 282         /**
 283          * {@code SwingWorker} is {@code STARTED}
 284          * before invoking {@code doInBackground}.
 285          */
 286         STARTED,
 287 
 288         /**
 289          * {@code SwingWorker} is {@code DONE}
 290          * after {@code doInBackground} method
 291          * is finished.
 292          */
 293         DONE
 294     }
 295 
 296     /**
 297      * Constructs this {@code SwingWorker}.
 298      */
 299     public SwingWorker() {
 300         Callable<T> callable =
 301                 new Callable<T>() {
 302                     public T call() throws Exception {
 303                         setState(StateValue.STARTED);
 304                         return doInBackground();
 305                     }
 306                 };
 307 
 308         future = new FutureTask<T>(callable) {
 309                        @Override
 310                        protected void done() {
 311                            doneEDT();
 312                            setState(StateValue.DONE);
 313                        }
 314                    };
 315 
 316        state = StateValue.PENDING;
 317        propertyChangeSupport = new SwingWorkerPropertyChangeSupport(this);
 318        doProcess = null;
 319        doNotifyProgressChange = null;
 320     }
 321 
 322     /**
 323      * Computes a result, or throws an exception if unable to do so.
 324      *
 325      * <p>
 326      * Note that this method is executed only once.
 327      *
 328      * <p>
 329      * Note: this method is executed in a background thread.
 330      *
 331      *
 332      * @return the computed result
 333      * @throws Exception if unable to compute a result
 334      *
 335      */
 336     protected abstract T doInBackground() throws Exception ;
 337 
 338     /**
 339      * Sets this {@code Future} to the result of computation unless
 340      * it has been cancelled.
 341      */
 342     public final void run() {
 343         future.run();
 344     }
 345 
 346     /**
 347      * Sends data chunks to the {@link #process} method. This method is to be
 348      * used from inside the {@code doInBackground} method to deliver
 349      * intermediate results
 350      * for processing on the <i>Event Dispatch Thread</i> inside the
 351      * {@code process} method.
 352      *
 353      * <p>
 354      * Because the {@code process} method is invoked asynchronously on
 355      * the <i>Event Dispatch Thread</i>
 356      * multiple invocations to the {@code publish} method
 357      * might occur before the {@code process} method is executed. For
 358      * performance purposes all these invocations are coalesced into one
 359      * invocation with concatenated arguments.
 360      *
 361      * <p>
 362      * For example:
 363      *
 364      * <pre>
 365      * publish(&quot;1&quot;);
 366      * publish(&quot;2&quot;, &quot;3&quot;);
 367      * publish(&quot;4&quot;, &quot;5&quot;, &quot;6&quot;);
 368      * </pre>
 369      *
 370      * might result in:
 371      *
 372      * <pre>
 373      * process(&quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;, &quot;5&quot;, &quot;6&quot;)
 374      * </pre>
 375      *
 376      * <p>
 377      * <b>Sample Usage</b>. This code snippet loads some tabular data and
 378      * updates {@code DefaultTableModel} with it. Note that it safe to mutate
 379      * the tableModel from inside the {@code process} method because it is
 380      * invoked on the <i>Event Dispatch Thread</i>.
 381      *
 382      * <pre>
 383      * class TableSwingWorker extends
 384      *         SwingWorker&lt;DefaultTableModel, Object[]&gt; {
 385      *     private final DefaultTableModel tableModel;
 386      *
 387      *     public TableSwingWorker(DefaultTableModel tableModel) {
 388      *         this.tableModel = tableModel;
 389      *     }
 390      *
 391      *     {@code @Override}
 392      *     protected DefaultTableModel doInBackground() throws Exception {
 393      *         for (Object[] row = loadData();
 394      *                  ! isCancelled() &amp;&amp; row != null;
 395      *                  row = loadData()) {
 396      *             publish((Object[]) row);
 397      *         }
 398      *         return tableModel;
 399      *     }
 400      *
 401      *     {@code @Override}
 402      *     protected void process(List&lt;Object[]&gt; chunks) {
 403      *         for (Object[] row : chunks) {
 404      *             tableModel.addRow(row);
 405      *         }
 406      *     }
 407      * }
 408      * </pre>
 409      *
 410      * @param chunks intermediate results to process
 411      *
 412      * @see #process
 413      *
 414      */
 415     @SafeVarargs
 416     @SuppressWarnings("varargs") // Passing chunks to add is safe
 417     protected final void publish(V... chunks) {
 418         synchronized (this) {
 419             if (doProcess == null) {
 420                 doProcess = new AccumulativeRunnable<V>() {
 421                     @Override
 422                     public void run(List<V> args) {
 423                         process(args);
 424                     }
 425                     @Override
 426                     protected void submit() {
 427                         doSubmit.add(this);
 428                     }
 429                 };
 430             }
 431         }
 432         doProcess.add(chunks);
 433     }
 434 
 435     /**
 436      * Receives data chunks from the {@code publish} method asynchronously on the
 437      * <i>Event Dispatch Thread</i>.
 438      *
 439      * <p>
 440      * Please refer to the {@link #publish} method for more details.
 441      *
 442      * @param chunks intermediate results to process
 443      *
 444      * @see #publish
 445      *
 446      */
 447     protected void process(List<V> chunks) {
 448     }
 449 
 450     /**
 451      * Executed on the <i>Event Dispatch Thread</i> after the {@code doInBackground}
 452      * method is finished. The default
 453      * implementation does nothing. Subclasses may override this method to
 454      * perform completion actions on the <i>Event Dispatch Thread</i>. Note
 455      * that you can query status inside the implementation of this method to
 456      * determine the result of this task or whether this task has been cancelled.
 457      *
 458      * @see #doInBackground
 459      * @see #isCancelled()
 460      * @see #get
 461      */
 462     protected void done() {
 463     }
 464 
 465     /**
 466      * Sets the {@code progress} bound property.
 467      * The value should be from 0 to 100.
 468      *
 469      * <p>
 470      * Because {@code PropertyChangeListener}s are notified asynchronously on
 471      * the <i>Event Dispatch Thread</i> multiple invocations to the
 472      * {@code setProgress} method might occur before any
 473      * {@code PropertyChangeListeners} are invoked. For performance purposes
 474      * all these invocations are coalesced into one invocation with the last
 475      * invocation argument only.
 476      *
 477      * <p>
 478      * For example, the following invokations:
 479      *
 480      * <pre>
 481      * setProgress(1);
 482      * setProgress(2);
 483      * setProgress(3);
 484      * </pre>
 485      *
 486      * might result in a single {@code PropertyChangeListener} notification with
 487      * the value {@code 3}.
 488      *
 489      * @param progress the progress value to set
 490      * @throws IllegalArgumentException is value not from 0 to 100
 491      */
 492     protected final void setProgress(int progress) {
 493         if (progress < 0 || progress > 100) {
 494             throw new IllegalArgumentException("the value should be from 0 to 100");
 495         }
 496         if (this.progress == progress) {
 497             return;
 498         }
 499         int oldProgress = this.progress;
 500         this.progress = progress;
 501         if (! getPropertyChangeSupport().hasListeners("progress")) {
 502             return;
 503         }
 504         synchronized (this) {
 505             if (doNotifyProgressChange == null) {
 506                 doNotifyProgressChange =
 507                     new AccumulativeRunnable<Integer>() {
 508                         @Override
 509                         public void run(List<Integer> args) {
 510                             firePropertyChange("progress",
 511                                args.get(0),
 512                                args.get(args.size() - 1));
 513                         }
 514                         @Override
 515                         protected void submit() {
 516                             doSubmit.add(this);
 517                         }
 518                     };
 519             }
 520         }
 521         doNotifyProgressChange.add(oldProgress, progress);
 522     }
 523 
 524     /**
 525      * Returns the {@code progress} bound property.
 526      *
 527      * @return the progress bound property.
 528      */
 529     public final int getProgress() {
 530         return progress;
 531     }
 532 
 533     /**
 534      * Schedules this {@code SwingWorker} for execution on a <i>worker</i>
 535      * thread. There are a number of <i>worker</i> threads available. In the
 536      * event all <i>worker</i> threads are busy handling other
 537      * {@code SwingWorkers} this {@code SwingWorker} is placed in a waiting
 538      * queue.
 539      *
 540      * <p>
 541      * Note:
 542      * {@code SwingWorker} is only designed to be executed once.  Executing a
 543      * {@code SwingWorker} more than once will not result in invoking the
 544      * {@code doInBackground} method twice.
 545      */
 546     public final void execute() {
 547         getWorkersExecutorService().execute(this);
 548     }
 549 
 550     // Future methods START
 551     /**
 552      * {@inheritDoc}
 553      */
 554     public final boolean cancel(boolean mayInterruptIfRunning) {
 555         return future.cancel(mayInterruptIfRunning);
 556     }
 557 
 558     /**
 559      * {@inheritDoc}
 560      */
 561     public final boolean isCancelled() {
 562         return future.isCancelled();
 563     }
 564 
 565     /**
 566      * {@inheritDoc}
 567      */
 568     public final boolean isDone() {
 569         return future.isDone();
 570     }
 571 
 572     /**
 573      * {@inheritDoc}
 574      * <p>
 575      * Note: calling {@code get} on the <i>Event Dispatch Thread</i> blocks
 576      * <i>all</i> events, including repaints, from being processed until this
 577      * {@code SwingWorker} is complete.
 578      *
 579      * <p>
 580      * When you want the {@code SwingWorker} to block on the <i>Event
 581      * Dispatch Thread</i> we recommend that you use a <i>modal dialog</i>.
 582      *
 583      * <p>
 584      * For example:
 585      *
 586      * <pre>
 587      * class SwingWorkerCompletionWaiter implements PropertyChangeListener {
 588      *     private JDialog dialog;
 589      *
 590      *     public SwingWorkerCompletionWaiter(JDialog dialog) {
 591      *         this.dialog = dialog;
 592      *     }
 593      *
 594      *     public void propertyChange(PropertyChangeEvent event) {
 595      *         if (&quot;state&quot;.equals(event.getPropertyName())
 596      *                 &amp;&amp; SwingWorker.StateValue.DONE == event.getNewValue()) {
 597      *             dialog.setVisible(false);
 598      *             dialog.dispose();
 599      *         }
 600      *     }
 601      * }
 602      * JDialog dialog = new JDialog(owner, true);
 603      * swingWorker.addPropertyChangeListener(
 604      *     new SwingWorkerCompletionWaiter(dialog));
 605      * swingWorker.execute();
 606      * //the dialog will be visible until the SwingWorker is done
 607      * dialog.setVisible(true);
 608      * </pre>
 609      *
 610      * @throws CancellationException {@inheritDoc}
 611      */
 612     public final T get() throws InterruptedException, ExecutionException {
 613         return future.get();
 614     }
 615 
 616     /**
 617      * {@inheritDoc}
 618      * <p>
 619      * Please refer to {@link #get} for more details.
 620      *
 621      * @throws CancellationException {@inheritDoc}
 622      */
 623     public final T get(long timeout, TimeUnit unit) throws InterruptedException,
 624             ExecutionException, TimeoutException {
 625         return future.get(timeout, unit);
 626     }
 627 
 628     // Future methods END
 629 
 630     // PropertyChangeSupports methods START
 631     /**
 632      * Adds a {@code PropertyChangeListener} to the listener list. The listener
 633      * is registered for all properties. The same listener object may be added
 634      * more than once, and will be called as many times as it is added. If
 635      * {@code listener} is {@code null}, no exception is thrown and no action is taken.
 636      *
 637      * <p>
 638      * Note: This is merely a convenience wrapper. All work is delegated to
 639      * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
 640      *
 641      * @param listener the {@code PropertyChangeListener} to be added
 642      */
 643     public final void addPropertyChangeListener(PropertyChangeListener listener) {
 644         getPropertyChangeSupport().addPropertyChangeListener(listener);
 645     }
 646 
 647     /**
 648      * Removes a {@code PropertyChangeListener} from the listener list. This
 649      * removes a {@code PropertyChangeListener} that was registered for all
 650      * properties. If {@code listener} was added more than once to the same
 651      * event source, it will be notified one less time after being removed. If
 652      * {@code listener} is {@code null}, or was never added, no exception is
 653      * thrown and no action is taken.
 654      *
 655      * <p>
 656      * Note: This is merely a convenience wrapper. All work is delegated to
 657      * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
 658      *
 659      * @param listener the {@code PropertyChangeListener} to be removed
 660      */
 661     public final void removePropertyChangeListener(PropertyChangeListener listener) {
 662         getPropertyChangeSupport().removePropertyChangeListener(listener);
 663     }
 664 
 665     /**
 666      * Reports a bound property update to any registered listeners. No event is
 667      * fired if {@code old} and {@code new} are equal and non-null.
 668      *
 669      * <p>
 670      * This {@code SwingWorker} will be the source for
 671      * any generated events.
 672      *
 673      * <p>
 674      * When called off the <i>Event Dispatch Thread</i>
 675      * {@code PropertyChangeListeners} are notified asynchronously on
 676      * the <i>Event Dispatch Thread</i>.
 677      * <p>
 678      * Note: This is merely a convenience wrapper. All work is delegated to
 679      * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
 680      *
 681      *
 682      * @param propertyName the programmatic name of the property that was
 683      *        changed
 684      * @param oldValue the old value of the property
 685      * @param newValue the new value of the property
 686      */
 687     public final void firePropertyChange(String propertyName, Object oldValue,
 688             Object newValue) {
 689         getPropertyChangeSupport().firePropertyChange(propertyName,
 690             oldValue, newValue);
 691     }
 692 
 693     /**
 694      * Returns the {@code PropertyChangeSupport} for this {@code SwingWorker}.
 695      * This method is used when flexible access to bound properties support is
 696      * needed.
 697      * <p>
 698      * This {@code SwingWorker} will be the source for
 699      * any generated events.
 700      *
 701      * <p>
 702      * Note: The returned {@code PropertyChangeSupport} notifies any
 703      * {@code PropertyChangeListener}s asynchronously on the <i>Event Dispatch
 704      * Thread</i> in the event that {@code firePropertyChange} or
 705      * {@code fireIndexedPropertyChange} are called off the <i>Event Dispatch
 706      * Thread</i>.
 707      *
 708      * @return {@code PropertyChangeSupport} for this {@code SwingWorker}
 709      */
 710     public final PropertyChangeSupport getPropertyChangeSupport() {
 711         return propertyChangeSupport;
 712     }
 713 
 714     // PropertyChangeSupports methods END
 715 
 716     /**
 717      * Returns the {@code SwingWorker} state bound property.
 718      *
 719      * @return the current state
 720      */
 721     public final StateValue getState() {
 722         /*
 723          * DONE is a speacial case
 724          * to keep getState and isDone is sync
 725          */
 726         if (isDone()) {
 727             return StateValue.DONE;
 728         } else {
 729             return state;
 730         }
 731     }
 732 
 733     /**
 734      * Sets this {@code SwingWorker} state bound property.
 735      * @param state the state to set
 736      */
 737     private void setState(StateValue state) {
 738         StateValue old = this.state;
 739         this.state = state;
 740         firePropertyChange("state", old, state);
 741     }
 742 
 743     /**
 744      * Invokes {@code done} on the EDT.
 745      */
 746     private void doneEDT() {
 747         Runnable doDone =
 748             new Runnable() {
 749                 public void run() {
 750                     done();
 751                 }
 752             };
 753         if (SwingUtilities.isEventDispatchThread()) {
 754             doDone.run();
 755         } else {
 756             doSubmit.add(doDone);
 757         }
 758     }
 759 
 760 
 761     /**
 762      * returns workersExecutorService.
 763      *
 764      * returns the service stored in the appContext or creates it if
 765      * necessary.
 766      *
 767      * @return ExecutorService for the {@code SwingWorkers}
 768      */
 769     private static synchronized ExecutorService getWorkersExecutorService() {
 770         final AppContext appContext = AppContext.getAppContext();
 771         ExecutorService executorService =
 772             (ExecutorService) appContext.get(SwingWorker.class);
 773         if (executorService == null) {
 774             //this creates daemon threads.
 775             ThreadFactory threadFactory =
 776                 new ThreadFactory() {
 777                     final ThreadFactory defaultFactory =
 778                         Executors.defaultThreadFactory();
 779                     public Thread newThread(final Runnable r) {
 780                         Thread thread =
 781                             defaultFactory.newThread(r);
 782                         thread.setName("SwingWorker-"
 783                             + thread.getName());
 784                         thread.setDaemon(true);
 785                         return thread;
 786                     }
 787                 };
 788 
 789             executorService =
 790                 new ThreadPoolExecutor(MAX_WORKER_THREADS, MAX_WORKER_THREADS,
 791                                        10L, TimeUnit.MINUTES,
 792                                        new LinkedBlockingQueue<Runnable>(),
 793                                        threadFactory);
 794             appContext.put(SwingWorker.class, executorService);
 795 
 796             // Don't use ShutdownHook here as it's not enough. We should track
 797             // AppContext disposal instead of JVM shutdown, see 6799345 for details
 798             final ExecutorService es = executorService;
 799             appContext.addPropertyChangeListener(AppContext.DISPOSED_PROPERTY_NAME,
 800                 new PropertyChangeListener() {
 801                     @Override
 802                     public void propertyChange(PropertyChangeEvent pce) {
 803                         boolean disposed = (Boolean)pce.getNewValue();
 804                         if (disposed) {
 805                             final WeakReference<ExecutorService> executorServiceRef =
 806                                 new WeakReference<ExecutorService>(es);
 807                             final ExecutorService executorService =
 808                                 executorServiceRef.get();
 809                             if (executorService != null) {
 810                                 AccessController.doPrivileged(
 811                                     new PrivilegedAction<Void>() {
 812                                         public Void run() {
 813                                             executorService.shutdown();
 814                                             return null;
 815                                         }
 816                                     }
 817                                 );
 818                             }
 819                         }
 820                     }
 821                 }
 822             );
 823         }
 824         return executorService;
 825     }
 826 
 827     private static final Object DO_SUBMIT_KEY = new StringBuilder("doSubmit");
 828     private static AccumulativeRunnable<Runnable> getDoSubmit() {
 829         synchronized (DO_SUBMIT_KEY) {
 830             final AppContext appContext = AppContext.getAppContext();
 831             Object doSubmit = appContext.get(DO_SUBMIT_KEY);
 832             if (doSubmit == null) {
 833                 doSubmit = new DoSubmitAccumulativeRunnable();
 834                 appContext.put(DO_SUBMIT_KEY, doSubmit);
 835             }
 836             @SuppressWarnings("unchecked")
 837             AccumulativeRunnable<Runnable> tmp = (AccumulativeRunnable<Runnable>) doSubmit;
 838             return tmp;
 839         }
 840     }
 841     private static class DoSubmitAccumulativeRunnable
 842           extends AccumulativeRunnable<Runnable> implements ActionListener {
 843         private static final int DELAY = 1000 / 30;
 844         @Override
 845         protected void run(List<Runnable> args) {
 846             for (Runnable runnable : args) {
 847                 runnable.run();
 848             }
 849         }
 850         @Override
 851         protected void submit() {
 852             Timer timer = new Timer(DELAY, this);
 853             timer.setRepeats(false);
 854             timer.start();
 855         }
 856         public void actionPerformed(ActionEvent event) {
 857             run();
 858         }
 859     }
 860 
 861     @SuppressWarnings("serial") // JDK-implementation class
 862     private class SwingWorkerPropertyChangeSupport
 863             extends PropertyChangeSupport {
 864         SwingWorkerPropertyChangeSupport(Object source) {
 865             super(source);
 866         }
 867         @Override
 868         public void firePropertyChange(final PropertyChangeEvent evt) {
 869             if (SwingUtilities.isEventDispatchThread()) {
 870                 super.firePropertyChange(evt);
 871             } else {
 872                 doSubmit.add(
 873                     new Runnable() {
 874                         public void run() {
 875                             SwingWorkerPropertyChangeSupport.this
 876                                 .firePropertyChange(evt);
 877                         }
 878                     });
 879             }
 880         }
 881     }
 882 }