/** * Mike Cox initially created this as a derivative of the the * {@link org.wonderly.swing.ComponentUpdateThread} class. */ package org.wonderly.swing; import java.awt.Component; import java.lang.reflect.InvocationTargetException; import java.security.AccessControlContext; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.concurrent.Callable; import java.util.logging.Level; import java.util.logging.Logger; import javax.security.auth.Subject; import javax.swing.Action; import javax.swing.SwingUtilities; /** * This class is a SwingWorker kind of class which provides method names * and behaviors like SwingWorker, but extends the available capabilities * with several additional features. *
* ThreadPoolExecutor exec = ... * * public void searchFor( String search ) { * * // Create an instanc which uses our executor for the * // background threads. * final SyncThread th = new SyncThread() { * public void schedule( Runnable r ) { * exec.execute( r ); * } * }; * * // Get all the current values from the remote instance * th.add( new SyncThread* @param() { * public String[] run() { * return remote.getValues(); * } * public void done() { * comp.setModel( new Model( getValue() ) ); * } * } ); * * // Add step to use data and select searched for * // value if found. * th.add( new SyncThread () { * public String run() { * String[] dt = th.getLastThreadValue(); * for( String s : dt ) { * if( findPattern( search, s ) ) { * return s; * } * } * return null; * } * public void done() { * if( getValue() == null ) { * comp.clearSelection(); * } else { * comp.setSelectedValue( getValue() ); * } * } * }); * * th.start(); * } *
The type for published values.
*/
public class SyncThread
* The Runnable passed should be queued to execute/running when this
* method returns.
*
* @param r the Runnable to execute.
*/
public void schedule( Runnable r ) {
new Thread( r ).start();
}
/**
* Start this process or executor sequence in a dedicated thread.
*/
public void start() {
// If thread list is null, run as a single thread sequence.
if (threads == null) {
schedule( new InContextRunnable() {
public Object doRun() {
runSingle(false);
return null;
}
});
// If thread list is not null, run as an executor.
} else {
schedule( new InContextRunnable() {
public Object doRun() {
runAllSteps();
return threadVal;
}
});
}
}
/**
* Run this process or executor sequence in the calling thread.
*/
public void block() {
if( SwingUtilities.isEventDispatchThread() ) {
throw new IllegalStateException("Attempt to block(), already in event thread");
}
// If thread list is null, run as a single sequence.
if (threads == null) {
runSingle(false);
// If thread list is not null, run as an executor.
} else {
runAllSteps();
}
}
/**
* Run this instance in another thread, asynchronously
*/
public void later() {
runSingle(true);
}
/**
* Run the steps in this instance
*
* @param later controls whether {@link #setup} is run now
* or later in the EDT queue.
*/
private void runSingle(boolean later) {
try {
doStep(later);
} catch (InterruptedException ex) {
reportException(ex);
}
}
/**
* This method is used to run the internal list of steps that where
* added to this instance.
*/
private void runAllSteps() {
try {
doSetup(false);
for (SyncThread thread : threads) {
thread.doStep(false);
threadVal = thread.getValue();
}
} catch (RuntimeException ex) {
reportException(ex);
throw ex;
} catch (InterruptedException ex) {
reportException(ex);
} finally {
try {
doDone(false);
} catch (InterruptedException ex) {
reportException(ex);
} catch (RuntimeException ex) {
reportException(ex);
throw ex;
}
}
}
/**
* Performs the three components of the execution model. The
* setup(), doInBackground(), done() steps are done in order.
* @param later This controls whether the EDT work is done
* synchronously if false, or queued if true.
* @throws java.lang.InterruptedException
*/
private void doStep(boolean later) throws InterruptedException {
try {
try {
doSetup(later);
doRun();
} catch (RuntimeException ex) {
reportException(ex);
throw ex;
}
} finally {
try {
doDone( later );
} catch (RuntimeException ex) {
reportException(ex);
throw ex;
}
}
}
/**
* Creates an InContextRunnable instance to call {@link #setup()} in, and
* to then disable components enumerated in the constructor().
* @param later
* @throws java.lang.InterruptedException
*/
private void doSetup(boolean later) throws InterruptedException {
runInSwing(new InContextRunnable() {
public Object doRun() {
setup();
disableComponents();
return null;
}
}, later);
}
/**
* Controls the execution of the run() method. The default
* implementatioin just does return value = run();
.
*/
public R call() {
return value = run();
}
/**
* This method will be called in the EDT after run() has returned.
*/
protected void done() {}
/**
* Call publish from doInBackground() to send preliminary data
* to be processed in the EDT.
*/
protected void publish(final P value) {
try {
runInSwing(new InContextRunnable() {
public Object doRun() {
process(value);
return null;
}
}, false);
} catch (InterruptedException ex) {
reportException(ex);
}
}
/**
* Override this to process data that was sent from publish() in the EDT.
*/
protected void process(P value) {}
/**
* Adds an additional SyncThread instance into the queue of things
* to execute together so that a set of EDT - non-EDT - EDT operations
* can be performed in sequence without having {@link #done} do the
* initiation of the next step.
* @param thread
*/
public void add(SyncThread thread) {
if (threads == null) {
threads = new ArrayListnew Thread(r).start()
* to initiate the operation. Subclasses can be created which use
* thread pools or other mechanisms.
* value = run();
.
* @throws java.lang.RuntimeException
*/
protected void doRun() throws RuntimeException {
value = run();
}
/**
* Creates an InContextRunnable instance to call done() in, which
* enables the components enumerated in the constructor() and then
* calls {@link #done()}.
* @param later
* @throws java.lang.InterruptedException
*/
private void doDone(boolean later) throws InterruptedException {
runInSwing(new InContextRunnable() {
public Object doRun() {
enableComponents();
done();
return null;
}
}, later);
}
/**
* Change component/action enabled status
* @param how true to enable, false to disable.
*/
private void setThingsEnabled(boolean how) {
for (int i = 0; acts != null && i < acts.length; ++i) {
acts[i].setEnabled(how);
}
for (int i = 0; comps != null && i < comps.length; ++i) {
// If we're disabling and this component has the focus, remember its index.
if (how == false && comps[i].hasFocus() == true) {
focusIndex = i;
}
comps[i].setEnabled(how);
// If we're enabling and this component had the focus before, set it back.
if (how == true && i == focusIndex) {
comps[i].requestFocus();
focusIndex = -1; // Reset for next time.
}
}
}
/**
* This method can be used to invoke the passed Runnable in the EventDispatchThread.
*
* @param r The Runnable to dispatch
* @param later if true, SwingUtilities.invokeLater() is used, otherwise
* SwingUtilities.invokeAndWait() is used.
* @throws java.lang.InterruptedException if invokeAndWait() is interupted.
*/
public static void runInSwing(final Runnable r, boolean later) throws InterruptedException {
try {
if (later) {
SwingUtilities.invokeLater(r);
} else {
if (SwingUtilities.isEventDispatchThread()) {
r.run();
} else {
SwingUtilities.invokeAndWait(r);
}
}
// Throw the cause to make sure we get the actual exception instead of one wrapped
// inside an InvocationTargetException.
} catch (InvocationTargetException e) {
Throwable th = e.getTargetException();
if (th instanceof RuntimeException) {
throw (RuntimeException)th;
} else if (th instanceof Error) {
throw (Error)th;
} else {
throw new RuntimeException("Invocation target threw "+th.getClass().getName(), th);
}
}
}
/**
* Many types of errors inside of this class are passed into this method and
* simply logged at a SEVERE level. A sub class can override this method to
* display dialogs or otherwise interact with the user when such problems
* are reported.
* @param t the problem to report.
*/
protected void reportException(Throwable t) {
log.log(Level.SEVERE, t.toString(), t);
}
/**
* This class provides a Runnable which utilises
* @param