diff options
Diffstat (limited to 'watchmaker/swing/src/java/main/org/uncommons/swing/SwingBackgroundTask.java')
-rw-r--r-- | watchmaker/swing/src/java/main/org/uncommons/swing/SwingBackgroundTask.java | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/watchmaker/swing/src/java/main/org/uncommons/swing/SwingBackgroundTask.java b/watchmaker/swing/src/java/main/org/uncommons/swing/SwingBackgroundTask.java new file mode 100644 index 0000000..e8fc73c --- /dev/null +++ b/watchmaker/swing/src/java/main/org/uncommons/swing/SwingBackgroundTask.java @@ -0,0 +1,147 @@ +//============================================================================= +// Copyright 2006-2010 Daniel W. Dyer +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//============================================================================= +package org.uncommons.swing; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; + +/** + * A task that is executed on a background thread and then updates + * a Swing GUI. A task may only be executed once. + * @author Daniel Dyer + * @param <V> Type of result generated by the task. + */ +public abstract class SwingBackgroundTask<V> +{ + // Used to assign thread IDs to make threads easier to identify when debugging. + private static final AtomicInteger INSTANCE_COUNT = new AtomicInteger(0); + + private final CountDownLatch latch = new CountDownLatch(1); + private final int id; + + + protected SwingBackgroundTask() + { + this.id = INSTANCE_COUNT.getAndIncrement(); + } + + + /** + * Asynchronous call that begins execution of the task and returns immediately. + * The {@link #performTask()} method will be invoked on a background thread and, + * when it has completed, {@link #postProcessing(Object)} will be invoked on the + * Event Dispatch Thread (or, if there is an exception, {@link #onError(Throwable)} + * will be invoked instead - also on the EDT). + * @see #performTask() + * @see #postProcessing(Object) + * @see #onError(Throwable) + * @see #waitForCompletion() + */ + public void execute() + { + Runnable task = new Runnable() + { + public void run() + { + try + { + final V result = performTask(); + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + postProcessing(result); + latch.countDown(); + } + }); + } + // If an exception occurs performing the task, we need + // to handle it. + catch (final Throwable throwable) + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + onError(throwable); + latch.countDown(); + } + }); + } + } + }; + new Thread(task, "SwingBackgroundTask-" + id).start(); + } + + + /** + * Waits for the execution of this task to complete. If the {@link #execute()} + * method has not yet been invoked, this method will block indefinitely. + * @throws InterruptedException If the thread executing the task + * is interrupted. + */ + public void waitForCompletion() throws InterruptedException + { + latch.await(); + } + + + /** + * Performs the processing of the task and returns a result. + * Implement in sub-classes to provide the task logic. This method will + * run on a background thread and not on the Event Dispatch Thread and + * therefore should not manipulate any Swing components. + * @return The result of executing this task. + * @throws Exception The task may throw an exception, in which case + * the {@link #onError(Throwable)} method will be invoked instead of + * {@link #postProcessing(Object)}. + */ + protected abstract V performTask() throws Exception; + + + /** + * This method is invoked, on the Event Dispatch Thread, after the task + * has been executed. + * This empty default implementation should be over-ridden in sub-classes + * in order to provide GUI updates that should occur following successful + * task completion. + * @param result The result from the {@link #performTask()} method. + */ + protected void postProcessing(V result) + { + // Over-ride in sub-class. + } + + + /** + * This method is invoked, on the Event Dispatch Thread, if there is an + * exception or error executing the {@link #performTask()} method. + * This default implementation displays a message dialog with details of the + * exception. It may be over-ridden in sub-classes. + * @param throwable The exception or error that was thrown while executing + * the task. + */ + protected void onError(Throwable throwable) + { + throwable.printStackTrace(); + JOptionPane.showMessageDialog(null, + throwable.getMessage(), + throwable.getClass().getName(), + JOptionPane.ERROR_MESSAGE); + } +} |