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.context.MgnlContext;
39 import info.magnolia.context.WebContext;
40 import info.magnolia.module.cache.Cache;
41 import info.magnolia.module.cache.CacheModule;
42 import info.magnolia.module.cache.CachePolicy;
43 import info.magnolia.module.cache.CachePolicyResult;
44 import info.magnolia.module.cache.FlushPolicy;
45 import info.magnolia.module.cache.cachekey.CacheKeyGenerator;
46 import info.magnolia.module.cache.cachekey.DefaultCacheKey;
47 import info.magnolia.module.cache.cachekey.DefaultCacheKeyGenerator;
48 import info.magnolia.module.cache.cachepolicy.instructor.CacheInstructor;
49 import info.magnolia.module.cache.cachepolicy.voters.BrowserTtlVoter;
50 import info.magnolia.module.cache.cachepolicy.voters.ServerTtlVoter;
51 import info.magnolia.module.cache.cachepolicy.voters.TtlVoting;
52 import info.magnolia.module.cache.filter.CacheResponseWrapper;
53 import info.magnolia.module.cache.filter.UncacheableEntry;
54 import info.magnolia.voting.voters.VoterSet;
55
56 import java.util.Collections;
57 import java.util.HashSet;
58 import java.util.Set;
59
60 import javax.inject.Inject;
61 import javax.inject.Provider;
62
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65
66
67
68
69 public class Default implements CachePolicy {
70
71 public static final String UUID_KEY_MAP_KEY = "uuid-key-mapping";
72 private static final Logger log = LoggerFactory.getLogger(Default.class);
73
74 private CacheKeyGenerator<?> cacheKeyGenerator = new DefaultCacheKeyGenerator();
75 private VoterSet shouldBypassVoters;
76 private VoterSet<CacheResponseWrapper> ttlVoters = new VoterSet<>();
77
78 private boolean refreshOnNoCacheRequests = false;
79
80 private final CacheModule cacheModule;
81 private final Provider<CacheInstructor> cacheInstructorProvider;
82
83 @Inject
84 public Default(CacheModule cacheModule, Provider<CacheInstructor> cacheInstructorProvider) {
85 this.cacheModule = cacheModule;
86 this.cacheInstructorProvider = cacheInstructorProvider;
87 ttlVoters.setVoting(new TtlVoting());
88 ttlVoters.addVoter(new ServerTtlVoter(cacheInstructorProvider));
89 ttlVoters.addVoter(new BrowserTtlVoter());
90 }
91
92
93 @Override
94 public CachePolicyResult shouldCache(final Cache cache, final AggregationState aggregationState, final FlushPolicy flushPolicy) {
95 final Object key = retrieveCacheKey(aggregationState);
96
97 if (shouldBypass(aggregationState, key)) {
98 return new CachePolicyResult(CachePolicyResult.bypass, key, null);
99 }
100
101 if (shouldRefresh(aggregationState, key)) {
102 log.debug("Cache refresh requested for {}", key);
103 return new CachePolicyResult(CachePolicyResult.store, key, null);
104 }
105
106
107
108
109
110
111
112
113
114
115
116 final Object cachedEntry = cache.get(key);
117
118
119 if (cachedEntry != null) {
120 if (cachedEntry instanceof UncacheableEntry) {
121 return new CachePolicyResult(CachePolicyResult.bypass, key, null);
122 } else {
123 return new CachePolicyResult(CachePolicyResult.useCache, key, cachedEntry);
124 }
125 }
126 return new CachePolicyResult(CachePolicyResult.store, key, null);
127 }
128
129
130
131
132
133
134 protected boolean shouldRefresh(AggregationState aggregationState, Object key) {
135 if (isRefreshOnNoCacheRequests()) {
136 String cacheControl = ((WebContext) MgnlContext.getInstance()).getRequest().getHeader(CacheConstants.HEADER_CACHE_CONTROL);
137 String pragma = ((WebContext) MgnlContext.getInstance()).getRequest().getHeader(CacheConstants.HEADER_PRAGMA);
138 return (cacheControl != null && cacheControl.equals(CacheConstants.HEADER_VALUE_NO_CACHE)) || (pragma != null && pragma.equals(CacheConstants.HEADER_VALUE_NO_CACHE));
139 }
140 return false;
141 }
142
143 protected boolean shouldBypass(AggregationState aggregationState, Object key) {
144 final String uri;
145 if (key instanceof DefaultCacheKey) {
146 uri = ((DefaultCacheKey) key).getUri();
147 } else {
148 uri = key.toString();
149 }
150
151 if (shouldBypassVoters != null) {
152 return shouldBypassVoters.vote(uri) <= 0;
153 }
154 log.warn("No cache voter defined.");
155 return false;
156 }
157
158 public Object retrieveCacheKey(final AggregationState aggregationState) {
159 CacheKeyGenerator<?> cacheKeyGenerator = cacheInstructorProvider.get().getKeyGenerator();
160 if (cacheKeyGenerator == null) {
161 cacheKeyGenerator = getCacheKeyGenerator();
162 }
163 return cacheKeyGenerator.generateKey(aggregationState);
164 }
165
166 @Override
167 public Object[] retrieveCacheKeys(final String uuid, final String repository) {
168 final String uuidKey = repository + ":" + uuid;
169 final Set<Object> keys = getUUIDKeySetFromCacheSafely(uuidKey);
170 return keys.toArray();
171 }
172
173 @Override
174 public void persistCacheKey(final String repo, final String uuid, final Object key) {
175 final String uuidKey = repo + ":" + uuid;
176 final Set<Object> uuidToCacheKeyMapping = getUUIDKeySetFromCacheSafely(uuidKey);
177 uuidToCacheKeyMapping.add(key);
178 }
179
180 @Override
181 public Object[] removeCacheKeys(final String uuid, final String repository) {
182 final String uuidKey = repository + ":" + uuid;
183 final Set keys = getUUIDKeySetFromCacheSafely(uuidKey);
184 getUuidKeyCache().remove(uuidKey);
185 return keys.toArray();
186 }
187
188 private Cache getUuidKeyCache() {
189 return cacheModule.getCacheFactory().getCache(UUID_KEY_MAP_KEY);
190 }
191
192
193
194
195 private synchronized Set<Object> getUUIDKeySetFromCacheSafely(String uuidKey) {
196 final Cache cache = getUuidKeyCache();
197 synchronized (cache) {
198 Set<Object> keys = (Set<Object>) cache.get(uuidKey);
199 if (keys == null) {
200 keys = Collections.synchronizedSet(new HashSet<Object>());
201 cache.put(uuidKey, keys);
202 }
203 return keys;
204 }
205 }
206
207 public boolean isRefreshOnNoCacheRequests() {
208 return this.refreshOnNoCacheRequests;
209 }
210
211 public void setRefreshOnNoCacheRequests(boolean allowNoCacheHeader) {
212 this.refreshOnNoCacheRequests = allowNoCacheHeader;
213 }
214
215 @Override
216 public VoterSet<CacheResponseWrapper> getTtlVoters() {
217 return ttlVoters;
218 }
219
220 public void setTtlVoters(VoterSet<CacheResponseWrapper> ttlVoters) {
221 this.ttlVoters = ttlVoters;
222 }
223
224 public VoterSet getShouldBypassVoters() {
225 return shouldBypassVoters;
226 }
227
228 public void setShouldBypassVoters(VoterSet shouldBypassVoters) {
229 this.shouldBypassVoters = shouldBypassVoters;
230 }
231
232 public CacheKeyGenerator<?> getCacheKeyGenerator() {
233 return cacheKeyGenerator;
234 }
235
236 public void setCacheKeyGenerator(CacheKeyGenerator<?> cacheKeyGenerator) {
237 this.cacheKeyGenerator = cacheKeyGenerator;
238 }
239 }