KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > SwingWorker


1 /*
2  * @(#)SwingWorker.java 1.8 06/11/30
3  *
4  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package javax.swing;
8
9 import java.beans.PropertyChangeListener JavaDoc;
10 import java.beans.PropertyChangeSupport JavaDoc;
11 import java.beans.PropertyChangeEvent JavaDoc;
12 import java.util.List JavaDoc;
13 import java.util.ArrayList JavaDoc;
14 import java.util.Collections JavaDoc;
15
16 import java.util.concurrent.*;
17 import java.util.concurrent.locks.*;
18
19 import java.awt.event.*;
20
21 import javax.swing.SwingUtilities JavaDoc;
22
23 import sun.awt.AppContext;
24 import sun.swing.AccumulativeRunnable;
25
26 /**
27  * An abstract class to perform lengthy GUI-interacting tasks in a
28  * dedicated thread.
29  *
30  * <p>
31  * When writing a multi-threaded application using Swing, there are
32  * two constraints to keep in mind:
33  * (refer to
34  * <a HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">
35  * How to Use Threads
36  * </a> for more details):
37  * <ul>
38  * <li> Time-consuming tasks should not be run on the <i>Event
39  * Dispatch Thread</i>. Otherwise the application becomes unresponsive.
40  * </li>
41  * <li> Swing components should be accessed on the <i>Event
42  * Dispatch Thread</i> only.
43  * </li>
44  * </ul>
45  *
46  * <p>
47  *
48  * <p>
49  * These constraints mean that a GUI application with time intensive
50  * computing needs at least two threads: 1) a thread to perform the lengthy
51  * task and 2) the <i>Event Dispatch Thread</i> (EDT) for all GUI-related
52  * activities. This involves inter-thread communication which can be
53  * tricky to implement.
54  *
55  * <p>
56  * {@code SwingWorker} is designed for situations where you need to have a long
57  * running task run in a background thread and provide updates to the UI
58  * either when done, or while processing.
59  * Subclasses of {@code SwingWorker} must implement
60  * the {@link #doInBackground} method to perform the background computation.
61  *
62  *
63  * <p>
64  * <b>Workflow</b>
65  * <p>
66  * There are three threads involved in the life cycle of a
67  * {@code SwingWorker} :
68  * <ul>
69  * <li>
70  * <p>
71  * <i>Current</i> thread: The {@link #execute} method is
72  * called on this thread. It schedules {@code SwingWorker} for the execution on a
73  * <i>worker</i>
74  * thread and returns immediately. One can wait for the {@code SwingWorker} to
75  * complete using the {@link #get get} methods.
76  * <li>
77  * <p>
78  * <i>Worker</i> thread: The {@link #doInBackground}
79  * method is called on this thread.
80  * This is where all background activities should happen. To notify
81  * {@code PropertyChangeListeners} about bound properties changes use the
82  * {@link #firePropertyChange firePropertyChange} and
83  * {@link #getPropertyChangeSupport} methods. By default there are two bound
84  * properties available: {@code state} and {@code progress}.
85  * <li>
86  * <p>
87  * <i>Event Dispatch Thread</i>: All Swing related activities occur
88  * on this thread. {@code SwingWorker} invokes the
89  * {@link #process process} and {@link #done} methods and notifies
90  * any {@code PropertyChangeListeners} on this thread.
91  * </ul>
92  *
93  * <p>
94  * Often, the <i>Current</i> thread is the <i>Event Dispatch
95  * Thread</i>.
96  *
97  *
98  * <p>
99  * Before the {@code doInBackground} method is invoked on a <i>worker</i> thread,
100  * {@code SwingWorker} notifies any {@code PropertyChangeListeners} about the
101  * {@code state} property change to {@code StateValue.STARTED}. After the
102  * {@code doInBackground} method is finished the {@code done} method is
103  * executed. Then {@code SwingWorker} notifies any {@code PropertyChangeListeners}
104  * about the {@code state} property change to {@code StateValue.DONE}.
105  *
106  * <p>
107  * {@code SwingWorker} is only designed to be executed once. Executing a
108  * {@code SwingWorker} more than once will not result in invoking the
109  * {@code doInBackground} method twice.
110  *
111  * <p>
112  * <b>Sample Usage</b>
113  * <p>
114  * The following example illustrates the simplest use case. Some
115  * processing is done in the background and when done you update a Swing
116  * component.
117  *
118  * <p>
119  * Say we want to find the "Meaning of Life" and display the result in
120  * a {@code JLabel}.
121  *
122  * <pre>
123  * final JLabel label;
124  * class MeaningOfLifeFinder extends SwingWorker&lt;String, Object&gt; {
125  * {@code @Override}
126  * public String doInBackground() {
127  * return findTheMeaningOfLife();
128  * }
129  *
130  * {@code @Override}
131  * protected void done() {
132  * try {
133  * label.setText(get());
134  * } catch (Exception ignore) {
135  * }
136  * }
137  * }
138  *
139  * (new MeaningOfLifeFinder()).execute();
140  * </pre>
141  *
142  * <p>
143  * The next example is useful in situations where you wish to process data
144  * as it is ready on the <i>Event Dispatch Thread</i>.
145  *
146  * <p>
147  * Now we want to find the first N prime numbers and display the results in a
148  * {@code JTextArea}. While this is computing, we want to update our
149  * progress in a {@code JProgressBar}. Finally, we also want to print
150  * the prime numbers to {@code System.out}.
151  * <pre>
152  * class PrimeNumbersTask extends
153  * SwingWorker&lt;List&lt;Integer&gt;, Integer&gt; {
154  * PrimeNumbersTask(JTextArea textArea, int numbersToFind) {
155  * //initialize
156  * }
157  *
158  * {@code @Override}
159  * public List&lt;Integer&gt; doInBackground() {
160  * while (! enough &amp;&amp; ! isCancelled()) {
161  * number = nextPrimeNumber();
162  * publish(number);
163  * setProgress(100 * numbers.size() / numbersToFind);
164  * }
165  * }
166  * return numbers;
167  * }
168  *
169  * {@code @Override}
170  * protected void process(List&lt;Integer&gt; chunks) {
171  * for (int number : chunks) {
172  * textArea.append(number + &quot;\n&quot;);
173  * }
174  * }
175  * }
176  *
177  * JTextArea textArea = new JTextArea();
178  * final JProgressBar progressBar = new JProgressBar(0, 100);
179  * PrimeNumbersTask task = new PrimeNumbersTask(textArea, N);
180  * task.addPropertyChangeListener(
181  * new PropertyChangeListener() {
182  * public void propertyChange(PropertyChangeEvent evt) {
183  * if (&quot;progress&quot;.equals(evt.getPropertyName())) {
184  * progressBar.setValue((Integer)evt.getNewValue());
185  * }
186  * }
187  * });
188  *
189  * task.execute();
190  * System.out.println(task.get()); //prints all prime numbers we have got
191  * </pre>
192  *
193  * <p>
194  * Because {@code SwingWorker} implements {@code Runnable}, a
195  * {@code SwingWorker} can be submitted to an
196  * {@link java.util.concurrent.Executor} for execution.
197  *
198  * @author Igor Kushnirskiy
199  * @version 1.8 11/30/06
200  *
201  * @param <T> the result type returned by this {@code SwingWorker's}
202  * {@code doInBackground} and {@code get} methods
203  * @param <V> the type used for carrying out intermediate results by this
204  * {@code SwingWorker's} {@code publish} and {@code process} methods
205  *
206  * @since 1.6
207  */

