Skip to contentPackage: DefaultMetadataCache$ExpirableMetadata
DefaultMetadataCache$ExpirableMetadata
Coverage
1: /*
2: * *************************************************************************************************************************************************************
3: *
4: * NorthernWind - lightweight CMS
5: * http://tidalwave.it/projects/northernwind
6: *
7: * Copyright (C) 2011 - 2025 Tidalwave s.a.s. (http://tidalwave.it)
8: *
9: * *************************************************************************************************************************************************************
10: *
11: * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
12: * You may obtain a copy of the License at
13: *
14: * http://www.apache.org/licenses/LICENSE-2.0
15: *
16: * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
17: * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
18: *
19: * *************************************************************************************************************************************************************
20: *
21: * git clone https://bitbucket.org/tidalwave/northernwind-src
22: * git clone https://github.com/tidalwave-it/northernwind-src
23: *
24: * *************************************************************************************************************************************************************
25: */
26: package it.tidalwave.northernwind.frontend.media.impl;
27:
28: import javax.annotation.Nonnegative;
29: import javax.annotation.Nonnull;
30: import javax.inject.Inject;
31: import java.time.Clock;
32: import java.time.ZonedDateTime;
33: import java.util.HashMap;
34: import java.util.Map;
35: import java.util.function.Supplier;
36: import java.io.IOException;
37: import it.tidalwave.util.Id;
38: import it.tidalwave.util.NotFoundException;
39: import it.tidalwave.northernwind.core.model.ResourceProperties;
40: import lombok.Getter;
41: import lombok.RequiredArgsConstructor;
42: import lombok.Setter;
43: import lombok.ToString;
44: import lombok.extern.slf4j.Slf4j;
45:
46: /***************************************************************************************************************************************************************
47: *
48: * A default implementation of {@link MetadataCache}.
49: *
50: * @author Fabrizio Giudici
51: *
52: **************************************************************************************************************************************************************/
53: @Slf4j
54: public class DefaultMetadataCache implements MetadataCache
55: {
56: /***********************************************************************************************************************************************************
57: * A holder of {@code Metadata} together with expiration information.
58: **********************************************************************************************************************************************************/
59: @RequiredArgsConstructor @Getter @ToString
60: class ExpirableMetadata
61: {
62: @Nonnull
63: private final Metadata metadata;
64:
65: private final ZonedDateTime creationTime = ZonedDateTime.now(clock.get());
66:
67: @Nonnull
68: private ZonedDateTime expirationTime = creationTime.plusSeconds(metadataExpirationTime);
69:
70: /***************************************************************************************************************
71: *
72: * Postpones the expiration time.
73: *
74: **************************************************************************************************************/
75: public void postponeExpirationTime()
76: {
77: expirationTime = ZonedDateTime.now(clock.get()).plusSeconds(metadataExpirationTime);
78: }
79: }
80:
81: public static final int DEFAULT_METADATA_EXPIRATION_TIME = 10 * 60;
82:
83: @Getter @Setter @Nonnull
84: private Supplier<Clock> clock = Clock::systemDefaultZone;
85:
86: /** Expiration time for metadata in seconds; after this time, metadata are reloaded. */
87: @Getter @Setter @Nonnegative
88: private int metadataExpirationTime = DEFAULT_METADATA_EXPIRATION_TIME;
89:
90: @Inject
91: private MetadataLoader metadataLoader;
92:
93: /* package */ final Map<Id, ExpirableMetadata> metadataMapById = new HashMap<>();
94:
95: /***********************************************************************************************************************************************************
96: * {@inheritDoc}
97: **********************************************************************************************************************************************************/
98: // FIXME: shouldn't synchronize the whole method, only map manipulation
99: @Override @Nonnull
100: public synchronized Metadata findMetadataById (@Nonnull final Id mediaId,
101: @Nonnull final ResourceProperties siteNodeProperties)
102: throws NotFoundException, IOException
103: {
104: log.debug("findMetadataById({}, ...)", mediaId);
105: var metadata = metadataMapById.get(mediaId);
106:
107: if ((metadata != null) && metadata.getExpirationTime().isAfter(ZonedDateTime.now(clock.get())))
108: {
109: log.debug(">>>> returning cached data which will expire at {}", metadata.getExpirationTime());
110: return metadata.getMetadata();
111: }
112:
113: final var file = metadataLoader.findMediaResourceFile(siteNodeProperties, mediaId);
114:
115: if (metadata != null)
116: {
117: final var fileLatestModificationTime = file.getLatestModificationTime();
118: final var metadataCreationTime = metadata.getCreationTime();
119:
120: if (fileLatestModificationTime.isAfter(metadataCreationTime))
121: {
122: log.debug(">>>>>>>> expiring metadata: file {} > metadata {}",
123: fileLatestModificationTime, metadataCreationTime);
124: metadata = null;
125: }
126: else
127: {
128: log.debug(">>>>>>>> postponing metadata expiration: file {} < metadata {}",
129: fileLatestModificationTime, metadataCreationTime);
130: metadata.postponeExpirationTime();
131: }
132: }
133:
134: if (metadata == null)
135: {
136: log.debug(">>>> loading metadata...");
137: metadata = new ExpirableMetadata(metadataLoader.loadMetadata(file));
138: metadataMapById.put(mediaId, metadata);
139: }
140:
141: return metadata.getMetadata();
142: }
143: }