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   * @version $Id: BackgroundInitializer.java 1583482 2014-03-31 22:54:57Z niallp $
84   * @param <T> the type of the object managed by this initializer class
85   */
86  public abstract class BackgroundInitializer<T> implements
87          ConcurrentInitializer<T> {
88      /** The external executor service for executing tasks. */
89      private ExecutorService externalExecutor; // @GuardedBy("this")
90  
91      /** A reference to the executor service that is actually used. */
92      private ExecutorService executor; // @GuardedBy("this")
93  
94      /** Stores the handle to the background task. */
95      private Future<T> future;  // @GuardedBy("this")
96  
97      /**
98       * Creates a new instance of {@code BackgroundInitializer}. No external
99       * {@code ExecutorService} is used.
100      */
101     protected BackgroundInitializer() {
102         this(null);
103     }
104 
105     /**
106      * Creates a new instance of {@code BackgroundInitializer} and initializes
107      * it with the given {@code ExecutorService}. If the {@code ExecutorService}
108      * is not null, the background task for initializing this object will be
109      * scheduled at this service. Otherwise a new temporary {@code
110      * ExecutorService} is created.
111      *
112      * @param exec an external {@code ExecutorService} to be used for task
113      * execution
114      */
115     protected BackgroundInitializer(final ExecutorService exec) {
116         setExternalExecutor(exec);
117     }
118 
119     /**
120      * Returns the external {@code ExecutorService} to be used by this class.
121      *
122      * @return the {@code ExecutorService}
123      */
124     public final synchronized ExecutorService getExternalExecutor() {
125         return externalExecutor;
126     }
127 
128     /**
129      * Returns a flag whether this {@code BackgroundInitializer} has already
130      * been started.
131      *
132      * @return a flag whether the {@link #start()} method has already been
133      * called
134      */
135     public synchronized boolean isStarted() {
136         return future != null;
137     }
138 
139     /**
140      * Sets an {@code ExecutorService} to be used by this class. The {@code
141      * ExecutorService} passed to this method is used for executing the
142      * background task. Thus it is possible to re-use an already existing
143      * {@code ExecutorService} or to use a specially configured one. If no
144      * {@code ExecutorService} is set, this instance creates a temporary one and
145      * destroys it after background initialization is complete. Note that this
146      * method must be called before {@link #start()}; otherwise an exception is
147      * thrown.
148      *
149      * @param externalExecutor the {@code ExecutorService} to be used
150      * @throws IllegalStateException if this initializer has already been
151      * started
152      */
153     public final synchronized void setExternalExecutor(
154             final ExecutorService externalExecutor) {
155         if (isStarted()) {
156             throw new IllegalStateException(
157                     "Cannot set ExecutorService after start()!");
158         }
159 
160         this.externalExecutor = externalExecutor;
161     }
162 
163     /**
164      * Starts the background initialization. With this method the initializer
165      * becomes active and invokes the {@link #initialize()} method in a
166      * background task. A {@code BackgroundInitializer} can be started exactly
167      * once. The return value of this method determines whether the start was
168      * successful: only the first invocation of this method returns <b>true</b>,
169      * following invocations will return <b>false</b>.
170      *
171      * @return a flag whether the initializer could be started successfully
172      */
173     public synchronized boolean start() {
174         // Not yet started?
175         if (!isStarted()) {
176 
177             // Determine the executor to use and whether a temporary one has to
178             // be created
179             ExecutorService tempExec;
180             executor = getExternalExecutor();
181             if (executor == null) {
182                 executor = tempExec = createExecutor();
183             } else {
184                 tempExec = null;
185             }
186 
187             future = executor.submit(createTask(tempExec));
188 
189             return true;
190         }
191 
192         return false;
193     }
194 
195     /**
196      * Returns the result of the background initialization. This method blocks
197      * until initialization is complete. If the background processing caused a
198      * runtime exception, it is directly thrown by this method. Checked
199      * exceptions, including {@code InterruptedException} are wrapped in a
200      * {@link ConcurrentException}. Calling this method before {@link #start()}
201      * was called causes an {@code IllegalStateException} exception to be
202      * thrown.
203      *
204      * @return the object produced by this initializer
205      * @throws ConcurrentException if a checked exception occurred during
206      * background processing
207      * @throws IllegalStateException if {@link #start()} has not been called
208      */
209     @Override
210     public T get() throws ConcurrentException {
211         try {
212             return getFuture().get();
213         } catch (final ExecutionException execex) {
214             ConcurrentUtils.handleCause(execex);
215             return null; // should not be reached
216         } catch (final InterruptedException iex) {
217             // reset interrupted state
218             Thread.currentThread().interrupt();
219             throw new ConcurrentException(iex);
220         }
221     }
222 
223     /**
224      * Returns the {@code Future} object that was created when {@link #start()}
225      * was called. Therefore this method can only be called after {@code
226      * start()}.
227      *
228      * @return the {@code Future} object wrapped by this initializer
229      * @throws IllegalStateException if {@link #start()} has not been called
230      */
231     public synchronized Future<T> getFuture() {
232         if (future == null) {
233             throw new IllegalStateException("start() must be called first!");
234         }
235 
236         return future;
237     }
238 
239     /**
240      * Returns the {@code ExecutorService} that is actually used for executing
241      * the background task. This method can be called after {@link #start()}
242      * (before {@code start()} it returns <b>null</b>). If an external executor
243      * was set, this is also the active executor. Otherwise this method returns
244      * the temporary executor that was created by this object.
245      *
246      * @return the {@code ExecutorService} for executing the background task
247      */
248     protected synchronized final ExecutorService getActiveExecutor() {
249         return executor;
250     }
251 
252     /**
253      * Returns the number of background tasks to be created for this
254      * initializer. This information is evaluated when a temporary {@code
255      * ExecutorService} is created. This base implementation returns 1. Derived
256      * classes that do more complex background processing can override it. This
257      * method is called from a synchronized block by the {@link #start()}
258      * method. Therefore overriding methods should be careful with obtaining
259      * other locks and return as fast as possible.
260      *
261      * @return the number of background tasks required by this initializer
262      */
263     protected int getTaskCount() {
264         return 1;
265     }
266 
267     /**
268      * Performs the initialization. This method is called in a background task
269      * when this {@code BackgroundInitializer} is started. It must be
270      * implemented by a concrete subclass. An implementation is free to perform
271      * arbitrary initialization. The object returned by this method can be
272      * queried using the {@link #get()} method.
273      *
274      * @return a result object
275      * @throws Exception if an error occurs
276      */
277     protected abstract T initialize() throws Exception;
278 
279     /**
280      * Creates a task for the background initialization. The {@code Callable}
281      * object returned by this method is passed to the {@code ExecutorService}.
282      * This implementation returns a task that invokes the {@link #initialize()}
283      * method. If a temporary {@code ExecutorService} is used, it is destroyed
284      * at the end of the task.
285      *
286      * @param execDestroy the {@code ExecutorService} to be destroyed by the
287      * task
288      * @return a task for the background initialization
289      */
290     private Callable<T> createTask(final ExecutorService execDestroy) {
291         return new InitializationTask(execDestroy);
292     }
293 
294     /**
295      * Creates the {@code ExecutorService} to be used. This method is called if
296      * no {@code ExecutorService} was provided at construction time.
297      *
298      * @return the {@code ExecutorService} to be used
299      */
300     private ExecutorService createExecutor() {
301         return Executors.newFixedThreadPool(getTaskCount());
302     }
303 
304     private class InitializationTask implements Callable<T> {
305         /** Stores the executor service to be destroyed at the end. */
306         private final ExecutorService execFinally;
307 
308         /**
309          * Creates a new instance of {@code InitializationTask} and initializes
310          * it with the {@code ExecutorService} to be destroyed at the end.
311          *
312          * @param exec the {@code ExecutorService}
313          */
314         public InitializationTask(final ExecutorService exec) {
315             execFinally = exec;
316         }
317 
318         /**
319          * Initiates initialization and returns the result.
320          *
321          * @return the result object
322          * @throws Exception if an error occurs
323          */
324         @Override
325         public T call() throws Exception {
326             try {
327                 return initialize();
328             } finally {
329                 if (execFinally != null) {
330                     execFinally.shutdown();
331                 }
332             }
333         }
334     }
335 }