/* * SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ /* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ package org.opensearch.common.util; import org.opensearch.common.CheckedSupplier; import java.util.Objects; import java.util.function.Consumer; /** * Encapsulates a {@link CheckedSupplier} which is lazily invoked once on the * first call to {@code #getOrCompute()}. The value which the * supplier returns is memorized and will be served until * {@code #reset()} is called. Each value returned by {@code #getOrCompute()}, * newly minted or cached, will be passed to the onGet * {@link Consumer}. On {@code #reset()} the value will be passed to the * onReset {@code Consumer} and the next {@code #getOrCompute()} * will regenerate the value. * * @opensearch.internal */ public final class LazyInitializable { private final CheckedSupplier supplier; private final Consumer onGet; private final Consumer onReset; private volatile T value; /** * Creates the simple LazyInitializable instance. * * @param supplier * The {@code CheckedSupplier} to generate values which will be * served on {@code #getOrCompute()} invocations. */ public LazyInitializable(CheckedSupplier supplier) { this(supplier, v -> {}, v -> {}); } /** * Creates the complete LazyInitializable instance. * * @param supplier * The {@code CheckedSupplier} to generate values which will be * served on {@code #getOrCompute()} invocations. * @param onGet * A {@code Consumer} which is called on each value, newly forged or * stale, that is returned by {@code #getOrCompute()} * @param onReset * A {@code Consumer} which is invoked on the value that will be * erased when calling {@code #reset()} */ public LazyInitializable(CheckedSupplier supplier, Consumer onGet, Consumer onReset) { this.supplier = supplier; this.onGet = onGet; this.onReset = onReset; } /** * Returns a value that was created by supplier. The value might * have been previously created, if not it will be created now, thread safe of * course. */ public T getOrCompute() throws E { final T readOnce = value; // Read volatile just once... final T result = readOnce == null ? maybeCompute(supplier) : readOnce; onGet.accept(result); return result; } /** * Clears the value, if it has been previously created by calling * {@code #getOrCompute()}. The onReset will be called on this * value. The next call to {@code #getOrCompute()} will recreate the value. */ public synchronized void reset() { if (value != null) { onReset.accept(value); value = null; } } /** * Creates a new value thread safely. */ private synchronized T maybeCompute(CheckedSupplier supplier) throws E { if (value == null) { value = Objects.requireNonNull(supplier.get()); } return value; } }