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