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
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 * Gets 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 * Gets 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 <strong>null</strong>). 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 * Gets 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 * Gets 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 return future;
306 }
307
308 /**
309 * Gets the number of background tasks to be created for this
310 * initializer. This information is evaluated when a temporary {@code
311 * ExecutorService} is created. This base implementation returns 1. Derived
312 * classes that do more complex background processing can override it. This
313 * method is called from a synchronized block by the {@link #start()}
314 * method. Therefore overriding methods should be careful with obtaining
315 * other locks and return as fast as possible.
316 *
317 * @return the number of background tasks required by this initializer.
318 */
319 protected int getTaskCount() {
320 return 1;
321 }
322
323 /**
324 * {@inheritDoc}
325 */
326 @Override
327 protected Exception getTypedException(final Exception e) {
328 //This Exception object will be used for type comparison in AbstractConcurrentInitializer.initialize but not thrown
329 return new Exception(e);
330 }
331
332 /**
333 * Tests whether this instance is initialized. Once initialized, always returns true.
334 * If initialization failed then the failure will be cached and this will never return
335 * true.
336 *
337 * @return true if initialization completed successfully, otherwise false.
338 * @since 3.14.0
339 */
340 @Override
341 public synchronized boolean isInitialized() {
342 if (future == null || !future.isDone()) {
343 return false;
344 }
345 try {
346 future.get();
347 return true;
348 } catch (CancellationException | ExecutionException | InterruptedException e) {
349 return false;
350 }
351 }
352
353 /**
354 * Tests whether this {@link BackgroundInitializer} has already
355 * been started.
356 *
357 * @return a flag whether the {@link #start()} method has already been
358 * called.
359 */
360 public synchronized boolean isStarted() {
361 return future != null;
362 }
363
364 /**
365 * Sets an {@link ExecutorService} to be used by this class. The {@code
366 * ExecutorService} passed to this method is used for executing the
367 * background task. Thus it is possible to re-use an already existing
368 * {@link ExecutorService} or to use a specially configured one. If no
369 * {@link ExecutorService} is set, this instance creates a temporary one and
370 * destroys it after background initialization is complete. Note that this
371 * method must be called before {@link #start()}; otherwise an exception is
372 * thrown.
373 *
374 * @param externalExecutor the {@link ExecutorService} to be used.
375 * @throws IllegalStateException if this initializer has already been
376 * started.
377 */
378 public final synchronized void setExternalExecutor(final ExecutorService externalExecutor) {
379 if (isStarted()) {
380 throw new IllegalStateException("Cannot set ExecutorService after start()!");
381 }
382 this.externalExecutor = externalExecutor;
383 }
384
385 /**
386 * Starts the background initialization. With this method the initializer
387 * becomes active and invokes the {@link #initialize()} method in a
388 * background task. A {@link BackgroundInitializer} can be started exactly
389 * once. The return value of this method determines whether the start was
390 * successful: only the first invocation of this method returns <strong>true</strong>,
391 * following invocations will return <strong>false</strong>.
392 *
393 * @return a flag whether the initializer could be started successfully.
394 */
395 public synchronized boolean start() {
396 // Not yet started?
397 if (!isStarted()) {
398 // Determine the executor to use and whether a temporary one has to be created.
399 final ExecutorService tempExec;
400 executor = getExternalExecutor();
401 if (executor == null) {
402 executor = tempExec = createExecutor();
403 } else {
404 tempExec = null;
405 }
406 future = executor.submit(createTask(tempExec));
407 return true;
408 }
409 return false;
410 }
411 }