PageRenderTime 60ms CodeModel.GetById 21ms app.highlight 31ms RepoModel.GetById 1ms app.codeStats 0ms

/bundles/plugins-trunk/CommonControls/common/swingworker/SwingWorker.java

#
Java | 720 lines | 272 code | 53 blank | 395 comment | 21 complexity | 4a5bad859a096ee411c00d89ab5e1fd3 MD5 | raw file
  1/*
  2 * $Id: SwingWorker.java,v 1.5 2007/03/01 19:55:54 idk Exp $
  3 *
  4 * Copyright 2005 Sun Microsystems, Inc. All rights
  5 * reserved. Use is subject to license terms.
  6 */
  7
  8//package org.jdesktop.swingworker;
  9package common.swingworker;
 10
 11import java.beans.PropertyChangeListener;
 12import java.beans.PropertyChangeSupport;
 13import java.beans.PropertyChangeEvent;
 14import java.util.List;
 15import java.util.concurrent.*;
 16import java.util.concurrent.atomic.AtomicInteger;
 17import java.util.concurrent.locks.*;
 18
 19import java.awt.event.*;
 20
 21import javax.swing.SwingUtilities;
 22import javax.swing.Timer;
 23
 24/** A version of javax.swing.SwingWorker for Java before 1.6 that is not needed anymore.
 25
 26 * @author Igor Kushnirskiy
 27 * @version $Revision: 1.5 $ $Date: 2007/03/01 19:55:54 $
 28 *
 29 * @param <T> the result type returned by this {@code SwingWorker's}
 30 *        {@code doInBackground} and {@code get} methods
 31 * @param <V> the type used for carrying out intermediate results by this
 32 *        {@code SwingWorker's} {@code publish} and {@code process} methods
 33 *
 34 * @deprecated use javax.swing.SwingWorker, as of Java 1.6.
 35 *
 36 */
 37public abstract class SwingWorker<T, V> implements Future<T>, Runnable {
 38    /**
 39     * number of worker threads.
 40     */
 41    private static final int MAX_WORKER_THREADS = 10;
 42
 43    /**
 44     * current progress.
 45     */
 46    private volatile int progress;
 47
 48    /**
 49     * current state.
 50     */
 51    private volatile StateValue state;
 52
 53    /**
 54     * everything is run inside this FutureTask. Also it is used as
 55     * a delegatee for the Future API.
 56     */
 57    private final FutureTask<T> future;
 58
 59    /**
 60     * all propertyChangeSupport goes through this.
 61     */
 62    private final PropertyChangeSupport propertyChangeSupport;
 63
 64    /**
 65     * handler for {@code process} method.
 66     */
 67    private AccumulativeRunnable<V> doProcess;
 68
 69    /**
 70     * handler for progress property change notifications.
 71     */
 72    private AccumulativeRunnable<Integer> doNotifyProgressChange;
 73
 74    private static final AccumulativeRunnable<Runnable> doSubmit =
 75        new DoSubmitAccumulativeRunnable();
 76
 77    private static ExecutorService executorService = null;
 78
 79    /**
 80     * Values for the {@code state} bound property.
 81     */
 82    public enum StateValue {
 83        /**
 84         * Initial {@code SwingWorker} state.
 85         */
 86        PENDING,
 87        /**
 88         * {@code SwingWorker} is {@code STARTED}
 89         * before invoking {@code doInBackground}.
 90         */
 91        STARTED,
 92
 93        /**
 94         * {@code SwingWorker} is {@code DONE}
 95         * after {@code doInBackground} method
 96         * is finished.
 97         */
 98        DONE
 99    };
100
101    /**
102     * Constructs this {@code SwingWorker}.
103     */
104    public SwingWorker() {
105        Callable<T> callable =
106                new Callable<T>() {
107                    public T call() throws Exception {      // NOPMD
108                        setState(StateValue.STARTED);
109                        return doInBackground();
110                    }
111                };
112
113        future = new FutureTask<T>(callable) {
114                       @Override
115                       protected void done() {
116                           doneEDT();
117                           setState(StateValue.DONE);
118                       }
119                   };
120
121       state = StateValue.PENDING;
122       propertyChangeSupport = new SwingWorkerPropertyChangeSupport(this);
123       doProcess = null;
124       doNotifyProgressChange = null;
125    }
126
127    /**
128     * Computes a result, or throws an exception if unable to do so.
129     *
130     * <p>
131     * Note that this method is executed only once.
132     *
133     * <p>
134     * Note: this method is executed in a background thread.
135     *
136     *
137     * @return the computed result
138     * @throws Exception if unable to compute a result
139     *
140     */
141    protected abstract T doInBackground() throws Exception ;
142
143    /**
144     * Sets this {@code Future} to the result of computation unless
145     * it has been canceled.
146     */
147    public final void run() {
148        future.run();
149    }
150
151    /**
152     * Sends data chunks to the {@link #process} method. This method is to be
153     * used from inside the {@code doInBackground} method to deliver
154     * intermediate results
155     * for processing on the <i>Event Dispatch Thread</i> inside the
156     * {@code process} method.
157     *
158     * <p>
159     * Because the {@code process} method is invoked asynchronously on
160     * the <i>Event Dispatch Thread</i>
161     * multiple invocations to the {@code publish} method
162     * might occur before the {@code process} method is executed. For
163     * performance purposes all these invocations are coalesced into one
164     * invocation with concatenated arguments.
165     *
166     * <p>
167     * For example:
168     *
169     * <pre>
170     * publish(&quot;1&quot;);
171     * publish(&quot;2&quot;, &quot;3&quot;);
172     * publish(&quot;4&quot;, &quot;5&quot;, &quot;6&quot;);
173     * </pre>
174     *
175     * might result in:
176     *
177     * <pre>
178     * process(&quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;, &quot;5&quot;, &quot;6&quot;)
179     * </pre>
180     *
181     * <p>
182     * <b>Sample Usage</b>. This code snippet loads some tabular data and
183     * updates {@code DefaultTableModel} with it. Note that it safe to mutate
184     * the tableModel from inside the {@code process} method because it is
185     * invoked on the <i>Event Dispatch Thread</i>.
186     *
187     * <pre>
188     * class TableSwingWorker extends
189     *         SwingWorker&lt;DefaultTableModel, Object[]&gt; {
190     *     private final DefaultTableModel tableModel;
191     *
192     *     public TableSwingWorker(DefaultTableModel tableModel) {
193     *         this.tableModel = tableModel;
194     *     }
195     *
196     *     {@code @Override}
197     *     protected DefaultTableModel doInBackground() throws Exception {
198     *         for (Object[] row = loadData();
199     *                  ! isCancelled() &amp;&amp; row != null;
200     *                  row = loadData()) {
201     *             publish((Object[]) row);
202     *         }
203     *         return tableModel;
204     *     }
205     *
206     *     {@code @Override}
207     *     protected void process(List&lt;Object[]&gt; chunks) {
208     *         for (Object[] row : chunks) {
209     *             tableModel.addRow(row);
210     *         }
211     *     }
212     * }
213     * </pre>
214     *
215     * @param chunks intermediate results to process
216     *
217     * @see #process
218     *
219     */
220    protected final void publish(V... chunks) {
221        synchronized (this) {
222            if (doProcess == null) {
223                doProcess = new AccumulativeRunnable<V>() {
224                    @Override
225                    public void run(List<V> args) {
226                        process(args);
227                    }
228                    @Override
229                    protected void submit() {
230                        doSubmit.add(this);
231                    }
232                };
233            }
234        }
235        doProcess.add(chunks);
236    }
237
238    /**
239     * Receives data chunks from the {@code publish} method asynchronously on the
240     * <i>Event Dispatch Thread</i>.
241     *
242     * <p>
243     * Please refer to the {@link #publish} method for more details.
244     *
245     * @param chunks intermediate results to process
246     *
247     * @see #publish
248     *
249     */
250    protected void process(List<V> chunks) {
251    }
252
253    /**
254     * Executed on the <i>Event Dispatch Thread</i> after the {@code doInBackground}
255     * method is finished. The default
256     * implementation does nothing. Subclasses may override this method to
257     * perform completion actions on the <i>Event Dispatch Thread</i>. Note
258     * that you can query status inside the implementation of this method to
259     * determine the result of this task or whether this task has been canceled.
260     *
261     * @see #doInBackground
262     * @see #isCancelled()
263     * @see #get
264     */
265    protected void done() {
266    }
267
268    /**
269     * Sets the {@code progress} bound property.
270     * The value should be from 0 to 100.
271     *
272     * <p>
273     * Because {@code PropertyChangeListener}s are notified asynchronously on
274     * the <i>Event Dispatch Thread</i> multiple invocations to the
275     * {@code setProgress} method might occur before any
276     * {@code PropertyChangeListeners} are invoked. For performance purposes
277     * all these invocations are coalesced into one invocation with the last
278     * invocation argument only.
279     *
280     * <p>
281     * For example, the following invocations:
282     *
283     * <pre>
284     * setProgress(1);
285     * setProgress(2);
286     * setProgress(3);
287     * </pre>
288     *
289     * might result in a single {@code PropertyChangeListener} notification with
290     * the value {@code 3}.
291     *
292     * @param progress the progress value to set
293     * @throws IllegalArgumentException is value not from 0 to 100
294     */
295    protected final void setProgress(int progress) {
296        if (progress < 0 || progress > 100) {
297            throw new IllegalArgumentException("the value should be from 0 to 100");
298        }
299        if (this.progress == progress) {
300            return;
301        }
302        int oldProgress = this.progress;
303        this.progress = progress;
304        if (! getPropertyChangeSupport().hasListeners("progress")) {
305            return;
306        }
307        synchronized (this) {
308            if (doNotifyProgressChange == null) {
309                doNotifyProgressChange =
310                    new AccumulativeRunnable<Integer>() {
311                        @Override
312                        public void run(List<Integer> args) {
313                            firePropertyChange("progress",
314                               args.get(0),
315                               args.get(args.size() - 1));
316                        }
317                        @Override
318                        protected void submit() {
319                            doSubmit.add(this);
320                        }
321                    };
322            }
323        }
324        doNotifyProgressChange.add(oldProgress, progress);
325    }
326
327    /**
328     * Returns the {@code progress} bound property.
329     *
330     * @return the progress bound property.
331     */
332    public final int getProgress() {
333        return progress;
334    }
335
336    /**
337     * Schedules this {@code SwingWorker} for execution on a <i>worker</i>
338     * thread. There are a number of <i>worker</i> threads available. In the
339     * event all <i>worker</i> threads are busy handling other
340     * {@code SwingWorkers} this {@code SwingWorker} is placed in a waiting
341     * queue.
342     *
343     * <p>
344     * Note:
345     * {@code SwingWorker} is only designed to be executed once.  Executing a
346     * {@code SwingWorker} more than once will not result in invoking the
347     * {@code doInBackground} method twice.
348     */
349    public final void execute() {
350        getWorkersExecutorService().execute(this);
351    }
352
353    // Future methods START
354    /**
355     * {@inheritDoc}
356     */
357    public /*final*/ boolean cancel(boolean mayInterruptIfRunning) {
358        return future.cancel(mayInterruptIfRunning);
359    }
360
361    /**
362     * {@inheritDoc}
363     */
364    public final boolean isCancelled() {
365        return future.isCancelled();
366    }
367
368    /**
369     * {@inheritDoc}
370     */
371    public final boolean isDone() {
372        return future.isDone();
373    }
374
375    /**
376     * {@inheritDoc}
377     * <p>
378     * Note: calling {@code get} on the <i>Event Dispatch Thread</i> blocks
379     * <i>all</i> events, including repaints, from being processed until this
380     * {@code SwingWorker} is complete.
381     *
382     * <p>
383     * When you want the {@code SwingWorker} to block on the <i>Event
384     * Dispatch Thread</i> we recommend that you use a <i>modal dialog</i>.
385     *
386     * <p>
387     * For example:
388     *
389     * <pre>
390     * class SwingWorkerCompletionWaiter extends PropertyChangeListener {
391     *     private JDialog dialog;
392     *
393     *     public SwingWorkerCompletionWaiter(JDialog dialog) {
394     *         this.dialog = dialog;
395     *     }
396     *
397     *     public void propertyChange(PropertyChangeEvent event) {
398     *         if (&quot;state&quot;.equals(event.getPropertyName())
399     *                 &amp;&amp; SwingWorker.StateValue.DONE == event.getNewValue()) {
400     *             dialog.setVisible(false);
401     *             dialog.dispose();
402     *         }
403     *     }
404     * }
405     * JDialog dialog = new JDialog(owner, true);
406     * swingWorker.addPropertyChangeListener(
407     *     new SwingWorkerCompletionWaiter(dialog));
408     * swingWorker.execute();
409     * //the dialog will be visible until the SwingWorker is done
410     * dialog.setVisible(true);
411     * </pre>
412     */
413    public final T get() throws InterruptedException, ExecutionException {
414        return future.get();
415    }
416
417    /**
418     * {@inheritDoc}
419     * <p>
420     * Please refer to {@link #get} for more details.
421     */
422    public final T get(long timeout, TimeUnit unit) throws InterruptedException,
423            ExecutionException, TimeoutException {
424        return future.get(timeout, unit);
425    }
426
427    // Future methods END
428
429    // PropertyChangeSupports methods START
430    /**
431     * Adds a {@code PropertyChangeListener} to the listener list. The listener
432     * is registered for all properties. The same listener object may be added
433     * more than once, and will be called as many times as it is added. If
434     * {@code listener} is {@code null}, no exception is thrown and no action is taken.
435     *
436     * <p>
437     * Note: This is merely a convenience wrapper. All work is delegated to
438     * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
439     *
440     * @param listener the {@code PropertyChangeListener} to be added
441     */
442    public final void addPropertyChangeListener(PropertyChangeListener listener) {
443        getPropertyChangeSupport().addPropertyChangeListener(listener);
444    }
445
446    /**
447     * Removes a {@code PropertyChangeListener} from the listener list. This
448     * removes a {@code PropertyChangeListener} that was registered for all
449     * properties. If {@code listener} was added more than once to the same
450     * event source, it will be notified one less time after being removed. If
451     * {@code listener} is {@code null}, or was never added, no exception is
452     * thrown and no action is taken.
453     *
454     * <p>
455     * Note: This is merely a convenience wrapper. All work is delegated to
456     * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
457     *
458     * @param listener the {@code PropertyChangeListener} to be removed
459     */
460    public final void removePropertyChangeListener(PropertyChangeListener listener) {
461        getPropertyChangeSupport().removePropertyChangeListener(listener);
462    }
463
464    /**
465     * Reports a bound property update to any registered listeners. No event is
466     * fired if {@code old} and {@code new} are equal and non-null.
467     *
468     * <p>
469     * This {@code SwingWorker} will be the source for
470     * any generated events.
471     *
472     * <p>
473     * When called off the <i>Event Dispatch Thread</i>
474     * {@code PropertyChangeListeners} are notified asynchronously on
475     * the <i>Event Dispatch Thread</i>.
476     * <p>
477     * Note: This is merely a convenience wrapper. All work is delegated to
478     * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
479     *
480     *
481     * @param propertyName the programmatic name of the property that was
482     *        changed
483     * @param oldValue the old value of the property
484     * @param newValue the new value of the property
485     */
486    public final void firePropertyChange(String propertyName, Object oldValue,
487            Object newValue) {
488        getPropertyChangeSupport().firePropertyChange(propertyName,
489            oldValue, newValue);
490    }
491
492    /**
493     * Returns the {@code PropertyChangeSupport} for this {@code SwingWorker}.
494     * This method is used when flexible access to bound properties support is
495     * needed.
496     * <p>
497     * This {@code SwingWorker} will be the source for
498     * any generated events.
499     *
500     * <p>
501     * Note: The returned {@code PropertyChangeSupport} notifies any
502     * {@code PropertyChangeListener}s asynchronously on the <i>Event Dispatch
503     * Thread</i> in the event that {@code firePropertyChange} or
504     * {@code fireIndexedPropertyChange} are called off the <i>Event Dispatch
505     * Thread</i>.
506     *
507     * @return {@code PropertyChangeSupport} for this {@code SwingWorker}
508     */
509    public final PropertyChangeSupport getPropertyChangeSupport() {
510        return propertyChangeSupport;
511    }
512
513    // PropertyChangeSupports methods END
514
515    /**
516     * Returns the {@code SwingWorker} state bound property.
517     *
518     * @return the current state
519     */
520    public final StateValue getState() {
521        /*
522         * DONE is a special case
523         * to keep getState and isDone is sync
524         */
525        if (isDone()) {
526            return StateValue.DONE;
527        } else {
528            return state;
529        }
530    }
531
532    /**
533     * Sets this {@code SwingWorker} state bound property.
534     * @param the state state to set
535     */
536    private void setState(StateValue state) {
537        StateValue old = this.state;
538        this.state = state;
539        firePropertyChange("state", old, state);
540    }
541
542    /**
543     * Invokes {@code done} on the EDT.
544     */
545    private void doneEDT() {
546        Runnable doDone =
547            new Runnable() {
548                public void run() {
549                    done();
550                }
551            };
552        if (SwingUtilities.isEventDispatchThread()) {
553            doDone.run();
554        } else {
555            doSubmit.add(doDone);
556        }
557    }
558
559
560    /**
561     * returns workersExecutorService.
562     *
563     * returns the service stored in the appContext or creates it if
564     * necessary. If the last one it triggers autoShutdown thread to
565     * get started.
566     *
567     * @return ExecutorService for the {@code SwingWorkers}
568     * @see #startAutoShutdownThread
569     */
570    private static synchronized ExecutorService getWorkersExecutorService() {
571        if (executorService == null) {
572            //this creates non-daemon threads.
573            ThreadFactory threadFactory =
574                new ThreadFactory() {
575                    final AtomicInteger threadNumber = new AtomicInteger(1);
576                    public Thread newThread(final Runnable r) {
577                        StringBuilder name =
578                            new StringBuilder("SwingWorker-pool-");
579                        name.append(System.identityHashCode(this));
580                        name.append("-thread-");
581                        name.append(threadNumber.getAndIncrement());
582
583                        Thread t = new Thread(r, name.toString());
584                        if (t.isDaemon())
585                            t.setDaemon(false);
586                        if (t.getPriority() != Thread.NORM_PRIORITY)
587                            t.setPriority(Thread.NORM_PRIORITY);
588                        return t;
589                    }
590                };
591
592            /*
593             * We want a to have no more than MAX_WORKER_THREADS
594             * running threads.
595             *
596             * We want a worker thread to wait no longer than 1 second
597             * for new tasks before terminating.
598             */
599            executorService = new ThreadPoolExecutor(0, MAX_WORKER_THREADS,
600                                         5L, TimeUnit.SECONDS,
601                                         new LinkedBlockingQueue<Runnable>(),
602                                         threadFactory) {
603
604                    private final ReentrantLock pauseLock = new ReentrantLock();
605                    private final Condition unpaused = pauseLock.newCondition();
606                    private boolean isPaused = false;
607                    private final ReentrantLock executeLock = new ReentrantLock();
608
609                    @Override
610                    public void execute(Runnable command) {
611                        /*
612                         * ThreadPoolExecutor first tries to run task
613                         * in a corePool. If all threads are busy it
614                         * tries to add task to the waiting queue. If it
615                         * fails it run task in maximumPool.
616                         *
617                         * We want corePool to be 0 and
618                         * maximumPool to be MAX_WORKER_THREADS
619                         * We need to change the order of the execution.
620                         * First try corePool then try maximumPool
621                         * pool and only then store to the waiting
622                         * queue. We can not do that because we would
623                         * need access to the private methods.
624                         *
625                         * Instead we enlarge corePool to
626                         * MAX_WORKER_THREADS before the execution and
627                         * shrink it back to 0 after.
628                         * It does pretty much what we need.
629                         *
630                         * While we changing the corePoolSize we need
631                         * to stop running worker threads from accepting new
632                         * tasks.
633                         */
634
635                        //we need atomicity for the execute method.
636                        executeLock.lock();
637                        try {
638
639                            pauseLock.lock();
640                            try {
641                                isPaused = true;
642                            } finally {
643                                pauseLock.unlock();
644                            }
645
646                            setCorePoolSize(MAX_WORKER_THREADS);
647                            super.execute(command);
648                            setCorePoolSize(0);
649
650                            pauseLock.lock();
651                            try {
652                                isPaused = false;
653                                unpaused.signalAll();
654                            } finally {
655                                pauseLock.unlock();
656                            }
657                        } finally {
658                            executeLock.unlock();
659                        }
660                    }
661                    @Override
662                    protected void afterExecute(Runnable r, Throwable t) {
663                        super.afterExecute(r, t);
664                        pauseLock.lock();
665                        try {
666                            while(isPaused) {
667                                unpaused.await();
668                            }
669                        } catch(InterruptedException ignore) {
670
671                        } finally {
672                            pauseLock.unlock();
673                        }
674                    }
675                };
676        }
677        return executorService;
678    }
679
680    private static class DoSubmitAccumulativeRunnable
681          extends AccumulativeRunnable<Runnable> implements ActionListener {
682        private final static int DELAY = (int) (1000 / 30);
683        @Override
684        protected void run(List<Runnable> args) {
685            for (Runnable runnable : args) {
686                runnable.run();
687            }
688        }
689        @Override
690        protected void submit() {
691            Timer timer = new Timer(DELAY, this);
692            timer.setRepeats(false);
693            timer.start();
694        }
695        public void actionPerformed(ActionEvent event) {
696            run();
697        }
698    }
699
700    private class SwingWorkerPropertyChangeSupport
701            extends PropertyChangeSupport {
702        SwingWorkerPropertyChangeSupport(Object source) {
703            super(source);
704        }
705        @Override
706        public void firePropertyChange(final PropertyChangeEvent evt) {
707            if (SwingUtilities.isEventDispatchThread()) {
708                super.firePropertyChange(evt);
709            } else {
710                doSubmit.add(
711                    new Runnable() {
712                        public void run() {
713                            SwingWorkerPropertyChangeSupport.this
714                                .firePropertyChange(evt);
715                        }
716                    });
717            }
718        }
719    }
720}