001    /*
002     * Copyright 2002-2004 the original author or authors.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     * 
008     * http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     */
015    package org.springframework.richclient.util;
016    
017    import java.lang.reflect.InvocationTargetException;
018    
019    import javax.swing.SwingUtilities;
020    
021    import EDU.oswego.cs.dl.util.concurrent.Callable;
022    import EDU.oswego.cs.dl.util.concurrent.FutureResult;
023    import EDU.oswego.cs.dl.util.concurrent.TimedCallable;
024    
025    /**
026     * An abstract class that you subclass to perform GUI-related work in a
027     * dedicated thread.
028     * <p>
029     * This class was adapted from the SwingWorker class presented in "Using a Swing
030     * Worker Thread" in the Swing Connection archives
031     * http://java.sun.com/products/jfc/tsc/archive/archive.html
032     * <p>
033     * This version of SwingWorker extends FutureResult and implements Runnable.
034     * Timeouts are supported.
035     */
036    public abstract class SwingWorker extends FutureResult implements Runnable {
037        /** Worker thread. */
038        protected Thread thread;
039    
040        /**
041         * Computes the value to be returned by the <code>get</code> method.
042         */
043        protected abstract Object construct() throws Exception;
044    
045        /**
046         * Called on the event dispatching thread (not on the worker thread) after
047         * the <code>construct</code> method has returned.
048         */
049        protected void finished() {
050        }
051    
052        protected Object getFinishedResult() {
053            try {
054                return get();
055            }
056            catch (InterruptedException e) {
057                throw new RuntimeException(
058                        "Interrupted exception should not have occured; are you calling from finished()?");
059            }
060            catch (InvocationTargetException e) {
061                return null;
062            }
063        }
064    
065        protected Throwable getTargetException() {
066            InvocationTargetException e = getException();
067            if (e != null) {
068                return e.getTargetException();
069            }
070            return e;
071        }
072    
073        /**
074         * Override to return a timeout period, in milliseconds. The timeout is the
075         * maximum time to wait for the worker to complete. There is no time limit
076         * if the timeout is <code>0</code> (default).
077         */
078        public long getTimeout() {
079            return 0;
080        }
081    
082        /**
083         * Calls the <code>construct</code> method to compute the result, and then
084         * invokes the <code>finished</code> method on the event dispatch thread.
085         */
086        public void run() {
087            Callable function = new Callable() {
088                public Object call() throws Exception {
089                    return construct();
090                }
091            };
092            Runnable doFinished = new Runnable() {
093                public void run() {
094                    finished();
095                }
096            };
097            /* Convert to TimedCallable if timeout is specified. */
098            long msecs = getTimeout();
099            if (msecs != 0) {
100                function = new TimedCallable(function, msecs);
101            }
102            setter(function).run();
103            SwingUtilities.invokeLater(doFinished);
104        }
105    
106        /**
107         * Starts the worker thread.
108         */
109        public synchronized void start() {
110            if (thread == null) {
111                thread = new Thread(this);
112            }
113            thread.start();
114        }
115    
116        /**
117         * Stops the worker and sets the exception to InterruptedException.
118         */
119        public synchronized void interrupt() {
120            if (thread != null) {
121                /*
122                 * Try-catch is workaround for JDK1.2.1 applet security bug.
123                 * JDK1.2.1 overzealously throws a security exception if an applet
124                 * interrupts a thread that is no longer alive.
125                 */
126                try {
127                    thread.interrupt();
128                }
129                catch (Exception ex) {
130                }
131            }
132            setException(new InterruptedException());
133        }
134    
135        /**
136         * Clears the worker thread variable and the FutureResult state, allowing
137         * this SwingWorker to be reused. This is not particularly recommended and
138         * must be done only when you know that the worker thread is finished and
139         * that no other object is depending on the properties of this SwingWorker.
140         */
141        public synchronized void clear() {
142            super.clear();
143            thread = null;
144        }
145    }