1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.module.cache.cachepolicy;
35
36 import info.magnolia.cms.cache.CacheConstants;
37 import info.magnolia.cms.core.AggregationState;
38 import info.magnolia.cms.util.DeprecationUtil;
39 import info.magnolia.context.MgnlContext;
40 import info.magnolia.context.WebContext;
41 import info.magnolia.module.cache.Cache;
42 import info.magnolia.module.cache.CacheModule;
43 import info.magnolia.module.cache.CachePolicy;
44 import info.magnolia.module.cache.CachePolicyResult;
45 import info.magnolia.module.cache.FlushPolicy;
46 import info.magnolia.module.cache.cachekey.CacheKeyGenerator;
47 import info.magnolia.module.cache.cachekey.DefaultCacheKey;
48 import info.magnolia.module.cache.cachekey.DefaultCacheKeyGenerator;
49 import info.magnolia.module.cache.cachepolicy.instructor.CacheInstructor;
50 import info.magnolia.module.cache.cachepolicy.voters.BrowserTtlVoter;
51 import info.magnolia.module.cache.cachepolicy.voters.ServerTtlVoter;
52 import info.magnolia.module.cache.cachepolicy.voters.TtlVoting;
53 import info.magnolia.module.cache.filter.CacheResponseWrapper;
54 import info.magnolia.module.cache.filter.UncacheableEntry;
55 import info.magnolia.objectfactory.Components;
56 import info.magnolia.voting.voters.VoterSet;
57
58 import java.util.Collections;
59 import java.util.HashSet;
60 import java.util.Set;
61
62 import javax.inject.Inject;
63 import javax.inject.Provider;
64
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
67
68
69
70
71 public class Default implements CachePolicy {
72
73 public static final String UUID_KEY_MAP_KEY = "uuid-key-mapping";
74 private static final Logger log = LoggerFactory.getLogger(Default.class);
75
76 private CacheKeyGenerator<?> cacheKeyGenerator = new DefaultCacheKeyGenerator();
77 private VoterSet shouldBypassVoters;
78 private VoterSet<CacheResponseWrapper> ttlVoters = new VoterSet<>();
79
80 private boolean refreshOnNoCacheRequests = false;
81
82 private final CacheModule cacheModule;
83 private final Provider<CacheInstructor> cacheInstructorProvider;
84
85 @Inject
86 public Default(CacheModule cacheModule, Provider<CacheInstructor> cacheInstructorProvider) {
87 this.cacheModule = cacheModule;
88 this.cacheInstructorProvider = cacheInstructorProvider;
89 ttlVoters.setVoting(new TtlVoting());
90 ttlVoters.addVoter(new ServerTtlVoter(cacheInstructorProvider));
91 ttlVoters.addVoter(new BrowserTtlVoter());
92 }
93
94
95
96
97 @Deprecated
98 public Default(CacheModule cacheModule) {
99 this(cacheModule, new Provider<CacheInstructor>() {
100 @Override
101 public CacheInstructor get() {
102 return Components.getComponent(CacheInstructor.class);
103 }
104 });
105 }
106
107
108
109
110 public Default() {
111 this(Components.getComponent(CacheModule.class));
112 }
113
114 @Override
115 public CachePolicyResult shouldCache(final Cache cache, final AggregationState aggregationState, final FlushPolicy flushPolicy) {
116 final Object key = retrieveCacheKey(aggregationState);
117
118 if (shouldBypass(aggregationState, key)) {
119 return new CachePolicyResult(CachePolicyResult.bypass, key, null);
120 }
121
122 if (shouldRefresh(aggregationState, key)) {
123 log.debug("Cache refresh requested for {}", key);
124 return new CachePolicyResult(CachePolicyResult.store, key, null);
125 }
126
127
128
129
130
131
132
133
134
135
136
137 final Object cachedEntry = cache.get(key);
138
139
140 if (cachedEntry != null) {
141 if (cachedEntry instanceof UncacheableEntry) {
142 return new CachePolicyResult(CachePolicyResult.bypass, key, null);
143 } else {
144 return new CachePolicyResult(CachePolicyResult.useCache, key, cachedEntry);
145 }
146 }
147 return new CachePolicyResult(CachePolicyResult.store, key, null);
148 }
149
150
151
152
153
154
155 protected boolean shouldRefresh(AggregationState aggregationState, Object key) {
156 if (isRefreshOnNoCacheRequests()) {
157 String cacheControl = ((WebContext) MgnlContext.getInstance()).getRequest().getHeader(CacheConstants.HEADER_CACHE_CONTROL);
158 String pragma = ((WebContext) MgnlContext.getInstance()).getRequest().getHeader(CacheConstants.HEADER_PRAGMA);
159 return (cacheControl != null && cacheControl.equals(CacheConstants.HEADER_VALUE_NO_CACHE)) || (pragma != null && pragma.equals(CacheConstants.HEADER_VALUE_NO_CACHE));
160 }
161 return false;
162 }
163
164 protected boolean shouldBypass(AggregationState aggregationState, Object key) {
165 final String uri;
166 if (key instanceof DefaultCacheKey) {
167 uri = ((DefaultCacheKey) key).getUri();
168 } else {
169 uri = key.toString();
170 }
171
172 if (shouldBypassVoters != null) {
173 return shouldBypassVoters.vote(uri) <= 0;
174 }
175 log.warn("No cache voter defined.");
176 return false;
177 }
178
179 public Object retrieveCacheKey(final AggregationState aggregationState) {
180 CacheKeyGenerator<?> cacheKeyGenerator = cacheInstructorProvider.get().getKeyGenerator();
181 if (cacheKeyGenerator == null) {
182 cacheKeyGenerator = getCacheKeyGenerator();
183 }
184 return cacheKeyGenerator.generateKey(aggregationState);
185 }
186
187 @Override
188 public Object[] retrieveCacheKeys(final String uuid, final String repository) {
189 final String uuidKey = repository + ":" + uuid;
190 final Set<Object> keys = getUUIDKeySetFromCacheSafely(uuidKey);
191 return keys.toArray();
192 }
193
194 @Override
195 public void persistCacheKey(final String repo, final String uuid, final Object key) {
196 final String uuidKey = repo + ":" + uuid;
197 final Set<Object> uuidToCacheKeyMapping = getUUIDKeySetFromCacheSafely(uuidKey);
198 uuidToCacheKeyMapping.add(key);
199 }
200
201 @Override
202 public Object[] removeCacheKeys(final String uuid, final String repository) {
203 final String uuidKey = repository + ":" + uuid;
204 final Set keys = getUUIDKeySetFromCacheSafely(uuidKey);
205 getUuidKeyCache().remove(uuidKey);
206 return keys.toArray();
207 }
208
209 private Cache getUuidKeyCache() {
210 return cacheModule.getCacheFactory().getCache(UUID_KEY_MAP_KEY);
211 }
212
213
214
215
216 private synchronized Set<Object> getUUIDKeySetFromCacheSafely(String uuidKey) {
217 final Cache cache = getUuidKeyCache();
218 synchronized (cache) {
219 Set<Object> keys = (Set<Object>) cache.get(uuidKey);
220 if (keys == null) {
221 keys = Collections.synchronizedSet(new HashSet<Object>());
222 cache.put(uuidKey, keys);
223 }
224 return keys;
225 }
226 }
227
228 public boolean isRefreshOnNoCacheRequests() {
229 return this.refreshOnNoCacheRequests;
230 }
231
232 public void setRefreshOnNoCacheRequests(boolean allowNoCacheHeader) {
233 this.refreshOnNoCacheRequests = allowNoCacheHeader;
234 }
235
236 @Override
237 public VoterSet<CacheResponseWrapper> getTtlVoters() {
238 return ttlVoters;
239 }
240
241 public void setTtlVoters(VoterSet<CacheResponseWrapper> ttlVoters) {
242 this.ttlVoters = ttlVoters;
243 }
244
245 public VoterSet getShouldBypassVoters() {
246 return shouldBypassVoters;
247 }
248
249 public void setShouldBypassVoters(VoterSet shouldBypassVoters) {
250 this.shouldBypassVoters = shouldBypassVoters;
251 }
252
253 public CacheKeyGenerator<?> getCacheKeyGenerator() {
254 return cacheKeyGenerator;
255 }
256
257 public void setCacheKeyGenerator(CacheKeyGenerator<?> cacheKeyGenerator) {
258 this.cacheKeyGenerator = cacheKeyGenerator;
259 }
260
261
262
263
264 @Deprecated
265 public VoterSet getVoters() {
266 DeprecationUtil.isDeprecated("Use info.magnolia.module.cache.cachepolicy.Default#getShouldBypassVoters instead.");
267 return shouldBypassVoters;
268 }
269
270
271
272
273 @Deprecated
274 public void setVoters(VoterSet voters) {
275 DeprecationUtil.isDeprecated("Use info.magnolia.module.cache.cachepolicy.Default#setShouldBypassVoters instead.");
276 this.shouldBypassVoters = voters;
277 }
278 }