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<String, Object> {
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<List<Integer>, Integer> {
172 * PrimeNumbersTask(JTextArea textArea, int numbersToFind) {
173 * //initialize
174 * }
175 *
176 * {@code @Override}
177 * public List<Integer> doInBackground() {
178 * while (! enough && ! 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<Integer> chunks) {
189 * for (int number : chunks) {
190 * textArea.append(number + "\n");
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 ("progress".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("1");
357 * publish("2", "3");
358 * publish("4", "5", "6");
359 * </pre>
360 *
361 * might result in:
362 *
363 * <pre>
364 * process("1", "2", "3", "4", "5", "6")
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<DefaultTableModel, Object[]> {
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() && row != null;
386 * row = loadData()) {
387 * publish((Object[]) row);
388 * }
389 * return tableModel;
390 * }
391 *
392 * {@code @Override}
393 * protected void process(List<Object[]> 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 ("state".equals(event.getPropertyName())
587 * && 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 }
--- EOF ---