AbstractConcurrentInitializer.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.Objects;

  19. import org.apache.commons.lang3.builder.AbstractSupplier;
  20. import org.apache.commons.lang3.exception.ExceptionUtils;
  21. import org.apache.commons.lang3.function.FailableConsumer;
  22. import org.apache.commons.lang3.function.FailableSupplier;

  23. /**
  24.  * Abstracts and defines operations for {@link ConcurrentInitializer} implementations.
  25.  *
  26.  * @param <T> the type of the object managed by this initializer class.
  27.  * @param <E> The exception type thrown by {@link #initialize()}.
  28.  * @since 3.14.0
  29.  */
  30. public abstract class AbstractConcurrentInitializer<T, E extends Exception> implements ConcurrentInitializer<T> {

  31.     /**
  32.      * Builds a new instance for subclasses.
  33.      *
  34.      * @param <T> the type of the object managed by the initializer class.
  35.      * @param <I> the type of the initializer class.
  36.      * @param <B> the type of builder.
  37.      * @param <E> The exception type thrown by {@link #initialize()}.
  38.      */
  39.     public abstract static class AbstractBuilder<I extends AbstractConcurrentInitializer<T, E>, T, B extends AbstractBuilder<I, T, B, E>, E extends Exception>
  40.             extends AbstractSupplier<I, B, E> {

  41.         /**
  42.          * Closer consumer called by {@link #close()}.
  43.          */
  44.         private FailableConsumer<T, ? extends Exception> closer = FailableConsumer.nop();

  45.         /**
  46.          * Initializer supplier called by {@link #initialize()}.
  47.          */
  48.         private FailableSupplier<T, ? extends Exception> initializer = FailableSupplier.nul();

  49.         /**
  50.          * Constructs a new instance.
  51.          */
  52.         public AbstractBuilder() {
  53.             // empty
  54.         }

  55.         /**
  56.          * Gets the closer consumer called by {@link #close()}.
  57.          *
  58.          * @return the closer consumer called by {@link #close()}.
  59.          */
  60.         public FailableConsumer<T, ? extends Exception> getCloser() {
  61.             return closer;
  62.         }

  63.         /**
  64.          * Gets the initializer supplier called by {@link #initialize()}.
  65.          *
  66.          * @return the initializer supplier called by {@link #initialize()}.
  67.          */
  68.         public FailableSupplier<T, ? extends Exception> getInitializer() {
  69.             return initializer;
  70.         }

  71.         /**
  72.          * Sets the closer consumer called by {@link #close()}.
  73.          *
  74.          * @param closer the consumer called by {@link #close()}.
  75.          * @return {@code this} instance.
  76.          */
  77.         public B setCloser(final FailableConsumer<T, ? extends Exception> closer) {
  78.             this.closer = closer != null ? closer : FailableConsumer.nop();
  79.             return asThis();
  80.         }

  81.         /**
  82.          * Sets the initializer supplier called by {@link #initialize()}.
  83.          *
  84.          * @param initializer the supplier called by {@link #initialize()}.
  85.          * @return {@code this} instance.
  86.          */
  87.         public B setInitializer(final FailableSupplier<T, ? extends Exception> initializer) {
  88.             this.initializer = initializer != null ? initializer : FailableSupplier.nul();
  89.             return asThis();
  90.         }

  91.     }

  92.     /**
  93.      * Closer consumer called by {@link #close()}.
  94.      */
  95.     private final FailableConsumer<? super T, ? extends Exception> closer;

  96.     /**
  97.      * Initializer supplier called by {@link #initialize()}.
  98.      */
  99.     private final FailableSupplier<? extends T, ? extends Exception> initializer;

  100.     /**
  101.      * Constructs a new instance.
  102.      */
  103.     public AbstractConcurrentInitializer() {
  104.         this(FailableSupplier.nul(), FailableConsumer.nop());
  105.     }

  106.     /**
  107.      * Constructs a new instance.
  108.      *
  109.      * @param initializer the initializer supplier called by {@link #initialize()}.
  110.      * @param closer the closer consumer called by {@link #close()}.
  111.      */
  112.     AbstractConcurrentInitializer(final FailableSupplier<? extends T, ? extends Exception> initializer, final FailableConsumer<? super T, ? extends Exception> closer) {
  113.         this.closer = Objects.requireNonNull(closer, "closer");
  114.         this.initializer = Objects.requireNonNull(initializer, "initializer");
  115.     }

  116.     /**
  117.      * Calls the closer with the manager object.
  118.      *
  119.      * @throws ConcurrentException Thrown by the closer.
  120.      * @since 3.14.0
  121.      */
  122.     public void close() throws ConcurrentException {
  123.         if (isInitialized()) {
  124.             try {
  125.                 closer.accept(get());
  126.             } catch (final Exception e) {
  127.                 // This intentionally does not duplicate the logic in initialize
  128.                 // or care about the generic type E.
  129.                 //
  130.                 // initialize may run inside a Future and it does not make sense
  131.                 // to wrap an exception stored inside a Future. However close()
  132.                 // always runs on the current thread so it always wraps in a
  133.                 // ConcurrentException
  134.                 throw new ConcurrentException(ExceptionUtils.throwUnchecked(e));
  135.             }
  136.         }
  137.     }

  138.     /**
  139.      * Gets an Exception with a type of E as defined by a concrete subclass of this class.
  140.      *
  141.      * @param e The actual exception that was thrown
  142.      * @return a new exception with the actual type of E, that wraps e.
  143.      */
  144.     protected abstract E getTypedException(Exception e);

  145.     /**
  146.      * Creates and initializes the object managed by this {@code
  147.      * ConcurrentInitializer}. This method is called by {@link #get()} when the object is accessed for the first time. An implementation can focus on the
  148.      * creation of the object. No synchronization is needed, as this is already handled by {@code get()}.
  149.      * <p>
  150.      * Subclasses and clients that do not provide an initializer are expected to implement this method.
  151.      * </p>
  152.      *
  153.      * @return the managed data object
  154.      * @throws E if an error occurs during object creation
  155.      */
  156.     @SuppressWarnings("unchecked")
  157.     protected T initialize() throws E {
  158.         try {
  159.             return initializer.get();
  160.         } catch (final Exception e) {
  161.             // Do this first so we don't pass a RuntimeException or Error into an exception constructor
  162.             ExceptionUtils.throwUnchecked(e);

  163.             // Depending on the subclass of AbstractConcurrentInitializer E can be Exception or ConcurrentException
  164.             // if E is Exception the if statement below will always be true, and the new Exception object created
  165.             // in getTypedException will never be thrown. If E is ConcurrentException and the if statement is false
  166.             // we throw the ConcurrentException returned from getTypedException, which wraps the original exception.
  167.             final E typedException = getTypedException(e);
  168.             if (typedException.getClass().isAssignableFrom(e.getClass())) {
  169.                 throw (E) e;
  170.             }
  171.             throw typedException;
  172.         }
  173.     }

  174.     /**
  175.      * Returns true if initialization has been completed. If initialization threw an exception this will return false, but it will return true if a subsequent
  176.      * call to initialize completes successfully. If the implementation of ConcurrentInitializer can initialize multiple objects, this will only return true if
  177.      * all objects have been initialized.
  178.      *
  179.      * @return true if all initialization is complete, otherwise false
  180.      */
  181.     protected abstract boolean isInitialized();

  182. }