Source code
Description: source: package: org.apache.commons.lang3.concurrent, class: BackgroundInitializer, class: Builder

    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.lang3.concurrent;
    018
    019import java.util.concurrent.Callable;
    020import java.util.concurrent.CancellationException;
    021import java.util.concurrent.ExecutionException;
    022import java.util.concurrent.ExecutorService;
    023import java.util.concurrent.Executors;
    024import java.util.concurrent.Future;
    025
    026import org.apache.commons.lang3.function.FailableConsumer;
    027import org.apache.commons.lang3.function.FailableSupplier;
    028
    029/**
    030 * A class that allows complex initialization operations in a background task.
    031 *
    032 * <p>
    033 * Applications often have to do some expensive initialization steps when they
    034 * are started, e.g. constructing a connection to a database, reading a
    035 * configuration file, etc. Doing these things in parallel can enhance
    036 * performance as the CPU load can be improved. However, when access to the
    037 * resources initialized in a background thread is actually required,
    038 * synchronization has to be performed to ensure that their initialization is
    039 * complete.
    040 * </p>
    041 * <p>
    042 * This abstract base class provides support for this use case. A concrete
    043 * subclass must implement the {@link #initialize()} method. Here an arbitrary
    044 * initialization can be implemented, and a result object can be returned. With
    045 * this method in place the basic usage of this class is as follows (where
    046 * {@code MyBackgroundInitializer} is a concrete subclass):
    047 * </p>
    048 *
    049 * <pre>
    050 * MyBackgroundInitializer initializer = new MyBackgroundInitializer();
    051 * initializer.start();
    052 * // Now do some other things. Initialization runs in a parallel thread
    053 * ...
    054 * // Wait for the end of initialization and access the result object
    055 * Object result = initializer.get();
    056 * </pre>
    057 *
    058 * <p>
    059 * After the construction of a {@link BackgroundInitializer} object its
    060 * {@link #start()} method has to be called. This starts the background
    061 * processing. The application can now continue to do other things. When it
    062 * needs access to the object produced by the {@link BackgroundInitializer} it
    063 * calls its {@link #get()} method. If initialization is already complete,
    064 * {@link #get()} returns the result object immediately. Otherwise it blocks
    065 * until the result object is fully constructed.
    066 * </p>
    067 * <p>
    068 * {@link BackgroundInitializer} is a thin wrapper around a {@link Future}
    069 * object and uses an {@link ExecutorService} for running the background
    070 * initialization task. It is possible to pass in an {@link ExecutorService} at
    071 * construction time or set one using {@code setExternalExecutor()} before
    072 * {@code start()} was called. Then this object is used to spawn the background
    073 * task. If no {@link ExecutorService} has been provided, {@code
    074 * BackgroundInitializer} creates a temporary {@link ExecutorService} and
    075 * destroys it when initialization is complete.
    076 * </p>
    077 * <p>
    078 * The methods provided by {@link BackgroundInitializer} provide for minimal
    079 * interaction with the wrapped {@link Future} object. It is also possible to
    080 * obtain the {@link Future} object directly. Then the enhanced functionality
    081 * offered by {@link Future} can be used, e.g. to check whether the background
    082 * operation is complete or to cancel the operation.
    083 * </p>
    084 *
    085 * @since 3.0
    086 * @param <T> the type of the object managed by this initializer class
    087 */
    088public class BackgroundInitializer<T> extends AbstractConcurrentInitializer<T, Exception> {
    089
    090    /**
    091     * Builds a new instance.
    092     *
    093     * @param <T> the type of the object managed by the initializer.
    094     * @param <I> the type of the initializer managed by this builder.
    095     * @since 3.14.0
    096     */
    097    public static class Builder<I extends BackgroundInitializer<T>, T> extends AbstractBuilder<I, T, Builder<I, T>, Exception> {
    098
    099        /**
    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}