Swing worker from org.jdesktop.swingworker : Swing Worker « JDK 6 « Java






Swing worker from org.jdesktop.swingworker


/* 
 * $Id: AccumulativeRunnable.java,v 1.2 2006/09/28 20:20:28 idk Exp $
 * 
 * Copyright ?2005 Sun Microsystems, Inc. All rights
 * reserved. Use is subject to license terms.
 */

package org.jdesktop.swingworker;

import java.util.*;
import javax.swing.SwingUtilities;

/**
 * An abstract class to be used in the cases where we need {@code Runnable}
 * to perform  some actions on an appendable set of data.
 * The set of data might be appended after the {@code Runnable} is
 * sent for the execution. Usually such {@code Runnables} are sent to
 * the EDT.
 *
 * <p>
 * Usage example:
 * 
 * <p>
 * Say we want to implement JLabel.setText(String text) which sends
 * {@code text} string to the JLabel.setTextImpl(String text) on the EDT. 
 * In the event JLabel.setText is called rapidly many times off the EDT
 * we will get many updates on the EDT but only the last one is important.
 * (Every next updates overrides the previous one.)
 * We might want to implement this {@code setText} in a way that only
 * the last update is delivered.
 * <p>
 * Here is how one can do this using {@code AccumulativeRunnable}:
 * <pre>
 * AccumulativeRunnable<String> doSetTextImpl = 
 * new  AccumulativeRunnable<String>() {
 *     @Override 
 *     protected void run(List&lt;String&gt; args) {
 *         //set to the last string being passed
 *         setTextImpl(args.get(args.size() - 1);
 *     }
 * }
 * void setText(String text) {
 *     //add text and send for the execution if needed.
 *     doSetTextImpl.add(text);
 * }
 * </pre>
 *
 * <p>
 * Say we want want to implement addDirtyRegion(Rectangle rect)
 * which sends this region to the 
 * handleDirtyRegions(List<Rect> regions) on the EDT.
 * addDirtyRegions better be accumulated before handling on the EDT.
 * 
 * <p>
 * Here is how it can be implemented using AccumulativeRunnable:
 * <pre>
 * AccumulativeRunnable<Rectangle> doHandleDirtyRegions = 
 *     new AccumulativeRunnable<Rectangle>() {
 *         @Override 
 *         protected void run(List&lt;Rectangle&gt; args) {
 *             handleDirtyRegions(args);
 *         }
 *     };
 *  void addDirtyRegion(Rectangle rect) {
 *      doHandleDirtyRegions.add(rect);
 *  }
 * </pre>
 *
 * @author Igor Kushnirskiy
 * @version $Revision: 1.2 $ $Date: 2006/09/28 20:20:28 $
 *
 * @param <T> the type this {@code Runnable} accumulates
 * 
 */
abstract class AccumulativeRunnable<T> implements Runnable {
    private List<T> arguments = null;
    
    /**
     * Equivalent to {@code Runnable.run} method with the
     * accumulated arguments to process.
     *
     * @param args accumulated arguments to process.
     */
    protected abstract void run(List<T> args);
    
    /**
     * {@inheritDoc}
     *
     * <p>
     * This implementation calls {@code run(List<T> args)} method
     * with the list of accumulated arguments.
     */
    public final void run() {
        run(flush());
    }
    
    /**
     * appends arguments and sends this {@code Runnable} for the
     * execution if needed.
     * <p>
     * This implementation uses {@see #submit} to send this 
     * {@code Runnable} for execution. 
     * @param args the arguments to accumulate
     */
    public final synchronized void add(T... args) {
        boolean isSubmitted = true;
        if (arguments == null) {
            isSubmitted = false;
            arguments = new ArrayList<T>();
        }
        Collections.addAll(arguments, args);
        if (!isSubmitted) {
            submit();
        }
    }

    /**
     * Sends this {@code Runnable} for the execution
     *
     * <p>
     * This method is to be executed only from {@code add} method.
     *
     * <p>
     * This implementation uses {@code SwingWorker.invokeLater}.
     */
    protected void submit() {
        SwingUtilities.invokeLater(this);
    }
        
    /**
     * Returns accumulated arguments and flashes the arguments storage.
     *
     * @return accumulated arguments
     */
    private final synchronized List<T> flush() {
        List<T> list = arguments;
        arguments = null;
        return list;
    }
}

///////////////////////////////////////////////////////////
/* 
 * $Id: SwingPropertyChangeSupport.java,v 1.1 2005/06/18 21:27:14 idk Exp $
 * 
 * Copyright ?2005 Sun Microsystems, Inc. All rights
 * reserved. Use is subject to license terms.
 */

package org.jdesktop.swingworker;

import java.beans.PropertyChangeSupport;
import java.beans.PropertyChangeEvent;

import javax.swing.SwingUtilities;

/**
 * This subclass of {@code java.beans.PropertyChangeSupport} is almost
 * identical in functionality. The only difference is if constructed with 
 * {@code SwingPropertyChangeSupport(sourceBean, true)} it ensures
 * listeners are only ever notified on the <i>Event Dispatch Thread</i>.
 *
 * @author Igor Kushnirskiy
 * @version $Revision: 1.1 $ $Date: 2005/06/18 21:27:14 $
 */

public final class SwingPropertyChangeSupport extends PropertyChangeSupport {

    /**
     * Constructs a SwingPropertyChangeSupport object.
     *
     * @param sourceBean  The bean to be given as the source for any
     *        events.
     * @throws NullPointerException if {@code sourceBean} is 
     *         {@code null}
     */
    public SwingPropertyChangeSupport(Object sourceBean) {
        this(sourceBean, false);
    }

