Clover icon

Magnolia Module Cache 5.5.9

  1. Project Clover database Mon Nov 25 2019 16:46:50 CET
  2. Package info.magnolia.module.cache.cachepolicy

File Default.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart7.png
45% of files have more coverage

Code metrics

18
55
13
1
278
175
22
0.4
4.23
13
1.69
20.4% of code in this file is excluded from these metrics.

Classes

Class Line # Actions
Default 71 55 20.4% 22 33
0.6162790761.6%
 

Contributing tests

This file is covered by 13 tests. .

Source view

1    /**
2    * This file Copyright (c) 2008-2018 Magnolia International
3    * Ltd. (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10    * This file is distributed in the hope that it will be
11    * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12    * implied warranty of MERCHANTABILITY or FITNESS FOR A
13    * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14    * Redistribution, except as permitted by whichever of the GPL
15    * or MNA you select, is prohibited.
16    *
17    * 1. For the GPL license (GPL), you can redistribute and/or
18    * modify this file under the terms of the GNU General
19    * Public License, Version 3, as published by the Free Software
20    * Foundation. You should have received a copy of the GNU
21    * General Public License, Version 3 along with this program;
22    * if not, write to the Free Software Foundation, Inc., 51
23    * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24    *
25    * 2. For the Magnolia Network Agreement (MNA), this file
26    * and the accompanying materials are made available under the
27    * terms of the MNA which accompanies this distribution, and
28    * is available at http://www.magnolia-cms.com/mna.html
29    *
30    * Any modifications to this file must keep this entire header
31    * intact.
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    * A basic CachePolicy driven by shouldBypassVoters. This policy implementation uses {@link info.magnolia.module.cache.cachekey.DefaultCacheKey} to identify each cache entry.
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  14 toggle @Inject
86    public Default(CacheModule cacheModule, Provider<CacheInstructor> cacheInstructorProvider) {
87  14 this.cacheModule = cacheModule;
88  14 this.cacheInstructorProvider = cacheInstructorProvider;
89  14 ttlVoters.setVoting(new TtlVoting());
90  14 ttlVoters.addVoter(new ServerTtlVoter(cacheInstructorProvider));
91  14 ttlVoters.addVoter(new BrowserTtlVoter());
92    }
93   
94    /**
95    * @deprecated since 5.5. Use {@link #Default(info.magnolia.module.cache.CacheModule, Provider)} instead.
96    */
 
97  2 toggle @Deprecated
98    public Default(CacheModule cacheModule) {
99  2 this(cacheModule, new Provider<CacheInstructor>() {
 
100  1 toggle @Override
101    public CacheInstructor get() {
102  1 return Components.getComponent(CacheInstructor.class);
103    }
104    });
105    }
106   
107    /**
108    * @deprecated since 5.4. Use {@link #Default(info.magnolia.module.cache.CacheModule, Provider)} instead,
109    */
 
110  0 toggle public Default() {
111  0 this(Components.getComponent(CacheModule.class));
112    }
113   
 
114  5 toggle @Override
115    public CachePolicyResult shouldCache(final Cache cache, final AggregationState aggregationState, final FlushPolicy flushPolicy) {
116  5 final Object key = retrieveCacheKey(aggregationState);
117   
118  5 if (shouldBypass(aggregationState, key)) {
119  0 return new CachePolicyResult(CachePolicyResult.bypass, key, null);
120    }
121   
122  5 if (shouldRefresh(aggregationState, key)) {
123  2 log.debug("Cache refresh requested for {}", key);
124  2 return new CachePolicyResult(CachePolicyResult.store, key, null);
125    }
126   
127    // no multithreaded expiring cache can guarantee existence of the key between two subsequent calls without synchronization here on common object or mutex
128    // and synchronization on the cache is too heavy weight
129    // if (cache.hasElement(key)) {
130    // final Object entry = cache.get(key);
131    // ... so unless having mutexes for our own cache keys and being able to synchronize on that
132    // simply make sure there is just one call ... ever. Either underlying cache has it's own key-mutex system (like ehCache) or
133    // stale value returned by cache might result in entries being generated multiple times or served stale for while ... so choose your underlying cache impl carefully
134    // either way, Magnolia should not need to care nor should it play a police for cache implementation
135   
136    // for default cache impl (blocking ehCache) this call will block on given key, and on given key only, if previously requested until value is available or timeout occurs
137  3 final Object cachedEntry = cache.get(key);
138    // also assume that if the value can't be retrieved for given key, underlying cache is not a pig and will throw exception at some point releasing the thread and propagating the error to the user
139   
140  3 if (cachedEntry != null) {
141  2 if (cachedEntry instanceof UncacheableEntry) {
142  1 return new CachePolicyResult(CachePolicyResult.bypass, key, null);
143    } else {
144  1 return new CachePolicyResult(CachePolicyResult.useCache, key, cachedEntry);
145    }
146    }
147  1 return new CachePolicyResult(CachePolicyResult.store, key, null);
148    }
149   
150    /**
151    * Checks whether requested content should be served from cache or refreshed instead.
152    *
153    * @return True if cache entry for the key should be recreated, false otherwise.
154    */
 
