comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:3dc0c5604566
1 /*
2 File: SwingWorker.java
3
4 Originally written by Joseph Bowbeer and released into the public domain.
5 This may be used for any purposes whatsoever without acknowledgment.
6
7 Originally part of jozart.swingutils.
8 Adapted for util.concurrent by Joseph Bowbeer.
9
10 */
11
12 package EDU.oswego.cs.dl.util.concurrent.misc;
13
14 import java.lang.reflect.InvocationTargetException;
15 import javax.swing.SwingUtilities;
16
17 import EDU.oswego.cs.dl.util.concurrent.*;
18
19 /**
20 * An abstract class that you subclass to perform GUI-related work
21 * in a dedicated thread.
22 * <p>
23 * This class was adapted from the SwingWorker written by Hans Muller
24 * and presented in "Using a Swing Worker Thread" in the Swing Connection
25 * - http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html
26 * <p>
27 * A closely related version of this class is described in
28 * "The Last Word in Swing Threads" in the Swing Connection
29 * - http://java.sun.com/products/jfc/tsc/articles/threads/threads3.html
30 * <p>
31 * This SwingWorker is a ThreadFactoryUser and implements Runnable. The
32 * default thread factory creates low-priority worker threads. A special
33 * constructor is provided for enabling a timeout. When the timeout
34 * expires, the worker thread is interrupted.
35 * <p>
36 * Note: Using a timeout of <code>Long.MAX_VALUE</code> will not impose a
37 * timeout but will create an additional thread of control that will respond
38 * to an interrupt even if the <code>construct</code> implementation ignores
39 * them.
40 * <p>
41 * <b>Sample Usage</b> <p>
42 * <pre>
43 * import EDU.oswego.cs.dl.util.concurrent.TimeoutException;
44 * import EDU.oswego.cs.dl.util.concurrent.misc.SwingWorker;
45 *
46 * public class SwingWorkerDemo extends javax.swing.JApplet {
47 *
48 * private static final int TIMEOUT = 5000; // 5 seconds
49 * private javax.swing.JLabel status;
50 * private javax.swing.JButton start;
51 * private SwingWorker worker;
52 *
53 * public SwingWorkerDemo() {
54 * status = new javax.swing.JLabel("Ready");
55 * status.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
56 * getContentPane().add(status, java.awt.BorderLayout.CENTER);
57 * start = new javax.swing.JButton("Start");
58 * getContentPane().add(start, java.awt.BorderLayout.SOUTH);
59 *
60 * start.addActionListener(new java.awt.event.ActionListener() {
61 * public void actionPerformed(java.awt.event.ActionEvent evt) {
62 * if (start.getText().equals("Start")) {
63 * start.setText("Stop");
64 * status.setText("Working...");
65 * worker = new DemoSwingWorker(TIMEOUT);
66 * worker.start();
67 * } else {
68 * worker.interrupt();
69 * }
70 * }
71 * });
72 * }
73 *
74 * private class DemoSwingWorker extends SwingWorker {
75 * private static final java.util.Random RAND = new java.util.Random();
76 * public DemoSwingWorker(long msecs) {
77 * super(msecs);
78 * }
79 * protected Object construct() throws InterruptedException {
80 * // Take a random nap. If we oversleep, the worker times out.
81 * Thread.sleep(RAND.nextInt(2*TIMEOUT));
82 * return "Success";
83 * }
84 * protected void finished() {
85 * start.setText("Start");
86 * try {
87 * Object result = get();
88 * status.setText((String) result);
89 * }
90 * catch (java.lang.reflect.InvocationTargetException e) {
91 * Throwable ex = e.getTargetException();
92 * if (ex instanceof TimeoutException) {
93 * status.setText("Timed out.");
94 * } else if (ex instanceof InterruptedException) {
95 * status.setText("Interrupted.");
96 * } else {
97 * status.setText("Exception: " + ex);
98 * }
99 * }
100 * catch (InterruptedException ex) {
101 * // event-dispatch thread won't be interrupted
102 * throw new IllegalStateException(ex+"");
103 * }
104 * }
105 * }
106 * }
107 * </pre>
108 *
109 * @author Joseph Bowbeer
110 * @author Hans Muller
111 * @version 3.0
112 *
113 * <p>[<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>]
114 */
115 public abstract class SwingWorker extends ThreadFactoryUser
116 implements Runnable {
117
118 /** Default thread factory. Creates low priority worker threads. */
119 private static final ThreadFactory FACTORY = new ThreadFactory() {
120 public Thread newThread(Runnable command) {
121 Thread t = new Thread(command);
122 t.setPriority(Thread.MIN_PRIORITY+1);
123 return t;
124 }
125 };
126
127 /** Holds the value to be returned by the <code>get</code> method. */
128 private final FutureResult result = new FutureResult();
129
130 /** Maximum time to wait for worker to complete. */
131 private final long timeout;
132
133 /** Worker thread. */
134 private Thread thread;
135
136 /** Creates new SwingWorker with no timeout. */
137 public SwingWorker() {
138 this(FACTORY, 0);
139 }
140
141 /**
142 * Creates new SwingWorker with specified timeout.
143 * @param msecs timeout in milliseconds, or <code>0</code>
144 * for no time limit.
145 */
146 public SwingWorker(long msecs) {
147 this(FACTORY, msecs);
148 }
149
150 /**
151 * Creates new SwingWorker with specified thread factory and timeout.
152 * @param factory factory for worker threads.
153 * @param msecs timeout in milliseconds, or <code>0</code>
154 * for no time limit.
155 */
156 protected SwingWorker(ThreadFactory factory, long msecs) {
157 setThreadFactory(factory);
158 if (msecs < 0) {
159 throw new IllegalArgumentException("timeout="+msecs);
160 }
161 timeout = msecs;
162 }
163
164 /**
165 * Computes the value to be returned by the <code>get</code> method.
166 */
167 protected abstract Object construct() throws Exception;
168
169 /**
170 * Called on the event dispatching thread (not on the worker thread)
171 * after the <code>construct</code> method has returned.
172 */
173 protected void finished() { }
174
175 /**
176 * Returns timeout period in milliseconds. Timeout is the
177 * maximum time to wait for worker to complete. There is
178 * no time limit if timeout is <code>0</code> (default).
179 */
180 public long getTimeout() {
181 return timeout;
182 }
183
184 /**
185 * Calls the <code>construct</code> method to compute the result,
186 * and then invokes the <code>finished</code> method on the event
187 * dispatch thread.
188 */
189 public void run() {
190
191 Callable function = new Callable() {
192 public Object call() throws Exception {
193 return construct();
194 }
195 };
196
197 Runnable doFinished = new Runnable() {
198 public void run() {
199 finished();
200 }
201 };
202
203 /* Convert to TimedCallable if timeout is specified. */
204 long msecs = getTimeout();
205 if (msecs != 0) {
206 TimedCallable tc = new TimedCallable(function, msecs);
207 tc.setThreadFactory(getThreadFactory());
208 function = tc;
209 }
210
211 result.setter(function).run();
212 SwingUtilities.invokeLater(doFinished);
213 }
214
215 /**
216 * Starts the worker thread.
217 */
218 public synchronized void start() {
219 if (thread == null) {
220 thread = getThreadFactory().newThread(this);
221 }
222 thread.start();
223 }
224
225 /**
226 * Stops the worker and sets the exception to InterruptedException.
227 */
228 public synchronized void interrupt() {
229 if (thread != null) {
230 /* Try-catch is workaround for JDK1.2 applet security bug.
231 On some platforms, a security exception is thrown if an
232 applet interrupts a thread that is no longer alive. */
233 try { thread.interrupt(); } catch (Exception ex) { }
234 }
235 result.setException(new InterruptedException());
236 }
237
238 /**
239 * Return the value created by the <code>construct</code> method,
240 * waiting if necessary until it is ready.
241 *
242 * @return the value created by the <code>construct</code> method
243 * @exception InterruptedException if current thread was interrupted
244 * @exception InvocationTargetException if the constructing thread
245 * encountered an exception or was interrupted.
246 */
247 public Object get()
248 throws InterruptedException, InvocationTargetException {
249 return result.get();
250 }
251
252 /**
253 * Wait at most msecs to access the constructed result.
254 * @return current value
255 * @exception TimeoutException if not ready after msecs
256 * @exception InterruptedException if current thread has been interrupted
257 * @exception InvocationTargetException if the constructing thread
258 * encountered an exception or was interrupted.
259 */
260 public Object timedGet(long msecs)
261 throws TimeoutException, InterruptedException, InvocationTargetException {
262 return result.timedGet(msecs);
263 }
264
265 /**
266 * Get the exception, or null if there isn't one (yet).
267 * This does not wait until the worker is ready, so should
268 * ordinarily only be called if you know it is.
269 * @return the exception encountered by the <code>construct</code>
270 * method wrapped in an InvocationTargetException
271 */
272 public InvocationTargetException getException() {
273 return result.getException();
274 }
275
276 /**
277 * Return whether the <code>get</code> method is ready to
278 * return a value.
279 *
280 * @return true if a value or exception has been set. else false
281 */
282 public boolean isReady() {
283 return result.isReady();
284 }
285
286 }