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+ */
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://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+ */
226+public abstract class SwingWorker<T, V> implements RunnableFuture<T> {
227+ /**
228+ * number of worker threads.
229+ */
230+ private static final int MAX_WORKER_THREADS = 10;
231+
232+ /**
233+ * current progress.
234+ */
235+ private volatile int progress;
236+
237+ /**
238+ * current state.
239+ */
240+ private volatile StateValue state;
241+
242+ /**
243+ * everything is run inside this FutureTask. Also it is used as
244+ * a delegatee for the Future API.
245+ */
246+ private final FutureTask<T> future;
247+
248+ /**
249+ * all propertyChangeSupport goes through this.
250+ */
251+ private final PropertyChangeSupport propertyChangeSupport;
252+
253+ /**
254+ * handler for {@code process} mehtod.
255+ */
256+ private AccumulativeRunnable<V> doProcess;
257+
258+ /**
259+ * handler for progress property change notifications.
260+ */
261+ private AccumulativeRunnable<Integer> doNotifyProgressChange;
262+
263+ private final AccumulativeRunnable<Runnable> doSubmit = getDoSubmit();
264+
265+ /**
266+ * Values for the {@code state} bound property.
267+ * @since 1.6
268+ */
269+ public enum StateValue {
270+ /**
271+ * Initial {@code SwingWorker} state.
272+ */
273+ PENDING,
274+ /**
275+ * {@code SwingWorker} is {@code STARTED}
276+ * before invoking {@code doInBackground}.
277+ */
278+ STARTED,
279+
280+ /**
281+ * {@code SwingWorker} is {@code DONE}
282+ * after {@code doInBackground} method
283+ * is finished.
284+ */
285+ DONE
286+ }
287+
288+ /**
289+ * Constructs this {@code SwingWorker}.
290+ */
291+ public SwingWorker() {
292+ Callable<T> callable =
293+ new Callable<T>() {
294+ public T call() throws Exception {
295+ setState(StateValue.STARTED);
296+ return doInBackground();
297+ }
298+ };
299+
300+ future = new FutureTask<T>(callable) {
301+ @Override
302+ protected void done() {
303+ doneEDT();
304+ setState(StateValue.DONE);
305+ }
306+ };
307+
308+ state = StateValue.PENDING;
309+ propertyChangeSupport = new SwingWorkerPropertyChangeSupport(this);
310+ doProcess = null;
311+ doNotifyProgressChange = null;
312+ }
313+
314+ /**
315+ * Computes a result, or throws an exception if unable to do so.
316+ *
317+ * <p>
318+ * Note that this method is executed only once.
319+ *
320+ * <p>
321+ * Note: this method is executed in a background thread.
322+ *
323+ *
324+ * @return the computed result
325+ * @throws Exception if unable to compute a result
326+ *
327+ */
328+ protected abstract T doInBackground() throws Exception ;
329+
330+ /**
331+ * Sets this {@code Future} to the result of computation unless
332+ * it has been cancelled.
333+ */
334+ public final void run() {
335+ future.run();
336+ }
337+
338+ /**
339+ * Sends data chunks to the {@link #process} method. This method is to be
340+ * used from inside the {@code doInBackground} method to deliver
341+ * intermediate results
342+ * for processing on the <i>Event Dispatch Thread</i> inside the
343+ * {@code process} method.
344+ *
345+ * <p>
346+ * Because the {@code process} method is invoked asynchronously on
347+ * the <i>Event Dispatch Thread</i>
348+ * multiple invocations to the {@code publish} method
349+ * might occur before the {@code process} method is executed. For
350+ * performance purposes all these invocations are coalesced into one
351+ * invocation with concatenated arguments.
352+ *
353+ * <p>
354+ * For example:
355+ *
356+ * <pre>
357+ * publish(&quot;1&quot;);
358+ * publish(&quot;2&quot;, &quot;3&quot;);
359+ * publish(&quot;4&quot;, &quot;5&quot;, &quot;6&quot;);
360+ * </pre>
361+ *
362+ * might result in:
363+ *
364+ * <pre>
365+ * process(&quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;, &quot;5&quot;, &quot;6&quot;)
366+ * </pre>
367+ *
368+ * <p>
369+ * <b>Sample Usage</b>. This code snippet loads some tabular data and
370+ * updates {@code DefaultTableModel} with it. Note that it safe to mutate
371+ * the tableModel from inside the {@code process} method because it is
372+ * invoked on the <i>Event Dispatch Thread</i>.
373+ *
374+ * <pre>
375+ * class TableSwingWorker extends
376+ * SwingWorker&lt;DefaultTableModel, Object[]&gt; {
377+ * private final DefaultTableModel tableModel;
378+ *
379+ * public TableSwingWorker(DefaultTableModel tableModel) {
380+ * this.tableModel = tableModel;
381+ * }
382+ *
383+ * {@code @Override}
384+ * protected DefaultTableModel doInBackground() throws Exception {
385+ * for (Object[] row = loadData();
386+ * ! isCancelled() &amp;&amp; row != null;
387+ * row = loadData()) {
388+ * publish((Object[]) row);
389+ * }
390+ * return tableModel;
391+ * }
392+ *
393+ * {@code @Override}
394+ * protected void process(List&lt;Object[]&gt; chunks) {
395+ * for (Object[] row : chunks) {
396+ * tableModel.addRow(row);
397+ * }
398+ * }
399+ * }
400+ * </pre>
401+ *
402+ * @param chunks intermediate results to process
403+ *
404+ * @see #process
405+ *
406+ */
407+ @SafeVarargs
408+ protected final void publish(V... chunks) {
409+ synchronized (this) {
410+ if (doProcess == null) {
411+ doProcess = new AccumulativeRunnable<V>() {
412+ @Override
413+ public void run(List<V> args) {
414+ process(args);
415+ }
416+ @Override
417+ protected void submit() {
418+ doSubmit.add(this);
419+ }
420+ };
421+ }
422+ }
423+ doProcess.add(chunks);
424+ }
425+
426+ /**
427+ * Receives data chunks from the {@code publish} method asynchronously on the
428+ * <i>Event Dispatch Thread</i>.
429+ *
430+ * <p>
431+ * Please refer to the {@link #publish} method for more details.
432+ *
433+ * @param chunks intermediate results to process
434+ *
435+ * @see #publish
436+ *
437+ */
438+ protected void process(List<V> chunks) {
439+ }
440+
441+ /**
442+ * Executed on the <i>Event Dispatch Thread</i> after the {@code doInBackground}
443+ * method is finished. The default
444+ * implementation does nothing. Subclasses may override this method to
445+ * perform completion actions on the <i>Event Dispatch Thread</i>. Note
446+ * that you can query status inside the implementation of this method to
447+ * determine the result of this task or whether this task has been cancelled.
448+ *
449+ * @see #doInBackground
450+ * @see #isCancelled()
451+ * @see #get
452+ */
453+ protected void done() {
454+ }
455+
456+ /**
457+ * Sets the {@code progress} bound property.
458+ * The value should be from 0 to 100.
459+ *
460+ * <p>
461+ * Because {@code PropertyChangeListener}s are notified asynchronously on
462+ * the <i>Event Dispatch Thread</i> multiple invocations to the
463+ * {@code setProgress} method might occur before any
464+ * {@code PropertyChangeListeners} are invoked. For performance purposes
465+ * all these invocations are coalesced into one invocation with the last
466+ * invocation argument only.
467+ *
468+ * <p>
469+ * For example, the following invokations:
470+ *
471+ * <pre>
472+ * setProgress(1);
473+ * setProgress(2);
474+ * setProgress(3);
475+ * </pre>
476+ *
477+ * might result in a single {@code PropertyChangeListener} notification with
478+ * the value {@code 3}.
479+ *
480+ * @param progress the progress value to set
481+ * @throws IllegalArgumentException is value not from 0 to 100
482+ */
483+ protected final void setProgress(int progress) {
484+ if (progress < 0 || progress > 100) {
485+ throw new IllegalArgumentException("the value should be from 0 to 100");
486+ }
487+ if (this.progress == progress) {
488+ return;
489+ }
490+ int oldProgress = this.progress;
491+ this.progress = progress;
492+ if (! getPropertyChangeSupport().hasListeners("progress")) {
493+ return;
494+ }
495+ synchronized (this) {
496+ if (doNotifyProgressChange == null) {
497+ doNotifyProgressChange =
498+ new AccumulativeRunnable<Integer>() {
499+ @Override
500+ public void run(List<Integer> args) {
501+ firePropertyChange("progress",
502+ args.get(0),
503+ args.get(args.size() - 1));
504+ }
505+ @Override
506+ protected void submit() {
507+ doSubmit.add(this);
508+ }
509+ };
510+ }
511+ }
512+ doNotifyProgressChange.add(oldProgress, progress);
513+ }
514+
515+ /**
516+ * Returns the {@code progress} bound property.
517+ *
518+ * @return the progress bound property.
519+ */
520+ public final int getProgress() {
521+ return progress;
522+ }
523+
524+ /**
525+ * Schedules this {@code SwingWorker} for execution on a <i>worker</i>
526+ * thread. There are a number of <i>worker</i> threads available. In the
527+ * event all <i>worker</i> threads are busy handling other
528+ * {@code SwingWorkers} this {@code SwingWorker} is placed in a waiting
529+ * queue.
530+ *
531+ * <p>
532+ * Note:
533+ * {@code SwingWorker} is only designed to be executed once. Executing a
534+ * {@code SwingWorker} more than once will not result in invoking the
535+ * {@code doInBackground} method twice.
536+ */
537+ public final void execute() {
538+ getWorkersExecutorService().execute(this);
539+ }
540+
541+ // Future methods START
542+ /**
543+ * {@inheritDoc}
544+ */
545+ public final boolean cancel(boolean mayInterruptIfRunning) {
546+ return future.cancel(mayInterruptIfRunning);
547+ }
548+
549+ /**
550+ * {@inheritDoc}
551+ */
552+ public final boolean isCancelled() {
553+ return future.isCancelled();
554+ }
555+
556+ /**
557+ * {@inheritDoc}
558+ */
559+ public final boolean isDone() {
560+ return future.isDone();
561+ }
562+
563+ /**
564+ * {@inheritDoc}
565+ * <p>
566+ * Note: calling {@code get} on the <i>Event Dispatch Thread</i> blocks
567+ * <i>all</i> events, including repaints, from being processed until this
568+ * {@code SwingWorker} is complete.
569+ *
570+ * <p>
571+ * When you want the {@code SwingWorker} to block on the <i>Event
572+ * Dispatch Thread</i> we recommend that you use a <i>modal dialog</i>.
573+ *
574+ * <p>
575+ * For example:
576+ *
577+ * <pre>
578+ * class SwingWorkerCompletionWaiter extends PropertyChangeListener {
579+ * private JDialog dialog;
580+ *
581+ * public SwingWorkerCompletionWaiter(JDialog dialog) {
582+ * this.dialog = dialog;
583+ * }
584+ *
585+ * public void propertyChange(PropertyChangeEvent event) {
586+ * if (&quot;state&quot;.equals(event.getPropertyName())
587+ * &amp;&amp; SwingWorker.StateValue.DONE == event.getNewValue()) {
588+ * dialog.setVisible(false);
589+ * dialog.dispose();
590+ * }
591+ * }
592+ * }
593+ * JDialog dialog = new JDialog(owner, true);
594+ * swingWorker.addPropertyChangeListener(
595+ * new SwingWorkerCompletionWaiter(dialog));
596+ * swingWorker.execute();
597+ * //the dialog will be visible until the SwingWorker is done
598+ * dialog.setVisible(true);
599+ * </pre>
600+ */
601+ public final T get() throws InterruptedException, ExecutionException {
602+ return future.get();
603+ }
604+
605+ /**
606+ * {@inheritDoc}
607+ * <p>
608+ * Please refer to {@link #get} for more details.
609+ */
610+ public final T get(long timeout, TimeUnit unit) throws InterruptedException,
611+ ExecutionException, TimeoutException {
612+ return future.get(timeout, unit);
613+ }
614+
615+ // Future methods END
616+
617+ // PropertyChangeSupports methods START
618+ /**
619+ * Adds a {@code PropertyChangeListener} to the listener list. The listener
620+ * is registered for all properties. The same listener object may be added
621+ * more than once, and will be called as many times as it is added. If
622+ * {@code listener} is {@code null}, no exception is thrown and no action is taken.
623+ *
624+ * <p>
625+ * Note: This is merely a convenience wrapper. All work is delegated to
626+ * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
627+ *
628+ * @param listener the {@code PropertyChangeListener} to be added
629+ */
630+ public final void addPropertyChangeListener(PropertyChangeListener listener) {
631+ getPropertyChangeSupport().addPropertyChangeListener(listener);
632+ }
633+
634+ /**
635+ * Removes a {@code PropertyChangeListener} from the listener list. This
636+ * removes a {@code PropertyChangeListener} that was registered for all
637+ * properties. If {@code listener} was added more than once to the same
638+ * event source, it will be notified one less time after being removed. If
639+ * {@code listener} is {@code null}, or was never added, no exception is
640+ * thrown and no action is taken.
641+ *
642+ * <p>
643+ * Note: This is merely a convenience wrapper. All work is delegated to
644+ * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
645+ *
646+ * @param listener the {@code PropertyChangeListener} to be removed
647+ */
648+ public final void removePropertyChangeListener(PropertyChangeListener listener) {
649+ getPropertyChangeSupport().removePropertyChangeListener(listener);
650+ }
651+
652+ /**
653+ * Reports a bound property update to any registered listeners. No event is
654+ * fired if {@code old} and {@code new} are equal and non-null.
655+ *
656+ * <p>
657+ * This {@code SwingWorker} will be the source for
658+ * any generated events.
659+ *
660+ * <p>
661+ * When called off the <i>Event Dispatch Thread</i>
662+ * {@code PropertyChangeListeners} are notified asynchronously on
663+ * the <i>Event Dispatch Thread</i>.
664+ * <p>
665+ * Note: This is merely a convenience wrapper. All work is delegated to
666+ * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
667+ *
668+ *
669+ * @param propertyName the programmatic name of the property that was
670+ * changed
671+ * @param oldValue the old value of the property
672+ * @param newValue the new value of the property
673+ */
674+ public final void firePropertyChange(String propertyName, Object oldValue,
675+ Object newValue) {
676+ getPropertyChangeSupport().firePropertyChange(propertyName,
677+ oldValue, newValue);
678+ }
679+
680+ /**
681+ * Returns the {@code PropertyChangeSupport} for this {@code SwingWorker}.
682+ * This method is used when flexible access to bound properties support is
683+ * needed.
684+ * <p>
685+ * This {@code SwingWorker} will be the source for
686+ * any generated events.
687+ *
688+ * <p>
689+ * Note: The returned {@code PropertyChangeSupport} notifies any
690+ * {@code PropertyChangeListener}s asynchronously on the <i>Event Dispatch
691+ * Thread</i> in the event that {@code firePropertyChange} or
692+ * {@code fireIndexedPropertyChange} are called off the <i>Event Dispatch
693+ * Thread</i>.
694+ *
695+ * @return {@code PropertyChangeSupport} for this {@code SwingWorker}
696+ */
697+ public final PropertyChangeSupport getPropertyChangeSupport() {
698+ return propertyChangeSupport;
699+ }
700+
701+ // PropertyChangeSupports methods END
702+
703+ /**
704+ * Returns the {@code SwingWorker} state bound property.
705+ *
706+ * @return the current state
707+ */
708+ public final StateValue getState() {
709+ /*
710+ * DONE is a speacial case
711+ * to keep getState and isDone is sync
712+ */
713+ if (isDone()) {
714+ return StateValue.DONE;
715+ } else {
716+ return state;
717+ }
718+ }
719+
720+ /**
721+ * Sets this {@code SwingWorker} state bound property.
722+ * @param state the state to set
723+ */
724+ private void setState(StateValue state) {
725+ StateValue old = this.state;
726+ this.state = state;
727+ firePropertyChange("state", old, state);
728+ }
729+
730+ /**
731+ * Invokes {@code done} on the EDT.
732+ */
733+ private void doneEDT() {
734+ Runnable doDone =
735+ new Runnable() {
736+ public void run() {
737+ done();
738+ }
739+ };
740+ if (SwingUtilities.isEventDispatchThread()) {
741+ doDone.run();
742+ } else {
743+ doSubmit.add(doDone);
744+ }
745+ }
746+
747+
748+ /**
749+ * returns workersExecutorService.
750+ *
751+ * returns the service stored in the appContext or creates it if
752+ * necessary.
753+ *
754+ * @return ExecutorService for the {@code SwingWorkers}
755+ */
756+ private static synchronized ExecutorService getWorkersExecutorService() {
757+ final AppContext appContext = AppContext.getAppContext();
758+ ExecutorService executorService =
759+ (ExecutorService) appContext.get(SwingWorker.class);
760+ if (executorService == null) {
761+ //this creates daemon threads.
762+ ThreadFactory threadFactory =
763+ new ThreadFactory() {
764+ final ThreadFactory defaultFactory =
765+ Executors.defaultThreadFactory();
766+ public Thread newThread(final Runnable r) {
767+ Thread thread =
768+ defaultFactory.newThread(r);
769+ thread.setName("SwingWorker-"
770+ + thread.getName());
771+ thread.setDaemon(true);
772+ return thread;
773+ }
774+ };
775+
776+ executorService =
777+ new ThreadPoolExecutor(MAX_WORKER_THREADS, MAX_WORKER_THREADS,
778+ 10L, TimeUnit.MINUTES,
779+ new LinkedBlockingQueue<Runnable>(),
780+ threadFactory);
781+ appContext.put(SwingWorker.class, executorService);
782+
783+ // Don't use ShutdownHook here as it's not enough. We should track
784+ // AppContext disposal instead of JVM shutdown, see 6799345 for details
785+ final ExecutorService es = executorService;
786+ appContext.addPropertyChangeListener(AppContext.DISPOSED_PROPERTY_NAME,
787+ new PropertyChangeListener() {
788+ @Override
789+ public void propertyChange(PropertyChangeEvent pce) {
790+ boolean disposed = (Boolean)pce.getNewValue();
791+ if (disposed) {
792+ final WeakReference<ExecutorService> executorServiceRef =
793+ new WeakReference<ExecutorService>(es);
794+ final ExecutorService executorService =
795+ executorServiceRef.get();
796+ if (executorService != null) {
797+ AccessController.doPrivileged(
798+ new PrivilegedAction<Void>() {
799+ public Void run() {
800+ executorService.shutdown();
801+ return null;
802+ }
803+ }
804+ );
805+ }
806+ }
807+ }
808+ }
809+ );
810+ }
811+ return executorService;
812+ }
813+
814+ private static final Object DO_SUBMIT_KEY = new StringBuilder("doSubmit");
815+ private static AccumulativeRunnable<Runnable> getDoSubmit() {
816+ synchronized (DO_SUBMIT_KEY) {
817+ final AppContext appContext = AppContext.getAppContext();
818+ Object doSubmit = appContext.get(DO_SUBMIT_KEY);
819+ if (doSubmit == null) {
820+ doSubmit = new DoSubmitAccumulativeRunnable();
821+ appContext.put(DO_SUBMIT_KEY, doSubmit);
822+ }
823+ return (AccumulativeRunnable<Runnable>) doSubmit;
824+ }
825+ }
826+ private static class DoSubmitAccumulativeRunnable
827+ extends AccumulativeRunnable<Runnable> implements ActionListener {
828+ private final static int DELAY = 1000 / 30;
829+ @Override
830+ protected void run(List<Runnable> args) {
831+ for (Runnable runnable : args) {
832+ runnable.run();
833+ }
834+ }
835+ @Override
836+ protected void submit() {
837+ Timer timer = new Timer(DELAY, this);
838+ timer.setRepeats(false);
839+ timer.start();
840+ }
841+ public void actionPerformed(ActionEvent event) {
842+ run();
843+ }
844+ }
845+
846+ private class SwingWorkerPropertyChangeSupport
847+ extends PropertyChangeSupport {
848+ SwingWorkerPropertyChangeSupport(Object source) {
849+ super(source);
850+ }
851+ @Override
852+ public void firePropertyChange(final PropertyChangeEvent evt) {
853+ if (SwingUtilities.isEventDispatchThread()) {
854+ super.firePropertyChange(evt);
855+ } else {
856+ doSubmit.add(
857+ new Runnable() {
858+ public void run() {
859+ SwingWorkerPropertyChangeSupport.this
860+ .firePropertyChange(evt);
861+ }
862+ });
863+ }
864+ }
865+ }
866+}