208 public abstract class SwingWorker<T, V> implements RunnableFuture<T> {
209     /**
210      * number of worker threads.
211      */

212     private static final int MAX_WORKER_THREADS = 10;
213
214     /**
215      * current progress.
216      */

217     private volatile int progress;
218
219     /**
220      * current state.
221      */

222     private volatile StateValue state;
223
224     /**
225      * everything is run inside this FutureTask. Also it is used as
226      * a delegatee for the Future API.
227      */

228     private final FutureTask<T> future;
229
230     /**
231      * all propertyChangeSupport goes through this.
232      */

233     private final PropertyChangeSupport JavaDoc propertyChangeSupport;
234
235     /**
236      * handler for {@code process} mehtod.
237      */

238     private AccumulativeRunnable<V> doProcess;
239
240     /**
241      * handler for progress property change notifications.
242      */

243     private AccumulativeRunnable<Integer JavaDoc> doNotifyProgressChange;
244
245     private final AccumulativeRunnable<Runnable JavaDoc> doSubmit = getDoSubmit();
246
247     /**
248      * Values for the {@code state} bound property.
249      * @since 1.6
250      */

251     public enum StateValue {
252         /**
253          * Initial {@code SwingWorker} state.
254          */

255         PENDING,
256         /**
257          * {@code SwingWorker} is {@code STARTED}
258          * before invoking {@code doInBackground}.
259          */

260         STARTED,
261
262         /**
263          * {@code SwingWorker} is {@code DONE}
264          * after {@code doInBackground} method
265          * is finished.
266          */

267         DONE
268     };
269
270     /**
271      * Constructs this {@code SwingWorker}.
272      */

273     public SwingWorker() {
274         Callable<T> callable =
275                 new Callable<T>() {
276                     public T call() throws Exception JavaDoc {
277                         setState(StateValue.STARTED);
278                         return doInBackground();
279                     }
280                 };
281
282         future = new FutureTask<T>(callable) {
283                        @Override JavaDoc
284                        protected void done() {
285                            doneEDT();
286                            setState(StateValue.DONE);
287                        }
288                    };
289
290        state = StateValue.PENDING;
291        propertyChangeSupport = new SwingWorkerPropertyChangeSupport(this);
292        doProcess = null;
293        doNotifyProgressChange = null;
294     }
295     
296     /**
297      * Computes a result, or throws an exception if unable to do so.
298      *
299      * <p>
300      * Note that this method is executed only once.
301      *
302      * <p>
303      * Note: this method is executed in a background thread.
304      *
305      *
306      * @return the computed result
307      * @throws Exception if unable to compute a result
308      *
309      */

310     protected abstract T doInBackground() throws Exception JavaDoc ;
311     
312     /**
313      * Sets this {@code Future} to the result of computation unless
314      * it has been cancelled.
315      */

316     public final void run() {
317         future.run();
318     }
319     
320     /**
321      * Sends data chunks to the {@link #process} method. This method is to be
322      * used from inside the {@code doInBackground} method to deliver
323      * intermediate results
324      * for processing on the <i>Event Dispatch Thread</i> inside the
325      * {@code process} method.
326      *
327      * <p>
328      * Because the {@code process} method is invoked asynchronously on
329      * the <i>Event Dispatch Thread</i>
330      * multiple invocations to the {@code publish} method
331      * might occur before the {@code process} method is executed. For
332      * performance purposes all these invocations are coalesced into one
333      * invocation with concatenated arguments.
334      *
335      * <p>
336      * For example:
337      *
338      * <pre>
339      * publish(&quot;1&quot;);
340      * publish(&quot;2&quot;, &quot;3&quot;);
341      * publish(&quot;4&quot;, &quot;5&quot;, &quot;6&quot;);
342      * </pre>
343      *
344      * might result in:
345      *
346      * <pre>
347      * process(&quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;, &quot;5&quot;, &quot;6&quot;)
348      * </pre>
349      *
350      * <p>
351      * <b>Sample Usage</b>. This code snippet loads some tabular data and
352      * updates {@code DefaultTableModel} with it. Note that it safe to mutate
353      * the tableModel from inside the {@code process} method because it is
354      * invoked on the <i>Event Dispatch Thread</i>.
355      *
356      * <pre>
357      * class TableSwingWorker extends
358      * SwingWorker&lt;DefaultTableModel, Object[]&gt; {
359      * private final DefaultTableModel tableModel;
360      *
361      * public TableSwingWorker(DefaultTableModel tableModel) {
362      * this.tableModel = tableModel;
363      * }
364      *
365      * {@code @Override}
366      * protected DefaultTableModel doInBackground() throws Exception {
367      * for (Object[] row = loadData();
368      * ! isCancelled() &amp;&amp; row != null;
369      * row = loadData()) {
370      * publish((Object[]) row);
371      * }
372      * return tableModel;
373      * }
374      *
375      * {@code @Override}
376      * protected void process(List&lt;Object[]&gt; chunks) {
377      * for (Object[] row : chunks) {
378      * tableModel.addRow(row);
379      * }
380      * }
381      * }
382      * </pre>
383      *
384      * @param chunks intermediate results to process
385      *
386      * @see #process
387      *
388      */

389     protected final void publish(V... chunks) {
390         synchronized (this) {
391             if (doProcess == null) {
392                 doProcess = new AccumulativeRunnable<V>() {
393                     @Override JavaDoc
394                     public void run(List JavaDoc<V> args) {
395                         process(args);
396                     }
397                     @Override JavaDoc
398                     protected void submit() {
399                         doSubmit.add(this);
400                     }
401                 };
402             }
403         }
404         doProcess.add(chunks);
405     }
406
407     /**
408      * Receives data chunks from the {@code publish} method asynchronously on the
409      * <i>Event Dispatch Thread</i>.
410      *
411      * <p>
412      * Please refer to the {@link #publish} method for more details.
413      *
414      * @param chunks intermediate results to process
415      *
416      * @see #publish
417      *
418      */

419     protected void process(List JavaDoc<V> chunks) {
420     }
421
422     /**
423      * Executed on the <i>Event Dispatch Thread</i> after the {@code doInBackground}
424      * method is finished. The default
425      * implementation does nothing. Subclasses may override this method to
426      * perform completion actions on the <i>Event Dispatch Thread</i>. Note
427      * that you can query status inside the implementation of this method to
428      * determine the result of this task or whether this task has been cancelled.
429      *
430      * @see #doInBackground
431      * @see #isCancelled()
432      * @see #get
433      */

434     protected void done() {
435     }
436
437     /**
438      * Sets the {@code progress} bound property.
439      * The value should be from 0 to 100.
440      *
441      * <p>
442      * Because {@code PropertyChangeListener}s are notified asynchronously on
443      * the <i>Event Dispatch Thread</i> multiple invocations to the
444      * {@code setProgress} method might occur before any
445      * {@code PropertyChangeListeners} are invoked. For performance purposes
446      * all these invocations are coalesced into one invocation with the last
447      * invocation argument only.
448      *
449      * <p>
450      * For example, the following invokations:
451      *
452      * <pre>
453      * setProgress(1);
454      * setProgress(2);
455      * setProgress(3);
456      * </pre>
457      *
458      * might result in a single {@code PropertyChangeListener} notification with
459      * the value {@code 3}.
460      *
461      * @param progress the progress value to set
462      * @throws IllegalArgumentException is value not from 0 to 100
463      */

464     protected final void setProgress(int progress) {
465         if (progress < 0 || progress > 100) {
466             throw new IllegalArgumentException JavaDoc("the value should be from 0 to 100");
467         }
468         if (this.progress == progress) {
469             return;
470         }
471         int oldProgress = this.progress;
472         this.progress = progress;
473         if (! getPropertyChangeSupport().hasListeners("progress")) {
474             return;
475         }
476         synchronized (this) {
477             if (doNotifyProgressChange == null) {
478                 doNotifyProgressChange =
479                     new AccumulativeRunnable<Integer JavaDoc>() {
480                         @Override JavaDoc
481                         public void run(List JavaDoc<Integer JavaDoc> args) {
482                             firePropertyChange("progress",
483                                args.get(0),
484                                args.get(args.size() - 1));
485                         }
486                         @Override JavaDoc
487                         protected void submit() {
488                             doSubmit.add(this);
489                         }
490                     };
491             }
492         }
493         doNotifyProgressChange.add(oldProgress, progress);
494     }
495
496     /**
497      * Returns the {@code progress} bound property.
498      *
499      * @return the progress bound property.
500      */

501     public final int getProgress() {
502         return progress;
503     }
504
505     /**
506      * Schedules this {@code SwingWorker} for execution on a <i>worker</i>
507      * thread. There are a number of <i>worker</i> threads available. In the
508      * event all <i>worker</i> threads are busy handling other
509      * {@code SwingWorkers} this {@code SwingWorker} is placed in a waiting
510      * queue.
511      *
512      * <p>
513      * Note:
514      * {@code SwingWorker} is only designed to be executed once. Executing a
515      * {@code SwingWorker} more than once will not result in invoking the
516      * {@code doInBackground} method twice.
517      */

518     public final void execute() {
519         getWorkersExecutorService().execute(this);
520     }
521
522     // Future methods START
523
/**
524      * {@inheritDoc}
525      */

526     public final boolean cancel(boolean mayInterruptIfRunning) {
527         return future.cancel(mayInterruptIfRunning);
528     }
529
530     /**
531      * {@inheritDoc}
532      */

533     public final boolean isCancelled() {
534         return future.isCancelled();
535     }
536
537     /**
538      * {@inheritDoc}
539      */

540     public final boolean isDone() {
541         return future.isDone();
542     }
543
544     /**
545      * {@inheritDoc}
546      * <p>
547      * Note: calling {@code get} on the <i>Event Dispatch Thread</i> blocks
548      * <i>all</i> events, including repaints, from being processed until this
549      * {@code SwingWorker} is complete.
550      *
551      * <p>
552      * When you want the {@code SwingWorker} to block on the <i>Event
553      * Dispatch Thread</i> we recommend that you use a <i>modal dialog</i>.
554      *
555      * <p>
556      * For example:
557      *
558      * <pre>
559      * class SwingWorkerCompletionWaiter extends PropertyChangeListener {
560      * private JDialog dialog;
561      *
562      * public SwingWorkerCompletionWaiter(JDialog dialog) {
563      * this.dialog = dialog;
564      * }
565      *
566      * public void propertyChange(PropertyChangeEvent event) {
567      * if (&quot;state&quot;.equals(event.getPropertyName())
568      * &amp;&amp; SwingWorker.StateValue.DONE == event.getNewValue()) {
569      * dialog.setVisible(false);
570      * dialog.dispose();
571      * }
572      * }
573      * }
574      * JDialog dialog = new JDialog(owner, true);
575      * swingWorker.addPropertyChangeListener(
576      * new SwingWorkerCompletionWaiter(dialog));
577      * swingWorker.execute();
578      * //the dialog will be visible until the SwingWorker is done
579      * dialog.setVisible(true);
580      * </pre>
581      */

582     public final T get() throws InterruptedException JavaDoc, ExecutionException {
583         return future.get();
584     }
585
586     /**
587      * {@inheritDoc}
588      * <p>
589      * Please refer to {@link #get} for more details.
590      */

591     public final T get(long timeout, TimeUnit unit) throws InterruptedException JavaDoc,
592             ExecutionException, TimeoutException {
593         return future.get(timeout, unit);
594     }
595
596     // Future methods END
597

598     // PropertyChangeSupports methods START
599
/**
600      * Adds a {@code PropertyChangeListener} to the listener list. The listener
601      * is registered for all properties. The same listener object may be added
602      * more than once, and will be called as many times as it is added. If
603      * {@code listener} is {@code null}, no exception is thrown and no action is taken.
604      *
605      * <p>
606      * Note: This is merely a convenience wrapper. All work is delegated to
607      * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
608      *
609      * @param listener the {@code PropertyChangeListener} to be added
610      */

611     public final void addPropertyChangeListener(PropertyChangeListener JavaDoc listener) {
612         getPropertyChangeSupport().addPropertyChangeListener(listener);
613     }
614
615     /**
616      * Removes a {@code PropertyChangeListener} from the listener list. This
617      * removes a {@code PropertyChangeListener} that was registered for all
618      * properties. If {@code listener} was added more than once to the same
619      * event source, it will be notified one less time after being removed. If
620      * {@code listener} is {@code null}, or was never added, no exception is
621      * thrown and no action is taken.
622      *
623      * <p>
624      * Note: This is merely a convenience wrapper. All work is delegated to
625      * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
626      *
627      * @param listener the {@code PropertyChangeListener} to be removed
628      */

629     public final void removePropertyChangeListener(PropertyChangeListener JavaDoc listener) {
630         getPropertyChangeSupport().removePropertyChangeListener(listener);
631     }
632
633     /**
634      * Reports a bound property update to any registered listeners. No event is
635      * fired if {@code old} and {@code new} are equal and non-null.
636      *
637      * <p>
638      * This {@code SwingWorker} will be the source for
639      * any generated events.
640      *
641      * <p>
642      * When called off the <i>Event Dispatch Thread</i>
643      * {@code PropertyChangeListeners} are notified asynchronously on
644      * the <i>Event Dispatch Thread</i>.
645      * <p>
646      * Note: This is merely a convenience wrapper. All work is delegated to
647      * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
648      *
649      *
650      * @param propertyName the programmatic name of the property that was
651      * changed
652      * @param oldValue the old value of the property
653      * @param newValue the new value of the property
654      */

655     public final void firePropertyChange(String JavaDoc propertyName, Object JavaDoc oldValue,
656             Object JavaDoc newValue) {
657         getPropertyChangeSupport().firePropertyChange(propertyName,
658             oldValue, newValue);
659     }
660
661     /**
662      * Returns the {@code PropertyChangeSupport} for this {@code SwingWorker}.
663      * This method is used when flexible access to bound properties support is
664      * needed.
665      * <p>
666      * This {@code SwingWorker} will be the source for
667      * any generated events.
668      *
669      * <p>
670      * Note: The returned {@code PropertyChangeSupport} notifies any
671      * {@code PropertyChangeListener}s asynchronously on the <i>Event Dispatch
672      * Thread</i> in the event that {@code firePropertyChange} or
673      * {@code fireIndexedPropertyChange} are called off the <i>Event Dispatch
674      * Thread</i>.
675      *
676      * @return {@code PropertyChangeSupport} for this {@code SwingWorker}
677      */

678     public final PropertyChangeSupport JavaDoc getPropertyChangeSupport() {
679         return propertyChangeSupport;
680     }
681
682     // PropertyChangeSupports methods END
683

684     /**
685      * Returns the {@code SwingWorker} state bound property.
686      *
687      * @return the current state
688      */

689     public final StateValue getState() {
690         /*
691          * DONE is a speacial case
692          * to keep getState and isDone is sync
693          */

694         if (isDone()) {
695             return StateValue.DONE;
696         } else {
697             return state;
698         }
699     }
700     
701     /**
702      * Sets this {@code SwingWorker} state bound property.
703      * @param the state state to set
704      */

705     private void setState(StateValue state) {
706         StateValue old = this.state;
707         this.state = state;
708         firePropertyChange("state", old, state);
709     }
710
711     /**
712      * Invokes {@code done} on the EDT.
713      */

714     private void doneEDT() {
715         Runnable JavaDoc doDone =
716             new Runnable JavaDoc() {
717                 public void run() {
718                     done();
719                 }
720             };
721         if (SwingUtilities.isEventDispatchThread()) {
722             doDone.run();
723         } else {
724             doSubmit.add(doDone);
725         }
726     }
727
728
729     /**
730      * returns workersExecutorService.
731      *
732      * returns the service stored in the appContext or creates it if
733      * necessary. If the last one it triggers autoShutdown thread to
734      * get started.
735      *
736      * @return ExecutorService for the {@code SwingWorkers}
737      * @see #startAutoShutdownThread
738      */

739     private static synchronized ExecutorService getWorkersExecutorService() {
740         final AppContext appContext = AppContext.getAppContext();
741         Object JavaDoc obj = appContext.get(SwingWorker JavaDoc.class);
742         if (obj == null) {
743             //this creates non-daemon threads.
744
ThreadFactory threadFactory =
745                 new ThreadFactory() {
746                     final ThreadFactory defaultFactory =
747                         Executors.defaultThreadFactory();
748                     public Thread JavaDoc newThread(final Runnable JavaDoc r) {
749                         Thread JavaDoc thread =
750                             defaultFactory.newThread(r);
751                         thread.setName("SwingWorker-"
752                             + thread.getName());
753                         return thread;
754                     }
755                 };
756
757             /*
758              * We want a to have no more than MAX_WORKER_THREADS
759              * running threads.
760              *
761              * We want a worker thread to wait no longer than 1 second
762              * for new tasks before terminating.
763              */

764             obj = new ThreadPoolExecutor(0, MAX_WORKER_THREADS,
765                                          1L, TimeUnit.SECONDS,
766                                          new LinkedBlockingQueue<Runnable JavaDoc>(),
767                                          threadFactory) {
768
769                     private final ReentrantLock pauseLock = new ReentrantLock();
770                     private final Condition unpaused = pauseLock.newCondition();
771                     private boolean isPaused = false;
772                     private final ReentrantLock executeLock = new ReentrantLock();
773                     
774                     @Override JavaDoc
775                     public void execute(Runnable JavaDoc command) {
776                         /*
777                          * ThreadPoolExecutor first tries to run task
778                          * in a corePool. If all threads are busy it
779                          * tries to add task to the waiting queue. If it
780                          * fails it run task in maximumPool.
781                          *
782                          * We want corePool to be 0 and
783                          * maximumPool to be MAX_WORKER_THREADS
784                          * We need to change the order of the execution.
785                          * First try corePool then try maximumPool
786                          * pool and only then store to the waiting
787                          * queue. We can not do that because we would
788                          * need access to the private methods.
789                          *
790                          * Instead we enlarge corePool to
791                          * MAX_WORKER_THREADS before the execution and
792                          * shrink it back to 0 after.
793                          * It does pretty much what we need.
794                          *
795                          * While we changing the corePoolSize we need
796                          * to stop running worker threads from accepting new
797                          * tasks.
798                          */

799                         
800                         //we need atomicity for the execute method.
801
executeLock.lock();
802                         try {
803
804                             pauseLock.lock();
805                             try {
806                                 isPaused = true;
807                             } finally {
808                                 pauseLock.unlock();
809                             }
810                             
811                             setCorePoolSize(MAX_WORKER_THREADS);
812                             super.execute(command);
813                             setCorePoolSize(0);
814                             
815                             pauseLock.lock();
816                             try {
817                                 isPaused = false;
818                                 unpaused.signalAll();
819                             } finally {
820                                 pauseLock.unlock();
821                             }
822                         } finally {
823                             executeLock.unlock();
824                         }
825                     }
826                     @Override JavaDoc
827                     protected void afterExecute(Runnable JavaDoc r, Throwable JavaDoc t) {
828                         super.afterExecute(r, t);
829                         pauseLock.lock();
830                         try {
831                             while(isPaused) {
832                                 unpaused.await();
833                             }
834                         } catch(InterruptedException JavaDoc ignore) {
835                             
836                         } finally {
837                             pauseLock.unlock();
838                         }
839                     }
840                 };
841             appContext.put(SwingWorker JavaDoc.class, obj);
842         }
843         return (ExecutorService)obj;
844     }
845     
846     private static final Object JavaDoc DO_SUBMIT_KEY = new StringBuilder JavaDoc("doSubmit");
847     private static AccumulativeRunnable<Runnable JavaDoc> getDoSubmit() {
848         synchronized (DO_SUBMIT_KEY) {
849             final AppContext appContext = AppContext.getAppContext();
850             Object JavaDoc doSubmit = appContext.get(DO_SUBMIT_KEY);
851             if (doSubmit == null) {
852                 doSubmit = new DoSubmitAccumulativeRunnable();
853                 appContext.put(DO_SUBMIT_KEY, doSubmit);
854             }
855             return (AccumulativeRunnable<Runnable JavaDoc>) doSubmit;
856         }
857     }
858     private static class DoSubmitAccumulativeRunnable
859           extends AccumulativeRunnable<Runnable JavaDoc> implements ActionListener {
860         private final static int DELAY = (int) (1000 / 30);
861         @Override JavaDoc
862         protected void run(List JavaDoc<Runnable JavaDoc> args) {
863             for (Runnable JavaDoc runnable : args) {
864                 runnable.run();
865             }
866         }
867         @Override JavaDoc
868         protected void submit() {
869             Timer JavaDoc timer = new Timer JavaDoc(DELAY, this);
870             timer.setRepeats(false);
871             timer.start();
872         }
873         public void actionPerformed(ActionEvent event) {
874             run();
875         }
876     }
877     
878     private class SwingWorkerPropertyChangeSupport
879             extends PropertyChangeSupport JavaDoc {
880         SwingWorkerPropertyChangeSupport(Object JavaDoc source) {
881             super(source);
882         }
883         @Override JavaDoc
884         public void firePropertyChange(final PropertyChangeEvent JavaDoc evt) {
885             if (SwingUtilities.isEventDispatchThread()) {
886                 super.firePropertyChange(evt);
887             } else {
888                 doSubmit.add(
889                     new Runnable JavaDoc() {
890                         public void run() {
891                             SwingWorkerPropertyChangeSupport.this
892                                 .firePropertyChange(evt);
893                         }
894                     });
895             }
896         }
897     }
898 }
899
Popular Tags