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.filter

File ContentCachedEntry.java

 

Coverage histogram

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

Code metrics

24
53
8
1
238
143
21
0.4
6.62
8
2.62
14.1% of code in this file is excluded from these metrics.

Classes

Class Line # Actions
ContentCachedEntry 68 53 14.1% 21 31
0.6352941463.5%
 

Contributing tests

This file is covered by 17 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.filter;
35   
36    import info.magnolia.cms.util.RequestHeaderUtil;
37    import info.magnolia.module.cache.util.GZipUtil;
38   
39    import java.io.IOException;
40    import java.io.ObjectInputStream;
41    import java.io.ObjectOutputStream;
42    import java.io.Serializable;
43    import java.util.ArrayList;
44    import java.util.Collection;
45    import java.util.HashMap;
46    import java.util.Iterator;
47    import java.util.Map;
48    import java.util.Map.Entry;
49   
50    import javax.servlet.FilterChain;
51    import javax.servlet.ServletException;
52    import javax.servlet.http.HttpServletRequest;
53    import javax.servlet.http.HttpServletResponse;
54   
55    import org.apache.commons.collections4.MultiMap;
56    import org.apache.commons.collections4.map.MultiValueMap;
57    import org.apache.commons.lang3.builder.ToStringBuilder;
58    import org.apache.commons.lang3.builder.ToStringStyle;
59   
60    /**
61    * Wraps a page response. It is assumed that the given content is gzipped
62    * if appropriate (i.e if the gzip filter is in the chain) and this class
63    * thus ungzips it to be able to serve both contents.
64    *
65    * @author gjoseph
66    * @version $Revision: $ ($Author: $)
67    */
 
68    public abstract class ContentCachedEntry implements CachedEntry, Serializable {
69   
70    private static final ToStringStyle BYTE_ARRAY_SIZE_STYLE = new ToStringStyle() {
 
71  0 toggle @Override
72    protected void appendDetail(StringBuffer buffer, String fieldName,
73    byte[] array) {
74  0 super.appendDetail(buffer, fieldName, array.length + " bytes");
75    }
76    };
77   
78    private final String contentType;
79    private final String characterEncoding;
80    private final int statusCode;
81    private transient MultiMap headers;
82    private Map serializableHeadersBackingList;
83    private final long lastModificationTime;
84    private String originalUrl;
85    private int timeToLiveInSeconds = -1;
86   
87    /**
88    *
89    * @param contentType MIME type of the cached content.
90    * @param characterEncoding Character encoding of the cached content.
91    * @param statusCode HTTP response status code (E.g. 200 - OK);
92    * @param headers Additional HTTP headers to be sent when serving this cached content.
93    * @param modificationDate Content modification date to set in the response.
94    * @param timeToLiveInSeconds
95    * @throws IOException when failing to compress the content.
96    */
 
97  17 toggle public ContentCachedEntry(String contentType, String characterEncoding, int statusCode, MultiMap headers, long modificationDate, String originalUrl, int timeToLiveInSeconds) throws IOException {
98  17 this.contentType = contentType;
99  17 this.characterEncoding = characterEncoding;
100  17 this.statusCode = statusCode;
101  17 this.headers = headers;
102  17 this.lastModificationTime = modificationDate;
103  17 this.originalUrl = originalUrl;
104  17 this.timeToLiveInSeconds = timeToLiveInSeconds;
105    }
106   
 
107    toggle @Override
108    public String getOriginalURL() {
109    return this.originalUrl;
110    }
111   
112    // TODO : replacing getOut() with streamTo(OutputStream out) could help subclasses stream content
113    // TODO : from a File buffer for example, instead of holding byte[]s.
114    // TODO : but this would require pushing a dependency on servlet api in here - because we need
115    // TODO : to know if we can push gzipped content... or this would need to be passed as an explicit
116    // TODO : parameter, which isn't too exciting either...
117   
118   
 
119    toggle public String getContentType() {
120    return contentType;
121    }
122   
 
123    toggle public String getCharacterEncoding() {
124    return characterEncoding;
125    }
126   
 
127    toggle public int getStatusCode() {
128    return statusCode;
129    }
130   
 
131    toggle public MultiMap getHeaders() {
132    return headers;
133    }
134   
 
135    toggle @Override
136    public long getLastModificationTime() {
137    return lastModificationTime;
138    }
139   
 
140    toggle @Override
141    public int getTimeToLiveInSeconds() {
142    return timeToLiveInSeconds;
143    }
144   
 
145  0 toggle @Override
146    public String toString() {
147  0 return ToStringBuilder.reflectionToString(this, BYTE_ARRAY_SIZE_STYLE);
148    }
149   
150    // serialization support until commons collection 3.3 is released
 
151  0 toggle private void writeObject(ObjectOutputStream out) throws IOException {
152  0 serializableHeadersBackingList = new HashMap();
153  0 Iterator iter = headers.entrySet().iterator();
154  0 while (iter.hasNext()) {
155  0 Map.Entry entry = (Entry) iter.next();
156  0 serializableHeadersBackingList.put(entry.getKey(), new ArrayList((Collection)entry.getValue()));
157    }
158  0 out.defaultWriteObject();
159    }
160   
 
161  0 toggle private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
162  0 in.defaultReadObject();
163  0 headers = new MultiValueMap();
164  0 Iterator iter = serializableHeadersBackingList.entrySet().iterator();
165  0 while (iter.hasNext()) {
166  0 Map.Entry entry = (Entry) iter.next();
167  0 Collection c = (Collection) entry.getValue();
168  0 for (Iterator ic = c.iterator(); ic.hasNext();) {
169  0 headers.put(entry.getKey(), ic.next());
170    }
171    }
172  0 serializableHeadersBackingList = null;
173    }
174   
 