    /**
     * Constructs a SwingPropertyChangeSupport object.
     * 
     * @param sourceBean the bean to be given as the source for any events
     * @param notifyOnEDT whether to notify listeners on the <i>Event
     *        Dispatch Thread</i> only
     *
     * @throws NullPointerException if {@code sourceBean} is 
     *         {@code null}
     * @since 1.6
     */
    public SwingPropertyChangeSupport(Object sourceBean, boolean notifyOnEDT) {
        super(sourceBean);
        this.notifyOnEDT = notifyOnEDT;
    }

    /**
     * {@inheritDoc}
     *
     * <p>
     * If {@see #isNotifyOnEDT} is {@code true} and called off the
     * <i>Event Dispatch Thread</i> this implementation uses 
     * {@code SwingUtilities.invokeLater} to send out the notification
     * on the <i>Event Dispatch Thread</i>. This ensures  listeners
     * are only ever notified on the <i>Event Dispatch Thread</i>.
     *
     * @throws NullPointerException if {@code evt} is 
     *         {@code null}
     * @since 1.6
     */
    public void firePropertyChange(final PropertyChangeEvent evt) {
        if (evt == null) {
            throw new NullPointerException();
        }
        if (! isNotifyOnEDT()
            || SwingUtilities.isEventDispatchThread()) {
            super.firePropertyChange(evt);
        } else {
            SwingUtilities.invokeLater(
                new Runnable() {
                    public void run() {
                        firePropertyChange(evt);
                    }
                });
        }
    }

    /**
     * Returns {@code notifyOnEDT} property.
     * 
     * @return {@code notifyOnEDT} property
     * @see #SwingPropertyChangeSupport(Object sourceBean, boolean notifyOnEDT)
     * @since 1.6
     */
    public final boolean isNotifyOnEDT() {
        return notifyOnEDT;
    }

    // Serialization version ID
    static final long serialVersionUID = 7162625831330845068L;

    /**
     * whether to notify listeners on EDT
     * 
     * @serial 
     * @since 1.6
     */ 
    private final boolean notifyOnEDT;
}


///////////////////////////////////////////////////////////
/* 
 * $Id: SwingWorker.java,v 1.5 2007/03/01 19:55:54 idk Exp $
 * 
 * Copyright ?2005 Sun Microsystems, Inc. All rights
 * reserved. Use is subject to license terms.
 */

package org.jdesktop.swingworker;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyChangeEvent;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.*;

import java.awt.event.*;

import javax.swing.SwingUtilities;
import javax.swing.Timer;

