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