History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/SwingWorker.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/SwingWorker.java+866−0
@@ -0,0 +1,866 @@
1/*
2 * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
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://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">
54 * How to Use Threads
55 * </a> for more details):
56 * <ul>
57 * <li> Time-consuming tasks should not be run on the <i>Event
58 * Dispatch Thread</i>. Otherwise the application becomes unresponsive.
59 * </li>
60 * <li> Swing components should be accessed on the <i>Event
61 * Dispatch Thread</i> only.
62 * </li>
63 * </ul>
64 *
65 * <p>
66 *
67 * <p>
68 * These constraints mean that a GUI application with time intensive
69 * computing needs at least two threads: 1) a thread to perform the lengthy
70 * task and 2) the <i>Event Dispatch Thread</i> (EDT) for all GUI-related
71 * activities. This involves inter-thread communication which can be
72 * tricky to implement.
73 *
74 * <p>
75 * {@code SwingWorker} is designed for situations where you need to have a long
76 * running task run in a background thread and provide updates to the UI
77 * either when done, or while processing.
78 * Subclasses of {@code SwingWorker} must implement
79 * the {@link #doInBackground} method to perform the background computation.
80 *
81 *
82 * <p>
83 * <b>Workflow</b>
84 * <p>
85 * There are three threads involved in the life cycle of a
86 * {@code SwingWorker} :
87 * <ul>
88 * <li>
89 * <p>
90 * <i>Current</i> thread: The {@link #execute} method is
91 * called on this thread. It schedules {@code SwingWorker} for the execution on a
92 * <i>worker</i>
93 * thread and returns immediately. One can wait for the {@code SwingWorker} to
94 * complete using the {@link #get get} methods.
95 * <li>
96 * <p>
97 * <i>Worker</i> thread: The {@link #doInBackground}
98 * method is called on this thread.
99 * This is where all background activities should happen. To notify
100 * {@code PropertyChangeListeners} about bound properties changes use the
101 * {@link #firePropertyChange firePropertyChange} and
102 * {@link #getPropertyChangeSupport} methods. By default there are two bound
103 * properties available: {@code state} and {@code progress}.
104 * <li>
105 * <p>
106 * <i>Event Dispatch Thread</i>: All Swing related activities occur
107 * on this thread. {@code SwingWorker} invokes the
108 * {@link #process process} and {@link #done} methods and notifies
109 * any {@code PropertyChangeListeners} on this thread.
110 * </ul>
111 *
112 * <p>
113 * Often, the <i>Current</i> thread is the <i>Event Dispatch
114 * Thread</i>.
115 *
116 *
117 * <p>
118 * Before the {@code doInBackground} method is invoked on a <i>worker</i> thread,
119 * {@code SwingWorker} notifies any {@code PropertyChangeListeners} about the
120 * {@code state} property change to {@code StateValue.STARTED}. After the
121 * {@code doInBackground} method is finished the {@code done} method is
122 * executed. Then {@code SwingWorker} notifies any {@code PropertyChangeListeners}
123 * about the {@code state} property change to {@code StateValue.DONE}.
124 *
125 * <p>
126 * {@code SwingWorker} is only designed to be executed once. Executing a
127 * {@code SwingWorker} more than once will not result in invoking the
128 * {@code doInBackground} method twice.
129 *
130 * <p>
131 * <b>Sample Usage</b>
132 * <p>
133 * The following example illustrates the simplest use case. Some
134 * processing is done in the background and when done you update a Swing
135 * component.
136 *
137 * <p>
138 * Say we want to find the "Meaning of Life" and display the result in
139 * a {@code JLabel}.
140 *
141 * <pre>
142 * final JLabel label;
143 * class MeaningOfLifeFinder extends SwingWorker&lt;String, Object&gt; {
144 * {@code @Override}
145 * public String doInBackground() {
146 * return findTheMeaningOfLife();
147 * }
148 *
149 * {@code @Override}
150 * protected void done() {
151 * try {
152 * label.setText(get());
153 * } catch (Exception ignore) {
154 * }
155 * }
156 * }
157 *
158 * (new MeaningOfLifeFinder()).execute();
159 * </pre>
160 *
161 * <p>
162 * The next example is useful in situations where you wish to process data
163 * as it is ready on the <i>Event Dispatch Thread</i>.
164 *
165 * <p>
166 * Now we want to find the first N prime numbers and display the results in a
167 * {@code JTextArea}. While this is computing, we want to update our
168 * progress in a {@code JProgressBar}. Finally, we also want to print
169 * the prime numbers to {@code System.out}.
170 * <pre>
171 * class PrimeNumbersTask extends
172 * SwingWorker&lt;List&lt;Integer&gt;, Integer&gt; {
173 * PrimeNumbersTask(JTextArea textArea, int numbersToFind) {
174 * //initialize
175 * }
176 *
177 * {@code @Override}
178 * public List&lt;Integer&gt; doInBackground() {
179 * while (! enough &amp;&amp; ! isCancelled()) {
180 * number = nextPrimeNumber();
181 * publish(number);
182 * setProgress(100 * numbers.size() / numbersToFind);
183 * }
184 * }
185 * return numbers;
186 * }
187 *
188 * {@code @Override}
189 * protected void process(List&lt;Integer&gt; chunks) {
190 * for (int number : chunks) {
191 * textArea.append(number + &quot;\n&quot;);
192 * }
193 * }
194 * }
195 *
196 * JTextArea textArea = new JTextArea();
197 * final JProgressBar progressBar = new JProgressBar(0, 100);
198 * PrimeNumbersTask task = new PrimeNumbersTask(textArea, N);
199 * task.addPropertyChangeListener(
200 * new PropertyChangeListener() {
201 * public void propertyChange(PropertyChangeEvent evt) {
202 * if (&quot;progress&quot;.equals(evt.getPropertyName())) {
203 * progressBar.setValue((Integer)evt.getNewValue());
204 * }
205 * }
206 * });
207 *
208 * task.execute();
209 * System.out.println(task.get()); //prints all prime numbers we have got
210 * </pre>
211 *
212 * <p>
213 * Because {@code SwingWorker} implements {@code Runnable}, a
214 * {@code SwingWorker} can be submitted to an
215 * {@link java.util.concurrent.Executor} for execution.
216 *
217 * @author Igor Kushnirskiy
218 *
219 * @param <T> the result type returned by this {@code SwingWorker's}
220 * {@code doInBackground} and {@code get} methods
221 * @param <V> the type used for carrying out intermediate results by this
222 * {@code SwingWorker's} {@code publish} and {@code process} methods
223 *
224 * @since 1.6
225 */
226public abstract class SwingWorker<T, V> implements RunnableFuture<T> {
227 /**
228 * number of worker threads.
229 */
230 private static final int MAX_WORKER_THREADS = 10;
231
232 /**
233 * current progress.
234 */
235 private volatile int progress;
236
237 /**
238 * current state.
239 */
240 private volatile StateValue state;
241
242 /**
243 * everything is run inside this FutureTask. Also it is used as
244 * a delegatee for the Future API.
245 */
246 private final FutureTask<T> future;
247
248 /**
249 * all propertyChangeSupport goes through this.
250 */
251 private final PropertyChangeSupport propertyChangeSupport;
252
253 /**
254 * handler for {@code process} mehtod.
255 */
256 private AccumulativeRunnable<V> doProcess;
257
258 /**
259 * handler for progress property change notifications.
260 */
261 private AccumulativeRunnable<Integer> doNotifyProgressChange;
262
263 private final AccumulativeRunnable<Runnable> doSubmit = getDoSubmit();
264
265 /**
266 * Values for the {@code state} bound property.
267 * @since 1.6
268 */
269 public enum StateValue {
270 /**
271 * Initial {@code SwingWorker} state.
272 */
273 PENDING,
274 /**
275 * {@code SwingWorker} is {@code STARTED}
276 * before invoking {@code doInBackground}.
277 */
278 STARTED,
279
280 /**
281 * {@code SwingWorker} is {@code DONE}
282 * after {@code doInBackground} method
283 * is finished.
284 */
285 DONE
286 }
287
288 /**
289 * Constructs this {@code SwingWorker}.
290 */
291 public SwingWorker() {
292 Callable<T> callable =
293 new Callable<T>() {
294 public T call() throws Exception {
295 setState(StateValue.STARTED);
296 return doInBackground();
297 }
298 };
299
300 future = new FutureTask<T>(callable) {
301 @Override
302 protected void done() {
303 doneEDT();
304 setState(StateValue.DONE);
305 }
306 };
307
308 state = StateValue.PENDING;
309 propertyChangeSupport = new SwingWorkerPropertyChangeSupport(this);
310 doProcess = null;
311 doNotifyProgressChange = null;
312 }
313
314 /**
315 * Computes a result, or throws an exception if unable to do so.
316 *
317 * <p>
318 * Note that this method is executed only once.
319 *
320 * <p>
321 * Note: this method is executed in a background thread.
322 *
323 *
324 * @return the computed result
325 * @throws Exception if unable to compute a result
326 *
327 */
328 protected abstract T doInBackground() throws Exception ;
329
330 /**
331 * Sets this {@code Future} to the result of computation unless
332 * it has been cancelled.
333 */
334 public final void run() {
335 future.run();
336 }
337
338 /**
339 * Sends data chunks to the {@link #process} method. This method is to be
340 * used from inside the {@code doInBackground} method to deliver
341 * intermediate results
342 * for processing on the <i>Event Dispatch Thread</i> inside the
343 * {@code process} method.
344 *
345 * <p>
346 * Because the {@code process} method is invoked asynchronously on
347 * the <i>Event Dispatch Thread</i>
348 * multiple invocations to the {@code publish} method
349 * might occur before the {@code process} method is executed. For
350 * performance purposes all these invocations are coalesced into one
351 * invocation with concatenated arguments.
352 *
353 * <p>
354 * For example:
355 *
356 * <pre>
357 * publish(&quot;1&quot;);
358 * publish(&quot;2&quot;, &quot;3&quot;);
359 * publish(&quot;4&quot;, &quot;5&quot;, &quot;6&quot;);
360 * </pre>
361 *
362 * might result in:
363 *
364 * <pre>
365 * process(&quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;, &quot;5&quot;, &quot;6&quot;)
366 * </pre>
367 *
368 * <p>
369 * <b>Sample Usage</b>. This code snippet loads some tabular data and
370 * updates {@code DefaultTableModel} with it. Note that it safe to mutate
371 * the tableModel from inside the {@code process} method because it is
372 * invoked on the <i>Event Dispatch Thread</i>.
373 *
374 * <pre>
375 * class TableSwingWorker extends
376 * SwingWorker&lt;DefaultTableModel, Object[]&gt; {
377 * private final DefaultTableModel tableModel;
378 *
379 * public TableSwingWorker(DefaultTableModel tableModel) {
380 * this.tableModel = tableModel;
381 * }
382 *
383 * {@code @Override}
384 * protected DefaultTableModel doInBackground() throws Exception {
385 * for (Object[] row = loadData();
386 * ! isCancelled() &amp;&amp; row != null;
387 * row = loadData()) {
388 * publish((Object[]) row);
389 * }
390 * return tableModel;
391 * }
392 *
393 * {@code @Override}
394 * protected void process(List&lt;Object[]&gt; chunks) {
395 * for (Object[] row : chunks) {
396 * tableModel.addRow(row);
397 * }
398 * }
399 * }
400 * </pre>
401 *
402 * @param chunks intermediate results to process
403 *
404 * @see #process
405 *
406 */
407 @SafeVarargs
408 protected final void publish(V... chunks) {
409 synchronized (this) {
410 if (doProcess == null) {
411 doProcess = new AccumulativeRunnable<V>() {
412 @Override
413 public void run(List<V> args) {
414 process(args);
415 }
416 @Override
417 protected void submit() {
418 doSubmit.add(this);
419 }
420 };
421 }
422 }
423 doProcess.add(chunks);
424 }
425
426 /**
427 * Receives data chunks from the {@code publish} method asynchronously on the
428 * <i>Event Dispatch Thread</i>.
429 *
430 * <p>
431 * Please refer to the {@link #publish} method for more details.
432 *
433 * @param chunks intermediate results to process
434 *
435 * @see #publish
436 *
437 */
438 protected void process(List<V> chunks) {
439 }
440
441 /**
442 * Executed on the <i>Event Dispatch Thread</i> after the {@code doInBackground}
443 * method is finished. The default
444 * implementation does nothing. Subclasses may override this method to
445 * perform completion actions on the <i>Event Dispatch Thread</i>. Note
446 * that you can query status inside the implementation of this method to
447 * determine the result of this task or whether this task has been cancelled.
448 *
449 * @see #doInBackground
450 * @see #isCancelled()
451 * @see #get
452 */
453 protected void done() {
454 }
455
456 /**
457 * Sets the {@code progress} bound property.
458 * The value should be from 0 to 100.
459 *
460 * <p>
461 * Because {@code PropertyChangeListener}s are notified asynchronously on
462 * the <i>Event Dispatch Thread</i> multiple invocations to the
463 * {@code setProgress} method might occur before any
464 * {@code PropertyChangeListeners} are invoked. For performance purposes
465 * all these invocations are coalesced into one invocation with the last
466 * invocation argument only.
467 *
468 * <p>
469 * For example, the following invokations:
470 *
471 * <pre>
472 * setProgress(1);
473 * setProgress(2);
474 * setProgress(3);
475 * </pre>
476 *
477 * might result in a single {@code PropertyChangeListener} notification with
478 * the value {@code 3}.
479 *
480 * @param progress the progress value to set
481 * @throws IllegalArgumentException is value not from 0 to 100
482 */
483 protected final void setProgress(int progress) {
484 if (progress < 0 || progress > 100) {
485 throw new IllegalArgumentException("the value should be from 0 to 100");
486 }
487 if (this.progress == progress) {
488 return;
489 }
490 int oldProgress = this.progress;
491 this.progress = progress;
492 if (! getPropertyChangeSupport().hasListeners("progress")) {
493 return;
494 }
495 synchronized (this) {
496 if (doNotifyProgressChange == null) {
497 doNotifyProgressChange =
498 new AccumulativeRunnable<Integer>() {
499 @Override
500 public void run(List<Integer> args) {
501 firePropertyChange("progress",
502 args.get(0),
503 args.get(args.size() - 1));
504 }
505 @Override
506 protected void submit() {
507 doSubmit.add(this);
508 }
509 };
510 }
511 }
512 doNotifyProgressChange.add(oldProgress, progress);
513 }
514
515 /**
516 * Returns the {@code progress} bound property.
517 *
518 * @return the progress bound property.
519 */
520 public final int getProgress() {
521 return progress;
522 }
523
524 /**
525 * Schedules this {@code SwingWorker} for execution on a <i>worker</i>
526 * thread. There are a number of <i>worker</i> threads available. In the
527 * event all <i>worker</i> threads are busy handling other
528 * {@code SwingWorkers} this {@code SwingWorker} is placed in a waiting
529 * queue.
530 *
531 * <p>
532 * Note:
533 * {@code SwingWorker} is only designed to be executed once. Executing a
534 * {@code SwingWorker} more than once will not result in invoking the
535 * {@code doInBackground} method twice.
536 */
537 public final void execute() {
538 getWorkersExecutorService().execute(this);
539 }
540
541 // Future methods START
542 /**
543 * {@inheritDoc}
544 */
545 public final boolean cancel(boolean mayInterruptIfRunning) {
546 return future.cancel(mayInterruptIfRunning);
547 }
548
549 /**
550 * {@inheritDoc}
551 */
552 public final boolean isCancelled() {
553 return future.isCancelled();
554 }
555
556 /**
557 * {@inheritDoc}
558 */
559 public final boolean isDone() {
560 return future.isDone();
561 }
562
563 /**
564 * {@inheritDoc}
565 * <p>
566 * Note: calling {@code get} on the <i>Event Dispatch Thread</i> blocks
567 * <i>all</i> events, including repaints, from being processed until this
568 * {@code SwingWorker} is complete.
569 *
570 * <p>
571 * When you want the {@code SwingWorker} to block on the <i>Event
572 * Dispatch Thread</i> we recommend that you use a <i>modal dialog</i>.
573 *
574 * <p>
575 * For example:
576 *
577 * <pre>
578 * class SwingWorkerCompletionWaiter extends PropertyChangeListener {
579 * private JDialog dialog;
580 *
581 * public SwingWorkerCompletionWaiter(JDialog dialog) {
582 * this.dialog = dialog;
583 * }
584 *
585 * public void propertyChange(PropertyChangeEvent event) {
586 * if (&quot;state&quot;.equals(event.getPropertyName())
587 * &amp;&amp; SwingWorker.StateValue.DONE == event.getNewValue()) {
588 * dialog.setVisible(false);
589 * dialog.dispose();
590 * }
591 * }
592 * }
593 * JDialog dialog = new JDialog(owner, true);
594 * swingWorker.addPropertyChangeListener(
595 * new SwingWorkerCompletionWaiter(dialog));
596 * swingWorker.execute();
597 * //the dialog will be visible until the SwingWorker is done
598 * dialog.setVisible(true);
599 * </pre>
600 */
601 public final T get() throws InterruptedException, ExecutionException {
602 return future.get();
603 }
604
605 /**
606 * {@inheritDoc}
607 * <p>
608 * Please refer to {@link #get} for more details.
609 */
610 public final T get(long timeout, TimeUnit unit) throws InterruptedException,
611 ExecutionException, TimeoutException {
612 return future.get(timeout, unit);
613 }
614
615 // Future methods END
616
617 // PropertyChangeSupports methods START
618 /**
619 * Adds a {@code PropertyChangeListener} to the listener list. The listener
620 * is registered for all properties. The same listener object may be added
621 * more than once, and will be called as many times as it is added. If
622 * {@code listener} is {@code null}, no exception is thrown and no action is taken.
623 *
624 * <p>
625 * Note: This is merely a convenience wrapper. All work is delegated to
626 * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
627 *
628 * @param listener the {@code PropertyChangeListener} to be added
629 */
630 public final void addPropertyChangeListener(PropertyChangeListener listener) {
631 getPropertyChangeSupport().addPropertyChangeListener(listener);
632 }
633
634 /**
635 * Removes a {@code PropertyChangeListener} from the listener list. This
636 * removes a {@code PropertyChangeListener} that was registered for all
637 * properties. If {@code listener} was added more than once to the same
638 * event source, it will be notified one less time after being removed. If
639 * {@code listener} is {@code null}, or was never added, no exception is
640 * thrown and no action is taken.
641 *
642 * <p>
643 * Note: This is merely a convenience wrapper. All work is delegated to
644 * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
645 *
646 * @param listener the {@code PropertyChangeListener} to be removed
647 */
648 public final void removePropertyChangeListener(PropertyChangeListener listener) {
649 getPropertyChangeSupport().removePropertyChangeListener(listener);
650 }
651
652 /**
653 * Reports a bound property update to any registered listeners. No event is
654 * fired if {@code old} and {@code new} are equal and non-null.
655 *
656 * <p>
657 * This {@code SwingWorker} will be the source for
658 * any generated events.
659 *
660 * <p>
661 * When called off the <i>Event Dispatch Thread</i>
662 * {@code PropertyChangeListeners} are notified asynchronously on
663 * the <i>Event Dispatch Thread</i>.
664 * <p>
665 * Note: This is merely a convenience wrapper. All work is delegated to
666 * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
667 *
668 *
669 * @param propertyName the programmatic name of the property that was
670 * changed
671 * @param oldValue the old value of the property
672 * @param newValue the new value of the property
673 */
674 public final void firePropertyChange(String propertyName, Object oldValue,
675 Object newValue) {
676 getPropertyChangeSupport().firePropertyChange(propertyName,
677 oldValue, newValue);
678 }
679
680 /**
681 * Returns the {@code PropertyChangeSupport} for this {@code SwingWorker}.
682 * This method is used when flexible access to bound properties support is
683 * needed.
684 * <p>
685 * This {@code SwingWorker} will be the source for
686 * any generated events.
687 *
688 * <p>
689 * Note: The returned {@code PropertyChangeSupport} notifies any
690 * {@code PropertyChangeListener}s asynchronously on the <i>Event Dispatch
691 * Thread</i> in the event that {@code firePropertyChange} or
692 * {@code fireIndexedPropertyChange} are called off the <i>Event Dispatch
693 * Thread</i>.
694 *
695 * @return {@code PropertyChangeSupport} for this {@code SwingWorker}
696 */
697 public final PropertyChangeSupport getPropertyChangeSupport() {
698 return propertyChangeSupport;
699 }
700
701 // PropertyChangeSupports methods END
702
703 /**
704 * Returns the {@code SwingWorker} state bound property.
705 *
706 * @return the current state
707 */
708 public final StateValue getState() {
709 /*
710 * DONE is a speacial case
711 * to keep getState and isDone is sync
712 */
713 if (isDone()) {
714 return StateValue.DONE;
715 } else {
716 return state;
717 }
718 }
719
720 /**
721 * Sets this {@code SwingWorker} state bound property.
722 * @param state the state to set
723 */
724 private void setState(StateValue state) {
725 StateValue old = this.state;
726 this.state = state;
727 firePropertyChange("state", old, state);
728 }
729
730 /**
731 * Invokes {@code done} on the EDT.
732 */
733 private void doneEDT() {
734 Runnable doDone =
735 new Runnable() {
736 public void run() {
737 done();
738 }
739 };
740 if (SwingUtilities.isEventDispatchThread()) {
741 doDone.run();
742 } else {
743 doSubmit.add(doDone);
744 }
745 }
746
747
748 /**
749 * returns workersExecutorService.
750 *
751 * returns the service stored in the appContext or creates it if
752 * necessary.
753 *
754 * @return ExecutorService for the {@code SwingWorkers}
755 */
756 private static synchronized ExecutorService getWorkersExecutorService() {
757 final AppContext appContext = AppContext.getAppContext();
758 ExecutorService executorService =
759 (ExecutorService) appContext.get(SwingWorker.class);
760 if (executorService == null) {
761 //this creates daemon threads.
762 ThreadFactory threadFactory =
763 new ThreadFactory() {
764 final ThreadFactory defaultFactory =
765 Executors.defaultThreadFactory();
766 public Thread newThread(final Runnable r) {
767 Thread thread =
768 defaultFactory.newThread(r);
769 thread.setName("SwingWorker-"
770 + thread.getName());
771 thread.setDaemon(true);
772 return thread;
773 }
774 };
775
776 executorService =
777 new ThreadPoolExecutor(MAX_WORKER_THREADS, MAX_WORKER_THREADS,
778 10L, TimeUnit.MINUTES,
779 new LinkedBlockingQueue<Runnable>(),
780 threadFactory);
781 appContext.put(SwingWorker.class, executorService);
782
783 // Don't use ShutdownHook here as it's not enough. We should track
784 // AppContext disposal instead of JVM shutdown, see 6799345 for details
785 final ExecutorService es = executorService;
786 appContext.addPropertyChangeListener(AppContext.DISPOSED_PROPERTY_NAME,
787 new PropertyChangeListener() {
788 @Override
789 public void propertyChange(PropertyChangeEvent pce) {
790 boolean disposed = (Boolean)pce.getNewValue();
791 if (disposed) {
792 final WeakReference<ExecutorService> executorServiceRef =
793 new WeakReference<ExecutorService>(es);
794 final ExecutorService executorService =
795 executorServiceRef.get();
796 if (executorService != null) {
797 AccessController.doPrivileged(
798 new PrivilegedAction<Void>() {
799 public Void run() {
800 executorService.shutdown();
801 return null;
802 }
803 }
804 );
805 }
806 }
807 }
808 }
809 );
810 }
811 return executorService;
812 }
813
814 private static final Object DO_SUBMIT_KEY = new StringBuilder("doSubmit");
815 private static AccumulativeRunnable<Runnable> getDoSubmit() {
816 synchronized (DO_SUBMIT_KEY) {
817 final AppContext appContext = AppContext.getAppContext();
818 Object doSubmit = appContext.get(DO_SUBMIT_KEY);
819 if (doSubmit == null) {
820 doSubmit = new DoSubmitAccumulativeRunnable();
821 appContext.put(DO_SUBMIT_KEY, doSubmit);
822 }
823 return (AccumulativeRunnable<Runnable>) doSubmit;
824 }
825 }
826 private static class DoSubmitAccumulativeRunnable
827 extends AccumulativeRunnable<Runnable> implements ActionListener {
828 private final static int DELAY = 1000 / 30;
829 @Override
830 protected void run(List<Runnable> args) {
831 for (Runnable runnable : args) {
832 runnable.run();
833 }
834 }
835 @Override
836 protected void submit() {
837 Timer timer = new Timer(DELAY, this);
838 timer.setRepeats(false);
839 timer.start();
840 }
841 public void actionPerformed(ActionEvent event) {
842 run();
843 }
844 }
845
846 private class SwingWorkerPropertyChangeSupport
847 extends PropertyChangeSupport {
848 SwingWorkerPropertyChangeSupport(Object source) {
849 super(source);
850 }
851 @Override
852 public void firePropertyChange(final PropertyChangeEvent evt) {
853 if (SwingUtilities.isEventDispatchThread()) {
854 super.firePropertyChange(evt);
855 } else {
856 doSubmit.add(
857 new Runnable() {
858 public void run() {
859 SwingWorkerPropertyChangeSupport.this
860 .firePropertyChange(evt);
861 }
862 });
863 }
864 }
865 }
866}