1/*
2 * Copyright (c) 2005, 2017, 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 */
25package javax.swing;
26
27import java.lang.ref.WeakReference;
28import java.security.AccessController;
29import java.security.PrivilegedAction;
30import java.beans.PropertyChangeListener;
31import java.beans.PropertyChangeSupport;
32import java.beans.PropertyChangeEvent;
33import java.util.List;
34import java.util.concurrent.*;
35import java.util.concurrent.locks.*;
36
37import java.awt.event.*;
38
39import javax.swing.SwingUtilities;
40
41import sun.awt.AppContext;
42import 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 */
225public 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 implements 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 static final 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}
870