001
014
015 package com.liferay.portal.servlet.filters.cache;
016
017 import com.liferay.portal.exception.NoSuchLayoutException;
018 import com.liferay.portal.kernel.exception.PortalException;
019 import com.liferay.portal.kernel.language.LanguageUtil;
020 import com.liferay.portal.kernel.log.Log;
021 import com.liferay.portal.kernel.log.LogFactoryUtil;
022 import com.liferay.portal.kernel.servlet.BrowserSnifferUtil;
023 import com.liferay.portal.kernel.servlet.BufferCacheServletResponse;
024 import com.liferay.portal.kernel.servlet.HttpHeaders;
025 import com.liferay.portal.kernel.struts.LastPath;
026 import com.liferay.portal.kernel.util.CharPool;
027 import com.liferay.portal.kernel.util.GetterUtil;
028 import com.liferay.portal.kernel.util.Http;
029 import com.liferay.portal.kernel.util.HttpUtil;
030 import com.liferay.portal.kernel.util.JavaConstants;
031 import com.liferay.portal.kernel.util.ParamUtil;
032 import com.liferay.portal.kernel.util.StringBundler;
033 import com.liferay.portal.kernel.util.StringPool;
034 import com.liferay.portal.kernel.util.StringUtil;
035 import com.liferay.portal.kernel.util.Validator;
036 import com.liferay.portal.kernel.util.WebKeys;
037 import com.liferay.portal.model.Group;
038 import com.liferay.portal.model.Layout;
039 import com.liferay.portal.model.LayoutTypePortlet;
040 import com.liferay.portal.security.auth.AuthTokenUtil;
041 import com.liferay.portal.service.GroupLocalServiceUtil;
042 import com.liferay.portal.service.LayoutLocalServiceUtil;
043 import com.liferay.portal.servlet.filters.BasePortalFilter;
044 import com.liferay.portal.util.PortalInstances;
045 import com.liferay.portal.util.PortalUtil;
046 import com.liferay.portal.util.PropsValues;
047 import com.liferay.util.servlet.filters.CacheResponseData;
048 import com.liferay.util.servlet.filters.CacheResponseUtil;
049
050 import javax.servlet.FilterChain;
051 import javax.servlet.FilterConfig;
052 import javax.servlet.http.HttpServletRequest;
053 import javax.servlet.http.HttpServletResponse;
054 import javax.servlet.http.HttpSession;
055
056
061 public class CacheFilter extends BasePortalFilter {
062
063 public static final String SKIP_FILTER = CacheFilter.class + "SKIP_FILTER";
064
065 @Override
066 public void init(FilterConfig filterConfig) {
067 super.init(filterConfig);
068
069 _pattern = GetterUtil.getInteger(
070 filterConfig.getInitParameter("pattern"));
071
072 if ((_pattern != _PATTERN_FRIENDLY) && (_pattern != _PATTERN_LAYOUT) &&
073 (_pattern != _PATTERN_RESOURCE)) {
074
075 _log.error("Cache pattern is invalid");
076 }
077 }
078
079 @Override
080 public boolean isFilterEnabled(
081 HttpServletRequest request, HttpServletResponse response) {
082
083 if (isCacheableRequest(request) && !isInclude(request) &&
084 !isAlreadyFiltered(request)) {
085
086 return true;
087 }
088 else {
089 return false;
090 }
091 }
092
093 protected String getCacheKey(HttpServletRequest request) {
094 StringBundler sb = new StringBundler(13);
095
096
097
098 sb.append(HttpUtil.getProtocol(request));
099 sb.append(Http.PROTOCOL_DELIMITER);
100 sb.append(request.getContextPath());
101 sb.append(request.getServletPath());
102 sb.append(request.getPathInfo());
103 sb.append(StringPool.QUESTION);
104
105 String queryString = request.getQueryString();
106
107 if (queryString == null) {
108 queryString = (String)request.getAttribute(
109 JavaConstants.JAVAX_SERVLET_FORWARD_QUERY_STRING);
110
111 if (queryString == null) {
112 String url = PortalUtil.getCurrentCompleteURL(request);
113
114 int pos = url.indexOf(StringPool.QUESTION);
115
116 if (pos > -1) {
117 queryString = url.substring(pos + 1);
118 }
119 }
120 }
121
122 if (queryString != null) {
123 sb.append(queryString);
124 }
125
126
127
128 sb.append(StringPool.POUND);
129
130 String languageId = (String)request.getAttribute(
131 WebKeys.I18N_LANGUAGE_ID);
132
133 if (Validator.isNull(languageId)) {
134 languageId = LanguageUtil.getLanguageId(request);
135 }
136
137 sb.append(languageId);
138
139
140
141 String userAgent = GetterUtil.getString(
142 request.getHeader(HttpHeaders.USER_AGENT));
143
144 sb.append(StringPool.POUND);
145 sb.append(StringUtil.toLowerCase(userAgent).hashCode());
146
147
148
149 sb.append(StringPool.POUND);
150 sb.append(BrowserSnifferUtil.acceptsGzip(request));
151
152 return StringUtil.toUpperCase(sb.toString().trim());
153 }
154
155 protected long getPlid(
156 long companyId, String pathInfo, String servletPath, long defaultPlid) {
157
158 if (_pattern == _PATTERN_LAYOUT) {
159 return defaultPlid;
160 }
161
162 if (Validator.isNull(pathInfo) ||
163 !pathInfo.startsWith(StringPool.SLASH)) {
164
165 return 0;
166 }
167
168
169
170 String friendlyURL = null;
171
172 int pos = pathInfo.indexOf(CharPool.SLASH, 1);
173
174 if (pos != -1) {
175 friendlyURL = pathInfo.substring(0, pos);
176 }
177 else if (pathInfo.length() > 1) {
178 friendlyURL = pathInfo;
179 }
180
181 if (Validator.isNull(friendlyURL)) {
182 return 0;
183 }
184
185 long groupId = 0;
186 boolean privateLayout = false;
187
188 try {
189 Group group = GroupLocalServiceUtil.getFriendlyURLGroup(
190 companyId, friendlyURL);
191
192 groupId = group.getGroupId();
193
194 if (servletPath.startsWith(
195 PropsValues.
196 LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING) ||
197 servletPath.startsWith(
198 PropsValues.
199 LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING)) {
200
201 privateLayout = true;
202 }
203 else if (servletPath.startsWith(
204 PropsValues.
205 LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING)) {
206
207 privateLayout = false;
208 }
209 }
210 catch (NoSuchLayoutException nsle) {
211 if (_log.isWarnEnabled()) {
212 _log.warn(nsle);
213 }
214 }
215 catch (Exception e) {
216 if (_log.isWarnEnabled()) {
217 _log.warn(e);
218 }
219
220 return 0;
221 }
222
223
224
225 friendlyURL = null;
226
227 if ((pos != -1) && ((pos + 1) != pathInfo.length())) {
228 friendlyURL = pathInfo.substring(pos);
229 }
230
231 if (Validator.isNull(friendlyURL)) {
232 try {
233 long plid = LayoutLocalServiceUtil.getDefaultPlid(
234 groupId, privateLayout);
235
236 return plid;
237 }
238 catch (Exception e) {
239 _log.warn(e);
240
241 return 0;
242 }
243 }
244 else if (friendlyURL.endsWith(StringPool.FORWARD_SLASH)) {
245 friendlyURL = friendlyURL.substring(0, friendlyURL.length() - 1);
246 }
247
248
249
250 try {
251 Layout layout = LayoutLocalServiceUtil.getFriendlyURLLayout(
252 groupId, privateLayout, friendlyURL);
253
254 return layout.getPlid();
255 }
256 catch (NoSuchLayoutException nsle) {
257 _log.warn(nsle);
258
259 return 0;
260 }
261 catch (Exception e) {
262 _log.error(e);
263
264 return 0;
265 }
266 }
267
268 protected boolean isAlreadyFiltered(HttpServletRequest request) {
269 if (request.getAttribute(SKIP_FILTER) != null) {
270 return true;
271 }
272 else {
273 return false;
274 }
275 }
276
277 protected boolean isCacheableData(
278 long companyId, HttpServletRequest request) {
279
280 try {
281 if (_pattern == _PATTERN_RESOURCE) {
282 return true;
283 }
284
285 long plid = getPlid(
286 companyId, request.getPathInfo(), request.getServletPath(),
287 ParamUtil.getLong(request, "p_l_id"));
288
289 if (plid <= 0) {
290 return false;
291 }
292
293 Layout layout = LayoutLocalServiceUtil.getLayout(plid);
294
295 if (!layout.isTypePortlet()) {
296 return false;
297 }
298
299 LayoutTypePortlet layoutTypePortlet =
300 (LayoutTypePortlet)layout.getLayoutType();
301
302 return layoutTypePortlet.isCacheable();
303 }
304 catch (Exception e) {
305 return false;
306 }
307 }
308
309 protected boolean isCacheableRequest(HttpServletRequest request) {
310 String portletId = ParamUtil.getString(request, "p_p_id");
311
312 if (Validator.isNotNull(portletId)) {
313 return false;
314 }
315
316 if ((_pattern == _PATTERN_FRIENDLY) || (_pattern == _PATTERN_LAYOUT)) {
317 long userId = PortalUtil.getUserId(request);
318 String remoteUser = request.getRemoteUser();
319
320 if ((userId > 0) || Validator.isNotNull(remoteUser)) {
321 return false;
322 }
323 }
324
325 if (_pattern == _PATTERN_LAYOUT) {
326 String plid = ParamUtil.getString(request, "p_l_id");
327
328 if (Validator.isNull(plid)) {
329 return false;
330 }
331 }
332
333 return true;
334 }
335
336 protected boolean isCacheableResponse(
337 BufferCacheServletResponse bufferCacheServletResponse) {
338
339 if ((bufferCacheServletResponse.getStatus() ==
340 HttpServletResponse.SC_OK) &&
341 (bufferCacheServletResponse.getBufferSize() <
342 PropsValues.CACHE_CONTENT_THRESHOLD_SIZE)) {
343
344 return true;
345 }
346 else {
347 return false;
348 }
349 }
350
351 protected boolean isInclude(HttpServletRequest request) {
352 String uri = (String)request.getAttribute(
353 JavaConstants.JAVAX_SERVLET_INCLUDE_REQUEST_URI);
354
355 if (uri == null) {
356 return false;
357 }
358 else {
359 return true;
360 }
361 }
362
363 @Override
364 protected void processFilter(
365 HttpServletRequest request, HttpServletResponse response,
366 FilterChain filterChain)
367 throws Exception {
368
369 request.setAttribute(SKIP_FILTER, Boolean.TRUE);
370
371 String key = getCacheKey(request);
372
373 String pAuth = request.getParameter("p_auth");
374
375 if (Validator.isNotNull(pAuth)) {
376 try {
377 AuthTokenUtil.checkCSRFToken(
378 request, CacheFilter.class.getName());
379 }
380 catch (PortalException pe) {
381 if (_log.isDebugEnabled()) {
382 _log.debug(
383 "Request is not cacheable " + key +
384 ", invalid token received");
385 }
386
387 processFilter(
388 CacheFilter.class.getName(), request, response,
389 filterChain);
390
391 return;
392 }
393
394 key = key.replace(StringUtil.toUpperCase(pAuth), "VALID");
395 }
396
397 long companyId = PortalInstances.getCompanyId(request);
398
399 CacheResponseData cacheResponseData = CacheUtil.getCacheResponseData(
400 companyId, key);
401
402 if ((cacheResponseData == null) || !cacheResponseData.isValid()) {
403 if (!_isValidCache(cacheResponseData) ||
404 !isCacheableData(companyId, request)) {
405
406 if (_log.isDebugEnabled()) {
407 _log.debug("Request is not cacheable " + key);
408 }
409
410 if (cacheResponseData == null) {
411 if (_log.isInfoEnabled()) {
412 _log.info("Caching request with invalid state " + key);
413 }
414
415 CacheUtil.putCacheResponseData(
416 companyId, key, new CacheResponseData());
417 }
418
419 processFilter(
420 CacheFilter.class.getName(), request, response,
421 filterChain);
422
423 return;
424 }
425
426 if (_log.isInfoEnabled()) {
427 _log.info("Caching request " + key);
428 }
429
430 BufferCacheServletResponse bufferCacheServletResponse =
431 new BufferCacheServletResponse(response);
432
433 processFilter(
434 CacheFilter.class.getName(), request,
435 bufferCacheServletResponse, filterChain);
436
437 cacheResponseData = new CacheResponseData(
438 bufferCacheServletResponse);
439
440 LastPath lastPath = (LastPath)request.getAttribute(
441 WebKeys.LAST_PATH);
442
443 if (lastPath != null) {
444 cacheResponseData.setAttribute(WebKeys.LAST_PATH, lastPath);
445 }
446
447
448
449
450
451
452 String cacheControl = GetterUtil.getString(
453 bufferCacheServletResponse.getHeader(
454 HttpHeaders.CACHE_CONTROL));
455
456 if ((bufferCacheServletResponse.getStatus() ==
457 HttpServletResponse.SC_OK) &&
458 !cacheControl.contains(HttpHeaders.PRAGMA_NO_CACHE_VALUE) &&
459 isCacheableRequest(request) &&
460 isCacheableResponse(bufferCacheServletResponse)) {
461
462 CacheUtil.putCacheResponseData(
463 companyId, key, cacheResponseData);
464 }
465 }
466 else {
467 LastPath lastPath = (LastPath)cacheResponseData.getAttribute(
468 WebKeys.LAST_PATH);
469
470 if (lastPath != null) {
471 HttpSession session = request.getSession();
472
473 session.setAttribute(WebKeys.LAST_PATH, lastPath);
474 }
475 }
476
477 CacheResponseUtil.write(response, cacheResponseData);
478 }
479
480 private boolean _isValidCache(CacheResponseData cacheResponseData) {
481 if ((cacheResponseData != null) && !cacheResponseData.isValid()) {
482 return false;
483 }
484
485 return true;
486 }
487
488 private static final int _PATTERN_FRIENDLY = 0;
489
490 private static final int _PATTERN_LAYOUT = 1;
491
492 private static final int _PATTERN_RESOURCE = 2;
493
494 private static final Log _log = LogFactoryUtil.getLog(CacheFilter.class);
495
496 private int _pattern;
497
498 }