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.ExecutionException;
21  import java.util.concurrent.ExecutorService;
22  import java.util.concurrent.Executors;
23  import java.util.concurrent.Future;
24  
25  /**
26   * <p>
27   * A class that allows complex initialization operations in a background task.
28   * </p>
29   * <p>
30   * Applications often have to do some expensive initialization steps when they
31   * are started, e.g. constructing a connection to a database, reading a
32   * configuration file, etc. Doing these things in parallel can enhance
33   * performance as the CPU load can be improved. However, when access to the
34   * resources initialized in a background thread is actually required,
35   * synchronization has to be performed to ensure that their initialization is
36   * complete.
37   * </p>
38   * <p>
39   * This abstract base class provides support for this use case. A concrete
40   * subclass must implement the {@link #initialize()} method. Here an arbitrary
41   * initialization can be implemented, and a result object can be returned. With
42   * this method in place the basic usage of this class is as follows (where
43   * {@code MyBackgroundInitializer} is a concrete subclass):
44   * </p>
45   *
46   * <pre>
47   * MyBackgroundInitializer initializer = new MyBackgroundInitializer();
48   * initializer.start();
49   * // Now do some other things. Initialization runs in a parallel thread
50   * ...
51   * // Wait for the end of initialization and access the result object
52   * Object result = initializer.get();
53   * </pre>
54   *
55   * <p>
56   * After the construction of a {@code BackgroundInitializer} object its
57   * {@link #start()} method has to be called. This starts the background
58   * processing. The application can now continue to do other things. When it
59   * needs access to the object produced by the {@code BackgroundInitializer} it
60   * calls its {@link #get()} method. If initialization is already complete,
61   * {@link #get()} returns the result object immediately. Otherwise it blocks
62   * until the result object is fully constructed.
63   * </p>
64   * <p>
65   * {@code BackgroundInitializer} is a thin wrapper around a {@code Future}
66   * object and uses an {@code ExecutorService} for running the background
67   * initialization task. It is possible to pass in an {@code ExecutorService} at
68   * construction time or set one using {@code setExternalExecutor()} before
69   * {@code start()} was called. Then this object is used to spawn the background
70   * task. If no {@code ExecutorService} has been provided, {@code
71   * BackgroundInitializer} creates a temporary {@code ExecutorService} and
72   * destroys it when initialization is complete.
73   * </p>
74   * <p>
75   * The methods provided by {@code BackgroundInitializer} provide for minimal
76   * interaction with the wrapped {@code Future} object. It is also possible to
77   * obtain the {@code Future} object directly. Then the enhanced functionality
78   * offered by {@code Future} can be used, e.g. to check whether the background
79   * operation is complete or to cancel the operation.
80   * </p>
81   *
82   * @since 3.0
83   * @param <T> the type of the object managed by this initializer class
84   */
85  public abstract class BackgroundInitializer<T> implements
86          ConcurrentInitializer<T> {
87      /** The external executor service for executing tasks. */
88      private ExecutorService externalExecutor; // @GuardedBy("this")
89  
90      /** A reference to the executor service that is actually used. */
91      private ExecutorService executor; // @GuardedBy("this")
92  
93      /** Stores the handle to the background task. */
94      private Future<T> future;  // @GuardedBy("this")
95  
96      /**
97       * Creates a new instance of {@code BackgroundInitializer}. No external
98       * {@code ExecutorService} is used.
99       */
100     protected BackgroundInitializer() {
101         this(null);
102     }
103 
104     /**
105      * Creates a new instance of {@code BackgroundInitializer} and initializes
106      * it with the given {@code ExecutorService}. If the {@code ExecutorService}
107      * is not null, the background task for initializing this object will be
108      * scheduled at this service. Otherwise a new temporary {@code
109      * ExecutorService} is created.
110      *
111      * @param exec an external {@code ExecutorService} to be used for task
112      * execution
113      */
114     protected BackgroundInitializer(final ExecutorService exec) {
115         setExternalExecutor(exec);
116     }
117 
118     /**
119      * Returns the external {@code ExecutorService} to be used by this class.
120      *
121      * @return the {@code ExecutorService}
122      */
123     public final synchronized ExecutorService getExternalExecutor() {
124         return externalExecutor;
125     }
126 
127     /**
128      * Returns a flag whether this {@code BackgroundInitializer} has already
129      * been started.
130      *
131      * @return a flag whether the {@link #start()} method has already been
132      * called
133      */
134     public synchronized boolean isStarted() {
135         return future != null;
136     }
137 
138     /**
139      * Sets an {@code ExecutorService} to be used by this class. The {@code
140      * ExecutorService} passed to this method is used for executing the
141      * background task. Thus it is possible to re-use an already existing
142      * {@code ExecutorService} or to use a specially configured one. If no
143      * {@code ExecutorService} is set, this instance creates a temporary one and
144      * destroys it after background initialization is complete. Note that this
145      * method must be called before {@link #start()}; otherwise an exception is
146      * thrown.
147      *
148      * @param externalExecutor the {@code ExecutorService} to be used
149      * @throws IllegalStateException if this initializer has already been
150      * started
151      */
152     public final synchronized void setExternalExecutor(
153             final ExecutorService externalExecutor) {
154         if (isStarted()) {
155             throw new IllegalStateException(
156                     "Cannot set ExecutorService after start()!");
157         }
158 
159         this.externalExecutor = externalExecutor;
160     }
161 
162     /**
163      * Starts the background initialization. With this method the initializer
164      * becomes active and invokes the {@link #initialize()} method in a
165      * background task. A {@code BackgroundInitializer} can be started exactly
166      * once. The return value of this method determines whether the start was
167      * successful: only the first invocation of this method returns <b>true</b>,
168      * following invocations will return <b>false</b>.
169      *
170      * @return a flag whether the initializer could be started successfully
171      */
172     public synchronized boolean start() {
173         // Not yet started?
174         if (!isStarted()) {
175 
176             // Determine the executor to use and whether a temporary one has to
177             // be created
178             ExecutorService tempExec;
179             executor = getExternalExecutor();
180             if (executor == null) {
181                 executor = tempExec = createExecutor();
182             } else {
183                 tempExec = null;
184             }
185 
186             future = executor.submit(createTask(tempExec));
187 
188             return true;
189         }
190 
191         return false;
192     }
193 
194     /**
195      * Returns the result of the background initialization. This method blocks
196      * until initialization is complete. If the background processing caused a
197      * runtime exception, it is directly thrown by this method. Checked
198      * exceptions, including {@code InterruptedException} are wrapped in a
199      * {@link ConcurrentException}. Calling this method before {@link #start()}
200      * was called causes an {@code IllegalStateException} exception to be
201      * thrown.
202      *
203      * @return the object produced by this initializer
204      * @throws ConcurrentException if a checked exception occurred during
205      * background processing
206      * @throws IllegalStateException if {@link #start()} has not been called
207      */
208     @Override
209     public T get() throws ConcurrentException {
210         try {
211             return getFuture().get();
212         } catch (final ExecutionException execex) {
213             ConcurrentUtils.handleCause(execex);
214             return null; // should not be reached
215         } catch (final InterruptedException iex) {
216             // reset interrupted state
217             Thread.currentThread().interrupt();
218             throw new ConcurrentException(iex);
219         }
220     }
221 
222     /**
223      * Returns the {@code Future} object that was created when {@link #start()}
224      * was called. Therefore this method can only be called after {@code
225      * start()}.
226      *
227      * @return the {@code Future} object wrapped by this initializer
228      * @throws IllegalStateException if {@link #start()} has not been called
229      */
230     public synchronized Future<T> getFuture() {
231         if (future == null) {
232             throw new IllegalStateException("start() must be called first!");
233         }
234 
235         return future;
236     }
237 
238     /**
239      * Returns the {@code ExecutorService} that is actually used for executing
240      * the background task. This method can be called after {@link #start()}
241      * (before {@code start()} it returns <b>null</b>). If an external executor
242      * was set, this is also the active executor. Otherwise this method returns
243      * the temporary executor that was created by this object.
244      *
245      * @return the {@code ExecutorService} for executing the background task
246      */
247     protected synchronized final ExecutorService getActiveExecutor() {
248         return executor;
249     }
250 
251     /**
252      * Returns the number of background tasks to be created for this
253      * initializer. This information is evaluated when a temporary {@code
254      * ExecutorService} is created. This base implementation returns 1. Derived
255      * classes that do more complex background processing can override it. This
256      * method is called from a synchronized block by the {@link #start()}
257      * method. Therefore overriding methods should be careful with obtaining
258      * other locks and return as fast as possible.
259      *
260      * @return the number of background tasks required by this initializer
261      */
262     protected int getTaskCount() {
263         return 1;
264     }
265 
266     /**
267      * Performs the initialization. This method is called in a background task
268      * when this {@code BackgroundInitializer} is started. It must be
269      * implemented by a concrete subclass. An implementation is free to perform
270      * arbitrary initialization. The object returned by this method can be
271      * queried using the {@link #get()} method.
272      *
273      * @return a result object
274      * @throws Exception if an error occurs
275      */
276     protected abstract T initialize() throws Exception;
277 
278     /**
279      * Creates a task for the background initialization. The {@code Callable}
280      * object returned by this method is passed to the {@code ExecutorService}.
281      * This implementation returns a task that invokes the {@link #initialize()}
282      * method. If a temporary {@code ExecutorService} is used, it is destroyed
283      * at the end of the task.
284      *
285      * @param execDestroy the {@code ExecutorService} to be destroyed by the
286      * task
287      * @return a task for the background initialization
288      */
289     private Callable<T> createTask(final ExecutorService execDestroy) {
290         return new InitializationTask(execDestroy);
291     }
292 
293     /**
294      * Creates the {@code ExecutorService} to be used. This method is called if
295      * no {@code ExecutorService} was provided at construction time.
296      *
297      * @return the {@code ExecutorService} to be used
298      */
299     private ExecutorService createExecutor() {
300         return Executors.newFixedThreadPool(getTaskCount());
301     }
302 
303     private class InitializationTask implements Callable<T> {
304         /** Stores the executor service to be destroyed at the end. */
305         private final ExecutorService execFinally;
306 
307         /**
308          * Creates a new instance of {@code InitializationTask} and initializes
309          * it with the {@code ExecutorService} to be destroyed at the end.
310          *
311          * @param exec the {@code ExecutorService}
312          */
313         public InitializationTask(final ExecutorService exec) {
314             execFinally = exec;
315         }
316 
317         /**
318          * Initiates initialization and returns the result.
319          *
320          * @return the result object
321          * @throws Exception if an error occurs
322          */
323         @Override
324         public T call() throws Exception {
325             try {
326                 return initialize();
327             } finally {
328                 if (execFinally != null) {
329                     execFinally.shutdown();
330                 }
331             }
332         }
333     }
334 }