001: /*
002: * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025: package javax.swing;
026:
027: import java.beans.PropertyChangeListener;
028: import java.beans.PropertyChangeSupport;
029: import java.beans.PropertyChangeEvent;
030: import java.util.List;
031: import java.util.ArrayList;
032: import java.util.Collections;
033:
034: import java.util.concurrent.*;
035: import java.util.concurrent.locks.*;
036:
037: import java.awt.event.*;
038:
039: import javax.swing.SwingUtilities;
040:
041: import sun.awt.AppContext;
042: import sun.swing.AccumulativeRunnable;
043:
044: /**
045: * An abstract class to perform lengthy GUI-interacting tasks in a
046: * dedicated thread.
047: *
048: * <p>
049: * When writing a multi-threaded application using Swing, there are
050: * two constraints to keep in mind:
051: * (refer to
052: * <a href="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">
053: * How to Use Threads
054: * </a> for more details):
055: * <ul>
056: * <li> Time-consuming tasks should not be run on the <i>Event
057: * Dispatch Thread</i>. Otherwise the application becomes unresponsive.
058: * </li>
059: * <li> Swing components should be accessed on the <i>Event
060: * Dispatch Thread</i> only.
061: * </li>
062: * </ul>
063: *
064: * <p>
065: *
066: * <p>
067: * These constraints mean that a GUI application with time intensive
068: * computing needs at least two threads: 1) a thread to perform the lengthy
069: * task and 2) the <i>Event Dispatch Thread</i> (EDT) for all GUI-related
070: * activities. This involves inter-thread communication which can be
071: * tricky to implement.
072: *
073: * <p>
074: * {@code SwingWorker} is designed for situations where you need to have a long
075: * running task run in a background thread and provide updates to the UI
076: * either when done, or while processing.
077: * Subclasses of {@code SwingWorker} must implement
078: * the {@link #doInBackground} method to perform the background computation.
079: *
080: *
081: * <p>
082: * <b>Workflow</b>
083: * <p>
084: * There are three threads involved in the life cycle of a
085: * {@code SwingWorker} :
086: * <ul>
087: * <li>
088: * <p>
089: * <i>Current</i> thread: The {@link #execute} method is
090: * called on this thread. It schedules {@code SwingWorker} for the execution on a
091: * <i>worker</i>
092: * thread and returns immediately. One can wait for the {@code SwingWorker} to
093: * complete using the {@link #get get} methods.
094: * <li>
095: * <p>
096: * <i>Worker</i> thread: The {@link #doInBackground}
097: * method is called on this thread.
098: * This is where all background activities should happen. To notify
099: * {@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: * @version 1.14 05/05/07
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 = 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(
309: 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("1");
358: * publish("2", "3");
359: * publish("4", "5", "6");
360: * </pre>
361: *
362: * might result in:
363: *
364: * <pre>
365: * process("1", "2", "3", "4", "5", "6")
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<DefaultTableModel, Object[]> {
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() && row != null;
387: * row = loadData()) {
388: * publish((Object[]) row);
389: * }
390: * return tableModel;
391: * }
392: *
393: * {@code @Override}
394: * protected void process(List<Object[]> 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: protected final void publish(V... chunks) {
408: synchronized (this ) {
409: if (doProcess == null) {
410: doProcess = new AccumulativeRunnable<V>() {
411: @Override
412: public void run(List<V> args) {
413: process(args);
414: }
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(
486: "the value should be from 0 to 100");
487: }
488: if (this .progress == progress) {
489: return;
490: }
491: int oldProgress = this .progress;
492: this .progress = progress;
493: if (!getPropertyChangeSupport().hasListeners("progress")) {
494: return;
495: }
496: synchronized (this ) {
497: if (doNotifyProgressChange == null) {
498: doNotifyProgressChange = new AccumulativeRunnable<Integer>() {
499: @Override
500: public void run(List<Integer> args) {
501: firePropertyChange("progress", args.get(0),
502: args.get(args.size() - 1));
503: }
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,
602: ExecutionException {
603: return future.get();
604: }
605:
606: /**
607: * {@inheritDoc}
608: * <p>
609: * Please refer to {@link #get} for more details.
610: */
611: public final T get(long timeout, TimeUnit unit)
612: throws InterruptedException, ExecutionException,
613: TimeoutException {
614: return future.get(timeout, unit);
615: }
616:
617: // Future methods END
618:
619: // PropertyChangeSupports methods START
620: /**
621: * Adds a {@code PropertyChangeListener} to the listener list. The listener
622: * is registered for all properties. The same listener object may be added
623: * more than once, and will be called as many times as it is added. If
624: * {@code listener} is {@code null}, no exception is thrown and no action is taken.
625: *
626: * <p>
627: * Note: This is merely a convenience wrapper. All work is delegated to
628: * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
629: *
630: * @param listener the {@code PropertyChangeListener} to be added
631: */
632: public final void addPropertyChangeListener(
633: PropertyChangeListener listener) {
634: getPropertyChangeSupport().addPropertyChangeListener(listener);
635: }
636:
637: /**
638: * Removes a {@code PropertyChangeListener} from the listener list. This
639: * removes a {@code PropertyChangeListener} that was registered for all
640: * properties. If {@code listener} was added more than once to the same
641: * event source, it will be notified one less time after being removed. If
642: * {@code listener} is {@code null}, or was never added, no exception is
643: * thrown and no action is taken.
644: *
645: * <p>
646: * Note: This is merely a convenience wrapper. All work is delegated to
647: * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
648: *
649: * @param listener the {@code PropertyChangeListener} to be removed
650: */
651: public final void removePropertyChangeListener(
652: PropertyChangeListener listener) {
653: getPropertyChangeSupport().removePropertyChangeListener(
654: listener);
655: }
656:
657: /**
658: * Reports a bound property update to any registered listeners. No event is
659: * fired if {@code old} and {@code new} are equal and non-null.
660: *
661: * <p>
662: * This {@code SwingWorker} will be the source for
663: * any generated events.
664: *
665: * <p>
666: * When called off the <i>Event Dispatch Thread</i>
667: * {@code PropertyChangeListeners} are notified asynchronously on
668: * the <i>Event Dispatch Thread</i>.
669: * <p>
670: * Note: This is merely a convenience wrapper. All work is delegated to
671: * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
672: *
673: *
674: * @param propertyName the programmatic name of the property that was
675: * changed
676: * @param oldValue the old value of the property
677: * @param newValue the new value of the property
678: */
679: public final void firePropertyChange(String propertyName,
680: Object oldValue, Object newValue) {
681: getPropertyChangeSupport().firePropertyChange(propertyName,
682: oldValue, newValue);
683: }
684:
685: /**
686: * Returns the {@code PropertyChangeSupport} for this {@code SwingWorker}.
687: * This method is used when flexible access to bound properties support is
688: * needed.
689: * <p>
690: * This {@code SwingWorker} will be the source for
691: * any generated events.
692: *
693: * <p>
694: * Note: The returned {@code PropertyChangeSupport} notifies any
695: * {@code PropertyChangeListener}s asynchronously on the <i>Event Dispatch
696: * Thread</i> in the event that {@code firePropertyChange} or
697: * {@code fireIndexedPropertyChange} are called off the <i>Event Dispatch
698: * Thread</i>.
699: *
700: * @return {@code PropertyChangeSupport} for this {@code SwingWorker}
701: */
702: public final PropertyChangeSupport getPropertyChangeSupport() {
703: return propertyChangeSupport;
704: }
705:
706: // PropertyChangeSupports methods END
707:
708: /**
709: * Returns the {@code SwingWorker} state bound property.
710: *
711: * @return the current state
712: */
713: public final StateValue getState() {
714: /*
715: * DONE is a speacial case
716: * to keep getState and isDone is sync
717: */
718: if (isDone()) {
719: return StateValue.DONE;
720: } else {
721: return state;
722: }
723: }
724:
725: /**
726: * Sets this {@code SwingWorker} state bound property.
727: * @param the state state to set
728: */
729: private void setState(StateValue state) {
730: StateValue old = this .state;
731: this .state = state;
732: firePropertyChange("state", old, state);
733: }
734:
735: /**
736: * Invokes {@code done} on the EDT.
737: */
738: private void doneEDT() {
739: Runnable doDone = new Runnable() {
740: public void run() {
741: done();
742: }
743: };
744: if (SwingUtilities.isEventDispatchThread()) {
745: doDone.run();
746: } else {
747: doSubmit.add(doDone);
748: }
749: }
750:
751: /**
752: * returns workersExecutorService.
753: *
754: * returns the service stored in the appContext or creates it if
755: * necessary. If the last one it triggers autoShutdown thread to
756: * get started.
757: *
758: * @return ExecutorService for the {@code SwingWorkers}
759: * @see #startAutoShutdownThread
760: */
761: private static synchronized ExecutorService getWorkersExecutorService() {
762: final AppContext appContext = AppContext.getAppContext();
763: Object obj = appContext.get(SwingWorker.class);
764: if (obj == null) {
765: //this creates non-daemon threads.
766: ThreadFactory threadFactory = new ThreadFactory() {
767: final ThreadFactory defaultFactory = Executors
768: .defaultThreadFactory();
769:
770: public Thread newThread(final Runnable r) {
771: Thread thread = defaultFactory.newThread(r);
772: thread.setName("SwingWorker-" + thread.getName());
773: return thread;
774: }
775: };
776:
777: /*
778: * We want a to have no more than MAX_WORKER_THREADS
779: * running threads.
780: *
781: * We want a worker thread to wait no longer than 1 second
782: * for new tasks before terminating.
783: */
784: obj = new ThreadPoolExecutor(0, MAX_WORKER_THREADS, 1L,
785: TimeUnit.SECONDS,
786: new LinkedBlockingQueue<Runnable>(), threadFactory) {
787:
788: private final ReentrantLock pauseLock = new ReentrantLock();
789: private final Condition unpaused = pauseLock
790: .newCondition();
791: private boolean isPaused = false;
792: private final ReentrantLock executeLock = new ReentrantLock();
793:
794: @Override
795: public void execute(Runnable command) {
796: /*
797: * ThreadPoolExecutor first tries to run task
798: * in a corePool. If all threads are busy it
799: * tries to add task to the waiting queue. If it
800: * fails it run task in maximumPool.
801: *
802: * We want corePool to be 0 and
803: * maximumPool to be MAX_WORKER_THREADS
804: * We need to change the order of the execution.
805: * First try corePool then try maximumPool
806: * pool and only then store to the waiting
807: * queue. We can not do that because we would
808: * need access to the private methods.
809: *
810: * Instead we enlarge corePool to
811: * MAX_WORKER_THREADS before the execution and
812: * shrink it back to 0 after.
813: * It does pretty much what we need.
814: *
815: * While we changing the corePoolSize we need
816: * to stop running worker threads from accepting new
817: * tasks.
818: */
819:
820: //we need atomicity for the execute method.
821: executeLock.lock();
822: try {
823:
824: pauseLock.lock();
825: try {
826: isPaused = true;
827: } finally {
828: pauseLock.unlock();
829: }
830:
831: setCorePoolSize(MAX_WORKER_THREADS);
832: super .execute(command);
833: setCorePoolSize(0);
834:
835: pauseLock.lock();
836: try {
837: isPaused = false;
838: unpaused.signalAll();
839: } finally {
840: pauseLock.unlock();
841: }
842: } finally {
843: executeLock.unlock();
844: }
845: }
846:
847: @Override
848: protected void afterExecute(Runnable r, Throwable t) {
849: super .afterExecute(r, t);
850: pauseLock.lock();
851: try {
852: while (isPaused) {
853: unpaused.await();
854: }
855: } catch (InterruptedException ignore) {
856:
857: } finally {
858: pauseLock.unlock();
859: }
860: }
861: };
862: appContext.put(SwingWorker.class, obj);
863: }
864: return (ExecutorService) obj;
865: }
866:
867: private static final Object DO_SUBMIT_KEY = new StringBuilder(
868: "doSubmit");
869:
870: private static AccumulativeRunnable<Runnable> getDoSubmit() {
871: synchronized (DO_SUBMIT_KEY) {
872: final AppContext appContext = AppContext.getAppContext();
873: Object doSubmit = appContext.get(DO_SUBMIT_KEY);
874: if (doSubmit == null) {
875: doSubmit = new DoSubmitAccumulativeRunnable();
876: appContext.put(DO_SUBMIT_KEY, doSubmit);
877: }
878: return (AccumulativeRunnable<Runnable>) doSubmit;
879: }
880: }
881:
882: private static class DoSubmitAccumulativeRunnable extends
883: AccumulativeRunnable<Runnable> implements ActionListener {
884: private final static int DELAY = (int) (1000 / 30);
885:
886: @Override
887: protected void run(List<Runnable> args) {
888: for (Runnable runnable : args) {
889: runnable.run();
890: }
891: }
892:
893: @Override
894: protected void submit() {
895: Timer timer = new Timer(DELAY, this );
896: timer.setRepeats(false);
897: timer.start();
898: }
899:
900: public void actionPerformed(ActionEvent event) {
901: run();
902: }
903: }
904:
905: private class SwingWorkerPropertyChangeSupport extends
906: PropertyChangeSupport {
907: SwingWorkerPropertyChangeSupport(Object source) {
908: super (source);
909: }
910:
911: @Override
912: public void firePropertyChange(final PropertyChangeEvent evt) {
913: if (SwingUtilities.isEventDispatchThread()) {
914: super .firePropertyChange(evt);
915: } else {
916: doSubmit.add(new Runnable() {
917: public void run() {
918: SwingWorkerPropertyChangeSupport.this
919: .firePropertyChange(evt);
920: }
921: });
922: }
923: }
924: }
925: }
|