View Javadoc
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 }