/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package org.netbeans.modules.progress.ui; import java.lang.reflect.InvocationTargetException; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.SwingUtilities; import org.netbeans.api.progress.RunOffAWT; /** * This class provides a wrapper class that provides a {@link Future} interface * to background processing work. This work can be handed off to the background to * be worked on, and another thread can use {@link Future#get()} to wait for a result * for example. * * @author gregg */ public abstract class ControlledProcessor implements Future, Callable, Runnable { protected final FutureTask fut; protected volatile T result; protected AtomicBoolean onCancel = new AtomicBoolean(); public ControlledProcessor() { fut = getFutureTask(); } /** * Called before the {@link Callable} processing work is carried out. * This is called from the thread that started execution, so the context * is not defined. If you need the EDT to do some work here, you need to * use {@link #runInSwing()} yourself to make that happen. */ protected void setup() { } /** * Called after the {@link Callable} processing work is carried out. * This is called from the thread that started execution, so the context * is not defined. If you need the EDT to do some work here, you need to * use {@link #runInSwing()} yourself to make that happen. */ protected void after() { } /** * A convenience method that will make sure the passed {@link Runnable} * is executed inside of the AWT EventDispatchThread. Execution returns * once the {@link Runnable} has been processed. * @param r the Runnable to execute. */ protected void runInSwing( Runnable r ) { if( SwingUtilities.isEventDispatchThread() ) { r.run(); } else { try { SwingUtilities.invokeAndWait(r); } catch (InterruptedException ex) { reportException(ex); } catch (InvocationTargetException ex) { reportException(ex); } } } /** * If you are controlling execution, litterally yourself, just * call this method, somehow and everything will get done. Other * threads, can use the Future interface methods to wait * for execution as well as check for completion. * @param operationDescr * @return */ public boolean process( String operationDescr ) { RunOffAWT.runOffAWT( this, operationDescr, onCancel ); if( onCancel.get() ){ cancel(true); } return onCancel.get(); } /** * This is the factory method for creating the needed {@link FutureTask} * value to process. Only subclasses with special needs should override this * method to return something other than new FutureTask(this). * @return the FutureTask value needed. */ protected FutureTask getFutureTask() { return new FutureTask(this); } /** * This method should be implemented by all uses of this class to * perform the work that should happen in the background. This * method should provide for cancellation by looking for {@link InterruptedException} * and watching checking {@link #isCancelled()}. * @return the resulting value. * @throws Exception */ protected abstract T performWork() throws Exception ; // let super javadoc appear here public final @Override T call() throws Exception { setup(); result = call(); after(); return result; } // let super javadoc appear here public @Override boolean cancel(boolean mayInterruptIfRunning) { onCancel.set( fut.cancel( mayInterruptIfRunning ) ); return onCancel.get(); } // let super javadoc appear here public @Override boolean isCancelled() { return fut.isCancelled(); } // let super javadoc appear here public @Override boolean isDone() { return fut.isDone(); } // let super javadoc appear here public @Override T get() throws InterruptedException, ExecutionException { return fut.get(); } // let super javadoc appear here public @Override T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return fut.get( timeout, unit ); } /** * Allows execution by a separate thread that doesn't directly care about * the results. Another thread might then use {@link Future#get()} to wait * for completion and process the results. */ public @Override void run() { try { call(); } catch (Exception ex) { reportException( ex ); } } /** * report exception occuring in processing here. Override to * do more than just {@link Execeptions#printStackTrace(java.lang.Throwable)} * @param ex the exception which has occured. */ protected void reportException( Throwable ex ) { Exceptions.printStackTrace(ex); } }