/**
 * An abstract class to perform lengthy GUI-interacting tasks in a
 * dedicated thread.
 * 
 * <p>
 * When writing a multi-threaded application using Swing, there are
 * two constraints to keep in mind:
 * (refer to 
 * <a href="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">
 *   How to Use Threads
 * </a> for more details):
 * <ul>
 *   <li> Time-consuming tasks should not be run on the <i>Event
 *        Dispatch Thread</i>. Otherwise the application becomes unresponsive.
 *   </li>
 *   <li> Swing components should be accessed  on the <i>Event
 *        Dispatch Thread</i> only.
 *   </li>
 * </ul>
 *
 * <p>
 *
 * <p>
 * These constraints mean that a GUI application with time intensive
 * computing needs at least two threads:  1) a thread to perform the lengthy
 * task and 2) the <i>Event Dispatch Thread</i> (EDT) for all GUI-related
 * activities.  This involves inter-thread communication which can be
 * tricky to implement.
 *
 * <p>
 * {@code SwingWorker} is designed for situations where you need to have a long 
 * running task run in a background thread and provide updates to the UI 
 * either when done, or while processing. 
 * Subclasses of {@code SwingWorker} must implement 
 * the {@see #doInBackground} method to perform the background computation.
 *
 *
 * <p>
 * <b>Workflow</b>
 * <p>
 * There are three threads involved in the life cycle of a 
 * {@code SwingWorker} :
 * <ul>
 * <li>
 * <p>
 * <i>Current</i> thread: The {@link #execute} method is
 * called on this thread. It schedules {@code SwingWorker} for the execution on a
 * <i>worker</i>
 * thread and returns immediately. One can wait for the {@code SwingWorker} to
 * complete using the {@link #get get} methods.
 * <li>
 * <p>
 * <i>Worker</i> thread: The {@link #doInBackground} 
 * method is called on this thread.
 * This is where all background activities should happen. To notify
 * {@code PropertyChangeListeners} about bound properties changes use the
 * {@link #firePropertyChange firePropertyChange} and
 * {@link #getPropertyChangeSupport} methods. By default there are two bound
 * properties available: {@code state} and {@code progress}.
 * <li>
 * <p>
 * <i>Event Dispatch Thread</i>:  All Swing related activities occur
 * on this thread. {@code SwingWorker} invokes the
 * {@link #process process} and {@link #done} methods and notifies
 * any {@code PropertyChangeListeners} on this thread.
 * </ul>
 * 
 * <p>
 * Often, the <i>Current</i> thread is the <i>Event Dispatch
 * Thread</i>. 
 *
 *
 * <p>
 * Before the {@code doInBackground} method is invoked on a <i>worker</i> thread,
 * {@code SwingWorker} notifies any {@code PropertyChangeListeners} about the
 * {@code state} property change to {@code StateValue.STARTED}.  After the
 * {@code doInBackground} method is finished the {@code done} method is
 * executed.  Then {@code SwingWorker} notifies any {@code PropertyChangeListeners}
 * about the {@code state} property change to {@code StateValue.DONE}.
 *
 * <p>
 * {@code SwingWorker} is only designed to be executed once.  Executing a
 * {@code SwingWorker} more than once will not result in invoking the
 * {@code doInBackground} method twice.
 *
 * <p>
 * <b>Sample Usage</b>
 * <p>
 * The following example illustrates the simplest use case.  Some 
 * processing is done in the background and when done you update a Swing 
 * component.
 *
 * <p>
 * Say we want to find the "Meaning of Life" and display the result in
 * a {@code JLabel}.
 * 
 * <pre>
 *   final JLabel label;
 *   class MeaningOfLifeFinder extends SwingWorker&lt;String, Object&gt; {
 *       {@code @Override}
 *       public String doInBackground() {
 *           return findTheMeaningOfLife();
 *       }
 *
 *       {@code @Override}
 *       protected void done() {
 *           try { 
 *               label.setText(get());
 *           } catch (Exception ignore) {
 *           }
 *       }
 *   }
 * 
 *   (new MeaningOfLifeFinder()).execute();
 * </pre>
 * 
 * <p>
 * The next example is useful in situations where you wish to process data 
 * as it is ready on the <i>Event Dispatch Thread</i>.
 *
 * <p>
 * Now we want to find the first N prime numbers and display the results in a
 * {@code JTextArea}.  While this is computing, we want to update our
 * progress in a {@code JProgressBar}.  Finally, we also want to print 
 * the prime numbers to {@code System.out}.
 * <pre>
 * class PrimeNumbersTask extends 
 *         SwingWorker&lt;List&lt;Integer&gt;, Integer&gt; {
 *     PrimeNumbersTask(JTextArea textArea, int numbersToFind) { 
 *         //initialize 
 *     }
 *
 *     {@code @Override}
 *     public List&lt;Integer&gt; doInBackground() {
 *         while (! enough &amp;&amp; ! isCancelled()) {
 *                 number = nextPrimeNumber();
 *                 publish(number);
 *                 setProgress(100 * numbers.size() / numbersToFind);
 *             }
 *         }
 *         return numbers;
 *     }
 *
 *     {@code @Override}
 *     protected void process(List&lt;Integer&gt; chunks) { 
 *         for (int number : chunks) {
 *             textArea.append(number + &quot;\n&quot;);
 *         }
 *     }
 * }
 *
 * JTextArea textArea = new JTextArea();
 * final JProgressBar progressBar = new JProgressBar(0, 100);
 * PrimeNumbersTask task = new PrimeNumbersTask(textArea, N);
 * task.addPropertyChangeListener(
 *     new PropertyChangeListener() {
 *         public  void propertyChange(PropertyChangeEvent evt) {
 *             if (&quot;progress&quot;.equals(evt.getPropertyName())) {
 *                 progressBar.setValue((Integer)evt.getNewValue());
 *             }
 *         }
 *     });
 *
 * task.execute();
 * System.out.println(task.get()); //prints all prime numbers we have got
 * </pre>
 * 
 * <p>
 * Because {@code SwingWorker} implements {@code Runnable}, a
 * {@code SwingWorker} can be submitted to an
 * {@link java.util.concurrent.Executor} for execution.
 *  
 * @author Igor Kushnirskiy
 * @version $Revision: 1.5 $ $Date: 2007/03/01 19:55:54 $
 * 
 * @param <T> the result type returned by this {@code SwingWorker's}
 *        {@code doInBackground} and {@code get} methods
 * @param <V> the type used for carrying out intermediate results by this
 *        {@code SwingWorker's} {@code publish} and {@code process} methods
 * 
 */
public abstract class SwingWorker<T, V> implements Future<T>, Runnable {
    /**
     * number of worker threads.
     */
    private static final int MAX_WORKER_THREADS = 10;

    /**
     * current progress.
     */
    private volatile int progress;

    /**
     * current state.
     */
    private volatile StateValue state;

    /**
     * everything is run inside this FutureTask. Also it is used as
     * a delegatee for the Future API.
     */
    private final FutureTask<T> future;

    /**
     * all propertyChangeSupport goes through this.
     */
    private final PropertyChangeSupport propertyChangeSupport;

    /**
     * handler for {@code process} method.
     */
    private AccumulativeRunnable<V> doProcess;

    /**
     * handler for progress property change notifications.
     */
    private AccumulativeRunnable<Integer> doNotifyProgressChange;
    
    private static final AccumulativeRunnable<Runnable> doSubmit = 
        new DoSubmitAccumulativeRunnable();
    
    private static ExecutorService executorService = null;

    /**
     * Values for the {@code state} bound property.
     */
    public enum StateValue {
        /**
         * Initial {@code SwingWorker} state.
         */
        PENDING,
        /**
         * {@code SwingWorker} is {@code STARTED} 
         * before invoking {@code doInBackground}.
         */
        STARTED,

        /**
         * {@code SwingWorker} is {@code DONE}
         * after {@code doInBackground} method
         * is finished.
         */
        DONE
    };

    /**
     * Constructs this {@code SwingWorker}.
     */
    public SwingWorker() {
        Callable<T> callable = 
                new Callable<T>() {
                    public T call() throws Exception {
                        setState(StateValue.STARTED);
                        return doInBackground();
                    }
                };

        future = new FutureTask<T>(callable) {
                       @Override
                       protected void done() {
                           doneEDT();
                           setState(StateValue.DONE);
                       }
                   };

       state = StateValue.PENDING;
       propertyChangeSupport = new SwingWorkerPropertyChangeSupport(this);
       doProcess = null;
       doNotifyProgressChange = null;
    }
    
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * <p>
     * Note that this method is executed only once.
     * 
     * <p>
     * Note: this method is executed in a background thread.
     *  
     *
     * @return the computed result
     * @throws Exception if unable to compute a result
     * 
     */
    protected abstract T doInBackground() throws Exception ;
    
