views:

171

answers:

3

I am trying to implement a SwingWorker class within my application. Is there a way to set a length of time that after which, the SwingWorker "times out"? I was thinking that maybe throwing an OutOfTime exception that I can catch and then deal with. I'm just not sure how to implement it.

Thanks for all your help!

+2  A: 

Why not embed your task within a Runnable, drop it into a new single-threaded ExecutorService and then perform a get() on the resulting Future with an appropriate timeout. That would give you your timeout functionality, since get() will throw an exception if the job doesn't complete in time.

Brian Agnew
Also: just calling get() with a timeout would still leave the (presumably expensive) work getting done later. The task also needs to be canceled (and written in such a way that canceling it actually works)
Sbodd
You can interrupt() your work via a Future.cancel() method
Brian Agnew
(which generates a ThreadInterruptedException, I think)
Brian Agnew
Yes - mostly just added that to make sure the asker knew. (And Future.cancel() just sets the thread.interrupted() flag - you then have to actually write your code to check for it. Some but not all of the Java-defined blocking methods are interruptible.)
Sbodd
+1  A: 

The short answer is "it's hard", depending on what your requirements are. I highly recommend reading Java Concurrency In Practice.

The basic thing you could do would be to (a) make sure your SwingWorker's Runnable is interrupt-friendly, and (b) set a Timer (or use the blocking get() call Brian mentioned) to cancel your Future.

Sbodd
A: 

The inner class MySwingWorker may well do what you need:

package com.misc;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class FutureStuffGUI extends JFrame {
/**
 * Provides a variant of SwingWorker which operates with a timeout.
 * 
 * @param <T>
 */
private static abstract class MySwingWorker<T> {

    private T result;
    private Exception raised;

    /**
     * Constructor.
     * 
     * @param timeout
     * @param timeUnit
     */
    public MySwingWorker(final long timeout, final TimeUnit timeUnit) {
        result = null;
        raised = null;

        System.out.println(Thread.currentThread().getName() + " starting");
        final FutureTask<T> future = new FutureTask<T>(new Callable<T>() {
            public T call() throws Exception {
                System.out.println(Thread.currentThread().getName() + " running");
                T result = doInBackground();
                return result;
            }
        });
        System.out.println(Thread.currentThread().getName() + " future: " + future);
        final Thread runner = new Thread(null, future, "FutureThread");
        Thread watcher = new Thread(null, new Runnable() {

            @Override
            public void run() {
                runner.start();
                try {
                    result = future.get(timeout, timeUnit);
                } catch (Exception ex) {
                    raised = ex;
                }
                SwingUtilities.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        assert SwingUtilities.isEventDispatchThread();
                        done();
                    }
                });
            }
        }, "WatcherThread");
        watcher.start();
    }

    /**
     * Implement this method as the long-running background task.
     * 
     * @return
     * @throws Exception
     */
    abstract protected T doInBackground() throws Exception;

    /**
     * This method is invoked from the UI Event Dispatch Thread on completion or timeout.
     */
    abstract protected void done();

    /**
     * This method should be invoked by the implementation of done() to retrieve
     * the result.
     * 
     * @return
     * @throws Exception
     */
    protected T get() throws Exception {
        assert SwingUtilities.isEventDispatchThread();
        if (raised != null) {
            throw raised;
        } else {
            return result;
        }
    }
}

public FutureStuffGUI() {
    super("Hello");
    init_components();
}

private void init_components() {
    JPanel panel = new JPanel();
    JButton button = new JButton("Press");
    panel.add(button);
    add(panel);
    pack();

    button.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            new MySwingWorker<String>(5, TimeUnit.SECONDS) {

                @Override
                protected String doInBackground() throws InterruptedException {
                    assert !SwingUtilities.isEventDispatchThread();
                    System.out.println(Thread.currentThread().getName() + " doInBackground");
//                        if (true) { throw new RuntimeException("Blow up"); }
                    Thread.sleep(6 * 1000);
                    return "Hello world!";
                }

                @Override
                protected void done() {
                    assert SwingUtilities.isEventDispatchThread();
                    String result;
                    try {
                        result = get();
                        System.out.println(Thread.currentThread().getName() + " done; result: " + result);
                    } catch (Exception ex) {
                        System.out.println(Thread.currentThread().getName() + " done; errored:");
                        ex.printStackTrace();
                    }
                }
            };
        };
    });
}

public static void main(String[] args) {
    FutureStuffGUI ui = new FutureStuffGUI();
    ui.setVisible(true);
}

}

pythonologist