com.meghnasoft.async.AbstractAsynchronousAction.java Source code

Java tutorial

Introduction

Here is the source code for com.meghnasoft.async.AbstractAsynchronousAction.java

Source

/*
 * Java Terrain and Stellar System Ports
 *
 * Released under the terms of the GNU General Public License
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 */

/*
 * AstractAsynchronousAction.java
 *
 * Created on 18 January 2006, 12:21
 */
package com.meghnasoft.async;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.lang.reflect.InvocationTargetException;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.SwingUtilities;

/*
 * AbstractAsynchronousAction.java
 * Copyright (C) 2003 Alamgir Farouk
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * Created on June 29, 2003
 * @author Alamgir Farouk
 *
 * Note: This class has been modified in a minor way by me for Javadoc
 * completeness, reformatting by Jalopy and changes for Stylecheck reports.
 * No functionality has been changed - MS
 *
 */

/**
 * AbstractAsynchronousAction class extends the standard AbstractAction
 * class from Java/Swing.
 * It generalizes the concept of 'actionPerformed()' to include actions
 * that may not return right away and hence are launched on a separate
 * thread.
 *
 * To use this class, a concrete class must be defined which extends this
 * abstract class, and use it in place of a Swing Action. See documentation
 * for further details.
 *
 * @author Alamgir Farouk
 * @see javax.swing.AbstractAction
 */
public abstract class AbstractAsynchronousAction extends AbstractAction {
    /** Our logger object */
    private static Log log = LogFactory.getLog(AbstractAsynchronousAction.class);

    /** Number of milliseconds in a second */
    private static final int SECOND = 1000;
    private ThreadVar threadVar;
    private Object taskOutput = new String("STILL COMPUTING");
    private Component theParentComponent;
    private String monitorMessage;
    private int popUpMonitorDelay;
    private String statusUpdate = null;
    private ActionEvent lastActionEvent;
    private Runnable asynchronousActionRunnable;

    // RequireThis OFF: log

    /**
     * This is constructor for AbstractAsynchronousAction class.
     *
     * @param name The name of the action, passed to the super class AbstractAction
     */
    public AbstractAsynchronousAction(String name) {
        this();
        putValue(Action.NAME, name);

        final Runnable doFinished = new Runnable() {
            public void run() {
                finished();
                firePropertyChange("enabled", Boolean.FALSE, Boolean.TRUE);
            }
        };

        this.asynchronousActionRunnable = new Runnable() {
            public void run() {
                try {
                    setTaskOutput(asynchronousActionPerformed(lastActionEvent));
                } catch (Exception excp) {
                    log.error("Aysynchronous action got exception", excp);
                } finally {
                    threadVar.clear();

                    try {
                        SwingUtilities.invokeAndWait(doFinished);
                    } catch (InterruptedException ex) {
                        if (log.isDebugEnabled()) {
                            log.debug("Asynchronous action got " + "interrupted exception", ex);
                        }
                    } catch (InvocationTargetException ex) {
                        if (log.isDebugEnabled()) {
                            log.debug("Asynchronous action got invocation " + "target exception", ex);
                        }
                    }
                }
            }
        };
    }

    /**
     * Constructor, private.
     *
     *
     */
    private AbstractAsynchronousAction() {
        //The key used for storing a longer description for the action, could be 
        //used for context-sensitive help.
        putValue(Action.LONG_DESCRIPTION, "Starts an asynchronous task");
        //The key used for storing the name for the action, used for a menu or button.
        //static String
        putValue(Action.NAME, "Start(long)");
        //The key used for storing a short description for the action, 
        //used for tooltip text.
        putValue(Action.SHORT_DESCRIPTION, "Start");
        //Useful constants that can be used as the storage-retreival key when 
        //setting or getting one of this object's properties (text or icon)
        putValue(Action.DEFAULT, "AsynchronousAction");
    } //default constructor

    /**
     * This is an implementation of the abstract method from the
     * Action interface. This method takes care of the threading issues
     * and is marked final to prevent overriding in sub-classes.
     * @param e An ActionEvent parameter passed in by the control
     * which is triggering the action. Action event contains inforamtion
     * about the event, for example if the 'Alt' key was held down while
     * the mouse was clicked.
     */
    public final void actionPerformed(ActionEvent e) {
        firePropertyChange("enabled", Boolean.TRUE, Boolean.FALSE);
        lastActionEvent = e;

        final Thread t = new Thread(asynchronousActionRunnable);
        threadVar = new ThreadVar(t);
        t.start();
    }

    /**
     * Get the taskOutput produced by the worker thread, or null if it
     * hasn't been constructed yet.
     *
     * @return Returns the task output or null if it hasn't been constructed
     * yet.
     */
    protected synchronized Object getTaskOutput() {
        return taskOutput;
    }

    /**
     * Set the taskOutput produced by worker thread
     *
     * @param x The object that is the task output
     */
    private synchronized void setTaskOutput(Object x) {
        taskOutput = x;
    }

    /**
     * This the method which does the long task! This
     * method is implemented in the concrete class.  The ActionEvent is
     * passed on from the actual event and is available to the implementation.
     *
     * @param e ActionEvent which contain information about actual event
     * which triggered the action. For example, for a button click, it could indicate if
     * the 'ALT' key was down during the click.
     *
     * @return The object returned by the asychronous action
     */
    public abstract Object asynchronousActionPerformed(ActionEvent e);

    /**
     * Method called when the task is completed. A callback
     * which is guaranteed to be called only after the action
     * has completed and the thread terminated.
     *
     */
    public abstract void finished();

    /**
     * Allows interruption of the task if it is waiting.
     *
     */
    public void interrupt() {
        final Thread t = threadVar.getThread();
        taskOutput = null;

        if (t != null) {
            t.interrupt();
        }
    }

    /**
     * Return the taskOutput created by the <code>asynchronousActionPerfomed</code>
     * method.Returns null if either the executing background thread or
     * the current thread was interrupted before a taskOutput was produced.
     *
     * @return the taskOutput created by the <code>asynchronousActionPerfomed</code>
     * method
     */
    public Object getTaskOutputBlocking() {
        final Thread t = threadVar.getThread();

        if (t == null) {
            return getTaskOutput();
        }

        try {
            t.join();

            return getTaskOutput();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();

            return null;
        }
    }

    /**
     * Class to maintain reference to current worker thread
     * under separate synchronization control.
     */
    private class ThreadVar {
        private Thread thread;

        ThreadVar(Thread t) {
            this.thread = t;
        }

        synchronized Thread getThread() {
            return this.thread;
        }

        synchronized void clear() {
            this.thread = null;
        }
    } //ThreadVar
}