    001    // $HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/framework/concurrent/Executor.java $
    002    /*----------------------------------------------------------------------------
    003     This file is part of deegree, http://deegree.org/
    004     Copyright (C) 2001-2009 by:
    005       Department of Geography, University of Bonn
    006     and
    007       lat/lon GmbH
    008
    009     This library is free software; you can redistribute it and/or modify it under
    010     the terms of the GNU Lesser General Public License as published by the Free
    011     Software Foundation; either version 2.1 of the License, or (at your option)
    012     any later version.
    013     This library is distributed in the hope that it will be useful, but WITHOUT
    014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
    016     details.
    017     You should have received a copy of the GNU Lesser General Public License
    018     along with this library; if not, write to the Free Software Foundation, Inc.,
    019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    020
    021     Contact information:
    022
    023     lat/lon GmbH
    024     Aennchenstr. 19, 53177 Bonn
    025     Germany
    026     http://lat-lon.de/
    027
    028     Department of Geography, University of Bonn
    029     Prof. Dr. Klaus Greve
    030     Postfach 1147, 53001 Bonn
    031     Germany
    032     http://www.geographie.uni-bonn.de/deegree/
    033
    034     e-mail: info@deegree.org
    035    ----------------------------------------------------------------------------*/
    036    package org.deegree.framework.concurrent;
    037
    038    import java.util.ArrayList;
    039    import java.util.List;
    040    import java.util.concurrent.Callable;
    041    import java.util.concurrent.CancellationException;
    042    import java.util.concurrent.ExecutionException;
    043    import java.util.concurrent.ExecutorService;
    044    import java.util.concurrent.Executors;
    045    import java.util.concurrent.Future;
    046    import java.util.concurrent.TimeUnit;
    047
    048    /**
    049     * The <code>Executor</code> is deegree's central place to:
    050     * <ul>
    051     * <li>Perform a task asynchronously (in an independent thread) optionally with a maximum execution
    052     * time.</li>
    053     * <li>Perform a task synchronously with a maximum execution time.</li>
    054     * <li>Perform several task synchronously (but in parallel threads) with a maximum execution time.</li>
    055     * </ul>
    056     * <p>
    057     * The <code>Executor</code> class is realized as a singleton and uses a cached thread pool
    058     * internally to minimize overhead for acquiring the necessary {@link Thread} instances and to
    059     * manage the number of concurrent threads.
    060     * </p>
    061     *
    062     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
    063     * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
    064     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
    065     * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider</a>
    066     * @author last edited by: $Author: mschneider $
    067     *
    068     * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
    069     * @see java.util.concurrent.ExecutorService
    070     * @see org.deegree.framework.concurrent.ExecutionFinishedListener
    071     */
    072    public class Executor {
    073
    074        private static Executor exec;
    075
    076        private ExecutorService execService;
    077
    078        /**
    079         * Private constructor required for singleton pattern.
    080         */
    081        private Executor() {
    082            this.execService = Executors.newCachedThreadPool();
    083        }
    084
    085        /**
    086         * Returns the only instance of this class (singleton pattern).
    087         *
    088         * @return the only instance of this class
    089         */
    090        public synchronized static Executor getInstance() {
    091
    092            if ( exec == null ) {
    093                exec = new Executor();
    094            }
    095            return exec;
    096        }
    097
    098        /**
    099         * Performs a task asynchronously (in an independent thread) without any time limit.
    100         *
    101         * @param <T>
    102         *            type of return value
    103         * @param task
    104         *            task to be performed (specified in the {@link Callable#call()} method)
    105         * @param finishedListener
    106         *            notified when the method call finishes (succesfully or abnormally), may be null
    107         */
    108        public <T> void performAsynchronously( Callable<T> task, ExecutionFinishedListener<T> finishedListener ) {
    109
    110            AsyncPerformer<T> runner = new AsyncPerformer<T>( task, finishedListener );
    111            this.execService.execute( runner );
    112        }
    113
    114        /**
    115         * Performs a task asynchronously (in an independent thread) with a given time limit.
    116         *
    117         * @param <T>
    118         *            type of return value
    119         * @param task
    120         *            task to be performed (specified in the {@link Callable#call()}} method)
    121         * @param finishedListener
    122         *            notified when the method call finishes (succesfully or abnormally), may be null
    123         * @param timeout
    124         *            maximum time allowed for execution in milliseconds
    125         */
    126        public <T> void performAsynchronously( Callable<T> task, ExecutionFinishedListener<T> finishedListener, long timeout ) {
    127
    128            AsyncPerformer<T> runner = new AsyncPerformer<T>( task, finishedListener, timeout );
    129            this.execService.execute( runner );
    130        }
    131
    132        /**
    133         * Performs a task synchronously with a given timeout.
    134         *
    135         * @param <T>
    136         *            type of return value
    137         * @param task
    138         *            tasks to be performed (specified in the {@link Callable#call()} method)
    139         * @param timeout
    140         *            maximum time allowed for execution in milliseconds
    141         * @return result value of the called method
    142         * @throws CancellationException
    143         *             if the execution time exceeds the specified timeout / thread has been cancelled
    144         * @throws InterruptedException
    145         *             if interrupted while waiting, in which case unfinished tasks are cancelled
    146         * @throws Throwable
    147         *             if the tasks throws an exception itself
    148         */
    149        public <T> T performSynchronously( Callable<T> task, long timeout )
    150                                throws CancellationException, InterruptedException, Throwable {
    151
    152            T result;
    153            List<Callable<T>> tasks = new ArrayList<Callable<T>>( 1 );
    154            tasks.add( task );
    155
    156            try {
    157                List<Future<T>> futures = this.execService.invokeAll( tasks, timeout, TimeUnit.MILLISECONDS );
    158                Future<T> future = futures.get( 0 );
    159                result = future.get();
    160            } catch ( ExecutionException e ) {
    161                throw ( e.getCause() );
    162            }
    163            return result;
    164        }
    165
    166        /**
    167         * Performs several tasks synchronously in parallel threads.
    168         * <p>
    169         * This method does not return before all tasks are finished (successfully or abnormally). For
    170         * each given {@link Callable}, an independent thread is used. For each task, an
    171         * {@link ExecutionFinishedEvent} is generated and returned.
    172         *
    173         * @param <T>
    174         *            the result type of the callables
    175         *
    176         * @param tasks
    177         *            tasks to be performed (specified in the {@link Callable#call()} methods)
    178         * @return ExecutionFinishedEvents for all tasks
    179         * @throws InterruptedException
    180         *             if the current thread was interrupted while waiting
    181         */
    182        public <T> List<ExecutionFinishedEvent<T>> performSynchronously( List<Callable<T>> tasks )
    183                                throws InterruptedException {
    184
    185            List<ExecutionFinishedEvent<T>> results = new ArrayList<ExecutionFinishedEvent<T>>( tasks.size() );
    186            List<Future<T>> futures = this.execService.invokeAll( tasks );
    187
    188            for ( int i = 0; i < tasks.size(); i++ ) {
    189
    190                ExecutionFinishedEvent<T> finishedEvent = null;
    191                Callable<T> task = tasks.get( i );
    192                Future<T> future = futures.get( i );
    193
    194                try {
    195                    T result = future.get();
    196                    finishedEvent = new ExecutionFinishedEvent<T>( task, result );
    197                } catch ( ExecutionException e ) {
    198                    finishedEvent = new ExecutionFinishedEvent<T>( e.getCause(), task );
    199                } catch ( CancellationException e ) {
    200                    finishedEvent = new ExecutionFinishedEvent<T>( e, task );
    201                }
    202                results.add( finishedEvent );
    203            }
    204            return results;
    205        }
    206
    207        /**
    208         * Performs several tasks synchronously with a given timeout in parallel threads.
    209         * <p>
    210         * This method does not return before all tasks are finished (successfully or abnormally). For
    211         * each given {@link Callable}, an independent thread is used. For each task, an
    212         * {@link ExecutionFinishedEvent} is generated and returned.
    213         *
    214         * @param <T>
    215         *            the result type of the tasks
    216         *
    217         * @param tasks
    218         *            tasks to be performed (specified in the {@link Callable#call()} methods)
    219         * @param timeout
    220         *            maximum time allowed for execution (in milliseconds)
    221         * @return ExecutionFinishedEvents for all tasks
    222         * @throws InterruptedException
    223         *             if the current thread was interrupted while waiting
    224         */
    225        public <T> List<ExecutionFinishedEvent<T>> performSynchronously( List<Callable<T>> tasks, long timeout )
    226                                throws InterruptedException {
    227
    228            List<ExecutionFinishedEvent<T>> results = new ArrayList<ExecutionFinishedEvent<T>>( tasks.size() );
    229            List<Future<T>> futures = this.execService.invokeAll( tasks, timeout, TimeUnit.MILLISECONDS );
    230
    231            for ( int i = 0; i < tasks.size(); i++ ) {
    232
    233                ExecutionFinishedEvent<T> finishedEvent = null;
    234                Callable<T> task = tasks.get( i );
    235                Future<T> future = futures.get( i );
    236
    237                try {
    238                    T result = future.get();
    239                    finishedEvent = new ExecutionFinishedEvent<T>( task, result );
    240                } catch ( ExecutionException e ) {
    241                    finishedEvent = new ExecutionFinishedEvent<T>( e.getCause(), task );
    242                } catch ( CancellationException e ) {
    243                    finishedEvent = new ExecutionFinishedEvent<T>( e, task );
    244                }
    245                results.add( finishedEvent );
    246            }
    247            return results;
    248        }
    249
    250        // ///////////////////////////////////////////////////////////////////////////
    251        // inner classes //
    252        // ///////////////////////////////////////////////////////////////////////////
    253
    254        /**
    255         * Inner class for performing task asynchronously.
    256         *
    257         * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
    258         * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
    259         * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
    260         * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider</a>
    261         * @author last edited by: $Author: mschneider $
    262         * @version $Revision: 18195 $
    263         */
    264        private class AsyncPerformer<T> implements Runnable {
    265
    266            private Callable<T> task;
    267
    268            private ExecutionFinishedListener<T> finishedListener;
    269
    270            private long timeout = -1;
    271
    272            AsyncPerformer( Callable<T> task, ExecutionFinishedListener<T> finishedListener ) {
    273                this.task = task;
    274                this.finishedListener = finishedListener;
    275            }
    276
    277            AsyncPerformer( Callable<T> task, ExecutionFinishedListener<T> finishedListener, long timeout ) {
    278                this.task = task;
    279                this.finishedListener = finishedListener;
    280                this.timeout = timeout;
    281            }
    282
    283            /**
    284             * Performs the task using {@link Executor#performSynchronously(Callable, long)}.
    285             *
    286             * @see java.lang.Runnable#run()
    287             */
    288            public void run() {
    289                ExecutionFinishedEvent<T> finishedEvent = null;
    290                try {
    291                    T result = null;
    292                    if ( this.timeout < 0 ) {
    293                        result = this.task.call();
    294                    } else {
    295                        result = Executor.getInstance().performSynchronously( task, timeout );
    296                    }
    297                    finishedEvent = new ExecutionFinishedEvent<T>( this.task, result );
    298                } catch ( Throwable t ) {
    299                    finishedEvent = new ExecutionFinishedEvent<T>( t, this.task );
    300                }
    301                if ( this.finishedListener != null ) {
    302                    this.finishedListener.executionFinished( finishedEvent );
    303                }
    304            }
    305        }
    306
    307    }