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 */ 017 018package org.apache.commons.lang3.concurrent; 019 020import java.util.Objects; 021 022import org.apache.commons.lang3.builder.AbstractSupplier; 023import org.apache.commons.lang3.exception.ExceptionUtils; 024import org.apache.commons.lang3.function.FailableConsumer; 025import org.apache.commons.lang3.function.FailableSupplier; 026 027/** 028 * Abstracts and defines operations for ConcurrentInitializer implementations. 029 * 030 * @param <T> the type of the object managed by this initializer class. 031 * @param <E> The exception type thrown by {@link #initialize()}. 032 * @since 3.14.0 033 */ 034public abstract class AbstractConcurrentInitializer<T, E extends Exception> implements ConcurrentInitializer<T> { 035 036 /** 037 * Builds a new instance for subclasses. 038 * 039 * @param <T> the type of the object managed by the initializer class. 040 * @param <I> the type of the initializer class. 041 * @param <B> the type of builder. 042 * @param <E> The exception type thrown by {@link #initialize()}. 043 */ 044 public abstract static class AbstractBuilder<I extends AbstractConcurrentInitializer<T, E>, T, B extends AbstractBuilder<I, T, B, E>, E extends Exception> 045 extends AbstractSupplier<I, B, E> { 046 047 /** 048 * Closer consumer called by {@link #close()}. 049 */ 050 private FailableConsumer<T, ? extends Exception> closer = FailableConsumer.nop(); 051 052 /** 053 * Initializer supplier called by {@link #initialize()}. 054 */ 055 private FailableSupplier<T, ? extends Exception> initializer = FailableSupplier.nul(); 056 057 /** 058 * Gets the closer consumer called by {@link #close()}. 059 * 060 * @return the closer consumer called by {@link #close()}. 061 */ 062 public FailableConsumer<T, ? extends Exception> getCloser() { 063 return closer; 064 } 065 066 /** 067 * Gets the initializer supplier called by {@link #initialize()}. 068 * 069 * @return the initializer supplier called by {@link #initialize()}. 070 */ 071 public FailableSupplier<T, ? extends Exception> getInitializer() { 072 return initializer; 073 } 074 075 /** 076 * Sets the closer consumer called by {@link #close()}. 077 * 078 * @param closer the consumer called by {@link #close()}. 079 * @return this 080 */ 081 public B setCloser(final FailableConsumer<T, ? extends Exception> closer) { 082 this.closer = closer != null ? closer : FailableConsumer.nop(); 083 return asThis(); 084 } 085 086 /** 087 * Sets the initializer supplier called by {@link #initialize()}. 088 * 089 * @param initializer the supplier called by {@link #initialize()}. 090 * @return this 091 */ 092 public B setInitializer(final FailableSupplier<T, ? extends Exception> initializer) { 093 this.initializer = initializer != null ? initializer : FailableSupplier.nul(); 094 return asThis(); 095 } 096 097 } 098 099 /** 100 * Closer consumer called by {@link #close()}. 101 */ 102 private final FailableConsumer<? super T, ? extends Exception> closer; 103 104 /** 105 * Initializer supplier called by {@link #initialize()}. 106 */ 107 private final FailableSupplier<? extends T, ? extends Exception> initializer; 108 109 /** 110 * Constructs a new instance. 111 */ 112 public AbstractConcurrentInitializer() { 113 this(FailableSupplier.nul(), FailableConsumer.nop()); 114 } 115 116 /** 117 * Constructs a new instance. 118 * 119 * @param initializer the initializer supplier called by {@link #initialize()}. 120 * @param closer the closer consumer called by {@link #close()}. 121 */ 122 AbstractConcurrentInitializer(final FailableSupplier<? extends T, ? extends Exception> initializer, final FailableConsumer<? super T, ? extends Exception> closer) { 123 this.closer = Objects.requireNonNull(closer, "closer"); 124 this.initializer = Objects.requireNonNull(initializer, "initializer"); 125 } 126 127 /** 128 * Calls the closer with the manager object. 129 * 130 * @throws ConcurrentException Thrown by the closer. 131 * @since 3.14.0 132 */ 133 public void close() throws ConcurrentException { 134 if (isInitialized()) { 135 try { 136 closer.accept(get()); 137 } catch (final Exception e) { 138 // This intentionally does not duplicate the logic in initialize 139 // or care about the generic type E. 140 // 141 // initialize may run inside a Future and it does not make sense 142 // to wrap an exception stored inside a Future. However close() 143 // always runs on the current thread so it always wraps in a 144 // ConcurrentException 145 throw new ConcurrentException(ExceptionUtils.throwUnchecked(e)); 146 } 147 } 148 } 149 150 /** 151 * Gets an Exception with a type of E as defined by a concrete subclass of this class. 152 * 153 * @param e The actual exception that was thrown 154 * @return a new exception with the actual type of E, that wraps e. 155 */ 156 protected abstract E getTypedException(Exception e); 157 158 /** 159 * Creates and initializes the object managed by this {@code 160 * ConcurrentInitializer}. This method is called by {@link #get()} when the object is accessed for the first time. An implementation can focus on the 161 * creation of the object. No synchronization is needed, as this is already handled by {@code get()}. 162 * <p> 163 * Subclasses and clients that do not provide an initializer are expected to implement this method. 164 * </p> 165 * 166 * @return the managed data object 167 * @throws E if an error occurs during object creation 168 */ 169 @SuppressWarnings("unchecked") 170 protected T initialize() throws E { 171 try { 172 return initializer.get(); 173 } catch (final Exception e) { 174 // Do this first so we don't pass a RuntimeException or Error into an exception constructor 175 ExceptionUtils.throwUnchecked(e); 176 177 // Depending on the subclass of AbstractConcurrentInitializer E can be Exception or ConcurrentException 178 // if E is Exception the if statement below will always be true, and the new Exception object created 179 // in getTypedException will never be thrown. If E is ConcurrentException and the if statement is false 180 // we throw the ConcurrentException returned from getTypedException, which wraps the original exception. 181 final E typedException = getTypedException(e); 182 if (typedException.getClass().isAssignableFrom(e.getClass())) { 183 throw (E) e; 184 } 185 throw typedException; 186 } 187 } 188 189 /** 190 * Returns true if initialization has been completed. If initialization threw an exception this will return false, but it will return true if a subsequent 191 * call to initialize completes successfully. If the implementation of ConcurrentInitializer can initialize multiple objects, this will only return true if 192 * all objects have been initialized. 193 * 194 * @return true if all initialization is complete, otherwise false 195 */ 196 protected abstract boolean isInitialized(); 197 198}