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