1 /*
   2  * Copyright (c) 2005, 2014, 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://docs.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  *
  66  * <p>
  67  * These constraints mean that a GUI application with time intensive
  68  * computing needs at least two threads:  1) a thread to perform the lengthy
  69  * task and 2) the <i>Event Dispatch Thread</i> (EDT) for all GUI-related
  70  * activities.  This involves inter-thread communication which can be
  71  * tricky to implement.
  72  *
  73  * <p>
  74  * {@code SwingWorker} is designed for situations where you need to have a long
  75  * running task run in a background thread and provide updates to the UI
  76  * either when done, or while processing.
  77  * Subclasses of {@code SwingWorker} must implement
  78  * the {@link #doInBackground} method to perform the background computation.
  79  *
  80  *
  81  * <p>
  82  * <b>Workflow</b>
  83  * <p>
  84  * There are three threads involved in the life cycle of a
  85  * {@code SwingWorker} :
  86  * <ul>
  87  * <li>
  88  * <p>
  89  * <i>Current</i> thread: The {@link #execute} method is
  90  * called on this thread. It schedules {@code SwingWorker} for the execution on a
  91  * <i>worker</i>
  92  * thread and returns immediately. One can wait for the {@code SwingWorker} to
  93  * complete using the {@link #get get} methods.
  94  * <li>
  95  * <p>
  96  * <i>Worker</i> thread: The {@link #doInBackground}
  97  * method is called on this thread.
  98  * This is where all background activities should happen. To notify
  99  * {@code PropertyChangeListeners} about bound properties changes use the
 100  * {@link #firePropertyChange firePropertyChange} and
 101  * {@link #getPropertyChangeSupport} methods. By default there are two bound
 102  * properties available: {@code state} and {@code progress}.
 103  * <li>
 104  * <p>
 105  * <i>Event Dispatch Thread</i>:  All Swing related activities occur
 106  * on this thread. {@code SwingWorker} invokes the
 107  * {@link #process process} and {@link #done} methods and notifies
 108  * any {@code PropertyChangeListeners} on this thread.
 109  * </ul>
 110  *
 111  * <p>
 112  * Often, the <i>Current</i> thread is the <i>Event Dispatch
 113  * Thread</i>.
 114  *
 115  *
 116  * <p>
 117  * Before the {@code doInBackground} method is invoked on a <i>worker</i> thread,
 118  * {@code SwingWorker} notifies any {@code PropertyChangeListeners} about the
 119  * {@code state} property change to {@code StateValue.STARTED}.  After the
 120  * {@code doInBackground} method is finished the {@code done} method is
 121  * executed.  Then {@code SwingWorker} notifies any {@code PropertyChangeListeners}
 122  * about the {@code state} property change to {@code StateValue.DONE}.
 123  *
 124  * <p>
 125  * {@code SwingWorker} is only designed to be executed once.  Executing a
 126  * {@code SwingWorker} more than once will not result in invoking the
 127  * {@code doInBackground} method twice.
 128  *
 129  * <p>
 130  * <b>Sample Usage</b>
 131  * <p>
 132  * The following example illustrates the simplest use case.  Some
 133  * processing is done in the background and when done you update a Swing
 134  * component.
 135  *
 136  * <p>
 137  * Say we want to find the "Meaning of Life" and display the result in
 138  * a {@code JLabel}.
 139  *
 140  * <pre>
 141  *   final JLabel label;
 142  *   class MeaningOfLifeFinder extends SwingWorker&lt;String, Object&gt; {
 143  *       {@code @Override}
 144  *       public String doInBackground() {
 145  *           return findTheMeaningOfLife();
 146  *       }
 147  *
 148  *       {@code @Override}
 149  *       protected void done() {
 150  *           try {
 151  *               label.setText(get());
 152  *           } catch (Exception ignore) {
 153  *           }
 154  *       }
 155  *   }
 156  *
 157  *   (new MeaningOfLifeFinder()).execute();
 158  * </pre>
 159  *
 160  * <p>
 161  * The next example is useful in situations where you wish to process data
 162  * as it is ready on the <i>Event Dispatch Thread</i>.
 163  *
 164  * <p>
 165  * Now we want to find the first N prime numbers and display the results in a
 166  * {@code JTextArea}.  While this is computing, we want to update our
 167  * progress in a {@code JProgressBar}.  Finally, we also want to print
 168  * the prime numbers to {@code System.out}.
 169  * <pre>
 170  * class PrimeNumbersTask extends
 171  *         SwingWorker&lt;List&lt;Integer&gt;, Integer&gt; {
 172  *     PrimeNumbersTask(JTextArea textArea, int numbersToFind) {
 173  *         //initialize
 174  *     }
 175  *
 176  *     {@code @Override}
 177  *     public List&lt;Integer&gt; doInBackground() {
 178  *         while (! enough &amp;&amp; ! isCancelled()) {
 179  *                 number = nextPrimeNumber();
 180  *                 publish(number);
 181  *                 setProgress(100 * numbers.size() / numbersToFind);
 182  *             }
 183  *         }
 184  *         return numbers;
 185  *     }
 186  *
 187  *     {@code @Override}
 188  *     protected void process(List&lt;Integer&gt; chunks) {
 189  *         for (int number : chunks) {
 190  *             textArea.append(number + &quot;\n&quot;);
 191  *         }
 192  *     }
 193  * }
 194  *
 195  * JTextArea textArea = new JTextArea();
 196  * final JProgressBar progressBar = new JProgressBar(0, 100);
 197  * PrimeNumbersTask task = new PrimeNumbersTask(textArea, N);
 198  * task.addPropertyChangeListener(
 199  *     new PropertyChangeListener() {
 200  *         public  void propertyChange(PropertyChangeEvent evt) {
 201  *             if (&quot;progress&quot;.equals(evt.getPropertyName())) {
 202  *                 progressBar.setValue((Integer)evt.getNewValue());
 203  *             }
 204  *         }
 205  *     });
 206  *
 207  * task.execute();
 208  * System.out.println(task.get()); //prints all prime numbers we have got
 209  * </pre>
 210  *
 211  * <p>
 212  * Because {@code SwingWorker} implements {@code Runnable}, a
 213  * {@code SwingWorker} can be submitted to an
 214  * {@link java.util.concurrent.Executor} for execution.
 215  *
 216  * @author Igor Kushnirskiy
 217  *
 218  * @param <T> the result type returned by this {@code SwingWorker's}
 219  *        {@code doInBackground} and {@code get} methods
 220  * @param <V> the type used for carrying out intermediate results by this
 221  *        {@code SwingWorker's} {@code publish} and {@code process} methods
 222  *
 223  * @since 1.6
 224  */
 225 public abstract class SwingWorker<T, V> implements RunnableFuture<T> {
 226     /**
 227      * number of worker threads.
 228      */
 229     private static final int MAX_WORKER_THREADS = 10;
 230 
 231     /**
 232      * current progress.
 233      */
 234     private volatile int progress;
 235 
 236     /**
 237      * current state.
 238      */
 239     private volatile StateValue state;
 240 
 241     /**
 242      * everything is run inside this FutureTask. Also it is used as
 243      * a delegatee for the Future API.
 244      */
 245     private final FutureTask<T> future;
 246 
 247     /**
 248      * all propertyChangeSupport goes through this.
 249      */
 250     private final PropertyChangeSupport propertyChangeSupport;
 251 
 252     /**
 253      * handler for {@code process} mehtod.
 254      */
 255     private AccumulativeRunnable<V> doProcess;
 256 
 257     /**
 258      * handler for progress property change notifications.
 259      */
 260     private AccumulativeRunnable<Integer> doNotifyProgressChange;
 261 
 262     private final AccumulativeRunnable<Runnable> doSubmit = getDoSubmit();
 263 
 264     /**
 265      * Values for the {@code state} bound property.
 266      * @since 1.6
 267      */
 268     public enum StateValue {
 269         /**
 270          * Initial {@code SwingWorker} state.
 271          */
 272         PENDING,
 273         /**
 274          * {@code SwingWorker} is {@code STARTED}
 275          * before invoking {@code doInBackground}.
 276          */
 277         STARTED,
 278 
 279         /**
 280          * {@code SwingWorker} is {@code DONE}
 281          * after {@code doInBackground} method
 282          * is finished.
 283          */
 284         DONE
 285     }
 286 
 287     /**
 288      * Constructs this {@code SwingWorker}.
 289      */
 290     public SwingWorker() {
 291         Callable<T> callable =
 292                 new Callable<T>() {
 293                     public T call() throws Exception {
 294                         setState(StateValue.STARTED);
 295                         return doInBackground();
 296                     }
 297                 };
 298 
 299         future = new FutureTask<T>(callable) {
 300                        @Override
 301                        protected void done() {
 302                            doneEDT();
 303                            setState(StateValue.DONE);
 304                        }
 305                    };
 306 
 307        state = StateValue.PENDING;
 308        propertyChangeSupport = new SwingWorkerPropertyChangeSupport(this);
 309        doProcess = null;
 310        doNotifyProgressChange = null;
 311     }
 312 
 313     /**
 314      * Computes a result, or throws an exception if unable to do so.
 315      *
 316      * <p>
 317      * Note that this method is executed only once.
 318      *
 319      * <p>
 320      * Note: this method is executed in a background thread.
 321      *
 322      *
 323      * @return the computed result
 324      * @throws Exception if unable to compute a result
 325      *
 326      */
 327     protected abstract T doInBackground() throws Exception ;
 328 
 329     /**
 330      * Sets this {@code Future} to the result of computation unless
 331      * it has been cancelled.
 332      */
 333     public final void run() {
 334         future.run();
 335     }
 336 
 337     /**
 338      * Sends data chunks to the {@link #process} method. This method is to be
 339      * used from inside the {@code doInBackground} method to deliver
 340      * intermediate results
 341      * for processing on the <i>Event Dispatch Thread</i> inside the
 342      * {@code process} method.
 343      *
 344      * <p>
 345      * Because the {@code process} method is invoked asynchronously on
 346      * the <i>Event Dispatch Thread</i>
 347      * multiple invocations to the {@code publish} method
 348      * might occur before the {@code process} method is executed. For
 349      * performance purposes all these invocations are coalesced into one
 350      * invocation with concatenated arguments.
 351      *
 352      * <p>
 353      * For example:
 354      *
 355      * <pre>
 356      * publish(&quot;1&quot;);
 357      * publish(&quot;2&quot;, &quot;3&quot;);
 358      * publish(&quot;4&quot;, &quot;5&quot;, &quot;6&quot;);
 359      * </pre>
 360      *
 361      * might result in:
 362      *
 363      * <pre>
 364      * process(&quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;, &quot;5&quot;, &quot;6&quot;)
 365      * </pre>
 366      *
 367      * <p>
 368      * <b>Sample Usage</b>. This code snippet loads some tabular data and
 369      * updates {@code DefaultTableModel} with it. Note that it safe to mutate
 370      * the tableModel from inside the {@code process} method because it is
 371      * invoked on the <i>Event Dispatch Thread</i>.
 372      *
 373      * <pre>
 374      * class TableSwingWorker extends
 375      *         SwingWorker&lt;DefaultTableModel, Object[]&gt; {
 376      *     private final DefaultTableModel tableModel;
 377      *
 378      *     public TableSwingWorker(DefaultTableModel tableModel) {
 379      *         this.tableModel = tableModel;
 380      *     }
 381      *
 382      *     {@code @Override}
 383      *     protected DefaultTableModel doInBackground() throws Exception {
 384      *         for (Object[] row = loadData();
 385      *                  ! isCancelled() &amp;&amp; row != null;
 386      *                  row = loadData()) {
 387      *             publish((Object[]) row);
 388      *         }
 389      *         return tableModel;
 390      *     }
 391      *
 392      *     {@code @Override}
 393      *     protected void process(List&lt;Object[]&gt; chunks) {
 394      *         for (Object[] row : chunks) {
 395      *             tableModel.addRow(row);
 396      *         }
 397      *     }
 398      * }
 399      * </pre>
 400      *
 401      * @param chunks intermediate results to process
 402      *
 403      * @see #process
 404      *
 405      */
 406     @SafeVarargs
 407     @SuppressWarnings("varargs") // Passing chunks to add is safe
 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             @SuppressWarnings("unchecked")
 824             AccumulativeRunnable<Runnable> tmp = (AccumulativeRunnable<Runnable>) doSubmit;
 825             return tmp;
 826         }
 827     }
 828     private static class DoSubmitAccumulativeRunnable
 829           extends AccumulativeRunnable<Runnable> implements ActionListener {
 830         private final static int DELAY = 1000 / 30;
 831         @Override
 832         protected void run(List<Runnable> args) {
 833             for (Runnable runnable : args) {
 834                 runnable.run();
 835             }
 836         }
 837         @Override
 838         protected void submit() {
 839             Timer timer = new Timer(DELAY, this);
 840             timer.setRepeats(false);
 841             timer.start();
 842         }
 843         public void actionPerformed(ActionEvent event) {
 844             run();
 845         }
 846     }
 847 
 848     @SuppressWarnings("serial") // JDK-implementation class
 849     private class SwingWorkerPropertyChangeSupport
 850             extends PropertyChangeSupport {
 851         SwingWorkerPropertyChangeSupport(Object source) {
 852             super(source);
 853         }
 854         @Override
 855         public void firePropertyChange(final PropertyChangeEvent evt) {
 856             if (SwingUtilities.isEventDispatchThread()) {
 857                 super.firePropertyChange(evt);
 858             } else {
 859                 doSubmit.add(
 860                     new Runnable() {
 861                         public void run() {
 862                             SwingWorkerPropertyChangeSupport.this
 863                                 .firePropertyChange(evt);
 864                         }
 865                     });
 866             }
 867         }
 868     }
 869 }