    /**
     * Sets this {@code Future} to the result of computation unless
     * it has been cancelled.
     */
    public final void run() {
        future.run();
    }
    
    /**
     * Sends data chunks to the {@link #process} method. This method is to be
     * used from inside the {@code doInBackground} method to deliver 
     * intermediate results
     * for processing on the <i>Event Dispatch Thread</i> inside the
     * {@code process} method.
     * 
     * <p>
     * Because the {@code process} method is invoked asynchronously on
     * the <i>Event Dispatch Thread</i>
     * multiple invocations to the {@code publish} method
     * might occur before the {@code process} method is executed. For
     * performance purposes all these invocations are coalesced into one
     * invocation with concatenated arguments.
     * 
     * <p>
     * For example:
     * 
     * <pre>
     * publish(&quot;1&quot;);
     * publish(&quot;2&quot;, &quot;3&quot;);
     * publish(&quot;4&quot;, &quot;5&quot;, &quot;6&quot;);
     * </pre>
     * 
     * might result in:
     * 
     * <pre>
     * process(&quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;, &quot;5&quot;, &quot;6&quot;)
     * </pre>
     *
     * <p>
     * <b>Sample Usage</b>. This code snippet loads some tabular data and
     * updates {@code DefaultTableModel} with it. Note that it safe to mutate
     * the tableModel from inside the {@code process} method because it is 
     * invoked on the <i>Event Dispatch Thread</i>.
     * 
     * <pre>
     * class TableSwingWorker extends 
     *         SwingWorker&lt;DefaultTableModel, Object[]&gt; {
     *     private final DefaultTableModel tableModel;
     * 
     *     public TableSwingWorker(DefaultTableModel tableModel) {
     *         this.tableModel = tableModel;
     *     }
     * 
     *     {@code @Override}
     *     protected DefaultTableModel doInBackground() throws Exception {
     *         for (Object[] row = loadData(); 
     *                  ! isCancelled() &amp;&amp; row != null; 
     *                  row = loadData()) {
     *             publish((Object[]) row);
     *         }
     *         return tableModel;
     *     }
     * 
     *     {@code @Override}
     *     protected void process(List&lt;Object[]&gt; chunks) {
     *         for (Object[] row : chunks) {
     *             tableModel.addRow(row);
     *         }
     *     }
     * }
     * </pre>
     * 
     * @param chunks intermediate results to process
     * 
     * @see #process
     * 
     */
    protected final void publish(V... chunks) {
        synchronized (this) {
            if (doProcess == null) {
                doProcess = new AccumulativeRunnable<V>() {
                    @Override
                    public void run(List<V> args) {
                        process(args);
                    }
                    @Override
                    protected void submit() {
                        doSubmit.add(this);
                    }
                };
            }
        }
        doProcess.add(chunks);
    }

    /**
     * Receives data chunks from the {@code publish} method asynchronously on the
     * <i>Event Dispatch Thread</i>.
     * 
     * <p>
     * Please refer to the {@link #publish} method for more details.
     * 
     * @param chunks intermediate results to process
     * 
     * @see #publish
     * 
     */
    protected void process(List<V> chunks) {
    }

    /**
     * Executed on the <i>Event Dispatch Thread</i> after the {@code doInBackground}
     * method is finished. The default
     * implementation does nothing. Subclasses may override this method to
     * perform completion actions on the <i>Event Dispatch Thread</i>. Note
     * that you can query status inside the implementation of this method to
     * determine the result of this task or whether this task has been cancelled.
     * 
     * @see #doInBackground
     * @see #isCancelled()
     * @see #get
     */
    protected void done() {
    }

    /**
     * Sets the {@code progress} bound property.
     * The value should be from 0 to 100.
     *
     * <p>
     * Because {@code PropertyChangeListener}s are notified asynchronously on
     * the <i>Event Dispatch Thread</i> multiple invocations to the
     * {@code setProgress} method might occur before any
     * {@code PropertyChangeListeners} are invoked. For performance purposes
     * all these invocations are coalesced into one invocation with the last
     * invocation argument only.
     * 
     * <p>
     * For example, the following invocations:
     * 
     * <pre>
     * setProgress(1);
     * setProgress(2);
     * setProgress(3);
     * </pre>
     * 
     * might result in a single {@code PropertyChangeListener} notification with
     * the value {@code 3}.
     * 
     * @param progress the progress value to set
     * @throws IllegalArgumentException is value not from 0 to 100
     */
    protected final void setProgress(int progress) {
        if (progress < 0 || progress > 100) {
            throw new IllegalArgumentException("the value should be from 0 to 100");
        }
        if (this.progress == progress) {
            return;
        }
        int oldProgress = this.progress;
        this.progress = progress;
        if (! getPropertyChangeSupport().hasListeners("progress")) {
            return;
        }
        synchronized (this) {
            if (doNotifyProgressChange == null) {
                doNotifyProgressChange = 
                    new AccumulativeRunnable<Integer>() {
                        @Override
                        public void run(List<Integer> args) {
                            firePropertyChange("progress", 
                               args.get(0), 
                               args.get(args.size() - 1));
                        }
                        @Override
                        protected void submit() {
                            doSubmit.add(this);
                        }
                    };
            }
        }
        doNotifyProgressChange.add(oldProgress, progress);
    }

