001 // $HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_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 }