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