001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2022 microBean™. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * 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 014 * implied. See the License for the specific language governing 015 * permissions and limitations under the License. 016 */ 017package org.microbean.invoke; 018 019import java.util.NoSuchElementException; 020import java.util.Optional; 021 022import java.util.concurrent.atomic.AtomicReference; 023 024import java.util.function.Supplier; 025 026/** 027 * An {@link OptionalSupplier} that computes the value it will 028 * return from its {@link #get()} method when that method is first 029 * invoked, and that returns that computed value for all subsequent 030 * invocations of that method. 031 * 032 * @author <a href="https://about.me/lairdnelson" 033 * target="_parent">Laird Nelson</a> 034 * 035 * @param <T> The type of object returned by the {@link #get()} 036 * method 037 * 038 * @see #CachingSupplier(Object) 039 * 040 * @see #CachingSupplier(Supplier) 041 * 042 * @see #get() 043 * 044 * @see #set(Object) 045 */ 046public final class CachingSupplier<T> implements OptionalSupplier<T> { 047 048 049 /* 050 * Instance fields. 051 */ 052 053 054 private final Supplier<? extends T> delegate; 055 056 private final AtomicReference<Optional<T>> ref; 057 058 059 /* 060 * Constructors. 061 */ 062 063 064 /** 065 * Creates a new {@link CachingSupplier}. 066 * 067 * @see #CachingSupplier(Supplier) 068 * 069 * @see #get() 070 * 071 * @see #set(Object) 072 * 073 * @see 074 * AtomicReference#updateAndGet(java.util.function.UnaryOperator) 075 */ 076 public CachingSupplier() { 077 this((Supplier<? extends T>)null); 078 } 079 080 /** 081 * Creates a new {@link CachingSupplier}. 082 * 083 * <p>An invocation of this constructor will result in the {@link 084 * #set(Object)} method always returning {@code false}.</p> 085 * 086 * @param value the value that will be returned by all invocations 087 * of the {@link #get()} method; may be {@code null} in which case 088 * the {@link #get()} method will return {@code null} forever 089 * 090 * @see #get() 091 */ 092 public CachingSupplier(final T value) { 093 super(); 094 final Optional<T> optional = Optional.ofNullable(value); 095 this.ref = new AtomicReference<>(optional); 096 this.delegate = OptionalSupplier.of(optional.orElse(null)); 097 } 098 099 /** 100 * Creates a new {@link CachingSupplier}. 101 * 102 * @param supplier the {@link Supplier} that will be used to supply 103 * the value that will be returned by all invocations of the {@link 104 * #get()} method; may be {@code null} in which case the {@link 105 * #get()} method will throw a {@link NoSuchElementException} until, 106 * at least, the {@link #set(Object)} method is called; <strong>must 107 * be safe for concurrent use by multiple threads and must be 108 * side-effect free</strong> 109 * 110 * @see #get() 111 */ 112 public CachingSupplier(final Supplier<? extends T> supplier) { 113 super(); 114 this.ref = new AtomicReference<>(); 115 this.delegate = supplier == null ? CachingSupplier::noSuchElementException : supplier; 116 } 117 118 119 /* 120 * Instance methods. 121 */ 122 123 124 /** 125 * Returns the value this {@link CachingSupplier} will forever 126 * supply, computing it if necessary with the first invocation by 127 * using the {@link Supplier} supplied at {@linkplain 128 * #CachingSupplier(Supplier) construction time}. 129 * 130 * <p>If the {@link Supplier} supplied at {@linkplain 131 * #CachingSupplier(Supplier) construction time} returns {@code 132 * null} from its {@link Supplier#get()} method, then this method 133 * will forever return {@code null} as well.</p> 134 * 135 * @return the value, which may very well be {@code null} 136 * 137 * @nullability This method may return {@code null}. 138 * 139 * @idempotency This method's idempotency and determinism are 140 * determined by the idempotency and determinisim of the {@link 141 * Supplier} supplied at {@linkplain #CachingSupplier(Supplier) 142 * construction time}. 143 * 144 * @threadsafety This method is safe for concurrent use by multiple 145 * threads. 146 * 147 * @see #CachingSupplier(Object) 148 * 149 * @see #CachingSupplier(Supplier) 150 * 151 * @see #set(Object) 152 */ 153 @Override // Supplier<T> 154 public final T get() { 155 Optional<T> optional = this.ref.get(); 156 if (optional == null) { 157 optional = Optional.ofNullable(this.delegate.get()); 158 if (!this.ref.compareAndSet(null, optional)) { 159 optional = this.ref.get(); 160 } 161 } 162 return optional.orElse(null); 163 } 164 165 /** 166 * Sets the value that will be returned forever afterwards by the 167 * {@link #get()} method and returns {@code true} if and only if the 168 * value was previously unset. 169 * 170 * @param newValue the new value that will be returned by the {@link 171 * #get()} method forever afterwards; may be {@code null} 172 * 173 * @return {@code true} if and only if this assignment was permitted; 174 * {@code false} otherwise 175 * 176 * @idempotency This method is idempotent but not deterministic. 177 * 178 * @threadsafety This method is safe for concurrent use by multiple 179 * threads. 180 * 181 * @see #get() 182 * 183 * @see #CachingSupplier(Supplier) 184 * 185 * @see AtomicReference#compareAndSet(Object, Object) 186 */ 187 public final boolean set(final T newValue) { 188 return this.ref.compareAndSet(null, Optional.ofNullable(newValue)); 189 } 190 191 /** 192 * Returns an {@link Determinism} suitable for this {@link CachingSupplier}. 193 * 194 * <p>In most cases, this method returns {@link 195 * Determinism#PRESENT}. In the case that the {@linkplain 196 * #CachingSupplier() zero-argument constructor} was invoked, and 197 * the {@link #set(Object)} method has not yet been called, this 198 * method will return {@link Determinism#DETERMINISTIC}. Once the 199 * {@link #set(Object)} method has been called, this method will 200 * return {@link Determinism#PRESENT}.</p> 201 * 202 * @return one of {@link Determinism#PRESENT} or {@link 203 * Determinism#DETERMINISTIC} 204 * 205 * @nullability This method does not return {@code null}. 206 * 207 * @idempotency This method is idempotent and deterministic. 208 * 209 * @threadsafety This method is safe for concurrent use by multiple 210 * threads. 211 */ 212 @Override 213 public final Determinism determinism() { 214 return this.ref.get() == null ? Determinism.DETERMINISTIC : Determinism.PRESENT; 215 } 216 217 /** 218 * Returns a non-{@code null} {@link String} representation of this 219 * {@link CachingSupplier}. 220 * 221 * <p>The format of the return value is deliberately unspecified and 222 * subject to change without notice from version to version of this 223 * class.</p> 224 * 225 * @return a {@link String} representation of this {@link 226 * CachingSupplier} 227 * 228 * @nullability This method never returns {@code null}. 229 * 230 * @idempotency This method is idempotent but not deterministic 231 * until a value has been supplied, either via the {@link 232 * #CachingSupplier(Object)} or {@link #CachingSupplier(Supplier)} 233 * constructors or the {@link #set(Object)} method. 234 * 235 * @threadsafety This method is safe for concurrent use by multiple 236 * threads. 237 */ 238 @Override // Object 239 public final String toString() { 240 return String.valueOf(this.get()); 241 } 242 243 244 /* 245 * Static methods. 246 */ 247 248 249 private static final <T> T noSuchElementException() { 250 throw new NoSuchElementException(); 251 } 252 253}