]> git.basschouten.com Git - openhab-addons.git/blob
7441f46a0dabffdbe86554afcc99ab7d5c0254fe
[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.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      * Puts a new value into the cache.
82      *
83      * @param value the new value
84      */
85     public final synchronized void putValue(@Nullable V value) {
86         this.value = value;
87         expiresAt = calcNextExpiresAt();
88     }
89
90     /**
91      * Invalidates the value in the cache.
92      */
93     public final synchronized void invalidateValue() {
94         value = null;
95         expiresAt = calcAlreadyExpired();
96     }
97
98     /**
99      * Refreshes and returns the value in the cache.
100      *
101      * @return the new value
102      */
103     public synchronized @Nullable V refreshValue() {
104         value = action.get();
105         expiresAt = calcNextExpiresAt();
106         return value;
107     }
108
109     /**
110      * Checks if the value is expired.
111      *
112      * @return true if the value is expired
113      */
114     public boolean isExpired() {
115         return !LocalDateTime.now().isBefore(expiresAt);
116     }
117
118     private LocalDateTime calcNextExpiresAt() {
119         LocalDateTime now = LocalDateTime.now();
120         LocalDateTime limit = now.withHour(beginningHour).truncatedTo(ChronoUnit.HOURS);
121         LocalDateTime result = now.isBefore(limit) ? limit : limit.plusDays(1);
122         logger.debug("calcNextExpiresAt result = {}", result.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
123         return result;
124     }
125
126     private LocalDateTime calcAlreadyExpired() {
127         return LocalDateTime.now().minusDays(1);
128     }
129 }