155  5 toggle protected boolean shouldRefresh(AggregationState aggregationState, Object key) {
156  5 if (isRefreshOnNoCacheRequests()) {
157  4 String cacheControl = ((WebContext) MgnlContext.getInstance()).getRequest().getHeader(CacheConstants.HEADER_CACHE_CONTROL);
158  4 String pragma = ((WebContext) MgnlContext.getInstance()).getRequest().getHeader(CacheConstants.HEADER_PRAGMA);
159  4 return (cacheControl != null && cacheControl.equals(CacheConstants.HEADER_VALUE_NO_CACHE)) || (pragma != null && pragma.equals(CacheConstants.HEADER_VALUE_NO_CACHE));
160    }
161  1 return false;
162    }
163   
 
164  5 toggle protected boolean shouldBypass(AggregationState aggregationState, Object key) {
165  5 final String uri;
166  5 if (key instanceof DefaultCacheKey) {
167  5 uri = ((DefaultCacheKey) key).getUri();
168    } else {
169  0 uri = key.toString();
170    }
171    // true if shouldBypassVoters vote positively
172  5 if (shouldBypassVoters != null) {
173  0 return shouldBypassVoters.vote(uri) <= 0;
174    }
175  5 log.warn("No cache voter defined.");
176  5 return false;
177    }
178   
 
179  11 toggle public Object retrieveCacheKey(final AggregationState aggregationState) {
180  11 CacheKeyGenerator<?> cacheKeyGenerator = cacheInstructorProvider.get().getKeyGenerator();
181  11 if (cacheKeyGenerator == null) {
182  10 cacheKeyGenerator = getCacheKeyGenerator();
183    }
184  11 return cacheKeyGenerator.generateKey(aggregationState);
185    }
186   
 
187  0 toggle @Override
188    public Object[] retrieveCacheKeys(final String uuid, final String repository) {
189  0 final String uuidKey = repository + ":" + uuid;
190  0 final Set<Object> keys = getUUIDKeySetFromCacheSafely(uuidKey);
191  0 return keys.toArray();
192    }
193   
 
194  0 toggle @Override
195    public void persistCacheKey(final String repo, final String uuid, final Object key) {
196  0 final String uuidKey = repo + ":" + uuid;
197  0 final Set<Object> uuidToCacheKeyMapping = getUUIDKeySetFromCacheSafely(uuidKey);
198  0 uuidToCacheKeyMapping.add(key);
199    }
200   
 
201  0 toggle @Override
202    public Object[] removeCacheKeys(final String uuid, final String repository) {
203  0 final String uuidKey = repository + ":" + uuid;
204  0 final Set keys = getUUIDKeySetFromCacheSafely(uuidKey);
205  0 getUuidKeyCache().remove(uuidKey);
206  0 return keys.toArray();
207    }
208   
 
209  0 toggle private Cache getUuidKeyCache() {
210  0 return cacheModule.getCacheFactory().getCache(UUID_KEY_MAP_KEY);
211    }
212   
213    /**
214    * Method to safely (without danger of blocking cache) obtain persistent mapping between UUIDs and cache keys.
215    */
 
216  0 toggle private synchronized Set<Object> getUUIDKeySetFromCacheSafely(String uuidKey) {
217  0 final Cache cache = getUuidKeyCache();
218  0 synchronized (cache) {
219  0 Set<Object> keys = (Set<Object>) cache.get(uuidKey);
220  0 if (keys == null) {
221  0 keys = Collections.synchronizedSet(new HashSet<Object>());
222  0 cache.put(uuidKey, keys);
223    }
224  0 return keys;
225    }
226    }
227   
 
228    toggle public boolean isRefreshOnNoCacheRequests() {
229    return this.refreshOnNoCacheRequests;
230    }
231   
 
232    toggle public void setRefreshOnNoCacheRequests(boolean allowNoCacheHeader) {
233    this.refreshOnNoCacheRequests = allowNoCacheHeader;
234    }
235   
 
236    toggle @Override
237    public VoterSet<CacheResponseWrapper> getTtlVoters() {
238    return ttlVoters;
239    }
240   
 
241    toggle public void setTtlVoters(VoterSet<CacheResponseWrapper> ttlVoters) {
242    this.ttlVoters = ttlVoters;
243    }
244   
 
245    toggle public VoterSet getShouldBypassVoters() {
246    return shouldBypassVoters;
247    }
248   
 
249    toggle public void setShouldBypassVoters(VoterSet shouldBypassVoters) {
250    this.shouldBypassVoters = shouldBypassVoters;
251    }
252   
 
253    toggle public CacheKeyGenerator<?> getCacheKeyGenerator() {
254    return cacheKeyGenerator;
255    }
256   
 
257    toggle public void setCacheKeyGenerator(CacheKeyGenerator<?> cacheKeyGenerator) {
258    this.cacheKeyGenerator = cacheKeyGenerator;
259    }
260   
261    /**
262    * @deprecated since 5.4. Use {@link #getShouldBypassVoters()} instead.
263    */
 
264    toggle @Deprecated
265    public VoterSet getVoters() {
266    DeprecationUtil.isDeprecated("Use info.magnolia.module.cache.cachepolicy.Default#getShouldBypassVoters instead.");
267    return shouldBypassVoters;
268    }
269   
270    /**
271    * @deprecated since 5.4. Use {@link #setShouldBypassVoters(info.magnolia.voting.voters.VoterSet)} ()} instead.
272    */
 
273    toggle @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    }