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

    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 *      https://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 * @param <T> the type of the object managed by this initializer class
    086 * @since 3.0
    087 */
    088public class BackgroundInitializer<T> extends AbstractConcurrentInitializer<T, Exception> {
    089
    090    /**
    091     * Builds a new instance.
    092     *
    093     * @param <T> The type of results supplied by this builder.
    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         * @param externalExecutor the {@link ExecutorService} to be used.
    122         * @return {@code this} instance.
    123         */
    124        public Builder<I, T> setExternalExecutor(final ExecutorService externalExecutor) {
    125            this.externalExecutor = externalExecutor;
    126            return asThis();
    127        }
    128
    129    }
    130
    131    private final class InitializationTask implements Callable<T> {
    132        /** Stores the executor service to be destroyed at the end. */
    133        private final ExecutorService execFinally;
    134
    135        /**
    136         * Creates a new instance of {@link InitializationTask} and initializes
    137         * it with the {@link ExecutorService} to be destroyed at the end.
    138         *
    139         * @param exec the {@link ExecutorService}
    140         */
    141        InitializationTask(final ExecutorService exec) {
    142            execFinally = exec;
    143        }
    144
    145        /**
    146         * Initiates initialization and returns the result.
    147         *
    148         * @return the result object
    149         * @throws Exception if an error occurs
    150         */
    151        @Override
    152        public T call() throws Exception {
    153            try {
    154                return initialize();
    155            } finally {
    156                if (execFinally != null) {
    157                    execFinally.shutdown();
    158                }
    159            }
    160        }
    161    }
    162
    163    /**
    164     * Creates a new builder.
    165     *
    166     * @param <T> the type of object to build.
    167     * @return a new builder.
    168     * @since 3.14.0
    169     */
    170    public static <T> Builder<BackgroundInitializer<T>, T> builder() {
    171        return new Builder<>();
    172    }
    173
    174    /** The external executor service for executing tasks. */
    175    private ExecutorService externalExecutor; // @GuardedBy("this")
    176
    177    /** A reference to the executor service that is actually used. */
    178    private ExecutorService executor; // @GuardedBy("this")
    179
    180    /** Stores the handle to the background task. */
    181    private Future<T> future;  // @GuardedBy("this")
    182
    183    /**
    184     * Creates a new instance of {@link BackgroundInitializer}. No external
    185     * {@link ExecutorService} is used.
    186     */
    187    protected BackgroundInitializer() {
    188        this(null);
    189    }
    190
    191    /**
    192     * Creates a new instance of {@link BackgroundInitializer} and initializes
    193     * it with the given {@link ExecutorService}. If the {@link ExecutorService}
    194     * is not null, the background task for initializing this object will be
    195     * scheduled at this service. Otherwise a new temporary {@code
    196     * ExecutorService} is created.
    197     *
    198     * @param exec an external {@link ExecutorService} to be used for task.
    199     * execution
    200     */
    201    protected BackgroundInitializer(final ExecutorService exec) {
    202        setExternalExecutor(exec);
    203    }
    204
    205    /**
    206     * Constructs a new instance.
    207     *
    208     * @param initializer the initializer supplier called by {@link #initialize()}.
    209     * @param closer the closer consumer called by {@link #close()}.
    210     * @param exec the {@link ExecutorService} to be used @see #setExternalExecutor(ExecutorService)
    211     */
    212    private BackgroundInitializer(final FailableSupplier<T, ConcurrentException> initializer, final FailableConsumer<T, ConcurrentException> closer, final ExecutorService exec) {
    213        super(initializer, closer);
    214        setExternalExecutor(exec);
    215    }
    216
    217    /**
    218     * Creates the {@link ExecutorService} to be used. This method is called if
    219     * no {@link ExecutorService} was provided at construction time.
    220     *
    221     * @return the {@link ExecutorService} to be used.
    222     */
    223    private ExecutorService createExecutor() {
    224        return Executors.newFixedThreadPool(getTaskCount());
    225    }
    226
    227    /**
    228     * Creates a task for the background initialization. The {@link Callable}
    229     * object returned by this method is passed to the {@link ExecutorService}.
    230     * This implementation returns a task that invokes the {@link #initialize()}
    231     * method. If a temporary {@link ExecutorService} is used, it is destroyed
    232     * at the end of the task.
    233     *
    234     * @param execDestroy the {@link ExecutorService} to be destroyed by the
    235     * task.
    236     * @return a task for the background initialization.
    237     */
    238    private Callable<T> createTask(final ExecutorService execDestroy) {
    239        return new InitializationTask(execDestroy);
    240    }
    241
    242    /**
    243     * Gets the result of the background initialization. This method blocks
    244     * until initialization is complete. If the background processing caused a
    245     * runtime exception, it is directly thrown by this method. Checked
    246     * exceptions, including {@link InterruptedException} are wrapped in a
    247     * {@link ConcurrentException}. Calling this method before {@link #start()}
    248     * was called causes an {@link IllegalStateException} exception to be
    249     * thrown.
    250     *
    251     * @return the object produced by this initializer.
    252     * @throws ConcurrentException if a checked exception occurred during
    253     * background processing.
    254     * @throws IllegalStateException if {@link #start()} has not been called.
    255     */
    256    @Override
    257    public T get() throws ConcurrentException {
    258        try {
    259            return getFuture().get();
    260        } catch (final ExecutionException execex) {
    261            ConcurrentUtils.handleCause(execex);
    262            return null; // should not be reached
    263        } catch (final InterruptedException iex) {
    264            // reset interrupted state
    265            Thread.currentThread().interrupt();
    266            throw new ConcurrentException(iex);
    267        }
    268    }
    269
    270    /**
    271     * Gets the {@link ExecutorService} that is actually used for executing
    272     * the background task. This method can be called after {@link #start()}
    273     * (before {@code start()} it returns <strong>null</strong>). If an external executor
    274     * was set, this is also the active executor. Otherwise this method returns
    275     * the temporary executor that was created by this object.
    276     *
    277     * @return the {@link ExecutorService} for executing the background task.
    278     */
    279    protected final synchronized ExecutorService getActiveExecutor() {
    280        return executor;
    281    }
    282
    283    /**
    284     * Gets the external {@link ExecutorService} to be used by this class.
    285     *
    286     * @return the {@link ExecutorService}.
    287     */
    288    public final synchronized ExecutorService getExternalExecutor() {
    289        return externalExecutor;
    290    }
    291
    292    /**
    293     * Gets the {@link Future} object that was created when {@link #start()}
    294     * was called. Therefore this method can only be called after {@code
    295     * start()}.
    296     *
    297     * @return the {@link Future} object wrapped by this initializer.
    298     * @throws IllegalStateException if {@link #start()} has not been called.
    299     */
    300    public synchronized Future<T> getFuture() {
    301        if (future == null) {
    302            throw new IllegalStateException("start() must be called first!");
    303        }
    304        return future;
    305    }
    306
    307    /**
    308     * Gets the number of background tasks to be created for this
    309     * initializer. This information is evaluated when a temporary {@code
    310     * ExecutorService} is created. This base implementation returns 1. Derived
    311     * classes that do more complex background processing can override it. This
    312     * method is called from a synchronized block by the {@link #start()}
    313     * method. Therefore overriding methods should be careful with obtaining
    314     * other locks and return as fast as possible.
    315     *
    316     * @return the number of background tasks required by this initializer.
    317     */
    318    protected int getTaskCount() {
    319        return 1;
    320    }
    321
    322    /**
    323     * {@inheritDoc}
    324     */
    325    @Override
    326    protected Exception getTypedException(final Exception e) {
    327        //This Exception object will be used for type comparison in AbstractConcurrentInitializer.initialize but not thrown
    328        return new Exception(e);
    329    }
    330
    331    /**
    332     * Tests whether this instance is initialized. Once initialized, always returns true.
    333     * If initialization failed then the failure will be cached and this will never return
    334     * true.
    335     *
    336     * @return true if initialization completed successfully, otherwise false.
    337     * @since 3.14.0
    338     */
    339    @Override
    340    public synchronized boolean isInitialized() {
    341        if (future == null || !future.isDone()) {
    342            return false;
    343        }
    344        try {
    345            future.get();
    346            return true;
    347        } catch (CancellationException | ExecutionException | InterruptedException e) {
    348            return false;
    349        }
    350    }
    351
    352    /**
    353     * Tests whether this {@link BackgroundInitializer} has already
    354     * been started.
    355     *
    356     * @return a flag whether the {@link #start()} method has already been
    357     * called.
    358     */
    359    public synchronized boolean isStarted() {
    360        return future != null;
    361    }
    362
    363    /**
    364     * Sets an {@link ExecutorService} to be used by this class. The {@code
    365     * ExecutorService} passed to this method is used for executing the
    366     * background task. Thus it is possible to re-use an already existing
    367     * {@link ExecutorService} or to use a specially configured one. If no
    368     * {@link ExecutorService} is set, this instance creates a temporary one and
    369     * destroys it after background initialization is complete. Note that this
    370     * method must be called before {@link #start()}; otherwise an exception is
    371     * thrown.
    372     *
    373     * @param externalExecutor the {@link ExecutorService} to be used.
    374     * @throws IllegalStateException if this initializer has already been
    375     * started.
    376     */
    377    public final synchronized void setExternalExecutor(final ExecutorService externalExecutor) {
    378        if (isStarted()) {
    379            throw new IllegalStateException("Cannot set ExecutorService after start()!");
    380        }
    381        this.externalExecutor = externalExecutor;
    382    }
    383
    384    /**
    385     * Starts the background initialization. With this method the initializer
    386     * becomes active and invokes the {@link #initialize()} method in a
    387     * background task. A {@link BackgroundInitializer} can be started exactly
    388     * once. The return value of this method determines whether the start was
    389     * successful: only the first invocation of this method returns <strong>true</strong>,
    390     * following invocations will return <strong>false</strong>.
    391     *
    392     * @return a flag whether the initializer could be started successfully.
    393     */
    394    public synchronized boolean start() {
    395        // Not yet started?
    396        if (!isStarted()) {
    397            // Determine the executor to use and whether a temporary one has to be created.
    398            final ExecutorService tempExec;
    399            executor = getExternalExecutor();
    400            if (executor == null) {
    401                executor = tempExec = createExecutor();
    402            } else {
    403                tempExec = null;
    404            }
    405            future = executor.submit(createTask(tempExec));
    406            return true;
    407        }
    408        return false;
    409    }
    410}