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