    /**
     * Returns the {@code progress} bound property.
     * 
     * @return the progress bound property.
     */
    public final int getProgress() {
        return progress;
    }

    /**
     * Schedules this {@code SwingWorker} for execution on a <i>worker</i>
     * thread. There are a number of <i>worker</i> threads available. In the
     * event all <i>worker</i> threads are busy handling other
     * {@code SwingWorkers} this {@code SwingWorker} is placed in a waiting
     * queue.
     *
     * <p>
     * Note:
     * {@code SwingWorker} is only designed to be executed once.  Executing a
     * {@code SwingWorker} more than once will not result in invoking the
     * {@code doInBackground} method twice.
     */
    public final void execute() {
        getWorkersExecutorService().execute(this);
    }

    // Future methods START
    /**
     * {@inheritDoc}
     */
    public final boolean cancel(boolean mayInterruptIfRunning) {
        return future.cancel(mayInterruptIfRunning);
    }

    /**
     * {@inheritDoc}
     */
    public final boolean isCancelled() {
        return future.isCancelled();
    }

    /**
     * {@inheritDoc}
     */
    public final boolean isDone() {
        return future.isDone();
    }

    /**
     * {@inheritDoc}
     * <p>
     * Note: calling {@code get} on the <i>Event Dispatch Thread</i> blocks
     * <i>all</i> events, including repaints, from being processed until this
     * {@code SwingWorker} is complete.
     * 
     * <p>
     * When you want the {@code SwingWorker} to block on the <i>Event
     * Dispatch Thread</i> we recommend that you use a <i>modal dialog</i>.
     *
     * <p>
     * For example:
     * 
     * <pre>
     * class SwingWorkerCompletionWaiter extends PropertyChangeListener {
     *     private JDialog dialog;
     * 
     *     public SwingWorkerCompletionWaiter(JDialog dialog) {
     *         this.dialog = dialog;
     *     }
     * 
     *     public void propertyChange(PropertyChangeEvent event) {
     *         if (&quot;state&quot;.equals(event.getPropertyName())
     *                 &amp;&amp; SwingWorker.StateValue.DONE == event.getNewValue()) {
     *             dialog.setVisible(false);
     *             dialog.dispose();
     *         }
     *     }
     * }
     * JDialog dialog = new JDialog(owner, true);
     * swingWorker.addPropertyChangeListener(
     *     new SwingWorkerCompletionWaiter(dialog));
     * swingWorker.execute();
     * //the dialog will be visible until the SwingWorker is done
     * dialog.setVisible(true); 
     * </pre>
     */
    public final T get() throws InterruptedException, ExecutionException {
        return future.get();
    }

    /**
     * {@inheritDoc}
     * <p>
     * Please refer to {@link #get} for more details.
     */
    public final T get(long timeout, TimeUnit unit) throws InterruptedException,
            ExecutionException, TimeoutException {
        return future.get(timeout, unit);
    }

    // Future methods END

    // PropertyChangeSupports methods START
    /**
     * Adds a {@code PropertyChangeListener} to the listener list. The listener
     * is registered for all properties. The same listener object may be added
     * more than once, and will be called as many times as it is added. If
     * {@code listener} is {@code null}, no exception is thrown and no action is taken.
     * 
     * <p>
     * Note: This is merely a convenience wrapper. All work is delegated to
     * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
     * 
     * @param listener the {@code PropertyChangeListener} to be added
     */
    public final void addPropertyChangeListener(PropertyChangeListener listener) {
        getPropertyChangeSupport().addPropertyChangeListener(listener);
    }

    /**
     * Removes a {@code PropertyChangeListener} from the listener list. This
     * removes a {@code PropertyChangeListener} that was registered for all
     * properties. If {@code listener} was added more than once to the same
     * event source, it will be notified one less time after being removed. If
     * {@code listener} is {@code null}, or was never added, no exception is
     * thrown and no action is taken.
     * 
     * <p>
     * Note: This is merely a convenience wrapper. All work is delegated to
     * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
     * 
     * @param listener the {@code PropertyChangeListener} to be removed
     */
    public final void removePropertyChangeListener(PropertyChangeListener listener) {
        getPropertyChangeSupport().removePropertyChangeListener(listener);
    }

    /**
     * Reports a bound property update to any registered listeners. No event is
     * fired if {@code old} and {@code new} are equal and non-null.
     * 
     * <p>
     * This {@code SwingWorker} will be the source for 
     * any generated events.
     *
     * <p>
     * When called off the <i>Event Dispatch Thread</i>
     * {@code PropertyChangeListeners} are notified asynchronously on
     * the <i>Event Dispatch Thread</i>.
     * <p>
     * Note: This is merely a convenience wrapper. All work is delegated to
     * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
     * 
     * 
     * @param propertyName the programmatic name of the property that was
     *        changed
     * @param oldValue the old value of the property
     * @param newValue the new value of the property
     */
    public final void firePropertyChange(String propertyName, Object oldValue,
            Object newValue) {
        getPropertyChangeSupport().firePropertyChange(propertyName,
            oldValue, newValue);
    }

    /**
     * Returns the {@code PropertyChangeSupport} for this {@code SwingWorker}. 
     * This method is used when flexible access to bound properties support is
     * needed.
     * <p>
     * This {@code SwingWorker} will be the source for 
     * any generated events.
     * 
     * <p>
     * Note: The returned {@code PropertyChangeSupport} notifies any
     * {@code PropertyChangeListener}s asynchronously on the <i>Event Dispatch
     * Thread</i> in the event that {@code firePropertyChange} or
     * {@code fireIndexedPropertyChange} are called off the <i>Event Dispatch
     * Thread</i>.
     * 
     * @return {@code PropertyChangeSupport} for this {@code SwingWorker}
     */
    public final PropertyChangeSupport getPropertyChangeSupport() {
        return propertyChangeSupport;
    }

