Java tutorial
/* * $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<String> 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<Rectangle> 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<String, Object> { * {@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<List<Integer>, Integer> { * PrimeNumbersTask(JTextArea textArea, int numbersToFind) { * //initialize * } * * {@code @Override} * public List<Integer> doInBackground() { * while (! enough && ! isCancelled()) { * number = nextPrimeNumber(); * publish(number); * setProgress(100 * numbers.size() / numbersToFind); * } * } * return numbers; * } * * {@code @Override} * protected void process(List<Integer> chunks) { * for (int number : chunks) { * textArea.append(number + "\n"); * } * } * } * * 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 ("progress".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("1"); * publish("2", "3"); * publish("4", "5", "6"); * </pre> * * might result in: * * <pre> * process("1", "2", "3", "4", "5", "6") * </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<DefaultTableModel, Object[]> { * private final DefaultTableModel tableModel; * * public TableSwingWorker(DefaultTableModel tableModel) { * this.tableModel = tableModel; * } * * {@code @Override} * protected DefaultTableModel doInBackground() throws Exception { * for (Object[] row = loadData(); * ! isCancelled() && row != null; * row = loadData()) { * publish((Object[]) row); * } * return tableModel; * } * * {@code @Override} * protected void process(List<Object[]> 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 ("state".equals(event.getPropertyName()) * && 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()); } } } ///////////////////////////////////////////////////////////