diff src/EDU/oswego/cs/dl/util/concurrent/misc/SwingWorker.java @ 0:3dc0c5604566

Initial checkin of blitz 2.0 fcs - no installer yet.
author Dan Creswell <dan.creswell@gmail.com>
date Sat, 21 Mar 2009 11:00:06 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/EDU/oswego/cs/dl/util/concurrent/misc/SwingWorker.java	Sat Mar 21 11:00:06 2009 +0000
@@ -0,0 +1,286 @@
+/*
+  File: SwingWorker.java
+
+  Originally written by Joseph Bowbeer and released into the public domain.
+  This may be used for any purposes whatsoever without acknowledgment.
+ 
+  Originally part of jozart.swingutils.
+  Adapted for util.concurrent by Joseph Bowbeer.
+
+*/
+
+package EDU.oswego.cs.dl.util.concurrent.misc;
+
+import java.lang.reflect.InvocationTargetException;
+import javax.swing.SwingUtilities;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+/**
+ * An abstract class that you subclass to perform GUI-related work
+ * in a dedicated thread.
+ * <p>
+ * This class was adapted from the SwingWorker written by Hans Muller
+ * and presented in "Using a Swing Worker Thread" in the Swing Connection
+ * - http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html
+ * <p>
+ * A closely related version of this class is described in
+ * "The Last Word in Swing Threads" in the Swing Connection
+ * - http://java.sun.com/products/jfc/tsc/articles/threads/threads3.html
+ * <p>
+ * This SwingWorker is a ThreadFactoryUser and implements Runnable. The
+ * default thread factory creates low-priority worker threads. A special
+ * constructor is provided for enabling a timeout. When the timeout
+ * expires, the worker thread is interrupted.
+ * <p>
+ * Note: Using a timeout of <code>Long.MAX_VALUE</code> will not impose a
+ * timeout but will create an additional thread of control that will respond
+ * to an interrupt even if the <code>construct</code> implementation ignores
+ * them.
+ * <p>
+ * <b>Sample Usage</b> <p>
+ * <pre>
+ * import EDU.oswego.cs.dl.util.concurrent.TimeoutException;
+ * import EDU.oswego.cs.dl.util.concurrent.misc.SwingWorker;
+ *
+ * public class SwingWorkerDemo extends javax.swing.JApplet {
+ *
+ *   private static final int TIMEOUT = 5000; // 5 seconds
+ *   private javax.swing.JLabel status;
+ *   private javax.swing.JButton start;
+ *   private SwingWorker worker;
+ *
+ *   public SwingWorkerDemo() {
+ *     status = new javax.swing.JLabel("Ready");
+ *     status.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
+ *     getContentPane().add(status, java.awt.BorderLayout.CENTER);
+ *     start = new javax.swing.JButton("Start");
+ *     getContentPane().add(start, java.awt.BorderLayout.SOUTH);
+ *
+ *     start.addActionListener(new java.awt.event.ActionListener() {
+ *       public void actionPerformed(java.awt.event.ActionEvent evt) {
+ *         if (start.getText().equals("Start")) {
+ *           start.setText("Stop");
+ *           status.setText("Working...");
+ *           worker = new DemoSwingWorker(TIMEOUT);
+ *           worker.start();
+ *         } else {
+ *           worker.interrupt();
+ *         }
+ *       }
+ *     });
+ *   }
+ *
+ *   private class DemoSwingWorker extends SwingWorker {
+ *     private static final java.util.Random RAND = new java.util.Random();
+ *     public DemoSwingWorker(long msecs) {
+ *       super(msecs);
+ *     }
+ *     protected Object construct() throws InterruptedException {
+ *       // Take a random nap. If we oversleep, the worker times out.
+ *       Thread.sleep(RAND.nextInt(2*TIMEOUT));
+ *       return "Success";
+ *     }
+ *     protected void finished() {
+ *       start.setText("Start");
+ *       try {
+ *         Object result = get();
+ *         status.setText((String) result);
+ *       }
+ *       catch (java.lang.reflect.InvocationTargetException e) {
+ *         Throwable ex = e.getTargetException();
+ *         if (ex instanceof TimeoutException) {
+ *           status.setText("Timed out.");
+ *         } else if (ex instanceof InterruptedException) {
+ *           status.setText("Interrupted.");
+ *         } else {
+ *           status.setText("Exception: " + ex);
+ *         }
+ *       }
+ *       catch (InterruptedException ex) {
+ *         // event-dispatch thread won't be interrupted 
+ *         throw new IllegalStateException(ex+"");
+ *       }
+ *     }
+ *   }
+ * }
+ * </pre>
+ *
+ * @author  Joseph Bowbeer
+ * @author  Hans Muller
+ * @version 3.0
+ *
+ * <p>[<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>]
+ */
+public abstract class SwingWorker extends ThreadFactoryUser
+implements Runnable {
+
+    /** Default thread factory. Creates low priority worker threads. */
+    private static final ThreadFactory FACTORY = new ThreadFactory() {
+        public Thread newThread(Runnable command) {
+            Thread t = new Thread(command);
+            t.setPriority(Thread.MIN_PRIORITY+1);
+            return t;
+        }
+    };
+
+    /** Holds the value to be returned by the <code>get</code> method. */
+    private final FutureResult result = new FutureResult();
+
+    /** Maximum time to wait for worker to complete. */
+    private final long timeout;
+    
+    /** Worker thread. */
+    private Thread thread;
+
+    /** Creates new SwingWorker with no timeout. */
+    public SwingWorker() {
+        this(FACTORY, 0);
+    }
+    
+    /**
+     * Creates new SwingWorker with specified timeout.
+     * @param msecs timeout in milliseconds, or <code>0</code>
+     * for no time limit.
+     */
+    public SwingWorker(long msecs) {
+        this(FACTORY, msecs);
+    }
+    
+    /**
+     * Creates new SwingWorker with specified thread factory and timeout.
+     * @param factory factory for worker threads.
+     * @param msecs timeout in milliseconds, or <code>0</code>
+     * for no time limit.
+     */
+    protected SwingWorker(ThreadFactory factory, long msecs) {
+        setThreadFactory(factory);
+        if (msecs < 0) {
+            throw new IllegalArgumentException("timeout="+msecs);
+        }
+        timeout = msecs;
+    }
+
+    /**
+     * Computes the value to be returned by the <code>get</code> method.
+     */
+    protected abstract Object construct() throws Exception;
+
+    /**
+     * Called on the event dispatching thread (not on the worker thread)
+     * after the <code>construct</code> method has returned.
+     */
+    protected void finished() { }
+
+    /**
+     * Returns timeout period in milliseconds. Timeout is the
+     * maximum time to wait for worker to complete. There is
+     * no time limit if timeout is <code>0</code> (default).
+     */
+    public long getTimeout() {
+        return timeout;
+    }
+
+    /**
+     * Calls the <code>construct</code> method to compute the result,
+     * and then invokes the <code>finished</code> method on the event
+     * dispatch thread.
+     */
+    public void run() {
+
+        Callable function = new Callable() {
+            public Object call() throws Exception {
+                return construct();
+            }
+        };
+
+        Runnable doFinished = new Runnable() {
+            public void run() {
+                finished();
+            }
+        };
+
+        /* Convert to TimedCallable if timeout is specified. */
+        long msecs = getTimeout();
+        if (msecs != 0) {
+            TimedCallable tc = new TimedCallable(function, msecs);
+            tc.setThreadFactory(getThreadFactory());
+            function = tc;
+        }
+
+        result.setter(function).run();
+        SwingUtilities.invokeLater(doFinished);
+    }
+
+    /**
+     * Starts the worker thread.
+     */
+    public synchronized void start() {
+        if (thread == null) {
+            thread = getThreadFactory().newThread(this);
+        }
+        thread.start();
+    }
+
+    /**
+     * Stops the worker and sets the exception to InterruptedException.
+     */
+    public synchronized void interrupt() {
+        if (thread != null) {
+            /* Try-catch is workaround for JDK1.2 applet security bug.
+               On some platforms, a security exception is thrown if an
+               applet interrupts a thread that is no longer alive. */
+            try { thread.interrupt(); } catch (Exception ex) { }
+        }
+        result.setException(new InterruptedException());
+    }
+
+    /**
+     * Return the value created by the <code>construct</code> method,
+     * waiting if necessary until it is ready.
+     *
+     * @return the value created by the <code>construct</code> method
+     * @exception InterruptedException if current thread was interrupted
+     * @exception InvocationTargetException if the constructing thread
+     * encountered an exception or was interrupted.
+     */
+    public Object get()
+    throws InterruptedException, InvocationTargetException {
+        return result.get();
+    }
+
+    /**
+     * Wait at most msecs to access the constructed result.
+     * @return current value
+     * @exception TimeoutException if not ready after msecs
+     * @exception InterruptedException if current thread has been interrupted
+     * @exception InvocationTargetException if the constructing thread
+     * encountered an exception or was interrupted.
+     */
+    public Object timedGet(long msecs) 
+    throws TimeoutException, InterruptedException, InvocationTargetException {
+        return result.timedGet(msecs);
+    }
+
+    /**
+     * Get the exception, or null if there isn't one (yet).
+     * This does not wait until the worker is ready, so should
+     * ordinarily only be called if you know it is.
+     * @return the exception encountered by the <code>construct</code>
+     * method wrapped in an InvocationTargetException
+     */
+    public InvocationTargetException getException() {
+        return result.getException();
+    }
+
+    /**
+     * Return whether the <code>get</code> method is ready to
+     * return a value.
+     *
+     * @return true if a value or exception has been set. else false
+     */
+    public boolean isReady() {
+        return result.isReady();
+    }
+
+}