]> git.basschouten.com Git - openhab-addons.git/blob
972f92981ad289ace8f2b151482002f52b1d7bc8
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.linky.internal.api;
14
15 import java.time.LocalDateTime;
16 import java.time.format.DateTimeFormatter;
17 import java.time.temporal.ChronoUnit;
18 import java.util.Optional;
19 import java.util.function.Supplier;
20
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 /**
27  * This is a simple expiring and reloading cache implementation.
28  *
29  * There must be provided an action in order to retrieve/calculate the value. This action will be called only if the
30  * answer from the last calculation is not valid anymore, i.e. if it is expired.
31  *
32  * The cache expires after the current day; it is possible to shift the beginning time of the day.
33  *
34  * Soft Reference is not used to store the cached value because JVM Garbage Collector is clearing it too much often.
35  *
36  * @author Laurent Garnier - Initial contribution
37  *
38  * @param <V> the type of the value
39  */
40 @NonNullByDefault
41 public class ExpiringDayCache<V> {
42     private final Logger logger = LoggerFactory.getLogger(ExpiringDayCache.class);
43
44     private final String name;
45     private final int beginningHour;
46     private final Supplier<@Nullable V> action;
47
48     private @Nullable V value;
49     private LocalDateTime expiresAt;
50
51     /**
52      * Create a new instance.
53      *
54      * @param name the name of this cache
55      * @param beginningHour the hour in the day at which the validity period is starting
56      * @param action the action to retrieve/calculate the value
57      */
58     public ExpiringDayCache(String name, int beginningHour, Supplier<@Nullable V> action) {
59         this.name = name;
60         this.beginningHour = beginningHour;
61         this.expiresAt = calcAlreadyExpired();
62         this.action = action;
63     }
64
65     /**
66      * Returns the value - possibly from the cache, if it is still valid.
67      */
68     public synchronized Optional<V> getValue() {
69         @Nullable
70         V cachedValue = value;
71         if (cachedValue == null || isExpired()) {
72             logger.debug("getValue from cache \"{}\" is requiring a fresh value", name);
73             cachedValue = refreshValue();
74         } else {
75             logger.debug("getValue from cache \"{}\" is returning a cached value", name);
76         }
77         return Optional.ofNullable(cachedValue);
78     }
79
80     /**
81      * Refreshes and returns the value in the cache.
82      *
83      * @return the new value
84      */
85     public synchronized @Nullable V refreshValue() {
86         value = action.get();
87         expiresAt = calcNextExpiresAt();
88         return value;
89     }
90
91     /**
92      * Checks if the value is expired.
93      *
94      * @return true if the value is expired
95      */
96     public boolean isExpired() {
97         return !LocalDateTime.now().isBefore(expiresAt);
98     }
99
100     private LocalDateTime calcNextExpiresAt() {
101         LocalDateTime now = LocalDateTime.now();
102         LocalDateTime limit = now.withHour(beginningHour).truncatedTo(ChronoUnit.HOURS);
103         LocalDateTime result = now.isBefore(limit) ? limit : limit.plusDays(1);
104         logger.debug("calcNextExpiresAt result = {}", result.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
105         return result;
106     }
107
108     private LocalDateTime calcAlreadyExpired() {
109         return LocalDateTime.now().minusDays(1);
110     }
111 }