// $Header: /home/deegree/jail/deegreerepository/deegree/src/org/deegree/framework/concurrent/Executor.java,v 1.8 2006/10/17 13:13:13 schmitz Exp $ /*---------------- FILE HEADER ------------------------------------------ This file is part of deegree. Copyright (C) 2001-2006 by: EXSE, Department of Geography, University of Bonn http://www.giub.uni-bonn.de/deegree/ lat/lon GmbH http://www.lat-lon.de 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 Contact: Andreas Poth lat/lon GmbH Aennchenstr. 19 53177 Bonn Germany E-Mail: poth@lat-lon.de Prof. Dr. Klaus Greve Department of Geography University of Bonn Meckenheimer Allee 166 53115 Bonn Germany E-Mail: greve@giub.uni-bonn.de ---------------------------------------------------------------------------*/ package org.deegree.framework.concurrent; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** * The <code>Executor</code> is deegree's central place to: * <ul> * <li>Perform a task asynchronously (in an independent thread) optionally with a maximum execution * time.</li> * <li>Perform a task synchronously with a maximum execution time.</li> * <li>Perform several task synchronously (but in parallel threads) with a maximum execution time.</li> * </ul> * <p> * The <code>Executor</code> class is realized as a singleton and uses a cached thread pool * internally to minimize overhead for acquiring the necessary {@link Thread} instances and to * manage the number of concurrent threads. * </p> * * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a> * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a> * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider</a> * @author last edited by: $Author: schmitz $ * * @version $Revision: 1.8 $, $Date: 2006/10/17 13:13:13 $ * @see java.util.concurrent.ExecutorService * @see org.deegree.framework.concurrent.ExecutionFinishedListener */ public class Executor { private static Executor exec; private ExecutorService execService; /** * Private constructor required for singleton pattern. */ private Executor() { this.execService = Executors.newCachedThreadPool(); } /** * Returns the only instance of this class (singleton pattern). * * @return the only instance of this class */ public synchronized static Executor getInstance() { if ( exec == null ) { exec = new Executor(); } return exec; } /** * Performs a task asynchronously (in an independent thread) without any time limit. * * @param <T> * type of return value * @param task * task to be performed (specified in the {@link Callable#call()} method) * @param finishedListener * notified when the method call finishes (succesfully or abnormally), may be null */ public <T> void performAsynchronously( Callable<T> task, ExecutionFinishedListener<T> finishedListener ) { AsyncPerformer runner = new AsyncPerformer<T>( task, finishedListener ); this.execService.execute( runner ); } /** * Performs a task asynchronously (in an independent thread) with a given time limit. * * @param <T> * type of return value * @param task * task to be performed (specified in the {@link Callable#call()}} method) * @param finishedListener * notified when the method call finishes (succesfully or abnormally), may be null * @param timeout * maximum time allowed for execution in milliseconds */ public <T> void performAsynchronously( Callable<T> task, ExecutionFinishedListener<T> finishedListener, long timeout ) { AsyncPerformer runner = new AsyncPerformer<T>( task, finishedListener, timeout ); this.execService.execute( runner ); } /** * Performs a task synchronously with a given timeout. * * @param <T> * type of return value * @param task * tasks to be performed (specified in the {@link Callable#call()} method) * @param timeout * maximum time allowed for execution in milliseconds * @return result value of the called method * @throws CancellationException * if the execution time exceeds the specified timeout / thread has been cancelled * @throws InterruptedException * if interrupted while waiting, in which case unfinished tasks are cancelled * @throws Throwable * if the tasks throws an exception itself */ public <T> T performSynchronously( Callable<T> task, long timeout ) throws CancellationException, InterruptedException, Throwable { T result; List<Callable<T>> tasks = new ArrayList<Callable<T>>( 1 ); tasks.add( task ); try { List<Future<T>> futures = this.execService.invokeAll( tasks, timeout, TimeUnit.MILLISECONDS ); Future<T> future = futures.get( 0 ); result = future.get(); } catch ( ExecutionException e ) { throw ( e.getCause() ); } return result; } /** * Performs several tasks synchronously in parallel threads. * <p> * This method does not return before all tasks are finished (successfully or abnormally). For * each given {@link Callable}, an independent thread is used. For each task, an * {@link ExecutionFinishedEvent} is generated and returned. * @param <T> the result type of the callables * * @param tasks * tasks to be performed (specified in the {@link Callable#call()} methods) * @return ExecutionFinishedEvents for all tasks * @throws InterruptedException * if the current thread was interrupted while waiting */ public <T> List<ExecutionFinishedEvent<T>> performSynchronously( List<Callable<T>> tasks ) throws InterruptedException { List<ExecutionFinishedEvent<T>> results = new ArrayList<ExecutionFinishedEvent<T>>( tasks.size() ); List<Future<T>> futures = this.execService.invokeAll( tasks ); for ( int i = 0; i < tasks.size(); i++ ) { ExecutionFinishedEvent<T> finishedEvent = null; Callable<T> task = tasks.get( i ); Future<T> future = futures.get( i ); try { T result = future.get(); finishedEvent = new ExecutionFinishedEvent<T>( task, result ); } catch ( ExecutionException e ) { finishedEvent = new ExecutionFinishedEvent<T>( e.getCause(), task ); } catch ( CancellationException e ) { finishedEvent = new ExecutionFinishedEvent<T>( e, task ); } results.add( finishedEvent ); } return results; } /** * Performs several tasks synchronously with a given timeout in parallel threads. * <p> * This method does not return before all tasks are finished (successfully or abnormally). For * each given {@link Callable}, an independent thread is used. For each task, an * {@link ExecutionFinishedEvent} is generated and returned. * @param <T> the result type of the tasks * * @param tasks * tasks to be performed (specified in the {@link Callable#call()} methods) * @param timeout * maximum time allowed for execution (in milliseconds) * @return ExecutionFinishedEvents for all tasks * @throws InterruptedException * if the current thread was interrupted while waiting */ public <T> List<ExecutionFinishedEvent<T>> performSynchronously( List<Callable<T>> tasks, long timeout ) throws InterruptedException { List<ExecutionFinishedEvent<T>> results = new ArrayList<ExecutionFinishedEvent<T>>( tasks.size() ); List<Future<T>> futures = this.execService.invokeAll( tasks, timeout, TimeUnit.MILLISECONDS ); for ( int i = 0; i < tasks.size(); i++ ) { ExecutionFinishedEvent<T> finishedEvent = null; Callable<T> task = tasks.get( i ); Future<T> future = futures.get( i ); try { T result = future.get(); finishedEvent = new ExecutionFinishedEvent<T>( task, result ); } catch ( ExecutionException e ) { finishedEvent = new ExecutionFinishedEvent<T>( e.getCause(), task ); } catch ( CancellationException e ) { finishedEvent = new ExecutionFinishedEvent<T>( e, task ); } results.add( finishedEvent ); } return results; } // /////////////////////////////////////////////////////////////////////////// // inner classes // // /////////////////////////////////////////////////////////////////////////// /** * Inner class for performing task asynchronously. * * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a> * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a> * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider</a> * @author last edited by: $Author: schmitz $ * @version $Revision: 1.8 $ */ private class AsyncPerformer<T> implements Runnable { private Callable<T> task; private ExecutionFinishedListener<T> finishedListener; private long timeout = -1; private AsyncPerformer( Callable<T> task, ExecutionFinishedListener<T> finishedListener ) { this.task = task; this.finishedListener = finishedListener; } private AsyncPerformer( Callable<T> task, ExecutionFinishedListener<T> finishedListener, long timeout ) { this.task = task; this.finishedListener = finishedListener; this.timeout = timeout; } /** * Performs the task using {@link Executor#performSynchronously(Callable, long)}. * * @see java.lang.Runnable#run() */ public void run() { ExecutionFinishedEvent<T> finishedEvent = null; try { T result = null; if ( this.timeout < 0 ) { result = this.task.call(); } else { result = Executor.getInstance().performSynchronously( task, timeout ); } finishedEvent = new ExecutionFinishedEvent<T>( this.task, result ); } catch ( Throwable t ) { finishedEvent = new ExecutionFinishedEvent<T>( t, this.task ); } if ( this.finishedListener != null ) { this.finishedListener.executionFinished( finishedEvent ); } } } } /* ************************************************************************************************* * Changes to this class. What the people have been up to: * $Log: Executor.java,v $ * Revision 1.8 2006/10/17 13:13:13 schmitz * Added the last missing type parameters. * * Revision 1.7 2006/10/08 18:29:05 poth * bug fix - method public <T> T performSynchronously( Callable<T> task, long timeout ) now considers max execution time as it should * * Revision 1.6 2006/08/10 13:29:30 mschneider * #performSynchronously( List<Callable<Object>>) and #performSynchronously( List<Callable<Object>>, long) return a List of ExecutionFinishedEvents now. Timeout or cancellation is now indicated by a CancellationException everywhere (for consistency and ease of use). * * Revision 1.5 2006/08/08 10:03:36 mschneider * Fixed comment footer. * * Revision 1.4 2006/08/08 09:56:44 mschneider * Added generics for type safety - constrained the return type of tasks. * * Revision 1.3 2006/08/07 19:50:06 poth * bug fix - -> performSynchronously( Callable task, long timeout ) * * Revision 1.2 2006/08/07 13:51:32 mschneider * Removed usage of Reflections. Added timeout and failure handling. * * Revision 1.1 2006/07/29 08:50:00 poth initial check in **************************************************************************************************/