    // PropertyChangeSupports methods END

    /**
     * Returns the {@code SwingWorker} state bound property.
     * 
     * @return the current state
     */
    public final StateValue getState() {
        /*
         * DONE is a special case
         * to keep getState and isDone is sync
         */
        if (isDone()) {
            return StateValue.DONE;
        } else {
            return state;
        }
    }
    
    /**
     * Sets this {@code SwingWorker} state bound property.
     * @param the state state to set
     */
    private void setState(StateValue state) {
        StateValue old = this.state;
        this.state = state;
        firePropertyChange("state", old, state);
    }

    /**
     * Invokes {@code done} on the EDT.
     */
    private void doneEDT() {
        Runnable doDone = 
            new Runnable() {
                public void run() {
                    done();
                }
            };
        if (SwingUtilities.isEventDispatchThread()) {
            doDone.run();
        } else {
            doSubmit.add(doDone);
        }
    }


    /**
     * returns workersExecutorService.
     *
     * returns the service stored in the appContext or creates it if
     * necessary. If the last one it triggers autoShutdown thread to
     * get started.
     * 
     * @return ExecutorService for the {@code SwingWorkers}
     * @see #startAutoShutdownThread
     */
    private static synchronized ExecutorService getWorkersExecutorService() {
        if (executorService == null) {
            //this creates non-daemon threads. 
            ThreadFactory threadFactory = 
                new ThreadFactory() {
                    final AtomicInteger threadNumber = new AtomicInteger(1);
                    public Thread newThread(final Runnable r) {
                        StringBuilder name = 
                            new StringBuilder("SwingWorker-pool-");
                        name.append(System.identityHashCode(this));
                        name.append("-thread-");
                        name.append(threadNumber.getAndIncrement());
                             
                        Thread t = new Thread(r, name.toString());;
                        if (t.isDaemon())
                            t.setDaemon(false);
                        if (t.getPriority() != Thread.NORM_PRIORITY)
                            t.setPriority(Thread.NORM_PRIORITY);
                        return t;
                    }
                };

            /*
             * We want a to have no more than MAX_WORKER_THREADS
             * running threads.
             *
             * We want a worker thread to wait no longer than 1 second
             * for new tasks before terminating.
             */
            executorService = new ThreadPoolExecutor(0, MAX_WORKER_THREADS,
                                         5L, TimeUnit.SECONDS,
                                         new LinkedBlockingQueue<Runnable>(),
                                         threadFactory) {

                    private final ReentrantLock pauseLock = new ReentrantLock();
                    private final Condition unpaused = pauseLock.newCondition();
                    private boolean isPaused = false;
                    private final ReentrantLock executeLock = new ReentrantLock();
                    
                    @Override
                    public void execute(Runnable command) {
                        /*
                         * ThreadPoolExecutor first tries to run task
                         * in a corePool. If all threads are busy it
                         * tries to add task to the waiting queue. If it
                         * fails it run task in maximumPool.
                         *
                         * We want corePool to be 0 and
                         * maximumPool to be MAX_WORKER_THREADS
                         * We need to change the order of the execution.
                         * First try corePool then try maximumPool
                         * pool and only then store to the waiting
                         * queue. We can not do that because we would
                         * need access to the private methods.
                         *
                         * Instead we enlarge corePool to
                         * MAX_WORKER_THREADS before the execution and
                         * shrink it back to 0 after. 
                         * It does pretty much what we need.
                         *
                         * While we changing the corePoolSize we need
                         * to stop running worker threads from accepting new
                         * tasks.
                         */
                        
                        //we need atomicity for the execute method.
                        executeLock.lock();
                        try {

                            pauseLock.lock();
                            try {
                                isPaused = true;
                            } finally {
                                pauseLock.unlock();
                            }
                            
                            setCorePoolSize(MAX_WORKER_THREADS);
                            super.execute(command);
                            setCorePoolSize(0);
                            
                            pauseLock.lock();
                            try {
                                isPaused = false;
                                unpaused.signalAll();
                            } finally {
                                pauseLock.unlock();
                            }
                        } finally {
                            executeLock.unlock();
                        }
                    }
                    @Override 
                    protected void afterExecute(Runnable r, Throwable t) { 
                        super.afterExecute(r, t);
                        pauseLock.lock();
                        try {
                            while(isPaused) {
                                unpaused.await();
                            }
                        } catch(InterruptedException ignore) {
                            
                        } finally {
                            pauseLock.unlock();
                        }
                    }
                };
        }
        return executorService; 
    }
   
    private static class DoSubmitAccumulativeRunnable 
          extends AccumulativeRunnable<Runnable> implements ActionListener {
        private final static int DELAY = (int) (1000 / 30);
        @Override
        protected void run(List<Runnable> args) {
            for (Runnable runnable : args) {
                runnable.run();
            }
        }
        @Override
        protected void submit() {
            Timer timer = new Timer(DELAY, this);
            timer.setRepeats(false);
            timer.start();
        }
        public void actionPerformed(ActionEvent event) {
            run();
        }
    }
    
