summaryrefslogtreecommitdiffstats
path: root/watchmaker/swing/src/java/main/org/uncommons/swing/SwingBackgroundTask.java
diff options
context:
space:
mode:
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.java147
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);
+ }
+}