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 * https://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 * @param <T> the type of the object managed by this initializer class 086 * @since 3.0 087 */ 088public class BackgroundInitializer<T> extends AbstractConcurrentInitializer<T, Exception> { 089 090 /** 091 * Builds a new instance. 092 * 093 * @param <T> The type of results supplied by this builder. 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 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 * @param externalExecutor the {@link ExecutorService} to be used. 122 * @return {@code this} instance. 123 */ 124 public Builder<I, T> setExternalExecutor(final ExecutorService externalExecutor) { 125 this.externalExecutor = externalExecutor; 126 return asThis(); 127 } 128 129 } 130 131 private final class InitializationTask implements Callable<T> { 132 /** Stores the executor service to be destroyed at the end. */ 133 private final ExecutorService execFinally; 134 135 /** 136 * Creates a new instance of {@link InitializationTask} and initializes 137 * it with the {@link ExecutorService} to be destroyed at the end. 138 * 139 * @param exec the {@link ExecutorService} 140 */ 141 InitializationTask(final ExecutorService exec) { 142 execFinally = exec; 143 } 144 145 /** 146 * Initiates initialization and returns the result. 147 * 148 * @return the result object 149 * @throws Exception if an error occurs 150 */ 151 @Override 152 public T call() throws Exception { 153 try { 154 return initialize(); 155 } finally { 156 if (execFinally != null) { 157 execFinally.shutdown(); 158 } 159 } 160 } 161 } 162 163 /** 164 * Creates a new builder. 165 * 166 * @param <T> the type of object to build. 167 * @return a new builder. 168 * @since 3.14.0 169 */ 170 public static <T> Builder<BackgroundInitializer<T>, T> builder() { 171 return new Builder<>(); 172 } 173 174 /** The external executor service for executing tasks. */ 175 private ExecutorService externalExecutor; // @GuardedBy("this") 176 177 /** A reference to the executor service that is actually used. */ 178 private ExecutorService executor; // @GuardedBy("this") 179 180 /** Stores the handle to the background task. */ 181 private Future<T> future; // @GuardedBy("this") 182 183 /** 184 * Creates a new instance of {@link BackgroundInitializer}. No external 185 * {@link ExecutorService} is used. 186 */ 187 protected BackgroundInitializer() { 188 this(null); 189 } 190 191 /** 192 * Creates a new instance of {@link BackgroundInitializer} and initializes 193 * it with the given {@link ExecutorService}. If the {@link ExecutorService} 194 * is not null, the background task for initializing this object will be 195 * scheduled at this service. Otherwise a new temporary {@code 196 * ExecutorService} is created. 197 * 198 * @param exec an external {@link ExecutorService} to be used for task. 199 * execution 200 */ 201 protected BackgroundInitializer(final ExecutorService exec) { 202 setExternalExecutor(exec); 203 } 204 205 /** 206 * Constructs a new instance. 207 * 208 * @param initializer the initializer supplier called by {@link #initialize()}. 209 * @param closer the closer consumer called by {@link #close()}. 210 * @param exec the {@link ExecutorService} to be used @see #setExternalExecutor(ExecutorService) 211 */ 212 private BackgroundInitializer(final FailableSupplier<T, ConcurrentException> initializer, final FailableConsumer<T, ConcurrentException> closer, final ExecutorService exec) { 213 super(initializer, closer); 214 setExternalExecutor(exec); 215 } 216 217 /** 218 * Creates the {@link ExecutorService} to be used. This method is called if 219 * no {@link ExecutorService} was provided at construction time. 220 * 221 * @return the {@link ExecutorService} to be used. 222 */ 223 private ExecutorService createExecutor() { 224 return Executors.newFixedThreadPool(getTaskCount()); 225 } 226 227 /** 228 * Creates a task for the background initialization. The {@link Callable} 229 * object returned by this method is passed to the {@link ExecutorService}. 230 * This implementation returns a task that invokes the {@link #initialize()} 231 * method. If a temporary {@link ExecutorService} is used, it is destroyed 232 * at the end of the task. 233 * 234 * @param execDestroy the {@link ExecutorService} to be destroyed by the 235 * task. 236 * @return a task for the background initialization. 237 */ 238 private Callable<T> createTask(final ExecutorService execDestroy) { 239 return new InitializationTask(execDestroy); 240 } 241 242 /** 243 * Gets the result of the background initialization. This method blocks 244 * until initialization is complete. If the background processing caused a 245 * runtime exception, it is directly thrown by this method. Checked 246 * exceptions, including {@link InterruptedException} are wrapped in a 247 * {@link ConcurrentException}. Calling this method before {@link #start()} 248 * was called causes an {@link IllegalStateException} exception to be 249 * thrown. 250 * 251 * @return the object produced by this initializer. 252 * @throws ConcurrentException if a checked exception occurred during 253 * background processing. 254 * @throws IllegalStateException if {@link #start()} has not been called. 255 */ 256 @Override 257 public T get() throws ConcurrentException { 258 try { 259 return getFuture().get(); 260 } catch (final ExecutionException execex) { 261 ConcurrentUtils.handleCause(execex); 262 return null; // should not be reached 263 } catch (final InterruptedException iex) { 264 // reset interrupted state 265 Thread.currentThread().interrupt(); 266 throw new ConcurrentException(iex); 267 } 268 } 269 270 /** 271 * Gets the {@link ExecutorService} that is actually used for executing 272 * the background task. This method can be called after {@link #start()} 273 * (before {@code start()} it returns <strong>null</strong>). If an external executor 274 * was set, this is also the active executor. Otherwise this method returns 275 * the temporary executor that was created by this object. 276 * 277 * @return the {@link ExecutorService} for executing the background task. 278 */ 279 protected final synchronized ExecutorService getActiveExecutor() { 280 return executor; 281 } 282 283 /** 284 * Gets the external {@link ExecutorService} to be used by this class. 285 * 286 * @return the {@link ExecutorService}. 287 */ 288 public final synchronized ExecutorService getExternalExecutor() { 289 return externalExecutor; 290 } 291 292 /** 293 * Gets the {@link Future} object that was created when {@link #start()} 294 * was called. Therefore this method can only be called after {@code 295 * start()}. 296 * 297 * @return the {@link Future} object wrapped by this initializer. 298 * @throws IllegalStateException if {@link #start()} has not been called. 299 */ 300 public synchronized Future<T> getFuture() { 301 if (future == null) { 302 throw new IllegalStateException("start() must be called first!"); 303 } 304 return future; 305 } 306 307 /** 308 * Gets the number of background tasks to be created for this 309 * initializer. This information is evaluated when a temporary {@code 310 * ExecutorService} is created. This base implementation returns 1. Derived 311 * classes that do more complex background processing can override it. This 312 * method is called from a synchronized block by the {@link #start()} 313 * method. Therefore overriding methods should be careful with obtaining 314 * other locks and return as fast as possible. 315 * 316 * @return the number of background tasks required by this initializer. 317 */ 318 protected int getTaskCount() { 319 return 1; 320 } 321 322 /** 323 * {@inheritDoc} 324 */ 325 @Override 326 protected Exception getTypedException(final Exception e) { 327 //This Exception object will be used for type comparison in AbstractConcurrentInitializer.initialize but not thrown 328 return new Exception(e); 329 } 330 331 /** 332 * Tests whether this instance is initialized. Once initialized, always returns true. 333 * If initialization failed then the failure will be cached and this will never return 334 * true. 335 * 336 * @return true if initialization completed successfully, otherwise false. 337 * @since 3.14.0 338 */ 339 @Override 340 public synchronized boolean isInitialized() { 341 if (future == null || !future.isDone()) { 342 return false; 343 } 344 try { 345 future.get(); 346 return true; 347 } catch (CancellationException | ExecutionException | InterruptedException e) { 348 return false; 349 } 350 } 351 352 /** 353 * Tests whether this {@link BackgroundInitializer} has already 354 * been started. 355 * 356 * @return a flag whether the {@link #start()} method has already been 357 * called. 358 */ 359 public synchronized boolean isStarted() { 360 return future != null; 361 } 362 363 /** 364 * Sets an {@link ExecutorService} to be used by this class. The {@code 365 * ExecutorService} passed to this method is used for executing the 366 * background task. Thus it is possible to re-use an already existing 367 * {@link ExecutorService} or to use a specially configured one. If no 368 * {@link ExecutorService} is set, this instance creates a temporary one and 369 * destroys it after background initialization is complete. Note that this 370 * method must be called before {@link #start()}; otherwise an exception is 371 * thrown. 372 * 373 * @param externalExecutor the {@link ExecutorService} to be used. 374 * @throws IllegalStateException if this initializer has already been 375 * started. 376 */ 377 public final synchronized void setExternalExecutor(final ExecutorService externalExecutor) { 378 if (isStarted()) { 379 throw new IllegalStateException("Cannot set ExecutorService after start()!"); 380 } 381 this.externalExecutor = externalExecutor; 382 } 383 384 /** 385 * Starts the background initialization. With this method the initializer 386 * becomes active and invokes the {@link #initialize()} method in a 387 * background task. A {@link BackgroundInitializer} can be started exactly 388 * once. The return value of this method determines whether the start was 389 * successful: only the first invocation of this method returns <strong>true</strong>, 390 * following invocations will return <strong>false</strong>. 391 * 392 * @return a flag whether the initializer could be started successfully. 393 */ 394 public synchronized boolean start() { 395 // Not yet started? 396 if (!isStarted()) { 397 // Determine the executor to use and whether a temporary one has to be created. 398 final ExecutorService tempExec; 399 executor = getExternalExecutor(); 400 if (executor == null) { 401 executor = tempExec = createExecutor(); 402 } else { 403 tempExec = null; 404 } 405 future = executor.submit(createTask(tempExec)); 406 return true; 407 } 408 return false; 409 } 410}