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 }