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.executor;
35
36 import info.magnolia.cms.cache.CacheConstants;
37 import info.magnolia.context.MgnlContext;
38 import info.magnolia.jcr.util.NodeUtil;
39 import info.magnolia.module.ModuleRegistry;
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.filter.CacheResponseWrapper;
45 import info.magnolia.module.cache.filter.CachedEntry;
46 import info.magnolia.module.cache.filter.CachedError;
47 import info.magnolia.module.cache.filter.CachedRedirect;
48 import info.magnolia.module.cache.filter.ContentCachedEntry;
49 import info.magnolia.module.cache.filter.DelegatingBlobCachedEntry;
50 import info.magnolia.module.cache.filter.InMemoryCachedEntry;
51 import info.magnolia.module.cache.filter.UncacheableEntry;
52 import info.magnolia.objectfactory.Components;
53
54 import java.io.IOException;
55
56 import javax.inject.Inject;
57 import javax.jcr.Node;
58 import javax.jcr.RepositoryException;
59 import javax.servlet.FilterChain;
60 import javax.servlet.ServletException;
61 import javax.servlet.http.HttpServletRequest;
62 import javax.servlet.http.HttpServletResponse;
63
64 import org.apache.commons.lang3.StringUtils;
65
66
67
68
69 public class Store extends AbstractExecutor {
70
71 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Store.class);
72 private final CacheModule cacheModule;
73
74
75
76
77 public Store() {
78 this.cacheModule = Components.getComponent(ModuleRegistry.class).getModuleInstance(CacheModule.class);
79 }
80
81 @Inject
82 public Store(CacheModule cacheModule) {
83 this.cacheModule = cacheModule;
84 }
85
86 @Override
87 public void processCacheRequest(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Cache cache, CachePolicyResult cachePolicyResult) throws IOException, ServletException {
88
89 CachedEntry cachedEntry = null;
90 final Object key = cachePolicyResult.getCacheKey();
91
92 final CacheResponseWrapper responseWrapper = new CacheResponseWrapper(response, CacheResponseWrapper.DEFAULT_THRESHOLD, false);
93 responseWrapper.setResponseExpirationDetectionEnabled();
94
95
96 final long cacheStorageDate = System.currentTimeMillis();
97 responseWrapper.setDateHeader("Last-Modified", cacheStorageDate);
98
99 try {
100 chain.doFilter(request, responseWrapper);
101 if (responseWrapper.getStatus() == HttpServletResponse.SC_NOT_MODIFIED) {
102 response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
103 } else {
104 responseWrapper.flushBuffer();
105 cachedEntry = makeCachedEntry(request, responseWrapper, cache, cachePolicyResult);
106 if (cachedEntry.getTimeToLiveInSeconds() == 0) {
107 cachedEntry = new UncacheableEntry(cachedEntry);
108 }
109 }
110
111 } catch (IOException e) {
112 responseWrapper.cleanUp();
113 throw e;
114 } catch (ServletException e) {
115 responseWrapper.cleanUp();
116 throw e;
117 } catch (Throwable e) {
118 responseWrapper.cleanUp();
119 throw new RuntimeException("Failed to process request with: " + e.getMessage(), e);
120 }
121
122 if (cachedEntry == null) {
123
124 cache.put(key, null);
125 return;
126 }
127
128 int timeToLiveInSeconds = cachedEntry.getTimeToLiveInSeconds();
129 cachePolicyResult.setCachedEntry(cachedEntry);
130
131 if (timeToLiveInSeconds == -1) {
132 cache.put(key, cachedEntry);
133 } else {
134 cache.put(key, cachedEntry, timeToLiveInSeconds);
135 }
136
137
138 final Node content = MgnlContext.getAggregationState().getMainContentNode();
139 try {
140 if (content != null && NodeUtil.isNodeType(content, "mix:referenceable")) {
141 final String uuid = content.getIdentifier();
142 String repo = content.getSession().getWorkspace().getName();
143 getCachePolicy(cache).persistCacheKey(repo, uuid, key);
144 }
145 } catch (RepositoryException e) {
146
147 responseWrapper.cleanUp();
148 throw new RuntimeException(e);
149 }
150 }
151
152 protected CachedEntry makeCachedEntry(HttpServletRequest request, CacheResponseWrapper cachedResponse, Cache cache, CachePolicyResult cachePolicyResult) throws IOException {
153
154 final String originalUrl = request.getRequestURL().toString();
155 int status = cachedResponse.getStatus();
156 int timeToLiveInSeconds = this.getTimeToLive(cachedResponse, cache);
157
158
159 if (cachedResponse.getRedirectionLocation() != null) {
160 return new CachedRedirect(cachedResponse.getStatus(), cachedResponse.getRedirectionLocation(), originalUrl, timeToLiveInSeconds);
161 }
162
163 if (cachedResponse.isError()) {
164 return new CachedError(cachedResponse.getStatus(), originalUrl, timeToLiveInSeconds);
165 }
166
167 final long modificationDate = cachedResponse.getLastModified();
168 final String contentType = cachedResponse.getContentType();
169
170 ContentCachedEntry cacheEntry;
171 if (!cachedResponse.isThresholdExceeded()) {
172 cacheEntry = new InMemoryCachedEntry(cachedResponse.getBufferedContent(),
173 contentType,
174 cachedResponse.getCharacterEncoding(),
175 status,
176 cachedResponse.getHeaders(),
177 modificationDate,
178 originalUrl,
179 timeToLiveInSeconds);
180 } else {
181 cacheEntry = new DelegatingBlobCachedEntry(cachedResponse.getContentLength(),
182 contentType,
183 cachedResponse.getCharacterEncoding(),
184 status,
185 cachedResponse.getHeaders(),
186 modificationDate,
187 originalUrl,
188 timeToLiveInSeconds);
189
190
191
192 ((DelegatingBlobCachedEntry) cacheEntry).bindContentFileToCurrentRequest(request, cachedResponse.getContentFile());
193 cachedResponse.getThresholdingOutputStream().close();
194 }
195 return cacheEntry;
196 }
197
198 protected int getTimeToLive(CacheResponseWrapper cachedResponse, Cache cache) {
199 return this.getCachePolicy(cache).getTtlVoters().vote(cachedResponse);
200 }
201
202 protected CachePolicy getCachePolicy(Cache cache) {
203 return cacheModule.getContentCaching(cache.getName()).getCachePolicy();
204 }
205
206
207
208
209
210 @Deprecated
211 protected int getTimeToLive(HttpServletRequest request, CacheResponseWrapper cachedResponse) {
212 Object attribute = request.getAttribute(CacheConstants.HEADER_X_MAGNOLIA_CACHE);
213 if (attribute instanceof String) {
214 String attributeAsString = (String) attribute;
215 String[] splitted = StringUtils.split(attributeAsString, ",");
216 for (String headerValue : splitted) {
217 if (headerValue.contains(CacheConstants.HEADER_VALUE_TTL + "=")) {
218 String ttlString = StringUtils.substringAfter(headerValue, CacheConstants.HEADER_VALUE_TTL + "=");
219 try {
220 return Integer.parseInt(ttlString);
221 } catch (NumberFormatException e) {
222 log.error("Unparsable TTL in '{}' attribute: {}", CacheConstants.HEADER_X_MAGNOLIA_CACHE, headerValue);
223 }
224 }
225 }
226 }
227 return cachedResponse.getTimeToLiveInSeconds();
228 }
229
230 }