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