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.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
62
63
64
65
66
67
68 public abstract class ContentCachedEntry implements CachedEntry, Serializable {
69
70 private static final ToStringStyle BYTE_ARRAY_SIZE_STYLE = new ToStringStyle() {
71 @Override
72 protected void appendDetail(StringBuffer buffer, String fieldName,
73 byte[] array) {
74 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
90
91
92
93
94
95
96
97 public ContentCachedEntry(String contentType, String characterEncoding, int statusCode, MultiMap headers, long modificationDate, String originalUrl, int timeToLiveInSeconds) throws IOException {
98 this.contentType = contentType;
99 this.characterEncoding = characterEncoding;
100 this.statusCode = statusCode;
101 this.headers = headers;
102 this.lastModificationTime = modificationDate;
103 this.originalUrl = originalUrl;
104 this.timeToLiveInSeconds = timeToLiveInSeconds;
105 }
106
107 @Override
108 public String getOriginalURL() {
109 return this.originalUrl;
110 }
111
112
113
114
115
116
117
118
119 public String getContentType() {
120 return contentType;
121 }
122
123 public String getCharacterEncoding() {
124 return characterEncoding;
125 }
126
127 public int getStatusCode() {
128 return statusCode;
129 }
130
131 public MultiMap getHeaders() {
132 return headers;
133 }
134
135 @Override
136 public long getLastModificationTime() {
137 return lastModificationTime;
138 }
139
140 @Override
141 public int getTimeToLiveInSeconds() {
142 return timeToLiveInSeconds;
143 }
144
145 @Override
146 public String toString() {
147 return ToStringBuilder.reflectionToString(this, BYTE_ARRAY_SIZE_STYLE);
148 }
149
150
151 private void writeObject(ObjectOutputStream out) throws IOException {
152 serializableHeadersBackingList = new HashMap();
153 Iterator iter = headers.entrySet().iterator();
154 while (iter.hasNext()) {
155 Map.Entry entry = (Entry) iter.next();
156 serializableHeadersBackingList.put(entry.getKey(), new ArrayList((Collection)entry.getValue()));
157 }
158 out.defaultWriteObject();
159 }
160
161 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
162 in.defaultReadObject();
163 headers = new MultiValueMap();
164 Iterator iter = serializableHeadersBackingList.entrySet().iterator();
165 while (iter.hasNext()) {
166 Map.Entry entry = (Entry) iter.next();
167 Collection c = (Collection) entry.getValue();
168 for (Iterator ic = c.iterator(); ic.hasNext();) {
169 headers.put(entry.getKey(), ic.next());
170 }
171 }
172 serializableHeadersBackingList = null;
173 }
174
175 @Override
176 public void replay(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
177 response.setStatus(getStatusCode());
178
179 boolean acceptsGzipEncoding = isAcceptsGzip(request) && canServeGzipContent();
180 addHeaders(acceptsGzipEncoding, response);
181
182
183 response.setContentType(getContentType());
184 response.setCharacterEncoding(getCharacterEncoding());
185
186 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
193
194 protected void addHeaders(final boolean acceptsGzipEncoding, final HttpServletResponse response) {
195 final MultiMap headers = getHeaders();
196
197 final Iterator it = headers.keySet().iterator();
198 while (it.hasNext()) {
199 final String header = (String) it.next();
200 if (!acceptsGzipEncoding) {
201
202 if ("Content-Encoding".equals(header) || "Vary".equals(header)) {
203 continue;
204 }
205 }
206 final Collection values = (Collection) headers.get(header);
207 final Iterator valIt = values.iterator();
208 while (valIt.hasNext()) {
209 final Object value = valIt.next();
210
211 if (value instanceof Long) {
212 response.setDateHeader(header, ((Long) value).longValue());
213 } else if (value instanceof Integer) {
214 response.setIntHeader(header, ((Integer) value).intValue());
215 } else if (value instanceof String) {
216 response.setHeader(header, (String) value);
217 } else {
218 throw new IllegalStateException("Unrecognized type for header [" + header + "], value is: " + value);
219 }
220 }
221 }
222
223 if(acceptsGzipEncoding){
224
225 if (!response.containsHeader("Content-Encoding")) {
226 RequestHeaderUtil.addAndVerifyHeader(response, "Content-Encoding", "gzip");
227 RequestHeaderUtil.addAndVerifyHeader(response, "Vary", "Accept-Encoding");
228 }
229 }
230 }
231
232 protected boolean isAcceptsGzip(HttpServletRequest request){
233 return GZipUtil.isAcceptsGzip(request);
234 }
235
236 abstract protected boolean canServeGzipContent();
237
238 }