001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.lang3.concurrent; 018 019import java.util.concurrent.Callable; 020import java.util.concurrent.CancellationException; 021import java.util.concurrent.ExecutionException; 022import java.util.concurrent.ExecutorService; 023import java.util.concurrent.Executors; 024import java.util.concurrent.Future; 025 026import org.apache.commons.lang3.function.FailableConsumer; 027import org.apache.commons.lang3.function.FailableSupplier; 028 029/** 030 * A class that allows complex initialization operations in a background task. 031 * 032 * <p> 033 * Applications often have to do some expensive initialization steps when they 034 * are started, e.g. constructing a connection to a database, reading a 035 * configuration file, etc. Doing these things in parallel can enhance 036 * performance as the CPU load can be improved. However, when access to the 037 * resources initialized in a background thread is actually required, 038 * synchronization has to be performed to ensure that their initialization is 039 * complete. 040 * </p> 041 * <p> 042 * This abstract base class provides support for this use case. A concrete 043 * subclass must implement the {@link #initialize()} method. Here an arbitrary 044 * initialization can be implemented, and a result object can be returned. With 045 * this method in place the basic usage of this class is as follows (where 046 * {@code MyBackgroundInitializer} is a concrete subclass): 047 * </p> 048 * 049 * <pre> 050 * MyBackgroundInitializer initializer = new MyBackgroundInitializer(); 051 * initializer.start(); 052 * // Now do some other things. Initialization runs in a parallel thread 053 * ... 054 * // Wait for the end of initialization and access the result object 055 * Object result = initializer.get(); 056 * </pre> 057 * 058 * <p> 059 * After the construction of a {@link BackgroundInitializer} object its 060 * {@link #start()} method has to be called. This starts the background 061 * processing. The application can now continue to do other things. When it 062 * needs access to the object produced by the {@link BackgroundInitializer} it 063 * calls its {@link #get()} method. If initialization is already complete, 064 * {@link #get()} returns the result object immediately. Otherwise it blocks 065 * until the result object is fully constructed. 066 * </p> 067 * <p> 068 * {@link BackgroundInitializer} is a thin wrapper around a {@link Future} 069 * object and uses an {@link ExecutorService} for running the background 070 * initialization task. It is possible to pass in an {@link ExecutorService} at 071 * construction time or set one using {@code setExternalExecutor()} before 072 * {@code start()} was called. Then this object is used to spawn the background 073 * task. If no {@link ExecutorService} has been provided, {@code 074 * BackgroundInitializer} creates a temporary {@link ExecutorService} and 075 * destroys it when initialization is complete. 076 * </p> 077 * <p> 078 * The methods provided by {@link BackgroundInitializer} provide for minimal 079 * interaction with the wrapped {@link Future} object. It is also possible to 080 * obtain the {@link Future} object directly. Then the enhanced functionality 081 * offered by {@link Future} can be used, e.g. to check whether the background 082 * operation is complete or to cancel the operation. 083 * </p> 084 * 085 * @since 3.0 086 * @param <T> the type of the object managed by this initializer class 087 */ 088public class BackgroundInitializer<T> extends AbstractConcurrentInitializer<T, Exception> { 089 090 /** 091 * Builds a new instance. 092 * 093 * @param <T> the type of the object managed by the initializer. 094 * @param <I> the type of the initializer managed by this builder. 095 * @since 3.14.0 096 */ 097 public static class Builder<I extends BackgroundInitializer<T>, T> extends AbstractBuilder<I, T, Builder<I, T>, Exception> { 098 099 /** 100 * The external executor service for executing tasks. null is an permitted value. 101 */ 102 private ExecutorService externalExecutor; 103 104 @SuppressWarnings("unchecked") 105 @Override 106 public I get() { 107 return (I) new BackgroundInitializer(getInitializer(), getCloser(), externalExecutor); 108 } 109 110 /** 111 * Sets the external executor service for executing tasks. null is an permitted value. 112 * 113 * @see org.apache.commons.lang3.concurrent.BackgroundInitializer#setExternalExecutor(ExecutorService) 114 * 115 * @param externalExecutor the {@link ExecutorService} to be used. 116 * @return this 117 */ 118 public Builder<I, T> setExternalExecutor(final ExecutorService externalExecutor) { 119 this.externalExecutor = externalExecutor; 120 return asThis(); 121 } 122 123 } 124 125 private final class InitializationTask implements Callable<T> { 126 /** Stores the executor service to be destroyed at the end. */ 127 private final ExecutorService execFinally; 128 129 /** 130 * Creates a new instance of {@link InitializationTask} and initializes 131 * it with the {@link ExecutorService} to be destroyed at the end. 132 * 133 * @param exec the {@link ExecutorService} 134 */ 135 InitializationTask(final ExecutorService exec) { 136 execFinally = exec; 137 } 138 139 /** 140 * Initiates initialization and returns the result. 141 * 142 * @return the result object 143 * @throws Exception if an error occurs 144 */ 145 @Override 146 public T call() throws Exception { 147 try { 148 return initialize(); 149 } finally { 150 if (execFinally != null) { 151 execFinally.shutdown(); 152 } 153 } 154 } 155 } 156 157 /** 158 * Creates a new builder. 159 * 160 * @param <T> the type of object to build. 161 * @return a new builder. 162 * @since 3.14.0 163 */ 164 public static <T> Builder<BackgroundInitializer<T>, T> builder() { 165 return new Builder<>(); 166 } 167 168 /** The external executor service for executing tasks. */ 169 private ExecutorService externalExecutor; // @GuardedBy("this") 170 171 /** A reference to the executor service that is actually used. */ 172 private ExecutorService executor; // @GuardedBy("this") 173 174 /** Stores the handle to the background task. */ 175 private Future<T> future; // @GuardedBy("this") 176 177 /** 178 * Creates a new instance of {@link BackgroundInitializer}. No external 179 * {@link ExecutorService} is used. 180 */ 181 protected BackgroundInitializer() { 182 this(null); 183 } 184 185 /** 186 * Creates a new instance of {@link BackgroundInitializer} and initializes 187 * it with the given {@link ExecutorService}. If the {@link ExecutorService} 188 * is not null, the background task for initializing this object will be 189 * scheduled at this service. Otherwise a new temporary {@code 190 * ExecutorService} is created. 191 * 192 * @param exec an external {@link ExecutorService} to be used for task 193 * execution 194 */ 195 protected BackgroundInitializer(final ExecutorService exec) { 196 setExternalExecutor(exec); 197 } 198 199 /** 200 * Constructs a new instance. 201 * 202 * @param initializer the initializer supplier called by {@link #initialize()}. 203 * @param closer the closer consumer called by {@link #close()}. 204 * @param exec the {@link ExecutorService} to be used @see #setExternalExecutor(ExecutorService) 205 */ 206 private BackgroundInitializer(final FailableSupplier<T, ConcurrentException> initializer, final FailableConsumer<T, ConcurrentException> closer, final ExecutorService exec) { 207 super(initializer, closer); 208 setExternalExecutor(exec); 209 } 210 211 /** 212 * Creates the {@link ExecutorService} to be used. This method is called if 213 * no {@link ExecutorService} was provided at construction time. 214 * 215 * @return the {@link ExecutorService} to be used 216 */ 217 private ExecutorService createExecutor() { 218 return Executors.newFixedThreadPool(getTaskCount()); 219 } 220 221 /** 222 * Creates a task for the background initialization. The {@link Callable} 223 * object returned by this method is passed to the {@link ExecutorService}. 224 * This implementation returns a task that invokes the {@link #initialize()} 225 * method. If a temporary {@link ExecutorService} is used, it is destroyed 226 * at the end of the task. 227 * 228 * @param execDestroy the {@link ExecutorService} to be destroyed by the 229 * task 230 * @return a task for the background initialization 231 */ 232 private Callable<T> createTask(final ExecutorService execDestroy) { 233 return new InitializationTask(execDestroy); 234 } 235 236 /** 237 * Returns the result of the background initialization. This method blocks 238 * until initialization is complete. If the background processing caused a 239 * runtime exception, it is directly thrown by this method. Checked 240 * exceptions, including {@link InterruptedException} are wrapped in a 241 * {@link ConcurrentException}. Calling this method before {@link #start()} 242 * was called causes an {@link IllegalStateException} exception to be 243 * thrown. 244 * 245 * @return the object produced by this initializer 246 * @throws ConcurrentException if a checked exception occurred during 247 * background processing 248 * @throws IllegalStateException if {@link #start()} has not been called 249 */ 250 @Override 251 public T get() throws ConcurrentException { 252 try { 253 return getFuture().get(); 254 } catch (final ExecutionException execex) { 255 ConcurrentUtils.handleCause(execex); 256 return null; // should not be reached 257 } catch (final InterruptedException iex) { 258 // reset interrupted state 259 Thread.currentThread().interrupt(); 260 throw new ConcurrentException(iex); 261 } 262 } 263 264 /** 265 * Returns the {@link ExecutorService} that is actually used for executing 266 * the background task. This method can be called after {@link #start()} 267 * (before {@code start()} it returns <b>null</b>). If an external executor 268 * was set, this is also the active executor. Otherwise this method returns 269 * the temporary executor that was created by this object. 270 * 271 * @return the {@link ExecutorService} for executing the background task 272 */ 273 protected final synchronized ExecutorService getActiveExecutor() { 274 return executor; 275 } 276 277 /** 278 * Returns the external {@link ExecutorService} to be used by this class. 279 * 280 * @return the {@link ExecutorService} 281 */ 282 public final synchronized ExecutorService getExternalExecutor() { 283 return externalExecutor; 284 } 285 286 /** 287 * Returns the {@link Future} object that was created when {@link #start()} 288 * was called. Therefore this method can only be called after {@code 289 * start()}. 290 * 291 * @return the {@link Future} object wrapped by this initializer 292 * @throws IllegalStateException if {@link #start()} has not been called 293 */ 294 public synchronized Future<T> getFuture() { 295 if (future == null) { 296 throw new IllegalStateException("start() must be called first!"); 297 } 298 299 return future; 300 } 301 302 /** 303 * Returns the number of background tasks to be created for this 304 * initializer. This information is evaluated when a temporary {@code 305 * ExecutorService} is created. This base implementation returns 1. Derived 306 * classes that do more complex background processing can override it. This 307 * method is called from a synchronized block by the {@link #start()} 308 * method. Therefore overriding methods should be careful with obtaining 309 * other locks and return as fast as possible. 310 * 311 * @return the number of background tasks required by this initializer 312 */ 313 protected int getTaskCount() { 314 return 1; 315 } 316 317 /** 318 * {@inheritDoc} 319 */ 320 @Override 321 protected Exception getTypedException(Exception e) { 322 //This Exception object will be used for type comparison in AbstractConcurrentInitializer.initialize but not thrown 323 return new Exception(e); 324 } 325 326 /** 327 * Tests whether this instance is initialized. Once initialized, always returns true. 328 * If initialization failed then the failure will be cached and this will never return 329 * true. 330 * 331 * @return true if initialization completed successfully, otherwise false 332 * @since 3.14.0 333 */ 334 @Override 335 public synchronized boolean isInitialized() { 336 if (future == null || ! future.isDone() ) { 337 return false; 338 } 339 340 try { 341 future.get(); 342 return true; 343 } catch (CancellationException | ExecutionException | InterruptedException e) { 344 return false; 345 } 346 } 347 348 /** 349 * Returns a flag whether this {@link BackgroundInitializer} has already 350 * been started. 351 * 352 * @return a flag whether the {@link #start()} method has already been 353 * called 354 */ 355 public synchronized boolean isStarted() { 356 return future != null; 357 } 358 359 /** 360 * Sets an {@link ExecutorService} to be used by this class. The {@code 361 * ExecutorService} passed to this method is used for executing the 362 * background task. Thus it is possible to re-use an already existing 363 * {@link ExecutorService} or to use a specially configured one. If no 364 * {@link ExecutorService} is set, this instance creates a temporary one and 365 * destroys it after background initialization is complete. Note that this 366 * method must be called before {@link #start()}; otherwise an exception is 367 * thrown. 368 * 369 * @param externalExecutor the {@link ExecutorService} to be used 370 * @throws IllegalStateException if this initializer has already been 371 * started 372 */ 373 public final synchronized void setExternalExecutor( 374 final ExecutorService externalExecutor) { 375 if (isStarted()) { 376 throw new IllegalStateException( 377 "Cannot set ExecutorService after start()!"); 378 } 379 380 this.externalExecutor = externalExecutor; 381 } 382 383 /** 384 * Starts the background initialization. With this method the initializer 385 * becomes active and invokes the {@link #initialize()} method in a 386 * background task. A {@link BackgroundInitializer} can be started exactly 387 * once. The return value of this method determines whether the start was 388 * successful: only the first invocation of this method returns <b>true</b>, 389 * following invocations will return <b>false</b>. 390 * 391 * @return a flag whether the initializer could be started successfully 392 */ 393 public synchronized boolean start() { 394 // Not yet started? 395 if (!isStarted()) { 396 397 // Determine the executor to use and whether a temporary one has to 398 // be created 399 final ExecutorService tempExec; 400 executor = getExternalExecutor(); 401 if (executor == null) { 402 executor = tempExec = createExecutor(); 403 } else { 404 tempExec = null; 405 } 406 407 future = executor.submit(createTask(tempExec)); 408 409 return true; 410 } 411 412 return false; 413 } 414}