1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.lang3.concurrent; 18 19 import java.util.concurrent.Callable; 20 import java.util.concurrent.CancellationException; 21 import java.util.concurrent.ExecutionException; 22 import java.util.concurrent.ExecutorService; 23 import java.util.concurrent.Executors; 24 import java.util.concurrent.Future; 25 26 import org.apache.commons.lang3.function.FailableConsumer; 27 import org.apache.commons.lang3.function.FailableSupplier; 28 29 /** 30 * A class that allows complex initialization operations in a background task. 31 * 32 * <p> 33 * Applications often have to do some expensive initialization steps when they 34 * are started, e.g. constructing a connection to a database, reading a 35 * configuration file, etc. Doing these things in parallel can enhance 36 * performance as the CPU load can be improved. However, when access to the 37 * resources initialized in a background thread is actually required, 38 * synchronization has to be performed to ensure that their initialization is 39 * complete. 40 * </p> 41 * <p> 42 * This abstract base class provides support for this use case. A concrete 43 * subclass must implement the {@link #initialize()} method. Here an arbitrary 44 * initialization can be implemented, and a result object can be returned. With 45 * this method in place the basic usage of this class is as follows (where 46 * {@code MyBackgroundInitializer} is a concrete subclass): 47 * </p> 48 * 49 * <pre> 50 * MyBackgroundInitializer initializer = new MyBackgroundInitializer(); 51 * initializer.start(); 52 * // Now do some other things. Initialization runs in a parallel thread 53 * ... 54 * // Wait for the end of initialization and access the result object 55 * Object result = initializer.get(); 56 * </pre> 57 * 58 * <p> 59 * After the construction of a {@link BackgroundInitializer} object its 60 * {@link #start()} method has to be called. This starts the background 61 * processing. The application can now continue to do other things. When it 62 * needs access to the object produced by the {@link BackgroundInitializer} it 63 * calls its {@link #get()} method. If initialization is already complete, 64 * {@link #get()} returns the result object immediately. Otherwise it blocks 65 * until the result object is fully constructed. 66 * </p> 67 * <p> 68 * {@link BackgroundInitializer} is a thin wrapper around a {@link Future} 69 * object and uses an {@link ExecutorService} for running the background 70 * initialization task. It is possible to pass in an {@link ExecutorService} at 71 * construction time or set one using {@code setExternalExecutor()} before 72 * {@code start()} was called. Then this object is used to spawn the background 73 * task. If no {@link ExecutorService} has been provided, {@code 74 * BackgroundInitializer} creates a temporary {@link ExecutorService} and 75 * destroys it when initialization is complete. 76 * </p> 77 * <p> 78 * The methods provided by {@link BackgroundInitializer} provide for minimal 79 * interaction with the wrapped {@link Future} object. It is also possible to 80 * obtain the {@link Future} object directly. Then the enhanced functionality 81 * offered by {@link Future} can be used, e.g. to check whether the background 82 * operation is complete or to cancel the operation. 83 * </p> 84 * 85 * @since 3.0 86 * @param <T> the type of the object managed by this initializer class 87 */ 88 public class BackgroundInitializer<T> extends AbstractConcurrentInitializer<T, Exception> { 89 90 /** 91 * Builds a new instance. 92 * 93 * @param <T> the type of the object managed by the initializer. 94 * @param <I> the type of the initializer managed by this builder. 95 * @since 3.14.0 96 */ 97 public static class Builder<I extends BackgroundInitializer<T>, T> extends AbstractBuilder<I, T, Builder<I, T>, Exception> { 98 99 /** 100 * The external executor service for executing tasks. null is a permitted value. 101 */ 102 private ExecutorService externalExecutor; 103 104 /** 105 * Constructs a new instance. 106 */ 107 public Builder() { 108 // empty 109 } 110 111 @SuppressWarnings("unchecked") 112 @Override 113 public I get() { 114 return (I) new BackgroundInitializer(getInitializer(), getCloser(), externalExecutor); 115 } 116 117 /** 118 * Sets the external executor service for executing tasks. null is a permitted value. 119 * 120 * @see org.apache.commons.lang3.concurrent.BackgroundInitializer#setExternalExecutor(ExecutorService) 121 * 122 * @param externalExecutor the {@link ExecutorService} to be used. 123 * @return {@code this} instance. 124 */ 125 public Builder<I, T> setExternalExecutor(final ExecutorService externalExecutor) { 126 this.externalExecutor = externalExecutor; 127 return asThis(); 128 } 129 130 } 131 132 private final class InitializationTask implements Callable<T> { 133 /** Stores the executor service to be destroyed at the end. */ 134 private final ExecutorService execFinally; 135 136 /** 137 * Creates a new instance of {@link InitializationTask} and initializes 138 * it with the {@link ExecutorService} to be destroyed at the end. 139 * 140 * @param exec the {@link ExecutorService} 141 */ 142 InitializationTask(final ExecutorService exec) { 143 execFinally = exec; 144 } 145 146 /** 147 * Initiates initialization and returns the result. 148 * 149 * @return the result object 150 * @throws Exception if an error occurs 151 */ 152 @Override 153 public T call() throws Exception { 154 try { 155 return initialize(); 156 } finally { 157 if (execFinally != null) { 158 execFinally.shutdown(); 159 } 160 } 161 } 162 } 163 164 /** 165 * Creates a new builder. 166 * 167 * @param <T> the type of object to build. 168 * @return a new builder. 169 * @since 3.14.0 170 */ 171 public static <T> Builder<BackgroundInitializer<T>, T> builder() { 172 return new Builder<>(); 173 } 174 175 /** The external executor service for executing tasks. */ 176 private ExecutorService externalExecutor; // @GuardedBy("this") 177 178 /** A reference to the executor service that is actually used. */ 179 private ExecutorService executor; // @GuardedBy("this") 180 181 /** Stores the handle to the background task. */ 182 private Future<T> future; // @GuardedBy("this") 183 184 /** 185 * Creates a new instance of {@link BackgroundInitializer}. No external 186 * {@link ExecutorService} is used. 187 */ 188 protected BackgroundInitializer() { 189 this(null); 190 } 191 192 /** 193 * Creates a new instance of {@link BackgroundInitializer} and initializes 194 * it with the given {@link ExecutorService}. If the {@link ExecutorService} 195 * is not null, the background task for initializing this object will be 196 * scheduled at this service. Otherwise a new temporary {@code 197 * ExecutorService} is created. 198 * 199 * @param exec an external {@link ExecutorService} to be used for task 200 * execution 201 */ 202 protected BackgroundInitializer(final ExecutorService exec) { 203 setExternalExecutor(exec); 204 } 205 206 /** 207 * Constructs a new instance. 208 * 209 * @param initializer the initializer supplier called by {@link #initialize()}. 210 * @param closer the closer consumer called by {@link #close()}. 211 * @param exec the {@link ExecutorService} to be used @see #setExternalExecutor(ExecutorService) 212 */ 213 private BackgroundInitializer(final FailableSupplier<T, ConcurrentException> initializer, final FailableConsumer<T, ConcurrentException> closer, final ExecutorService exec) { 214 super(initializer, closer); 215 setExternalExecutor(exec); 216 } 217 218 /** 219 * Creates the {@link ExecutorService} to be used. This method is called if 220 * no {@link ExecutorService} was provided at construction time. 221 * 222 * @return the {@link ExecutorService} to be used 223 */ 224 private ExecutorService createExecutor() { 225 return Executors.newFixedThreadPool(getTaskCount()); 226 } 227 228 /** 229 * Creates a task for the background initialization. The {@link Callable} 230 * object returned by this method is passed to the {@link ExecutorService}. 231 * This implementation returns a task that invokes the {@link #initialize()} 232 * method. If a temporary {@link ExecutorService} is used, it is destroyed 233 * at the end of the task. 234 * 235 * @param execDestroy the {@link ExecutorService} to be destroyed by the 236 * task 237 * @return a task for the background initialization 238 */ 239 private Callable<T> createTask(final ExecutorService execDestroy) { 240 return new InitializationTask(execDestroy); 241 } 242 243 /** 244 * Returns the result of the background initialization. This method blocks 245 * until initialization is complete. If the background processing caused a 246 * runtime exception, it is directly thrown by this method. Checked 247 * exceptions, including {@link InterruptedException} are wrapped in a 248 * {@link ConcurrentException}. Calling this method before {@link #start()} 249 * was called causes an {@link IllegalStateException} exception to be 250 * thrown. 251 * 252 * @return the object produced by this initializer 253 * @throws ConcurrentException if a checked exception occurred during 254 * background processing 255 * @throws IllegalStateException if {@link #start()} has not been called 256 */ 257 @Override 258 public T get() throws ConcurrentException { 259 try { 260 return getFuture().get(); 261 } catch (final ExecutionException execex) { 262 ConcurrentUtils.handleCause(execex); 263 return null; // should not be reached 264 } catch (final InterruptedException iex) { 265 // reset interrupted state 266 Thread.currentThread().interrupt(); 267 throw new ConcurrentException(iex); 268 } 269 } 270 271 /** 272 * Returns the {@link ExecutorService} that is actually used for executing 273 * the background task. This method can be called after {@link #start()} 274 * (before {@code start()} it returns <b>null</b>). If an external executor 275 * was set, this is also the active executor. Otherwise this method returns 276 * the temporary executor that was created by this object. 277 * 278 * @return the {@link ExecutorService} for executing the background task 279 */ 280 protected final synchronized ExecutorService getActiveExecutor() { 281 return executor; 282 } 283 284 /** 285 * Returns the external {@link ExecutorService} to be used by this class. 286 * 287 * @return the {@link ExecutorService} 288 */ 289 public final synchronized ExecutorService getExternalExecutor() { 290 return externalExecutor; 291 } 292 293 /** 294 * Returns the {@link Future} object that was created when {@link #start()} 295 * was called. Therefore this method can only be called after {@code 296 * start()}. 297 * 298 * @return the {@link Future} object wrapped by this initializer 299 * @throws IllegalStateException if {@link #start()} has not been called 300 */ 301 public synchronized Future<T> getFuture() { 302 if (future == null) { 303 throw new IllegalStateException("start() must be called first!"); 304 } 305 306 return future; 307 } 308 309 /** 310 * Returns the number of background tasks to be created for this 311 * initializer. This information is evaluated when a temporary {@code 312 * ExecutorService} is created. This base implementation returns 1. Derived 313 * classes that do more complex background processing can override it. This 314 * method is called from a synchronized block by the {@link #start()} 315 * method. Therefore overriding methods should be careful with obtaining 316 * other locks and return as fast as possible. 317 * 318 * @return the number of background tasks required by this initializer 319 */ 320 protected int getTaskCount() { 321 return 1; 322 } 323 324 /** 325 * {@inheritDoc} 326 */ 327 @Override 328 protected Exception getTypedException(final Exception e) { 329 //This Exception object will be used for type comparison in AbstractConcurrentInitializer.initialize but not thrown 330 return new Exception(e); 331 } 332 333 /** 334 * Tests whether this instance is initialized. Once initialized, always returns true. 335 * If initialization failed then the failure will be cached and this will never return 336 * true. 337 * 338 * @return true if initialization completed successfully, otherwise false 339 * @since 3.14.0 340 */ 341 @Override 342 public synchronized boolean isInitialized() { 343 if (future == null || ! future.isDone() ) { 344 return false; 345 } 346 347 try { 348 future.get(); 349 return true; 350 } catch (CancellationException | ExecutionException | InterruptedException e) { 351 return false; 352 } 353 } 354 355 /** 356 * Returns a flag whether this {@link BackgroundInitializer} has already 357 * been started. 358 * 359 * @return a flag whether the {@link #start()} method has already been 360 * called 361 */ 362 public synchronized boolean isStarted() { 363 return future != null; 364 } 365 366 /** 367 * Sets an {@link ExecutorService} to be used by this class. The {@code 368 * ExecutorService} passed to this method is used for executing the 369 * background task. Thus it is possible to re-use an already existing 370 * {@link ExecutorService} or to use a specially configured one. If no 371 * {@link ExecutorService} is set, this instance creates a temporary one and 372 * destroys it after background initialization is complete. Note that this 373 * method must be called before {@link #start()}; otherwise an exception is 374 * thrown. 375 * 376 * @param externalExecutor the {@link ExecutorService} to be used 377 * @throws IllegalStateException if this initializer has already been 378 * started 379 */ 380 public final synchronized void setExternalExecutor( 381 final ExecutorService externalExecutor) { 382 if (isStarted()) { 383 throw new IllegalStateException( 384 "Cannot set ExecutorService after start()!"); 385 } 386 387 this.externalExecutor = externalExecutor; 388 } 389 390 /** 391 * Starts the background initialization. With this method the initializer 392 * becomes active and invokes the {@link #initialize()} method in a 393 * background task. A {@link BackgroundInitializer} can be started exactly 394 * once. The return value of this method determines whether the start was 395 * successful: only the first invocation of this method returns <b>true</b>, 396 * following invocations will return <b>false</b>. 397 * 398 * @return a flag whether the initializer could be started successfully 399 */ 400 public synchronized boolean start() { 401 // Not yet started? 402 if (!isStarted()) { 403 404 // Determine the executor to use and whether a temporary one has to 405 // be created 406 final ExecutorService tempExec; 407 executor = getExternalExecutor(); 408 if (executor == null) { 409 executor = tempExec = createExecutor(); 410 } else { 411 tempExec = null; 412 } 413 414 future = executor.submit(createTask(tempExec)); 415 416 return true; 417 } 418 419 return false; 420 } 421 }