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 * https://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 * @param <T> the type of the object managed by this initializer class
86 * @since 3.0
87 */
88 public class BackgroundInitializer<T> extends AbstractConcurrentInitializer<T, Exception> {
89
90 /**
91 * Builds a new instance.
92 *
93 * @param <T> The type of results supplied by this builder.
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 * @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 }