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