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