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 18 package org.apache.commons.lang3.concurrent; 19 20 import java.util.Objects; 21 22 import org.apache.commons.lang3.builder.AbstractSupplier; 23 import org.apache.commons.lang3.exception.ExceptionUtils; 24 import org.apache.commons.lang3.function.FailableConsumer; 25 import org.apache.commons.lang3.function.FailableSupplier; 26 27 /** 28 * Abstracts and defines operations for {@link ConcurrentInitializer} implementations. 29 * 30 * @param <T> the type of the object managed by this initializer class. 31 * @param <E> The exception type thrown by {@link #initialize()}. 32 * @since 3.14.0 33 */ 34 public abstract class AbstractConcurrentInitializer<T, E extends Exception> implements ConcurrentInitializer<T> { 35 36 /** 37 * Builds a new instance for subclasses. 38 * 39 * @param <T> the type of the object managed by the initializer class. 40 * @param <I> the type of the initializer class. 41 * @param <B> the type of builder. 42 * @param <E> The exception type thrown by {@link #initialize()}. 43 */ 44 public abstract static class AbstractBuilder<I extends AbstractConcurrentInitializer<T, E>, T, B extends AbstractBuilder<I, T, B, E>, E extends Exception> 45 extends AbstractSupplier<I, B, E> { 46 47 /** 48 * Closer consumer called by {@link #close()}. 49 */ 50 private FailableConsumer<T, ? extends Exception> closer = FailableConsumer.nop(); 51 52 /** 53 * Initializer supplier called by {@link #initialize()}. 54 */ 55 private FailableSupplier<T, ? extends Exception> initializer = FailableSupplier.nul(); 56 57 /** 58 * Constructs a new instance. 59 */ 60 public AbstractBuilder() { 61 // empty 62 } 63 64 /** 65 * Gets the closer consumer called by {@link #close()}. 66 * 67 * @return the closer consumer called by {@link #close()}. 68 */ 69 public FailableConsumer<T, ? extends Exception> getCloser() { 70 return closer; 71 } 72 73 /** 74 * Gets the initializer supplier called by {@link #initialize()}. 75 * 76 * @return the initializer supplier called by {@link #initialize()}. 77 */ 78 public FailableSupplier<T, ? extends Exception> getInitializer() { 79 return initializer; 80 } 81 82 /** 83 * Sets the closer consumer called by {@link #close()}. 84 * 85 * @param closer the consumer called by {@link #close()}. 86 * @return {@code this} instance. 87 */ 88 public B setCloser(final FailableConsumer<T, ? extends Exception> closer) { 89 this.closer = closer != null ? closer : FailableConsumer.nop(); 90 return asThis(); 91 } 92 93 /** 94 * Sets the initializer supplier called by {@link #initialize()}. 95 * 96 * @param initializer the supplier called by {@link #initialize()}. 97 * @return {@code this} instance. 98 */ 99 public B setInitializer(final FailableSupplier<T, ? extends Exception> initializer) { 100 this.initializer = initializer != null ? initializer : FailableSupplier.nul(); 101 return asThis(); 102 } 103 104 } 105 106 /** 107 * Closer consumer called by {@link #close()}. 108 */ 109 private final FailableConsumer<? super T, ? extends Exception> closer; 110 111 /** 112 * Initializer supplier called by {@link #initialize()}. 113 */ 114 private final FailableSupplier<? extends T, ? extends Exception> initializer; 115 116 /** 117 * Constructs a new instance. 118 */ 119 public AbstractConcurrentInitializer() { 120 this(FailableSupplier.nul(), FailableConsumer.nop()); 121 } 122 123 /** 124 * Constructs a new instance. 125 * 126 * @param initializer the initializer supplier called by {@link #initialize()}. 127 * @param closer the closer consumer called by {@link #close()}. 128 */ 129 AbstractConcurrentInitializer(final FailableSupplier<? extends T, ? extends Exception> initializer, final FailableConsumer<? super T, ? extends Exception> closer) { 130 this.closer = Objects.requireNonNull(closer, "closer"); 131 this.initializer = Objects.requireNonNull(initializer, "initializer"); 132 } 133 134 /** 135 * Calls the closer with the manager object. 136 * 137 * @throws ConcurrentException Thrown by the closer. 138 * @since 3.14.0 139 */ 140 public void close() throws ConcurrentException { 141 if (isInitialized()) { 142 try { 143 closer.accept(get()); 144 } catch (final Exception e) { 145 // This intentionally does not duplicate the logic in initialize 146 // or care about the generic type E. 147 // 148 // initialize may run inside a Future and it does not make sense 149 // to wrap an exception stored inside a Future. However close() 150 // always runs on the current thread so it always wraps in a 151 // ConcurrentException 152 throw new ConcurrentException(ExceptionUtils.throwUnchecked(e)); 153 } 154 } 155 } 156 157 /** 158 * Gets an Exception with a type of E as defined by a concrete subclass of this class. 159 * 160 * @param e The actual exception that was thrown 161 * @return a new exception with the actual type of E, that wraps e. 162 */ 163 protected abstract E getTypedException(Exception e); 164 165 /** 166 * Creates and initializes the object managed by this {@code 167 * ConcurrentInitializer}. This method is called by {@link #get()} when the object is accessed for the first time. An implementation can focus on the 168 * creation of the object. No synchronization is needed, as this is already handled by {@code get()}. 169 * <p> 170 * Subclasses and clients that do not provide an initializer are expected to implement this method. 171 * </p> 172 * 173 * @return the managed data object 174 * @throws E if an error occurs during object creation 175 */ 176 @SuppressWarnings("unchecked") 177 protected T initialize() throws E { 178 try { 179 return initializer.get(); 180 } catch (final Exception e) { 181 // Do this first so we don't pass a RuntimeException or Error into an exception constructor 182 ExceptionUtils.throwUnchecked(e); 183 184 // Depending on the subclass of AbstractConcurrentInitializer E can be Exception or ConcurrentException 185 // if E is Exception the if statement below will always be true, and the new Exception object created 186 // in getTypedException will never be thrown. If E is ConcurrentException and the if statement is false 187 // we throw the ConcurrentException returned from getTypedException, which wraps the original exception. 188 final E typedException = getTypedException(e); 189 if (typedException.getClass().isAssignableFrom(e.getClass())) { 190 throw (E) e; 191 } 192 throw typedException; 193 } 194 } 195 196 /** 197 * Returns true if initialization has been completed. If initialization threw an exception this will return false, but it will return true if a subsequent 198 * call to initialize completes successfully. If the implementation of ConcurrentInitializer can initialize multiple objects, this will only return true if 199 * all objects have been initialized. 200 * 201 * @return true if all initialization is complete, otherwise false 202 */ 203 protected abstract boolean isInitialized(); 204 205 }