175  7 toggle @Override
176    public void replay(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
177  7 response.setStatus(getStatusCode());
178   
179  7 boolean acceptsGzipEncoding = isAcceptsGzip(request) && canServeGzipContent();
180  7 addHeaders(acceptsGzipEncoding, response);
181   
182    // TODO : cookies ?
183  7 response.setContentType(getContentType());
184  7 response.setCharacterEncoding(getCharacterEncoding());
185   
186  7 writeContent(request, response, chain, acceptsGzipEncoding);
187    }
188   
189    protected abstract void writeContent(HttpServletRequest request, HttpServletResponse response, FilterChain chain, boolean acceptsGzipEncoding) throws IOException, ServletException;
190   
191    /**
192    * Sets headers in the response object.
193    */
 
194  7 toggle protected void addHeaders(final boolean acceptsGzipEncoding, final HttpServletResponse response) {
195  7 final MultiMap headers = getHeaders();
196   
197  7 final Iterator it = headers.keySet().iterator();
198  15 while (it.hasNext()) {
199  8 final String header = (String) it.next();
200  8 if (!acceptsGzipEncoding) {
201    //TODO: this should not be necessary any more ...
202  4 if ("Content-Encoding".equals(header) || "Vary".equals(header)) {
203  2 continue;
204    }
205    }
206  6 final Collection values = (Collection) headers.get(header);
207  6 final Iterator valIt = values.iterator();
208  13 while (valIt.hasNext()) {
209  7 final Object value = valIt.next();
210    //we don't use RequestHeaderUtil.setHeader(response, header, val) since we want to be able override headers (e.g with those set in rendering)
211  7 if (value instanceof Long) {
212  2 response.setDateHeader(header, ((Long) value).longValue());
213  5 } else if (value instanceof Integer) {
214  0 response.setIntHeader(header, ((Integer) value).intValue());
215  5 } else if (value instanceof String) {
216  5 response.setHeader(header, (String) value);
217    } else {
218  0 throw new IllegalStateException("Unrecognized type for header [" + header + "], value is: " + value);
219    }
220    }
221    }
222   
223  7 if(acceptsGzipEncoding){
224    // write the headers as well (if not written already)
225  5 if (!response.containsHeader("Content-Encoding")) {
226  1 RequestHeaderUtil.addAndVerifyHeader(response, "Content-Encoding", "gzip");
227  1 RequestHeaderUtil.addAndVerifyHeader(response, "Vary", "Accept-Encoding"); // needed for proxies
228    }
229    }
230    }
231   
 
232  7 toggle protected boolean isAcceptsGzip(HttpServletRequest request){
233  7 return GZipUtil.isAcceptsGzip(request);
234    }
235   
236    abstract protected boolean canServeGzipContent();
237   
238    }