2 * Copyright (c) 2010-2020 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.linky.internal;
15 import java.time.LocalDateTime;
16 import java.time.format.DateTimeFormatter;
17 import java.time.temporal.ChronoUnit;
18 import java.util.function.Supplier;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
26 * This is a simple expiring and reloading cache implementation.
28 * There must be provided an action in order to retrieve/calculate the value. This action will be called only if the
29 * answer from the last calculation is not valid anymore, i.e. if it is expired.
31 * The cache expires after the current day; it is possible to shift the beginning time of the day.
33 * Soft Reference is not used to store the cached value because JVM Garbage Collector is clearing it too much often.
35 * @author Laurent Garnier - Initial contribution
37 * @param <V> the type of the value
40 public class ExpiringDayCache<V> {
41 private final Logger logger = LoggerFactory.getLogger(ExpiringDayCache.class);
43 private final String name;
44 private final int beginningHour;
45 private final Supplier<@Nullable V> action;
47 private @Nullable V value;
48 private LocalDateTime expiresAt;
51 * Create a new instance.
53 * @param name the name of this cache
54 * @param beginningHour the hour in the day at which the validity period is starting
55 * @param action the action to retrieve/calculate the value
57 public ExpiringDayCache(String name, int beginningHour, Supplier<@Nullable V> action) {
59 this.beginningHour = beginningHour;
60 this.expiresAt = calcAlreadyExpired();
65 * Returns the value - possibly from the cache, if it is still valid.
67 public synchronized @Nullable V getValue() {
69 V cachedValue = value;
70 if (cachedValue == null || isExpired()) {
71 logger.debug("getValue from cache \"{}\" is requiring a fresh value", name);
72 cachedValue = refreshValue();
74 logger.debug("getValue from cache \"{}\" is returing a cached value", name);
80 * Puts a new value into the cache.
82 * @param value the new value
84 public final synchronized void putValue(@Nullable V value) {
86 expiresAt = calcNextExpiresAt();
90 * Invalidates the value in the cache.
92 public final synchronized void invalidateValue() {
94 expiresAt = calcAlreadyExpired();
98 * Refreshes and returns the value in the cache.
100 * @return the new value
102 public synchronized @Nullable V refreshValue() {
103 value = action.get();
104 expiresAt = calcNextExpiresAt();
109 * Checks if the value is expired.
111 * @return true if the value is expired
113 public boolean isExpired() {
114 return !LocalDateTime.now().isBefore(expiresAt);
117 private LocalDateTime calcNextExpiresAt() {
118 LocalDateTime now = LocalDateTime.now();
119 LocalDateTime limit = now.withHour(beginningHour).truncatedTo(ChronoUnit.HOURS);
120 LocalDateTime result = now.isBefore(limit) ? limit : limit.plusDays(1);
121 logger.debug("calcNextExpiresAt result = {}", result.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
125 private LocalDateTime calcAlreadyExpired() {
126 return LocalDateTime.now().minusDays(1);