/* ALMA - Atacama Large Millimiter Array * Copyright (c) European Southern Observatory, 2012 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package alma.acs.gui.util.threadsupport; import java.lang.reflect.InvocationTargetException; import java.util.List; import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; /** * <code>EDTExecutor</code> executes tasks in the swing event dispatcher thread (EDT). * <P> * <code>EDTExecutor</code> is a helper that allows to submit tasks to the EDT like * {@link SwingUtilities#invokeLater(Runnable)} does. The advantage is that it handles the case * of executing the code from inside or outside of the EDT in a transparent way. * <BR> * In the same way, <code>EDTExecutor</code> allows to run commands synchronously inside the EDT * in the same way {@link SwingUtilities#invokeAndWait(Runnable)} does. * <P> * {@link SwingWorker} has something in common with <code>EDTExecutor</code> but it is too heavy * and for most applications we don't need such a complexity. * <P> * We don't really need <code>EDTExecutor</code> to be a {@link ExecutorService} because it * is very easy but in future we might want to extend by adding more features * (without reaching the same completeness of {@link SwingWorker} of course). * <P> * Life cycle methods are kept easy because this class does not manage the life cycle of the EDT. * In this perspective, and to avoid useless blocking due to synchronization, <code>EDTExecutor</code> * does not keep track of tasks scheduled to be run and not yet executed (@see {@link #shutdownNow()}. * * @author acaproni * @since ACS 10.2 */ public class EDTExecutor extends AbstractExecutorService { /** * The singleton */ private static final EDTExecutor executor = new EDTExecutor(); /** * A flag set is the executor has been shut down. */ protected volatile boolean hasBeenShutdown=false; /** * The constructor is private because it is a singleton. */ private EDTExecutor() {} /** * * @return The singleton */ public static EDTExecutor instance() { return executor; } /** * Shuts down the Executor (and not the swing EDT): * <code>EDTExecutor</code> will not submit other tasks to the EDT. * * */ @Override public void shutdown() { hasBeenShutdown=true; } /** * Shuts down the Executor (and not the swing EDT): * <code>EDTExecutor</code> will not submit other tasks to the EDT. * * @return <code>null</code> because this executor does not have a queue of tasks to submit. */ @Override public List<Runnable> shutdownNow() { hasBeenShutdown=true; return null; } @Override public boolean isShutdown() { return hasBeenShutdown; } @Override public synchronized boolean isTerminated() { return hasBeenShutdown; } @Override public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { if (isTerminated()) { return true; } // Wait until the timeout elapses (or a InterruptedExceprtion occurs) // and check again boolean ret; try { unit.sleep(timeout); } catch (InterruptedException ie) { } finally { ret=isTerminated(); } return ret; } /** * Executes the passed command in the EDT. * <P> * It can be called from inside or outside of the swing event dispatcher thread. * * @param command The command to execute in the EDT */ @Override public void execute(Runnable command) { if (command==null) { // Nothing to run! return; } if (hasBeenShutdown) { // Do not accept further commands return; } if (SwingUtilities.isEventDispatchThread()) { command.run(); } else { SwingUtilities.invokeLater(command); } } /** * Execute the passed command in the EDT synchronously. * <P> * It can be called from inside or outside of the swing event dispatcher thread. * * @param command The command to execute in the EDT * * @throws InvocationTargetException if an exception is thrown while running the <code>command</code> * @throws InterruptedException if we're interrupted while waiting for the event dispatching thread to finish excecuting <code>command.run()</code> */ public void executeSync(Runnable command) throws InvocationTargetException, InterruptedException { if (command==null) { // Nothing to run! return; } if (hasBeenShutdown) { // Do not accept further commands return; } if (SwingUtilities.isEventDispatchThread()) { command.run(); } else { SwingUtilities.invokeAndWait(command); } } }