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 }