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.ByteBufferServletResponse;
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 sb.append(request.getQueryString());
109
110
111
112 sb.append(StringPool.POUND);
113
114 String languageId = (String)request.getAttribute(
115 WebKeys.I18N_LANGUAGE_ID);
116
117 if (Validator.isNull(languageId)) {
118 languageId = LanguageUtil.getLanguageId(request);
119 }
120
121 sb.append(languageId);
122
123
124
125 String userAgent = GetterUtil.getString(
126 request.getHeader(HttpHeaders.USER_AGENT));
127
128 sb.append(StringPool.POUND);
129 sb.append(userAgent.toLowerCase().hashCode());
130
131
132
133 sb.append(StringPool.POUND);
134 sb.append(BrowserSnifferUtil.acceptsGzip(request));
135
136 return sb.toString().trim().toUpperCase();
137 }
138
139 protected long getPlid(
140 long companyId, String pathInfo, String servletPath, long defaultPlid) {
141
142 if (_pattern == _PATTERN_LAYOUT) {
143 return defaultPlid;
144 }
145
146 if (Validator.isNull(pathInfo) ||
147 !pathInfo.startsWith(StringPool.SLASH)) {
148
149 return 0;
150 }
151
152
153
154 String friendlyURL = null;
155
156 int pos = pathInfo.indexOf(CharPool.SLASH, 1);
157
158 if (pos != -1) {
159 friendlyURL = pathInfo.substring(0, pos);
160 }
161 else {
162 if (pathInfo.length() > 1) {
163 friendlyURL = pathInfo.substring(0, pathInfo.length());
164 }
165 }
166
167 if (Validator.isNull(friendlyURL)) {
168 return 0;
169 }
170
171 long groupId = 0;
172 boolean privateLayout = false;
173
174 try {
175 Group group = GroupLocalServiceUtil.getFriendlyURLGroup(
176 companyId, friendlyURL);
177
178 groupId = group.getGroupId();
179
180 if (servletPath.startsWith(
181 PropsValues.
182 LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING) ||
183 servletPath.startsWith(
184 PropsValues.
185 LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING)) {
186
187 privateLayout = true;
188 }
189 else if (servletPath.startsWith(
190 PropsValues.
191 LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING)) {
192
193 privateLayout = false;
194 }
195 }
196 catch (NoSuchLayoutException nsle) {
197 if (_log.isWarnEnabled()) {
198 _log.warn(nsle);
199 }
200 }
201 catch (Exception e) {
202 if (_log.isWarnEnabled()) {
203 _log.error(e);
204 }
205
206 return 0;
207 }
208
209
210
211 friendlyURL = null;
212
213 if ((pos != -1) && ((pos + 1) != pathInfo.length())) {
214 friendlyURL = pathInfo.substring(pos, pathInfo.length());
215 }
216
217 if (Validator.isNull(friendlyURL)) {
218 try {
219 long plid = LayoutLocalServiceUtil.getDefaultPlid(
220 groupId, privateLayout);
221
222 return plid;
223 }
224 catch (Exception e) {
225 _log.warn(e);
226
227 return 0;
228 }
229 }
230
231
232
233 try {
234 Layout layout = LayoutLocalServiceUtil.getFriendlyURLLayout(
235 groupId, privateLayout, friendlyURL);
236
237 return layout.getPlid();
238 }
239 catch (NoSuchLayoutException nsle) {
240 _log.warn(nsle);
241
242 return 0;
243 }
244 catch (Exception e) {
245 _log.error(e);
246
247 return 0;
248 }
249 }
250
251 protected boolean isAlreadyFiltered(HttpServletRequest request) {
252 if (request.getAttribute(SKIP_FILTER) != null) {
253 return true;
254 }
255 else {
256 return false;
257 }
258 }
259
260 protected boolean isCacheableColumn(long companyId, String columnSettings)
261 throws SystemException {
262
263 String[] portletIds = StringUtil.split(columnSettings);
264
265 for (String portletId : portletIds) {
266 portletId = PortletConstants.getRootPortletId(portletId);
267
268 Portlet portlet = PortletLocalServiceUtil.getPortletById(
269 companyId, portletId);
270
271 if (!portlet.isLayoutCacheable()) {
272 return false;
273 }
274 }
275
276 return true;
277 }
278
279 protected boolean isCacheableData(
280 long companyId, HttpServletRequest request) {
281
282 try {
283 if (_pattern == _PATTERN_RESOURCE) {
284 return true;
285 }
286
287 long plid = getPlid(
288 companyId, request.getPathInfo(), request.getServletPath(),
289 ParamUtil.getLong(request, "p_l_id"));
290
291 if (plid <= 0) {
292 return false;
293 }
294
295 Layout layout = LayoutLocalServiceUtil.getLayout(plid);
296
297 if (!layout.isTypePortlet()) {
298 return false;
299 }
300
301 UnicodeProperties properties = layout.getTypeSettingsProperties();
302
303 for (int i = 0; i < 10; i++) {
304 String columnId = "column-" + i;
305
306 String settings = properties.getProperty(
307 columnId, StringPool.BLANK);
308
309 if (!isCacheableColumn(companyId, settings)) {
310 return false;
311 }
312 }
313
314 String columnIdsString = properties.get(
315 LayoutTypePortletConstants.NESTED_COLUMN_IDS);
316
317 if (columnIdsString != null) {
318 String[] columnIds = StringUtil.split(columnIdsString);
319
320 for (String columnId : columnIds) {
321 String settings = properties.getProperty(
322 columnId, StringPool.BLANK);
323
324 if (!isCacheableColumn(companyId, settings)) {
325 return false;
326 }
327 }
328 }
329
330 return true;
331 }
332 catch (Exception e) {
333 return false;
334 }
335 }
336
337 protected boolean isCacheableRequest(HttpServletRequest request) {
338 String portletId = ParamUtil.getString(request, "p_p_id");
339
340 if (Validator.isNotNull(portletId)) {
341 return false;
342 }
343
344 if ((_pattern == _PATTERN_FRIENDLY) || (_pattern == _PATTERN_LAYOUT)) {
345 long userId = PortalUtil.getUserId(request);
346 String remoteUser = request.getRemoteUser();
347
348 if ((userId > 0) || Validator.isNotNull(remoteUser)) {
349 return false;
350 }
351 }
352
353 if (_pattern == _PATTERN_LAYOUT) {
354 String plid = ParamUtil.getString(request, "p_l_id");
355
356 if (Validator.isNull(plid)) {
357 return false;
358 }
359 }
360
361 return true;
362 }
363
364 protected boolean isCacheableResponse(
365 ByteBufferServletResponse byteBufferResponse) {
366
367 if ((byteBufferResponse.getStatus() == HttpServletResponse.SC_OK) &&
368 (byteBufferResponse.getBufferSize() <
369 PropsValues.CACHE_CONTENT_THRESHOLD_SIZE)) {
370
371 return true;
372 }
373 else {
374 return false;
375 }
376 }
377
378 protected boolean isInclude(HttpServletRequest request) {
379 String uri = (String)request.getAttribute(
380 JavaConstants.JAVAX_SERVLET_INCLUDE_REQUEST_URI);
381
382 if (uri == null) {
383 return false;
384 }
385 else {
386 return true;
387 }
388 }
389
390 @Override
391 protected void processFilter(
392 HttpServletRequest request, HttpServletResponse response,
393 FilterChain filterChain)
394 throws Exception {
395
396 request.setAttribute(SKIP_FILTER, Boolean.TRUE);
397
398 String key = getCacheKey(request);
399
400 long companyId = PortalInstances.getCompanyId(request);
401
402 CacheResponseData cacheResponseData = CacheUtil.getCacheResponseData(
403 companyId, key);
404
405 if (cacheResponseData == null) {
406 if (!isCacheableData(companyId, request)) {
407 if (_log.isDebugEnabled()) {
408 _log.debug("Request is not cacheable " + key);
409 }
410
411 processFilter(
412 CacheFilter.class, request, response, filterChain);
413
414 return;
415 }
416
417 if (_log.isInfoEnabled()) {
418 _log.info("Caching request " + key);
419 }
420
421 ByteBufferServletResponse byteBufferResponse =
422 new ByteBufferServletResponse(response);
423
424 processFilter(
425 CacheFilter.class, request, byteBufferResponse, filterChain);
426
427 cacheResponseData = new CacheResponseData(byteBufferResponse);
428
429 LastPath lastPath = (LastPath)request.getAttribute(
430 WebKeys.LAST_PATH);
431
432 if (lastPath != null) {
433 cacheResponseData.setAttribute(WebKeys.LAST_PATH, lastPath);
434 }
435
436
437
438
439
440
441 String cacheControl = GetterUtil.getString(
442 byteBufferResponse.getHeader(HttpHeaders.CACHE_CONTROL));
443
444 if ((byteBufferResponse.getStatus() == HttpServletResponse.SC_OK) &&
445 !cacheControl.contains(HttpHeaders.PRAGMA_NO_CACHE_VALUE) &&
446 isCacheableRequest(request) &&
447 isCacheableResponse(byteBufferResponse)) {
448
449 CacheUtil.putCacheResponseData(
450 companyId, key, cacheResponseData);
451 }
452 }
453 else {
454 LastPath lastPath = (LastPath)cacheResponseData.getAttribute(
455 WebKeys.LAST_PATH);
456
457 if (lastPath != null) {
458 HttpSession session = request.getSession();
459
460 session.setAttribute(WebKeys.LAST_PATH, lastPath);
461 }
462 }
463
464 CacheResponseUtil.write(response, cacheResponseData);
465 }
466
467 private static final int _PATTERN_FRIENDLY = 0;
468
469 private static final int _PATTERN_LAYOUT = 1;
470
471 private static final int _PATTERN_RESOURCE = 2;
472
473 private static Log _log = LogFactoryUtil.getLog(CacheFilter.class);
474
475 private int _pattern;
476
477 }