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