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.juneau.commons.function; 018 019import static org.apache.juneau.commons.utils.AssertionUtils.*; 020import static org.apache.juneau.commons.utils.Utils.*; 021 022import java.util.*; 023import java.util.concurrent.atomic.*; 024import java.util.function.*; 025 026/** 027 * A thread-safe supplier that caches the result of the first call and supports resetting the cache. 028 * 029 * <p> 030 * This class extends {@link OptionalSupplier} to provide both standard {@link Supplier#get()} functionality 031 * and a {@link #reset()} method to clear the cache, forcing recomputation on the next call to {@link #get()}. 032 * It also inherits all Optional-like convenience methods from {@link OptionalSupplier}. 033 * 034 * <h5 class='section'>Usage:</h5> 035 * <p class='bjava'> 036 * <jc>// Create a resettable supplier</jc> 037 * ResettableSupplier<String> <jv>supplier</jv> = <jk>new</jk> ResettableSupplier<>(() -> expensiveComputation()); 038 * 039 * <jc>// First call computes and caches</jc> 040 * String <jv>result1</jv> = <jv>supplier</jv>.get(); 041 * 042 * <jc>// Subsequent calls return cached value</jc> 043 * String <jv>result2</jv> = <jv>supplier</jv>.get(); 044 * 045 * <jc>// Use Optional-like methods</jc> 046 * <jk>if</jk> (<jv>supplier</jv>.isPresent()) { 047 * String <jv>upper</jv> = <jv>supplier</jv>.map(String::toUpperCase).orElse(<js>"default"</js>); 048 * } 049 * 050 * <jc>// Reset forces recomputation on next get()</jc> 051 * <jv>supplier</jv>.reset(); 052 * String <jv>result3</jv> = <jv>supplier</jv>.get(); <jc>// Recomputes</jc> 053 * </p> 054 * 055 * <h5 class='section'>Thread Safety:</h5> 056 * <p> 057 * This class is thread-safe for both {@link #get()} and {@link #reset()} operations. 058 * If multiple threads call get() simultaneously after a reset, the supplier may be invoked 059 * multiple times, but only one result will be cached. 060 * 061 * <h5 class='section'>Notes:</h5> 062 * <ul> 063 * <li>The supplier may be called multiple times if threads race, but only one result is cached. 064 * <li>The cached value can be <jk>null</jk> if the supplier returns <jk>null</jk>. 065 * <li>After reset, the next get() will recompute the value. 066 * <li>This is particularly useful for testing when configuration changes and cached values need to be recalculated. 067 * <li>Inherits all Optional-like methods from {@link OptionalSupplier} (isPresent(), isEmpty(), map(), orElse(), etc.). 068 * </ul> 069 * 070 * <h5 class='section'>See Also:</h5> 071 * <ul> 072 * <li class='jm'>{@link org.apache.juneau.commons.utils.Utils#memr(Supplier)} 073 * <li class='jc'>{@link OptionalSupplier} - Base interface providing Optional-like methods 074 * </ul> 075 * 076 * @param <T> The type of value supplied. 077 */ 078public class ResettableSupplier<T> implements OptionalSupplier<T> { 079 private final Supplier<T> supplier; 080 private final AtomicReference<Optional<T>> cache = new AtomicReference<>(); 081 082 /** 083 * Constructor. 084 * 085 * @param supplier The underlying supplier to call when computing values. Must not be <jk>null</jk>. 086 */ 087 public ResettableSupplier(Supplier<T> supplier) { 088 this.supplier = assertArgNotNull("supplier", supplier); 089 } 090 091 /** 092 * Returns the cached value if present, otherwise computes it using the underlying supplier. 093 * 094 * @return The cached or newly computed value. 095 */ 096 @Override 097 public T get() { 098 Optional<T> h = cache.get(); 099 if (h == null) { 100 h = opt(supplier.get()); 101 if (! cache.compareAndSet(null, h)) { 102 // Another thread beat us, use their value 103 h = cache.get(); 104 } 105 } 106 return h.orElse(null); 107 } 108 109 /** 110 * Clears the cached value, forcing the next call to {@link #get()} to recompute the value. 111 * 112 * <p> 113 * This method is thread-safe and can be called from multiple threads. After reset, 114 * the next get() call will invoke the underlying supplier to compute a fresh value. 115 */ 116 public void reset() { 117 cache.set(null); 118 } 119 120 /** 121 * Sets the cached value directly without invoking the underlying supplier. 122 * 123 * <p> 124 * This method allows you to override the cached value, bypassing the supplier. 125 * Subsequent calls to {@link #get()} will return the set value until {@link #reset()} is called 126 * or the value is set again. 127 * 128 * <p> 129 * This method is thread-safe and is particularly useful for testing when you need to 130 * inject a specific value without invoking the supplier. 131 * 132 * <h5 class='section'>Example:</h5> 133 * <p class='bjava'> 134 * <jc>// Create a supplier</jc> 135 * ResettableSupplier<String> <jv>supplier</jv> = <jk>new</jk> ResettableSupplier<>(() -> <js>"computed"</js>); 136 * 137 * <jc>// Set a value directly without invoking the supplier</jc> 138 * <jv>supplier</jv>.<jsm>set</jsm>(<js>"injected"</js>); 139 * 140 * <jc>// get() returns the injected value</jc> 141 * assertEquals(<js>"injected"</js>, <jv>supplier</jv>.<jsm>get</jsm>()); 142 * 143 * <jc>// Reset clears the cache, next get() will invoke the supplier</jc> 144 * <jv>supplier</jv>.<jsm>reset</jsm>(); 145 * assertEquals(<js>"computed"</js>, <jv>supplier</jv>.<jsm>get</jsm>()); 146 * </p> 147 * 148 * @param value The value to cache. Can be <jk>null</jk>. 149 */ 150 public void set(T value) { 151 cache.set(opt(value)); 152 } 153 154 /** 155 * Returns <jk>true</jk> if the supplier has not yet been called (cache is empty). 156 * 157 * <p> 158 * This method checks whether the cached value is <jk>null</jk>, which indicates that 159 * {@link #get()} has not yet been called, or the cache was reset. 160 * 161 * @return <jk>true</jk> if the supplier has not been called yet, <jk>false</jk> if a value has been cached. 162 */ 163 public boolean isSupplied() { 164 return cache.get() == null; 165 } 166 167 /** 168 * Creates a copy of this supplier. 169 * 170 * <p> 171 * If a value has been cached, the copy will use a supplier that returns that cached value. 172 * If no value has been cached yet, the copy will use the original supplier. 173 * 174 * @return A new {@link ResettableSupplier} instance with the same state as this supplier. 175 */ 176 public ResettableSupplier<T> copy() { 177 Optional<T> o = cache.get(); 178 if (o == null) 179 return new ResettableSupplier<>(supplier); 180 return new ResettableSupplier<>(()->o.get()); 181 } 182 183 /** 184 * If a value is present, applies the provided mapping function to it and returns a ResettableSupplier describing the result. 185 * 186 * <p> 187 * The returned ResettableSupplier maintains its own cache, independent of this supplier. 188 * Resetting the mapped supplier does not affect this supplier, and vice versa. 189 * 190 * @param <U> The type of the result of the mapping function. 191 * @param mapper A mapping function to apply to the value, if present. Must not be <jk>null</jk>. 192 * @return A ResettableSupplier describing the result of applying a mapping function to the value of this ResettableSupplier, if a value is present, otherwise an empty ResettableSupplier. 193 */ 194 @Override 195 public <U> ResettableSupplier<U> map(Function<? super T, ? extends U> mapper) { 196 assertArgNotNull("mapper", mapper); 197 return new ResettableSupplier<>(() -> { 198 T value = get(); 199 return nn(value) ? mapper.apply(value) : null; 200 }); 201 } 202 203 /** 204 * If a value is present, and the value matches the given predicate, returns a ResettableSupplier describing the value, otherwise returns an empty ResettableSupplier. 205 * 206 * <p> 207 * The returned ResettableSupplier maintains its own cache, independent of this supplier. 208 * Resetting the filtered supplier does not affect this supplier, and vice versa. 209 * 210 * @param predicate A predicate to apply to the value, if present. Must not be <jk>null</jk>. 211 * @return A ResettableSupplier describing the value of this ResettableSupplier if a value is present and the value matches the given predicate, otherwise an empty ResettableSupplier. 212 */ 213 @Override 214 public ResettableSupplier<T> filter(Predicate<? super T> predicate) { 215 assertArgNotNull("predicate", predicate); 216 return new ResettableSupplier<>(() -> { 217 T value = get(); 218 return (nn(value) && predicate.test(value)) ? value : null; 219 }); 220 } 221}