    private class SwingWorkerPropertyChangeSupport 
            extends PropertyChangeSupport {
        SwingWorkerPropertyChangeSupport(Object source) {
            super(source);
        }
        @Override
        public void firePropertyChange(final PropertyChangeEvent evt) {
            if (SwingUtilities.isEventDispatchThread()) {
                super.firePropertyChange(evt);
            } else {
                doSubmit.add(
                    new Runnable() {
                        public void run() {
                            SwingWorkerPropertyChangeSupport.this
                                .firePropertyChange(evt);
                        }
                    });
            }
        }
    }
}


///////////////////////////////////////////////////////////

/* 
 * $Id: PrimeNumbersDemo.java,v 1.2 2006/09/28 20:20:30 idk Exp $
 * 
 * Copyright ?2005 Sun Microsystems, Inc. All rights
 * reserved. Use is subject to license terms.
 */

package org.jdesktop.swingworker;

import java.awt.BorderLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class PrimeNumbersDemo {
    public static void main(String[] args) throws Exception {
        JFrame frame = new JFrame("Prime Numbers Demo");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        JTextArea textArea = new JTextArea();
        textArea.setEditable(false);
        frame.add(new JScrollPane(textArea), BorderLayout.CENTER);
        PrimeNumbersTask task = new PrimeNumbersTask(textArea, 10000);
        final JProgressBar progressBar = new JProgressBar(0, 100);
        progressBar.setIndeterminate(true);
        frame.add(progressBar, BorderLayout.NORTH);

        frame.setSize(500, 500);
        frame.setVisible(true);

        task.addPropertyChangeListener(
            new PropertyChangeListener() {
                public  void propertyChange(PropertyChangeEvent evt) {
                    if ("progress".equals(evt.getPropertyName())) {
                        progressBar.setIndeterminate(false);
                        progressBar.setValue((Integer)evt.getNewValue());
                    }
                }
            });
        task.execute();

        return;
    }
    
    /**
     * Finds first N prime numbers.
     */
    static class PrimeNumbersTask extends SwingWorker<List<Integer>, Integer> {
        final int numbersToFind;
        //sorted list of consequent prime numbers
        private final List<Integer> primeNumbers;
        private final JTextArea textArea;

        PrimeNumbersTask(JTextArea textArea, int numbersToFind) {
            this.textArea = textArea;
            this.numbersToFind = numbersToFind;
            this.primeNumbers = new ArrayList<Integer>(numbersToFind);
        }

        @Override
        public List<Integer> doInBackground() {
            int number = 2;
            while(primeNumbers.size() < numbersToFind
                  && !isCancelled()) {
                if (isPrime(number)) {
                    primeNumbers.add(number);
                    setProgress(100 * primeNumbers.size() / numbersToFind);
                    publish(number);
                }
                number++;
            }
            return primeNumbers;
        }

        @Override
        protected void process(List<Integer> chunks) {
            StringBuilder strBuilder = new StringBuilder();
            for (int number : chunks) {
                strBuilder.append(number).append('\n');
            }
            textArea.append(strBuilder.toString());
        }
        private boolean isPrime(int number) {
            for (int prime : primeNumbers) {
                if (number % prime == 0) {
                    return false;
                }
            }
            return true;
        }
    }
}



///////////////////////////////////////////////////////////


/* 
 * $Id: SwingWorkerTest.java,v 1.3 2007/03/01 19:34:02 idk Exp $
 * 
 * Copyright ?2005 Sun Microsystems, Inc. All rights
 * reserved. Use is subject to license terms.
 */
package org.jdesktop.swingworker;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Exchanger;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import javax.swing.SwingUtilities;

import junit.framework.TestCase;

import org.jdesktop.swingworker.SwingWorker.StateValue;

public class SwingWorkerTest extends TestCase {

    private final static int TIME_OUT = 30;
    private final static TimeUnit TIME_OUT_UNIT = TimeUnit.SECONDS;
    
    public static void main(String[] args) {
        junit.swingui.TestRunner.run(SwingWorkerTest.class);
    }
   
    
    // is to be run on a worker thread.
    public final void testdoInBackground() throws Exception {
        SwingWorker<Thread,?> test = new SwingWorker<Thread, Object>() {
            @Override
            protected Thread doInBackground() throws Exception {
                return Thread.currentThread();
            }
        };
        test.execute();
        Thread result = test.get(TIME_OUT, TIME_OUT_UNIT);
        assertNotNull(result);
        assertNotSame(Thread.currentThread(), result);
    }

    //{@code process} gets everything from {@code publish}
    //should be executed on the EDT
    public final void testPublishAndProcess() throws Exception {
        final Exchanger<List<Integer>> listExchanger = 
            new Exchanger<List<Integer>>();
        final Exchanger<Boolean> boolExchanger = 
            new Exchanger<Boolean>();
        SwingWorker<List<Integer>,Integer> test = 
            new SwingWorker<List<Integer>, Integer>() {
                List<Integer> receivedArgs = 
                    Collections.synchronizedList(new ArrayList<Integer>());
                Boolean isOnEDT = Boolean.TRUE;
                final int NUMBERS = 100;
                @Override
                protected List<Integer> doInBackground() throws Exception {
                    List<Integer> ret = 
                        Collections.synchronizedList(
                            new ArrayList<Integer>(NUMBERS));
                    for (int i = 0; i < NUMBERS; i++) {
                        publish(i);
                        ret.add(i);
                    }
                    return ret;
                }
                @Override
                protected void process(List<Integer> args) {
                    for(Integer i : args) {
                        receivedArgs.add(i);
                    }
                    isOnEDT = isOnEDT && SwingUtilities.isEventDispatchThread();
                    if (receivedArgs.size() == NUMBERS) {
                        try {
                            boolExchanger.exchange(isOnEDT);
                            listExchanger.exchange(receivedArgs);
                        } catch (InterruptedException ignore) {
                            ignore.printStackTrace();
                        }
                    }
                }
        };
        test.execute();
        assertTrue(boolExchanger.exchange(null, TIME_OUT, TIME_OUT_UNIT));
        assertEquals(test.get(TIME_OUT, TIME_OUT_UNIT), 
            listExchanger.exchange(null, TIME_OUT, TIME_OUT_UNIT));
    }

