2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.pentair.internal.utils;
15 import java.lang.ref.SoftReference;
16 import java.time.Duration;
17 import java.util.function.Supplier;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
23 * This is a modified version of the ExpiryCache which adds functions such as getLastKnownValue. It also allows an
24 * interface via Supplier which will return the value, or through a function which calls putValue.
26 * There must be provided an action in order to retrieve/calculate the value. This action will be called only if the
27 * answer from the last calculation is not valid anymore, i.e. if it is expired.
29 * @author Christoph Weitkamp - Initial contribution
30 * @author Martin van Wingerden - Add Duration constructor
31 * @author Jeff James - Added added getLastKnownValue
33 * @param <V> the type of the value
36 public class ExpiringCache<V> {
37 private final long expiry;
39 private SoftReference<@Nullable V> value = new SoftReference<>(null);
40 private long expiresAt;
42 public interface RefreshAction {
46 public ExpiringCache() {
51 * Create a new instance.
53 * @param expiry the duration for how long the value stays valid
54 * @param action the action to retrieve/calculate the value
55 * @throws IllegalArgumentException For an expire value <=0.
57 public ExpiringCache(Duration expiry) {
58 if (expiry.isNegative() || expiry.isZero()) {
59 throw new IllegalArgumentException("Cache expire time must be greater than 0");
61 this.expiry = expiry.toNanos();
65 * Create a new instance.
67 * @param expiry the duration in milliseconds for how long the value stays valid
68 * @param action the action to retrieve/calculate the value
70 public ExpiringCache(long expiry) {
71 this(Duration.ofMillis(expiry));
75 * Returns the value - possibly from the cache, if it is still valid.
77 public synchronized @Nullable V getValue(Supplier<@Nullable V> action) {
79 V cachedValue = value.get();
80 if (cachedValue == null || isExpired()) {
81 return refreshValue(action);
87 * Returns the value - either from the cache or will call the action function which is responsible for calling
90 public synchronized @Nullable V getValue(RefreshAction action) {
92 V cachedValue = value.get();
93 if (cachedValue == null || isExpired()) {
95 cachedValue = value.get();
102 * Returns the last known value
104 public synchronized @Nullable V getLastKnownValue() {
109 * Puts a new value into the cache.
111 * @param value the new value
113 public final synchronized void putValue(@Nullable V value) {
114 this.value = new SoftReference<>(value);
115 expiresAt = calcExpiresAt();
119 * Invalidates the value in the cache.
121 public final synchronized void invalidateValue() {
122 value = new SoftReference<>(null);
127 * Refreshes and returns the value in the cache.
128 * If null returned from action.get, the get action should have sued putValue to update the item
130 * @return the new value
132 public synchronized @Nullable V refreshValue(Supplier<@Nullable V> action) {
134 V freshValue = action.get();
135 if (freshValue == null) {
138 value = new SoftReference<>(freshValue);
139 expiresAt = calcExpiresAt();
144 * Checks if the value is expired.
146 * @return true if the value is expired
148 public boolean isExpired() {
149 return expiresAt < System.nanoTime();
152 private long calcExpiresAt() {
153 return System.nanoTime() + expiry;