aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/apache/commons/lang3/concurrent/BackgroundInitializer.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/apache/commons/lang3/concurrent/BackgroundInitializer.java')
-rw-r--r--src/main/java/org/apache/commons/lang3/concurrent/BackgroundInitializer.java333
1 files changed, 333 insertions, 0 deletions
diff --git a/src/main/java/org/apache/commons/lang3/concurrent/BackgroundInitializer.java b/src/main/java/org/apache/commons/lang3/concurrent/BackgroundInitializer.java
new file mode 100644
index 000000000..c74e14afa
--- /dev/null
+++ b/src/main/java/org/apache/commons/lang3/concurrent/BackgroundInitializer.java
@@ -0,0 +1,333 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.concurrent;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+/**
+ * A class that allows complex initialization operations in a background task.
+ *
+ * <p>
+ * Applications often have to do some expensive initialization steps when they
+ * are started, e.g. constructing a connection to a database, reading a
+ * configuration file, etc. Doing these things in parallel can enhance
+ * performance as the CPU load can be improved. However, when access to the
+ * resources initialized in a background thread is actually required,
+ * synchronization has to be performed to ensure that their initialization is
+ * complete.
+ * </p>
+ * <p>
+ * This abstract base class provides support for this use case. A concrete
+ * subclass must implement the {@link #initialize()} method. Here an arbitrary
+ * initialization can be implemented, and a result object can be returned. With
+ * this method in place the basic usage of this class is as follows (where
+ * {@code MyBackgroundInitializer} is a concrete subclass):
+ * </p>
+ *
+ * <pre>
+ * MyBackgroundInitializer initializer = new MyBackgroundInitializer();
+ * initializer.start();
+ * // Now do some other things. Initialization runs in a parallel thread
+ * ...
+ * // Wait for the end of initialization and access the result object
+ * Object result = initializer.get();
+ * </pre>
+ *
+ * <p>
+ * After the construction of a {@link BackgroundInitializer} object its
+ * {@link #start()} method has to be called. This starts the background
+ * processing. The application can now continue to do other things. When it
+ * needs access to the object produced by the {@link BackgroundInitializer} it
+ * calls its {@link #get()} method. If initialization is already complete,
+ * {@link #get()} returns the result object immediately. Otherwise it blocks
+ * until the result object is fully constructed.
+ * </p>
+ * <p>
+ * {@link BackgroundInitializer} is a thin wrapper around a {@link Future}
+ * object and uses an {@link ExecutorService} for running the background
+ * initialization task. It is possible to pass in an {@link ExecutorService} at
+ * construction time or set one using {@code setExternalExecutor()} before
+ * {@code start()} was called. Then this object is used to spawn the background
+ * task. If no {@link ExecutorService} has been provided, {@code
+ * BackgroundInitializer} creates a temporary {@link ExecutorService} and
+ * destroys it when initialization is complete.
+ * </p>
+ * <p>
+ * The methods provided by {@link BackgroundInitializer} provide for minimal
+ * interaction with the wrapped {@link Future} object. It is also possible to
+ * obtain the {@link Future} object directly. Then the enhanced functionality
+ * offered by {@link Future} can be used, e.g. to check whether the background
+ * operation is complete or to cancel the operation.
+ * </p>
+ *
+ * @since 3.0
+ * @param <T> the type of the object managed by this initializer class
+ */
+public abstract class BackgroundInitializer<T> implements
+ ConcurrentInitializer<T> {
+ /** The external executor service for executing tasks. */
+ private ExecutorService externalExecutor; // @GuardedBy("this")
+
+ /** A reference to the executor service that is actually used. */
+ private ExecutorService executor; // @GuardedBy("this")
+
+ /** Stores the handle to the background task. */
+ private Future<T> future; // @GuardedBy("this")
+
+ /**
+ * Creates a new instance of {@link BackgroundInitializer}. No external
+ * {@link ExecutorService} is used.
+ */
+ protected BackgroundInitializer() {
+ this(null);
+ }
+
+ /**
+ * Creates a new instance of {@link BackgroundInitializer} and initializes
+ * it with the given {@link ExecutorService}. If the {@link ExecutorService}
+ * is not null, the background task for initializing this object will be
+ * scheduled at this service. Otherwise a new temporary {@code
+ * ExecutorService} is created.
+ *
+ * @param exec an external {@link ExecutorService} to be used for task
+ * execution
+ */
+ protected BackgroundInitializer(final ExecutorService exec) {
+ setExternalExecutor(exec);
+ }
+
+ /**
+ * Returns the external {@link ExecutorService} to be used by this class.
+ *
+ * @return the {@link ExecutorService}
+ */
+ public final synchronized ExecutorService getExternalExecutor() {
+ return externalExecutor;
+ }
+
+ /**
+ * Returns a flag whether this {@link BackgroundInitializer} has already
+ * been started.
+ *
+ * @return a flag whether the {@link #start()} method has already been
+ * called
+ */
+ public synchronized boolean isStarted() {
+ return future != null;
+ }
+
+ /**
+ * Sets an {@link ExecutorService} to be used by this class. The {@code
+ * ExecutorService} passed to this method is used for executing the
+ * background task. Thus it is possible to re-use an already existing
+ * {@link ExecutorService} or to use a specially configured one. If no
+ * {@link ExecutorService} is set, this instance creates a temporary one and
+ * destroys it after background initialization is complete. Note that this
+ * method must be called before {@link #start()}; otherwise an exception is
+ * thrown.
+ *
+ * @param externalExecutor the {@link ExecutorService} to be used
+ * @throws IllegalStateException if this initializer has already been
+ * started
+ */
+ public final synchronized void setExternalExecutor(
+ final ExecutorService externalExecutor) {
+ if (isStarted()) {
+ throw new IllegalStateException(
+ "Cannot set ExecutorService after start()!");
+ }
+
+ this.externalExecutor = externalExecutor;
+ }
+
+ /**
+ * Starts the background initialization. With this method the initializer
+ * becomes active and invokes the {@link #initialize()} method in a
+ * background task. A {@link BackgroundInitializer} can be started exactly
+ * once. The return value of this method determines whether the start was
+ * successful: only the first invocation of this method returns <b>true</b>,
+ * following invocations will return <b>false</b>.
+ *
+ * @return a flag whether the initializer could be started successfully
+ */
+ public synchronized boolean start() {
+ // Not yet started?
+ if (!isStarted()) {
+
+ // Determine the executor to use and whether a temporary one has to
+ // be created
+ final ExecutorService tempExec;
+ executor = getExternalExecutor();
+ if (executor == null) {
+ executor = tempExec = createExecutor();
+ } else {
+ tempExec = null;
+ }
+
+ future = executor.submit(createTask(tempExec));
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the result of the background initialization. This method blocks
+ * until initialization is complete. If the background processing caused a
+ * runtime exception, it is directly thrown by this method. Checked
+ * exceptions, including {@link InterruptedException} are wrapped in a
+ * {@link ConcurrentException}. Calling this method before {@link #start()}
+ * was called causes an {@link IllegalStateException} exception to be
+ * thrown.
+ *
+ * @return the object produced by this initializer
+ * @throws ConcurrentException if a checked exception occurred during
+ * background processing
+ * @throws IllegalStateException if {@link #start()} has not been called
+ */
+ @Override
+ public T get() throws ConcurrentException {
+ try {
+ return getFuture().get();
+ } catch (final ExecutionException execex) {
+ ConcurrentUtils.handleCause(execex);
+ return null; // should not be reached
+ } catch (final InterruptedException iex) {
+ // reset interrupted state
+ Thread.currentThread().interrupt();
+ throw new ConcurrentException(iex);
+ }
+ }
+
+ /**
+ * Returns the {@link Future} object that was created when {@link #start()}
+ * was called. Therefore this method can only be called after {@code
+ * start()}.
+ *
+ * @return the {@link Future} object wrapped by this initializer
+ * @throws IllegalStateException if {@link #start()} has not been called
+ */
+ public synchronized Future<T> getFuture() {
+ if (future == null) {
+ throw new IllegalStateException("start() must be called first!");
+ }
+
+ return future;
+ }
+
+ /**
+ * Returns the {@link ExecutorService} that is actually used for executing
+ * the background task. This method can be called after {@link #start()}
+ * (before {@code start()} it returns <b>null</b>). If an external executor
+ * was set, this is also the active executor. Otherwise this method returns
+ * the temporary executor that was created by this object.
+ *
+ * @return the {@link ExecutorService} for executing the background task
+ */
+ protected final synchronized ExecutorService getActiveExecutor() {
+ return executor;
+ }
+
+ /**
+ * Returns the number of background tasks to be created for this
+ * initializer. This information is evaluated when a temporary {@code
+ * ExecutorService} is created. This base implementation returns 1. Derived
+ * classes that do more complex background processing can override it. This
+ * method is called from a synchronized block by the {@link #start()}
+ * method. Therefore overriding methods should be careful with obtaining
+ * other locks and return as fast as possible.
+ *
+ * @return the number of background tasks required by this initializer
+ */
+ protected int getTaskCount() {
+ return 1;
+ }
+
+ /**
+ * Performs the initialization. This method is called in a background task
+ * when this {@link BackgroundInitializer} is started. It must be
+ * implemented by a concrete subclass. An implementation is free to perform
+ * arbitrary initialization. The object returned by this method can be
+ * queried using the {@link #get()} method.
+ *
+ * @return a result object
+ * @throws Exception if an error occurs
+ */
+ protected abstract T initialize() throws Exception;
+
+ /**
+ * Creates a task for the background initialization. The {@link Callable}
+ * object returned by this method is passed to the {@link ExecutorService}.
+ * This implementation returns a task that invokes the {@link #initialize()}
+ * method. If a temporary {@link ExecutorService} is used, it is destroyed
+ * at the end of the task.
+ *
+ * @param execDestroy the {@link ExecutorService} to be destroyed by the
+ * task
+ * @return a task for the background initialization
+ */
+ private Callable<T> createTask(final ExecutorService execDestroy) {
+ return new InitializationTask(execDestroy);
+ }
+
+ /**
+ * Creates the {@link ExecutorService} to be used. This method is called if
+ * no {@link ExecutorService} was provided at construction time.
+ *
+ * @return the {@link ExecutorService} to be used
+ */
+ private ExecutorService createExecutor() {
+ return Executors.newFixedThreadPool(getTaskCount());
+ }
+
+ private class InitializationTask implements Callable<T> {
+ /** Stores the executor service to be destroyed at the end. */
+ private final ExecutorService execFinally;
+
+ /**
+ * Creates a new instance of {@link InitializationTask} and initializes
+ * it with the {@link ExecutorService} to be destroyed at the end.
+ *
+ * @param exec the {@link ExecutorService}
+ */
+ InitializationTask(final ExecutorService exec) {
+ execFinally = exec;
+ }
+
+ /**
+ * Initiates initialization and returns the result.
+ *
+ * @return the result object
+ * @throws Exception if an error occurs
+ */
+ @Override
+ public T call() throws Exception {
+ try {
+ return initialize();
+ } finally {
+ if (execFinally != null) {
+ execFinally.shutdown();
+ }
+ }
+ }
+ }
+}