    // done is executed on the EDT
    // receives the return value from doInBackground using get()
    public final void testDone() throws Exception {
        final String testString  = "test"; 
        final Exchanger<Boolean> exchanger = new Exchanger<Boolean>();
        SwingWorker<?,?> test = new SwingWorker<String, Object>() {
            @Override
            protected String doInBackground() throws Exception {
                return testString;
            }
            @Override
            protected void done() {
                try {
                    exchanger.exchange(
                        testString == get()
                        && SwingUtilities.isEventDispatchThread());
                } catch (Exception ignore) {
                }
            }
        };
        test.execute();
        assertTrue(exchanger.exchange(null, TIME_OUT, TIME_OUT_UNIT));
    }

    //PropertyChangeListener should be notified on the EDT only
    public final void testPropertyChange() throws Exception {
        final Exchanger<Boolean> boolExchanger = 
            new Exchanger<Boolean>();
        final SwingWorker<?,?> test = 
            new SwingWorker<Object, Object>() {
                @Override
                protected Object doInBackground() throws Exception {
                    firePropertyChange("test", null, "test");
                    return null;
                }
            };
        test.addPropertyChangeListener(
            new PropertyChangeListener() {
                boolean isOnEDT = true;

                public  void propertyChange(PropertyChangeEvent evt) {
                    isOnEDT &= SwingUtilities.isEventDispatchThread();
                    if ("state".equals(evt.getPropertyName())
                        && StateValue.DONE == evt.getNewValue()) {
                        try {
                            boolExchanger.exchange(isOnEDT);
                        } catch (Exception ignore) {
                            ignore.printStackTrace();
                        }
                    }
                }
            });
        test.execute();
        assertTrue(boolExchanger.exchange(null, TIME_OUT, TIME_OUT_UNIT));
    }
    
    //the sequence should be
    //StateValue.STARTED, done, StateValue.DONE
    public final void testWorkFlow() throws Exception {
        final List<Object> goldenSequence = 
            Arrays.asList(new Object[]{StateValue.STARTED, "done", 
                                       StateValue.DONE});
        final List<Object> sequence = 
                    Collections.synchronizedList(new ArrayList<Object>());

        final Exchanger<List<Object>> listExchanger = new Exchanger<List<Object>>();
        
        final SwingWorker<?,?> test = 
            new SwingWorker<Object,Object>() {
                @Override
                protected Object doInBackground() throws Exception {
                    return null;
                }
                @Override
                protected void done() {
                    sequence.add("done");
                }
            };
        test.addPropertyChangeListener(
            new PropertyChangeListener() {
                public  void propertyChange(PropertyChangeEvent evt) {
                    if ("state".equals(evt.getPropertyName())) {
                        sequence.add(evt.getNewValue());
                        if (StateValue.DONE == evt.getNewValue()) {
                            try {
                                listExchanger.exchange(sequence);
                            } catch (Exception ignore) {
                                ignore.printStackTrace();
                            }
                        }
                    }
                }
            });
        test.execute();
        assertEquals(goldenSequence, 
                     listExchanger.exchange(null, TIME_OUT, TIME_OUT_UNIT));
    }
    
    /* 
     * regression test for 6493680
     * [SwingWorker notifications might be out of order.]
     */ 
    public final void test6493680() throws Exception {
        final AtomicInteger lastProgressValue = new AtomicInteger(-1);
        final Exchanger<Boolean> exchanger = new Exchanger<Boolean>();
        class Test {
            private final AtomicInteger lastProgressValue = 
                new AtomicInteger(-1);
            private final Exchanger<Boolean> exchanger = 
                new Exchanger<Boolean>();

            boolean test() throws Exception {
                TestSwingWorker swingWorker = new TestSwingWorker();
                swingWorker.addPropertyChangeListener(
                    new PropertyChangeListener() {
                        public void propertyChange(PropertyChangeEvent evt) {
                            if ("progress" == evt.getPropertyName()) {
                                lastProgressValue.set((Integer) evt.getNewValue());
                            }
                        }
                    });

                swingWorker.execute();
                return exchanger.exchange(true);
            }

            class TestSwingWorker extends SwingWorker<Void, Void> {
                @Override
                protected Void doInBackground() throws Exception {
                    for (int i = 0; i <= 100; i++) {
                        Thread.sleep(1);
                        setProgress(i);
                    }
                    return null;
                }
                @Override
                protected void done() {
                    boolean isPassed = (lastProgressValue.get() == 100);
                    try {
                        exchanger.exchange(isPassed);
                    } catch (Exception ingore) {
                    }
                }
            }
        }
        /*
         * because timing is involved in this bug we will run the test
         * NUMBER_OF_TRIES times.
         * the tes`t passes if it does not fail once.
         */
         final int NUMBER_OF_TRIES = 50;
         for (int i = 0; i < NUMBER_OF_TRIES; i++) {
             assertTrue((new Test()).test());
         }
    }
}

///////////////////////////////////////////////////////////


           
       








swing-worker-src-1.1.zip( 27 k)

Related examples in the same category

1.Swing Worker from JDK 6 SE
2.Swing Worker Image LoaderSwing Worker Image Loader