001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.openide.util;
042:
043: import java.util.HashSet;
044: import java.util.Iterator;
045: import java.util.logging.Level;
046:
047: /** A task that may be executed in a separate thread and permits examination of its status.
048: * Other threads can check if it is finished or wait for it
049: * to finish.
050: * <P>
051: * For example:
052: * <p><code><PRE>
053: * Runnable r = new Runnable () {
054: * public void run () {
055: * // do something
056: * }
057: * };
058: * Task task = new Task (r);
059: * RequestProcessor.postRequest (task);
060: * </PRE></code>
061: * <p>In a different thread one can then test <CODE>task.isFinished ()</CODE>
062: * or wait for it with <CODE>task.waitFinished ()</CODE>.
063: *
064: * @author Jaroslav Tulach
065: */
066: public class Task extends Object implements Runnable {
067: /** Dummy task which is already finished. */
068: public static final Task EMPTY = new Task();
069:
070: static {
071: EMPTY.finished = true;
072: }
073:
074: /** map of subclasses to booleans whether they override waitFinished() or not
075: */
076: private static java.util.WeakHashMap<Class, Boolean> overrides;
077:
078: /** request processor for workarounding compatibility problem with
079: * classes that do not override waitFinished (long)
080: */
081: private static RequestProcessor RP;
082:
083: /** what to run */
084: final Runnable run;
085:
086: /** flag if we have finished */
087: private boolean finished;
088:
089: /** listeners for the finish of task (TaskListener) */
090: private HashSet<TaskListener> list;
091:
092: /** Create a new task.
093: * The runnable should provide its own error-handling, as
094: * by default thrown exceptions are simply logged and not rethrown.
095: * @param run runnable to run that computes the task
096: */
097: public Task(Runnable run) {
098: this .run = run;
099:
100: if (run == null) {
101: finished = true;
102: }
103: }
104:
105: /** Constructor for subclasses that wants to control whole execution
106: * itself.
107: * @since 1.5
108: */
109: protected Task() {
110: this .run = null;
111: }
112:
113: /** Test whether the task has finished running.
114: * @return <code>true</code> if so
115: */
116: public final boolean isFinished() {
117: synchronized (this ) {
118: return finished;
119: }
120: }
121:
122: /** Wait until the task is finished.
123: * Changed not to be <code>final</code> in version 1.5
124: */
125: public void waitFinished() {
126: synchronized (this ) {
127: while (!finished) {
128: try {
129: wait();
130: } catch (InterruptedException ex) {
131: }
132: }
133: }
134: }
135:
136: /** Wait until the task is finished, but only a given time.
137: * @param milliseconds time in milliseconds to wait for the result
138: * @exception InterruptedException when the waiting has been interrupted
139: * @return true if the task is really finished, or false if the time out
140: * has been exceeded
141: * @since 5.0
142: */
143: public boolean waitFinished(long milliseconds)
144: throws InterruptedException {
145: synchronized (this ) {
146: if (overridesTimeoutedWaitFinished()) {
147: // the the task overrides waitFinished (timeout) or is
148: // one of the basic tasks, then we can just simply do our bese
149: // code. Otherwise we have to execute threading workaround
150: if (finished) {
151: return true;
152: }
153:
154: long expectedEnd = System.currentTimeMillis()
155: + milliseconds;
156:
157: for (;;) {
158: wait(milliseconds);
159:
160: if (finished) {
161: return true;
162: }
163:
164: long now = System.currentTimeMillis();
165:
166: if (expectedEnd <= now) {
167: return false;
168: }
169:
170: milliseconds = expectedEnd - now;
171: }
172: }
173: }
174:
175: // as we know that RequestProcessor implements the waitFinished(long)
176: // correctly we just post a task for waitFinished() into some
177: // of its threads and wait just the given milliseconds time
178: // for the result, by that we can guarantee the semantics
179: // of the call
180: class Run implements Runnable {
181: public void run() {
182: Task.this .waitFinished();
183: }
184: }
185:
186: RequestProcessor.Task task = RP.post(new Run());
187:
188: return task.waitFinished(milliseconds);
189: }
190:
191: /** Changes the state of the task to be running. Any call after this
192: * one and before notifyFinished to waitFinished blocks.
193: * @since 1.5
194: */
195: protected final void notifyRunning() {
196: synchronized (this ) {
197: if (RequestProcessor.logger().isLoggable(Level.FINE)) {
198: RequestProcessor.logger()
199: .fine("notifyRunning: " + this ); // NOI18N
200: }
201: this .finished = false;
202: notifyAll();
203: }
204: }
205:
206: /** Notify all waiters that this task has finished.
207: * @see #run
208: */
209: protected final void notifyFinished() {
210: Iterator it;
211:
212: synchronized (this ) {
213: finished = true;
214: if (RequestProcessor.logger().isLoggable(Level.FINE)) {
215: RequestProcessor.logger().fine(
216: "notifyFinished: " + this ); // NOI18N
217: }
218: notifyAll();
219:
220: // fire the listeners
221: if (list == null) {
222: return;
223: }
224:
225: it = ((HashSet) list.clone()).iterator();
226: }
227:
228: while (it.hasNext()) {
229: TaskListener l = (TaskListener) it.next();
230: l.taskFinished(this );
231: }
232: }
233:
234: /** Start the task.
235: * When it finishes (even with an exception) it calls
236: * {@link #notifyFinished}.
237: * Subclasses may override this method, but they
238: * then need to call {@link #notifyFinished} explicitly.
239: * <p>Note that this call runs synchronously, but typically the creator
240: * of the task will call this method in a separate thread.
241: */
242: public void run() {
243: try {
244: notifyRunning();
245:
246: if (run != null) {
247: run.run();
248: }
249: } finally {
250: notifyFinished();
251: }
252: }
253:
254: /** Add a listener to the task.
255: * @param l the listener to add
256: */
257: public synchronized void addTaskListener(TaskListener l) {
258: if (list == null) {
259: list = new HashSet<TaskListener>();
260: }
261:
262: list.add(l);
263:
264: if (finished) {
265: l.taskFinished(this );
266: }
267: }
268:
269: /** Remove a listener from the task.
270: * @param l the listener to remove
271: */
272: public synchronized void removeTaskListener(TaskListener l) {
273: if (list == null) {
274: return;
275: }
276:
277: list.remove(l);
278: }
279:
280: public String toString() {
281: return "task " + run; // NOI18N
282: }
283:
284: /** Checks whether the class overrides wait finished.
285: */
286: private boolean overridesTimeoutedWaitFinished() {
287: // yes we implement it corretly
288: if (getClass() == Task.class) {
289: return true;
290: }
291:
292: // RequestProcessor.Task overrides correctly
293: if (getClass() == RequestProcessor.Task.class) {
294: return true;
295: }
296:
297: java.util.WeakHashMap<Class, Boolean> m;
298: Boolean does;
299:
300: synchronized (Task.class) {
301: if (overrides == null) {
302: overrides = new java.util.WeakHashMap<Class, Boolean>();
303: RP = new RequestProcessor(
304: "Timeout waitFinished compatibility processor",
305: 255); // NOI18N
306: }
307:
308: m = overrides;
309:
310: does = m.get(getClass());
311:
312: if (does != null) {
313: return does.booleanValue();
314: }
315:
316: try {
317: java.lang.reflect.Method method = getClass().getMethod(
318: "waitFinished", new Class[] { Long.TYPE }); // NOI18N
319: does = Boolean
320: .valueOf(method.getDeclaringClass() != Task.class);
321: m.put(getClass(), does);
322:
323: return does.booleanValue();
324: } catch (Exception ex) {
325: Exceptions.printStackTrace(ex);
326:
327: return true;
328: }
329: }
330: }
331:
332: /** Reveal the identity of the worker runnable.
333: * Used for debugging from RequestProcessor.
334: */
335: String debug() {
336: return (run == null) ? "null" : run.getClass().getName();
337: }
338: }
|