1 /*
   2  * Copyright (c) 2005, 2013, 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://download.oracle.com/javase/tutorial/uiswing/concurrency/index.html">
  54  *   Concurrency in Swing
  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     @SafeVarargs
 408     @SuppressWarnings("varargs") // Passing chunks to add is safe
 409     protected final void publish(V... chunks) {
 410         synchronized (this) {
 411             if (doProcess == null) {
 412                 doProcess = new AccumulativeRunnable<V>() {
 413                     @Override
 414                     public void run(List<V> args) {
 415                         process(args);
 416                     }
 417                     @Override
 418                     protected void submit() {
 419                         doSubmit.add(this);
 420                     }
 421                 };
 422             }
 423         }
 424         doProcess.add(chunks);
 425     }
 426 
 427     /**
 428      * Receives data chunks from the {@code publish} method asynchronously on the
 429      * <i>Event Dispatch Thread</i>.
 430      *
 431      * <p>
 432      * Please refer to the {@link #publish} method for more details.
 433      *
 434      * @param chunks intermediate results to process
 435      *
 436      * @see #publish
 437      *
 438      */
 439     protected void process(List<V> chunks) {
 440     }
 441 
 442     /**
 443      * Executed on the <i>Event Dispatch Thread</i> after the {@code doInBackground}
 444      * method is finished. The default
 445      * implementation does nothing. Subclasses may override this method to
 446      * perform completion actions on the <i>Event Dispatch Thread</i>. Note
 447      * that you can query status inside the implementation of this method to
 448      * determine the result of this task or whether this task has been cancelled.
 449      *
 450      * @see #doInBackground
 451      * @see #isCancelled()
 452      * @see #get
 453      */
 454     protected void done() {
 455     }
 456 
 457     /**
 458      * Sets the {@code progress} bound property.
 459      * The value should be from 0 to 100.
 460      *
 461      * <p>
 462      * Because {@code PropertyChangeListener}s are notified asynchronously on
 463      * the <i>Event Dispatch Thread</i> multiple invocations to the
 464      * {@code setProgress} method might occur before any
 465      * {@code PropertyChangeListeners} are invoked. For performance purposes
 466      * all these invocations are coalesced into one invocation with the last
 467      * invocation argument only.
 468      *
 469      * <p>
 470      * For example, the following invokations:
 471      *
 472      * <pre>
 473      * setProgress(1);
 474      * setProgress(2);
 475      * setProgress(3);
 476      * </pre>
 477      *
 478      * might result in a single {@code PropertyChangeListener} notification with
 479      * the value {@code 3}.
 480      *
 481      * @param progress the progress value to set
 482      * @throws IllegalArgumentException is value not from 0 to 100
 483      */
 484     protected final void setProgress(int progress) {
 485         if (progress < 0 || progress > 100) {
 486             throw new IllegalArgumentException("the value should be from 0 to 100");
 487         }
 488         if (this.progress == progress) {
 489             return;
 490         }
 491         int oldProgress = this.progress;
 492         this.progress = progress;
 493         if (! getPropertyChangeSupport().hasListeners("progress")) {
 494             return;
 495         }
 496         synchronized (this) {
 497             if (doNotifyProgressChange == null) {
 498                 doNotifyProgressChange =
 499                     new AccumulativeRunnable<Integer>() {
 500                         @Override
 501                         public void run(List<Integer> args) {
 502                             firePropertyChange("progress",
 503                                args.get(0),
 504                                args.get(args.size() - 1));
 505                         }
 506                         @Override
 507                         protected void submit() {
 508                             doSubmit.add(this);
 509                         }
 510                     };
 511             }
 512         }
 513         doNotifyProgressChange.add(oldProgress, progress);
 514     }
 515 
 516     /**
 517      * Returns the {@code progress} bound property.
 518      *
 519      * @return the progress bound property.
 520      */
 521     public final int getProgress() {
 522         return progress;
 523     }
 524 
 525     /**
 526      * Schedules this {@code SwingWorker} for execution on a <i>worker</i>
 527      * thread. There are a number of <i>worker</i> threads available. In the
 528      * event all <i>worker</i> threads are busy handling other
 529      * {@code SwingWorkers} this {@code SwingWorker} is placed in a waiting
 530      * queue.
 531      *
 532      * <p>
 533      * Note:
 534      * {@code SwingWorker} is only designed to be executed once.  Executing a
 535      * {@code SwingWorker} more than once will not result in invoking the
 536      * {@code doInBackground} method twice.
 537      */
 538     public final void execute() {
 539         getWorkersExecutorService().execute(this);
 540     }
 541 
 542     // Future methods START
 543     /**
 544      * {@inheritDoc}
 545      */
 546     public final boolean cancel(boolean mayInterruptIfRunning) {
 547         return future.cancel(mayInterruptIfRunning);
 548     }
 549 
 550     /**
 551      * {@inheritDoc}
 552      */
 553     public final boolean isCancelled() {
 554         return future.isCancelled();
 555     }
 556 
 557     /**
 558      * {@inheritDoc}
 559      */
 560     public final boolean isDone() {
 561         return future.isDone();
 562     }
 563 
 564     /**
 565      * {@inheritDoc}
 566      * <p>
 567      * Note: calling {@code get} on the <i>Event Dispatch Thread</i> blocks
 568      * <i>all</i> events, including repaints, from being processed until this
 569      * {@code SwingWorker} is complete.
 570      *
 571      * <p>
 572      * When you want the {@code SwingWorker} to block on the <i>Event
 573      * Dispatch Thread</i> we recommend that you use a <i>modal dialog</i>.
 574      *
 575      * <p>
 576      * For example:
 577      *
 578      * <pre>
 579      * class SwingWorkerCompletionWaiter extends PropertyChangeListener {
 580      *     private JDialog dialog;
 581      *
 582      *     public SwingWorkerCompletionWaiter(JDialog dialog) {
 583      *         this.dialog = dialog;
 584      *     }
 585      *
 586      *     public void propertyChange(PropertyChangeEvent event) {
 587      *         if (&quot;state&quot;.equals(event.getPropertyName())
 588      *                 &amp;&amp; SwingWorker.StateValue.DONE == event.getNewValue()) {
 589      *             dialog.setVisible(false);
 590      *             dialog.dispose();
 591      *         }
 592      *     }
 593      * }
 594      * JDialog dialog = new JDialog(owner, true);
 595      * swingWorker.addPropertyChangeListener(
 596      *     new SwingWorkerCompletionWaiter(dialog));
 597      * swingWorker.execute();
 598      * //the dialog will be visible until the SwingWorker is done
 599      * dialog.setVisible(true);
 600      * </pre>
 601      */
 602     public final T get() throws InterruptedException, ExecutionException {
 603         return future.get();
 604     }
 605 
 606     /**
 607      * {@inheritDoc}
 608      * <p>
 609      * Please refer to {@link #get} for more details.
 610      */
 611     public final T get(long timeout, TimeUnit unit) throws InterruptedException,
 612             ExecutionException, TimeoutException {
 613         return future.get(timeout, unit);
 614     }
 615 
 616     // Future methods END
 617 
 618     // PropertyChangeSupports methods START
 619     /**
 620      * Adds a {@code PropertyChangeListener} to the listener list. The listener
 621      * is registered for all properties. The same listener object may be added
 622      * more than once, and will be called as many times as it is added. If
 623      * {@code listener} is {@code null}, no exception is thrown and no action is taken.
 624      *
 625      * <p>
 626      * Note: This is merely a convenience wrapper. All work is delegated to
 627      * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
 628      *
 629      * @param listener the {@code PropertyChangeListener} to be added
 630      */
 631     public final void addPropertyChangeListener(PropertyChangeListener listener) {
 632         getPropertyChangeSupport().addPropertyChangeListener(listener);
 633     }
 634 
 635     /**
 636      * Removes a {@code PropertyChangeListener} from the listener list. This
 637      * removes a {@code PropertyChangeListener} that was registered for all
 638      * properties. If {@code listener} was added more than once to the same
 639      * event source, it will be notified one less time after being removed. If
 640      * {@code listener} is {@code null}, or was never added, no exception is
 641      * thrown and no action is taken.
 642      *
 643      * <p>
 644      * Note: This is merely a convenience wrapper. All work is delegated to
 645      * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
 646      *
 647      * @param listener the {@code PropertyChangeListener} to be removed
 648      */
 649     public final void removePropertyChangeListener(PropertyChangeListener listener) {
 650         getPropertyChangeSupport().removePropertyChangeListener(listener);
 651     }
 652 
 653     /**
 654      * Reports a bound property update to any registered listeners. No event is
 655      * fired if {@code old} and {@code new} are equal and non-null.
 656      *
 657      * <p>
 658      * This {@code SwingWorker} will be the source for
 659      * any generated events.
 660      *
 661      * <p>
 662      * When called off the <i>Event Dispatch Thread</i>
 663      * {@code PropertyChangeListeners} are notified asynchronously on
 664      * the <i>Event Dispatch Thread</i>.
 665      * <p>
 666      * Note: This is merely a convenience wrapper. All work is delegated to
 667      * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
 668      *
 669      *
 670      * @param propertyName the programmatic name of the property that was
 671      *        changed
 672      * @param oldValue the old value of the property
 673      * @param newValue the new value of the property
 674      */
 675     public final void firePropertyChange(String propertyName, Object oldValue,
 676             Object newValue) {
 677         getPropertyChangeSupport().firePropertyChange(propertyName,
 678             oldValue, newValue);
 679     }
 680 
 681     /**
 682      * Returns the {@code PropertyChangeSupport} for this {@code SwingWorker}.
 683      * This method is used when flexible access to bound properties support is
 684      * needed.
 685      * <p>
 686      * This {@code SwingWorker} will be the source for
 687      * any generated events.
 688      *
 689      * <p>
 690      * Note: The returned {@code PropertyChangeSupport} notifies any
 691      * {@code PropertyChangeListener}s asynchronously on the <i>Event Dispatch
 692      * Thread</i> in the event that {@code firePropertyChange} or
 693      * {@code fireIndexedPropertyChange} are called off the <i>Event Dispatch
 694      * Thread</i>.
 695      *
 696      * @return {@code PropertyChangeSupport} for this {@code SwingWorker}
 697      */
 698     public final PropertyChangeSupport getPropertyChangeSupport() {
 699         return propertyChangeSupport;
 700     }
 701 
 702     // PropertyChangeSupports methods END
 703 
 704     /**
 705      * Returns the {@code SwingWorker} state bound property.
 706      *
 707      * @return the current state
 708      */
 709     public final StateValue getState() {
 710         /*
 711          * DONE is a speacial case
 712          * to keep getState and isDone is sync
 713          */
 714         if (isDone()) {
 715             return StateValue.DONE;
 716         } else {
 717             return state;
 718         }
 719     }
 720 
 721     /**
 722      * Sets this {@code SwingWorker} state bound property.
 723      * @param state the state to set
 724      */
 725     private void setState(StateValue state) {
 726         StateValue old = this.state;
 727         this.state = state;
 728         firePropertyChange("state", old, state);
 729     }
 730 
 731     /**
 732      * Invokes {@code done} on the EDT.
 733      */
 734     private void doneEDT() {
 735         Runnable doDone =
 736             new Runnable() {
 737                 public void run() {
 738                     done();
 739                 }
 740             };
 741         if (SwingUtilities.isEventDispatchThread()) {
 742             doDone.run();
 743         } else {
 744             doSubmit.add(doDone);
 745         }
 746     }
 747 
 748 
 749     /**
 750      * returns workersExecutorService.
 751      *
 752      * returns the service stored in the appContext or creates it if
 753      * necessary.
 754      *
 755      * @return ExecutorService for the {@code SwingWorkers}
 756      */
 757     private static synchronized ExecutorService getWorkersExecutorService() {
 758         final AppContext appContext = AppContext.getAppContext();
 759         ExecutorService executorService =
 760             (ExecutorService) appContext.get(SwingWorker.class);
 761         if (executorService == null) {
 762             //this creates daemon threads.
 763             ThreadFactory threadFactory =
 764                 new ThreadFactory() {
 765                     final ThreadFactory defaultFactory =
 766                         Executors.defaultThreadFactory();
 767                     public Thread newThread(final Runnable r) {
 768                         Thread thread =
 769                             defaultFactory.newThread(r);
 770                         thread.setName("SwingWorker-"
 771                             + thread.getName());
 772                         thread.setDaemon(true);
 773                         return thread;
 774                     }
 775                 };
 776 
 777             executorService =
 778                 new ThreadPoolExecutor(MAX_WORKER_THREADS, MAX_WORKER_THREADS,
 779                                        10L, TimeUnit.MINUTES,
 780                                        new LinkedBlockingQueue<Runnable>(),
 781                                        threadFactory);
 782             appContext.put(SwingWorker.class, executorService);
 783 
 784             // Don't use ShutdownHook here as it's not enough. We should track
 785             // AppContext disposal instead of JVM shutdown, see 6799345 for details
 786             final ExecutorService es = executorService;
 787             appContext.addPropertyChangeListener(AppContext.DISPOSED_PROPERTY_NAME,
 788                 new PropertyChangeListener() {
 789                     @Override
 790                     public void propertyChange(PropertyChangeEvent pce) {
 791                         boolean disposed = (Boolean)pce.getNewValue();
 792                         if (disposed) {
 793                             final WeakReference<ExecutorService> executorServiceRef =
 794                                 new WeakReference<ExecutorService>(es);
 795                             final ExecutorService executorService =
 796                                 executorServiceRef.get();
 797                             if (executorService != null) {
 798                                 AccessController.doPrivileged(
 799                                     new PrivilegedAction<Void>() {
 800                                         public Void run() {
 801                                             executorService.shutdown();
 802                                             return null;
 803                                         }
 804                                     }
 805                                 );
 806                             }
 807                         }
 808                     }
 809                 }
 810             );
 811         }
 812         return executorService;
 813     }
 814 
 815     private static final Object DO_SUBMIT_KEY = new StringBuilder("doSubmit");
 816     private static AccumulativeRunnable<Runnable> getDoSubmit() {
 817         synchronized (DO_SUBMIT_KEY) {
 818             final AppContext appContext = AppContext.getAppContext();
 819             Object doSubmit = appContext.get(DO_SUBMIT_KEY);
 820             if (doSubmit == null) {
 821                 doSubmit = new DoSubmitAccumulativeRunnable();
 822                 appContext.put(DO_SUBMIT_KEY, doSubmit);
 823             }
 824             return (AccumulativeRunnable<Runnable>) doSubmit;
 825         }
 826     }
 827     private static class DoSubmitAccumulativeRunnable
 828           extends AccumulativeRunnable<Runnable> implements ActionListener {
 829         private final static int DELAY = 1000 / 30;
 830         @Override
 831         protected void run(List<Runnable> args) {
 832             for (Runnable runnable : args) {
 833                 runnable.run();
 834             }
 835         }
 836         @Override
 837         protected void submit() {
 838             Timer timer = new Timer(DELAY, this);
 839             timer.setRepeats(false);
 840             timer.start();
 841         }
 842         public void actionPerformed(ActionEvent event) {
 843             run();
 844         }
 845     }
 846 
 847     private class SwingWorkerPropertyChangeSupport
 848             extends PropertyChangeSupport {
 849         SwingWorkerPropertyChangeSupport(Object source) {
 850             super(source);
 851         }
 852         @Override
 853         public void firePropertyChange(final PropertyChangeEvent evt) {
 854             if (SwingUtilities.isEventDispatchThread()) {
 855                 super.firePropertyChange(evt);
 856             } else {
 857                 doSubmit.add(
 858                     new Runnable() {
 859                         public void run() {
 860                             SwingWorkerPropertyChangeSupport.this
 861                                 .firePropertyChange(evt);
 862                         }
 863                     });
 864             }
 865         }
 866     }
 867 }