]> git.basschouten.com Git - openhab-addons.git/blob
018c4e384a172c5402acecea860df8ebf5dbf25e
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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;
14
15 import java.time.LocalDateTime;
16 import java.time.format.DateTimeFormatter;
17 import java.time.temporal.ChronoUnit;
18 import java.util.function.Supplier;
19
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 /**
26  * This is a simple expiring and reloading cache implementation.
27  *
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.
30  *
31  * The cache expires after the current day; it is possible to shift the beginning time of the day.
32  *
33  * Soft Reference is not used to store the cached value because JVM Garbage Collector is clearing it too much often.
34  *
35  * @author Laurent Garnier - Initial contribution
36  *
37  * @param <V> the type of the value
38  */
39 @NonNullByDefault
40 public class ExpiringDayCache<V> {
41     private final Logger logger = LoggerFactory.getLogger(ExpiringDayCache.class);
42
43     private final String name;
44     private final int beginningHour;
45     private final Supplier<@Nullable V> action;
46
47     private @Nullable V value;
48     private LocalDateTime expiresAt;
49
50     /**
51      * Create a new instance.
52      *
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
56      */
57     public ExpiringDayCache(String name, int beginningHour, Supplier<@Nullable V> action) {
58         this.name = name;
59         this.beginningHour = beginningHour;
60         this.expiresAt = calcAlreadyExpired();
61         this.action = action;
62     }
63
64     /**
65      * Returns the value - possibly from the cache, if it is still valid.
66      */
67     public synchronized @Nullable V getValue() {
68         @Nullable
69         V cachedValue = value;
70         if (cachedValue == null || isExpired()) {
71             logger.debug("getValue from cache \"{}\" is requiring a fresh value", name);
72             cachedValue = refreshValue();
73         } else {
74             logger.debug("getValue from cache \"{}\" is returing a cached value", name);
75         }
76         return cachedValue;
77     }
78
79     /**
80      * Puts a new value into the cache.
81      *
82      * @param value the new value
83      */
84     public final synchronized void putValue(@Nullable V value) {
85         this.value = value;
86         expiresAt = calcNextExpiresAt();
87     }
88
89     /**
90      * Invalidates the value in the cache.
91      */
92     public final synchronized void invalidateValue() {
93         value = null;
94         expiresAt = calcAlreadyExpired();
95     }
96
97     /**
98      * Refreshes and returns the value in the cache.
99      *
100      * @return the new value
101      */
102     public synchronized @Nullable V refreshValue() {
103         value = action.get();
104         expiresAt = calcNextExpiresAt();
105         return value;
106     }
107
108     /**
109      * Checks if the value is expired.
110      *
111      * @return true if the value is expired
112      */
113     public boolean isExpired() {
114         return !LocalDateTime.now().isBefore(expiresAt);
115     }
116
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));
122         return result;
123     }
124
125     private LocalDateTime calcAlreadyExpired() {
126         return LocalDateTime.now().minusDays(1);
127     }
128 }