BackgroundInitializer.java

  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. import java.util.concurrent.Callable;
  19. import java.util.concurrent.CancellationException;
  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. import org.apache.commons.lang3.function.FailableConsumer;
  25. import org.apache.commons.lang3.function.FailableSupplier;

  26. /**
  27.  * A class that allows complex initialization operations in a background task.
  28.  *
  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 {@link 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 {@link 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.  * {@link BackgroundInitializer} is a thin wrapper around a {@link Future}
  66.  * object and uses an {@link ExecutorService} for running the background
  67.  * initialization task. It is possible to pass in an {@link 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 {@link ExecutorService} has been provided, {@code
  71.  * BackgroundInitializer} creates a temporary {@link ExecutorService} and
  72.  * destroys it when initialization is complete.
  73.  * </p>
  74.  * <p>
  75.  * The methods provided by {@link BackgroundInitializer} provide for minimal
  76.  * interaction with the wrapped {@link Future} object. It is also possible to
  77.  * obtain the {@link Future} object directly. Then the enhanced functionality
  78.  * offered by {@link 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 class BackgroundInitializer<T> extends AbstractConcurrentInitializer<T, Exception> {

  86.     /**
  87.      * Builds a new instance.
  88.      *
  89.      * @param <T> the type of the object managed by the initializer.
  90.      * @param <I> the type of the initializer managed by this builder.
  91.      * @since 3.14.0
  92.      */
  93.     public static class Builder<I extends BackgroundInitializer<T>, T> extends AbstractBuilder<I, T, Builder<I, T>, Exception> {

  94.         /**
  95.          * The external executor service for executing tasks. null is a permitted value.
  96.          */
  97.         private ExecutorService externalExecutor;

  98.         /**
  99.          * Constructs a new instance.
  100.          */
  101.         public Builder() {
  102.             // empty
  103.         }

  104.         @SuppressWarnings("unchecked")
  105.         @Override
  106.         public I get() {
  107.             return (I) new BackgroundInitializer(getInitializer(), getCloser(), externalExecutor);
  108.         }

  109.         /**
  110.          * Sets the external executor service for executing tasks. null is a permitted value.
  111.          *
  112.          * @see org.apache.commons.lang3.concurrent.BackgroundInitializer#setExternalExecutor(ExecutorService)
  113.          *
  114.          * @param externalExecutor the {@link ExecutorService} to be used.
  115.          * @return {@code this} instance.
  116.          */
  117.         public Builder<I, T> setExternalExecutor(final ExecutorService externalExecutor) {
  118.             this.externalExecutor = externalExecutor;
  119.             return asThis();
  120.         }

  121.     }

  122.     private final class InitializationTask implements Callable<T> {
  123.         /** Stores the executor service to be destroyed at the end. */
  124.         private final ExecutorService execFinally;

  125.         /**
  126.          * Creates a new instance of {@link InitializationTask} and initializes
  127.          * it with the {@link ExecutorService} to be destroyed at the end.
  128.          *
  129.          * @param exec the {@link ExecutorService}
  130.          */
  131.         InitializationTask(final ExecutorService exec) {
  132.             execFinally = exec;
  133.         }

  134.         /**
  135.          * Initiates initialization and returns the result.
  136.          *
  137.          * @return the result object
  138.          * @throws Exception if an error occurs
  139.          */
  140.         @Override
  141.         public T call() throws Exception {
  142.             try {
  143.                 return initialize();
  144.             } finally {
  145.                 if (execFinally != null) {
  146.                     execFinally.shutdown();
  147.                 }
  148.             }
  149.         }
  150.     }

  151.     /**
  152.      * Creates a new builder.
  153.      *
  154.      * @param <T> the type of object to build.
  155.      * @return a new builder.
  156.      * @since 3.14.0
  157.      */
  158.     public static <T> Builder<BackgroundInitializer<T>, T> builder() {
  159.         return new Builder<>();
  160.     }

  161.     /** The external executor service for executing tasks. */
  162.     private ExecutorService externalExecutor; // @GuardedBy("this")

  163.     /** A reference to the executor service that is actually used. */
  164.     private ExecutorService executor; // @GuardedBy("this")

  165.     /** Stores the handle to the background task. */
  166.     private Future<T> future;  // @GuardedBy("this")

  167.     /**
  168.      * Creates a new instance of {@link BackgroundInitializer}. No external
  169.      * {@link ExecutorService} is used.
  170.      */
  171.     protected BackgroundInitializer() {
  172.         this(null);
  173.     }

  174.     /**
  175.      * Creates a new instance of {@link BackgroundInitializer} and initializes
  176.      * it with the given {@link ExecutorService}. If the {@link ExecutorService}
  177.      * is not null, the background task for initializing this object will be
  178.      * scheduled at this service. Otherwise a new temporary {@code
  179.      * ExecutorService} is created.
  180.      *
  181.      * @param exec an external {@link ExecutorService} to be used for task
  182.      * execution
  183.      */
  184.     protected BackgroundInitializer(final ExecutorService exec) {
  185.         setExternalExecutor(exec);
  186.     }

  187.     /**
  188.      * Constructs a new instance.
  189.      *
  190.      * @param initializer the initializer supplier called by {@link #initialize()}.
  191.      * @param closer the closer consumer called by {@link #close()}.
  192.      * @param exec the {@link ExecutorService} to be used @see #setExternalExecutor(ExecutorService)
  193.      */
  194.     private BackgroundInitializer(final FailableSupplier<T, ConcurrentException> initializer, final FailableConsumer<T, ConcurrentException> closer, final ExecutorService exec) {
  195.         super(initializer, closer);
  196.         setExternalExecutor(exec);
  197.     }

  198.     /**
  199.      * Creates the {@link ExecutorService} to be used. This method is called if
  200.      * no {@link ExecutorService} was provided at construction time.
  201.      *
  202.      * @return the {@link ExecutorService} to be used
  203.      */
  204.     private ExecutorService createExecutor() {
  205.         return Executors.newFixedThreadPool(getTaskCount());
  206.     }

  207.     /**
  208.      * Creates a task for the background initialization. The {@link Callable}
  209.      * object returned by this method is passed to the {@link ExecutorService}.
  210.      * This implementation returns a task that invokes the {@link #initialize()}
  211.      * method. If a temporary {@link ExecutorService} is used, it is destroyed
  212.      * at the end of the task.
  213.      *
  214.      * @param execDestroy the {@link ExecutorService} to be destroyed by the
  215.      * task
  216.      * @return a task for the background initialization
  217.      */
  218.     private Callable<T> createTask(final ExecutorService execDestroy) {
  219.         return new InitializationTask(execDestroy);
  220.     }

  221.     /**
  222.      * Returns the result of the background initialization. This method blocks
  223.      * until initialization is complete. If the background processing caused a
  224.      * runtime exception, it is directly thrown by this method. Checked
  225.      * exceptions, including {@link InterruptedException} are wrapped in a
  226.      * {@link ConcurrentException}. Calling this method before {@link #start()}
  227.      * was called causes an {@link IllegalStateException} exception to be
  228.      * thrown.
  229.      *
  230.      * @return the object produced by this initializer
  231.      * @throws ConcurrentException if a checked exception occurred during
  232.      * background processing
  233.      * @throws IllegalStateException if {@link #start()} has not been called
  234.      */
  235.     @Override
  236.     public T get() throws ConcurrentException {
  237.         try {
  238.             return getFuture().get();
  239.         } catch (final ExecutionException execex) {
  240.             ConcurrentUtils.handleCause(execex);
  241.             return null; // should not be reached
  242.         } catch (final InterruptedException iex) {
  243.             // reset interrupted state
  244.             Thread.currentThread().interrupt();
  245.             throw new ConcurrentException(iex);
  246.         }
  247.     }

  248.     /**
  249.      * Returns the {@link ExecutorService} that is actually used for executing
  250.      * the background task. This method can be called after {@link #start()}
  251.      * (before {@code start()} it returns <b>null</b>). If an external executor
  252.      * was set, this is also the active executor. Otherwise this method returns
  253.      * the temporary executor that was created by this object.
  254.      *
  255.      * @return the {@link ExecutorService} for executing the background task
  256.      */
  257.     protected final synchronized ExecutorService getActiveExecutor() {
  258.         return executor;
  259.     }

  260.     /**
  261.      * Returns the external {@link ExecutorService} to be used by this class.
  262.      *
  263.      * @return the {@link ExecutorService}
  264.      */
  265.     public final synchronized ExecutorService getExternalExecutor() {
  266.         return externalExecutor;
  267.     }

  268.     /**
  269.      * Returns the {@link Future} object that was created when {@link #start()}
  270.      * was called. Therefore this method can only be called after {@code
  271.      * start()}.
  272.      *
  273.      * @return the {@link Future} object wrapped by this initializer
  274.      * @throws IllegalStateException if {@link #start()} has not been called
  275.      */
  276.     public synchronized Future<T> getFuture() {
  277.         if (future == null) {
  278.             throw new IllegalStateException("start() must be called first!");
  279.         }

  280.         return future;
  281.     }

  282.     /**
  283.      * Returns the number of background tasks to be created for this
  284.      * initializer. This information is evaluated when a temporary {@code
  285.      * ExecutorService} is created. This base implementation returns 1. Derived
  286.      * classes that do more complex background processing can override it. This
  287.      * method is called from a synchronized block by the {@link #start()}
  288.      * method. Therefore overriding methods should be careful with obtaining
  289.      * other locks and return as fast as possible.
  290.      *
  291.      * @return the number of background tasks required by this initializer
  292.      */
  293.     protected int getTaskCount() {
  294.         return 1;
  295.     }

  296.     /**
  297.      * {@inheritDoc}
  298.      */
  299.     @Override
  300.     protected Exception getTypedException(final Exception e) {
  301.         //This Exception object will be used for type comparison in AbstractConcurrentInitializer.initialize but not thrown
  302.         return new Exception(e);
  303.     }

  304.     /**
  305.      * Tests whether this instance is initialized. Once initialized, always returns true.
  306.      * If initialization failed then the failure will be cached and this will never return
  307.      * true.
  308.      *
  309.      * @return true if initialization completed successfully, otherwise false
  310.      * @since 3.14.0
  311.      */
  312.     @Override
  313.     public synchronized boolean isInitialized() {
  314.         if (future == null || ! future.isDone() ) {
  315.             return false;
  316.         }

  317.         try {
  318.             future.get();
  319.             return true;
  320.         } catch (CancellationException | ExecutionException | InterruptedException e) {
  321.             return false;
  322.         }
  323.     }

  324.     /**
  325.      * Returns a flag whether this {@link BackgroundInitializer} has already
  326.      * been started.
  327.      *
  328.      * @return a flag whether the {@link #start()} method has already been
  329.      * called
  330.      */
  331.     public synchronized boolean isStarted() {
  332.         return future != null;
  333.     }

  334.     /**
  335.      * Sets an {@link ExecutorService} to be used by this class. The {@code
  336.      * ExecutorService} passed to this method is used for executing the
  337.      * background task. Thus it is possible to re-use an already existing
  338.      * {@link ExecutorService} or to use a specially configured one. If no
  339.      * {@link ExecutorService} is set, this instance creates a temporary one and
  340.      * destroys it after background initialization is complete. Note that this
  341.      * method must be called before {@link #start()}; otherwise an exception is
  342.      * thrown.
  343.      *
  344.      * @param externalExecutor the {@link ExecutorService} to be used
  345.      * @throws IllegalStateException if this initializer has already been
  346.      * started
  347.      */
  348.     public final synchronized void setExternalExecutor(
  349.             final ExecutorService externalExecutor) {
  350.         if (isStarted()) {
  351.             throw new IllegalStateException(
  352.                     "Cannot set ExecutorService after start()!");
  353.         }

  354.         this.externalExecutor = externalExecutor;
  355.     }

  356.     /**
  357.      * Starts the background initialization. With this method the initializer
  358.      * becomes active and invokes the {@link #initialize()} method in a
  359.      * background task. A {@link BackgroundInitializer} can be started exactly
  360.      * once. The return value of this method determines whether the start was
  361.      * successful: only the first invocation of this method returns <b>true</b>,
  362.      * following invocations will return <b>false</b>.
  363.      *
  364.      * @return a flag whether the initializer could be started successfully
  365.      */
  366.     public synchronized boolean start() {
  367.         // Not yet started?
  368.         if (!isStarted()) {

  369.             // Determine the executor to use and whether a temporary one has to
  370.             // be created
  371.             final ExecutorService tempExec;
  372.             executor = getExternalExecutor();
  373.             if (executor == null) {
  374.                 executor = tempExec = createExecutor();
  375.             } else {
  376.                 tempExec = null;
  377.             }

  378.             future = executor.submit(createTask(tempExec));

  379.             return true;
  380.         }

  381.         return false;
  382.     }
  383. }