1
22
23 package com.liferay.portal.servlet.filters.cache;
24
25 import com.liferay.portal.NoSuchLayoutException;
26 import com.liferay.portal.SystemException;
27 import com.liferay.portal.kernel.language.LanguageUtil;
28 import com.liferay.portal.kernel.log.Log;
29 import com.liferay.portal.kernel.log.LogFactoryUtil;
30 import com.liferay.portal.kernel.servlet.BrowserSnifferUtil;
31 import com.liferay.portal.kernel.util.GetterUtil;
32 import com.liferay.portal.kernel.util.Http;
33 import com.liferay.portal.kernel.util.HttpUtil;
34 import com.liferay.portal.kernel.util.JavaConstants;
35 import com.liferay.portal.kernel.util.ParamUtil;
36 import com.liferay.portal.kernel.util.StringPool;
37 import com.liferay.portal.kernel.util.StringUtil;
38 import com.liferay.portal.kernel.util.UnicodeProperties;
39 import com.liferay.portal.kernel.util.Validator;
40 import com.liferay.portal.model.Group;
41 import com.liferay.portal.model.Layout;
42 import com.liferay.portal.model.LayoutConstants;
43 import com.liferay.portal.model.Portlet;
44 import com.liferay.portal.model.PortletConstants;
45 import com.liferay.portal.model.impl.LayoutTypePortletImpl;
46 import com.liferay.portal.service.GroupLocalServiceUtil;
47 import com.liferay.portal.service.LayoutLocalServiceUtil;
48 import com.liferay.portal.service.PortletLocalServiceUtil;
49 import com.liferay.portal.servlet.filters.BasePortalFilter;
50 import com.liferay.portal.struts.LastPath;
51 import com.liferay.portal.util.PortalInstances;
52 import com.liferay.portal.util.PortalUtil;
53 import com.liferay.portal.util.PropsValues;
54 import com.liferay.portal.util.WebKeys;
55 import com.liferay.util.servlet.filters.CacheResponse;
56 import com.liferay.util.servlet.filters.CacheResponseData;
57 import com.liferay.util.servlet.filters.CacheResponseUtil;
58
59 import javax.servlet.FilterChain;
60 import javax.servlet.FilterConfig;
61 import javax.servlet.http.HttpServletRequest;
62 import javax.servlet.http.HttpServletResponse;
63 import javax.servlet.http.HttpSession;
64
65
72 public class CacheFilter extends BasePortalFilter {
73
74 public static final String SKIP_FILTER = CacheFilter.class + "SKIP_FILTER";
75
76 public void init(FilterConfig filterConfig) {
77 super.init(filterConfig);
78
79 _pattern = GetterUtil.getInteger(
80 filterConfig.getInitParameter("pattern"));
81
82 if ((_pattern != _PATTERN_FRIENDLY) &&
83 (_pattern != _PATTERN_LAYOUT) &&
84 (_pattern != _PATTERN_RESOURCE)) {
85
86 _log.error("Cache pattern is invalid");
87 }
88 }
89
90 protected String getBrowserType(HttpServletRequest request) {
91 if (BrowserSnifferUtil.isIe(request) &&
92 BrowserSnifferUtil.getMajorVersion(request) == 7.0) {
93
94 return _BROWSER_TYPE_IE_7;
95 }
96 else if (BrowserSnifferUtil.isIe(request)) {
97 return _BROWSER_TYPE_IE;
98 }
99 else {
100 return _BROWSER_TYPE_OTHER;
101 }
102 }
103
104 protected String getCacheKey(HttpServletRequest request) {
105 StringBuilder sb = new StringBuilder();
106
107
109 sb.append(HttpUtil.getProtocol(request));
110 sb.append(Http.PROTOCOL_DELIMITER);
111 sb.append(request.getContextPath());
112 sb.append(request.getServletPath());
113 sb.append(request.getPathInfo());
114 sb.append(StringPool.QUESTION);
115 sb.append(request.getQueryString());
116
117
119 sb.append(StringPool.POUND);
120
121 String languageId = (String)request.getAttribute(
122 WebKeys.I18N_LANGUAGE_ID);
123
124 if (Validator.isNull(languageId)) {
125 languageId = LanguageUtil.getLanguageId(request);
126 }
127
128 sb.append(languageId);
129
130
132 sb.append(StringPool.POUND);
133 sb.append(getBrowserType(request));
134
135
137 sb.append(StringPool.POUND);
138 sb.append(BrowserSnifferUtil.acceptsGzip(request));
139
140 return sb.toString().trim().toUpperCase();
141 }
142
143 protected long getPlid(
144 long companyId, String pathInfo, String servletPath, long defaultPlid) {
145
146 if (_pattern == _PATTERN_LAYOUT) {
147 return defaultPlid;
148 }
149
150 if (Validator.isNull(pathInfo) ||
151 !pathInfo.startsWith(StringPool.SLASH)) {
152
153 return 0;
154 }
155
156
158 String friendlyURL = null;
159
160 int pos = pathInfo.indexOf(StringPool.SLASH, 1);
161
162 if (pos != -1) {
163 friendlyURL = pathInfo.substring(0, pos);
164 }
165 else {
166 if (pathInfo.length() > 1) {
167 friendlyURL = pathInfo.substring(0, pathInfo.length());
168 }
169 }
170
171 if (Validator.isNull(friendlyURL)) {
172 return 0;
173 }
174
175 long groupId = 0;
176 boolean privateLayout = false;
177
178 try {
179 Group group = GroupLocalServiceUtil.getFriendlyURLGroup(
180 companyId, friendlyURL);
181
182 groupId = group.getGroupId();
183
184 if (servletPath.startsWith(
185 PropsValues.
186 LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING) ||
187 servletPath.startsWith(
188 PropsValues.
189 LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING)) {
190
191 privateLayout = true;
192 }
193 else if (servletPath.startsWith(
194 PropsValues.
195 LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING)) {
196
197 privateLayout = false;
198 }
199 }
200 catch (NoSuchLayoutException nsle) {
201 if (_log.isWarnEnabled()) {
202 _log.warn(nsle);
203 }
204 }
205 catch (Exception e) {
206 if (_log.isWarnEnabled()) {
207 _log.error(e);
208 }
209
210 return 0;
211 }
212
213
215 friendlyURL = null;
216
217 if ((pos != -1) && ((pos + 1) != pathInfo.length())) {
218 friendlyURL = pathInfo.substring(pos, pathInfo.length());
219 }
220
221 if (Validator.isNull(friendlyURL)) {
222 return 0;
223 }
224
225
227 try {
228 Layout layout = LayoutLocalServiceUtil.getFriendlyURLLayout(
229 groupId, privateLayout, friendlyURL);
230
231 return layout.getPlid();
232 }
233 catch (NoSuchLayoutException nsle) {
234 _log.warn(nsle);
235
236 return 0;
237 }
238 catch (Exception e) {
239 _log.error(e);
240
241 return 0;
242 }
243 }
244
245 protected boolean isAlreadyFiltered(HttpServletRequest request) {
246 if (request.getAttribute(SKIP_FILTER) != null) {
247 return true;
248 }
249 else {
250 return false;
251 }
252 }
253
254 protected boolean isCacheableColumn(long companyId, String columnSettings)
255 throws SystemException {
256
257 String[] portletIds = StringUtil.split(columnSettings);
258
259 for (String portletId : portletIds) {
260 portletId = PortletConstants.getRootPortletId(portletId);
261
262 Portlet portlet = PortletLocalServiceUtil.getPortletById(
263 companyId, portletId);
264
265 if (!portlet.isLayoutCacheable()) {
266 return false;
267 }
268 }
269
270 return true;
271 }
272
273 protected boolean isCacheableData(
274 long companyId, HttpServletRequest request) {
275
276 try {
277 if (_pattern == _PATTERN_RESOURCE) {
278 return true;
279 }
280
281 long plid = getPlid(
282 companyId, request.getPathInfo(), request.getServletPath(),
283 ParamUtil.getLong(request, "p_l_id"));
284
285 if (plid <= 0) {
286 return false;
287 }
288
289 Layout layout = LayoutLocalServiceUtil.getLayout(plid);
290
291 if (!layout.getType().equals(LayoutConstants.TYPE_PORTLET)) {
292 return false;
293 }
294
295 UnicodeProperties properties = layout.getTypeSettingsProperties();
296
297 for (int i = 0; i < 10; i++) {
298 String columnId = "column-" + i;
299
300 String settings = properties.getProperty(
301 columnId, StringPool.BLANK);
302
303 if (!isCacheableColumn(companyId, settings)) {
304 return false;
305 }
306 }
307
308 if (properties.containsKey(
309 LayoutTypePortletImpl.NESTED_COLUMN_IDS)) {
310
311 String[] columnIds = StringUtil.split(
312 properties.get(LayoutTypePortletImpl.NESTED_COLUMN_IDS));
313
314 for (String columnId : columnIds) {
315 String settings = properties.getProperty(
316 columnId, StringPool.BLANK);
317
318 if (!isCacheableColumn(companyId, settings)) {
319 return false;
320 }
321 }
322 }
323
324 return true;
325 }
326 catch (Exception e) {
327 return false;
328 }
329 }
330
331 protected boolean isCacheableRequest(HttpServletRequest request) {
332 String portletId = ParamUtil.getString(request, "p_p_id");
333
334 if (Validator.isNotNull(portletId)) {
335 return false;
336 }
337
338 if ((_pattern == _PATTERN_FRIENDLY) || (_pattern == _PATTERN_LAYOUT)) {
339 long userId = PortalUtil.getUserId(request);
340 String remoteUser = request.getRemoteUser();
341
342 if ((userId > 0) || Validator.isNotNull(remoteUser)) {
343 return false;
344 }
345 }
346
347 if (_pattern == _PATTERN_LAYOUT) {
348 String plid = ParamUtil.getString(request, "p_l_id");
349
350 if (Validator.isNull(plid)) {
351 return false;
352 }
353 }
354
355 return true;
356 }
357
358 protected boolean isCacheableResponse(CacheResponse cacheResponse) {
359 if (cacheResponse.getStatus() == HttpServletResponse.SC_OK) {
360 return true;
361 }
362 else {
363 return false;
364 }
365 }
366
367 protected boolean isInclude(HttpServletRequest request) {
368 String uri = (String)request.getAttribute(
369 JavaConstants.JAVAX_SERVLET_INCLUDE_REQUEST_URI);
370
371 if (uri == null) {
372 return false;
373 }
374 else {
375 return true;
376 }
377 }
378
379 protected void processFilter(
380 HttpServletRequest request, HttpServletResponse response,
381 FilterChain filterChain)
382 throws Exception {
383
384 if (isCacheableRequest(request) && !isInclude(request) &&
385 !isAlreadyFiltered(request)) {
386
387 request.setAttribute(SKIP_FILTER, Boolean.TRUE);
388
389 String key = getCacheKey(request);
390
391 long companyId = PortalInstances.getCompanyId(request);
392
393 CacheResponseData cacheResponseData =
394 CacheUtil.getCacheResponseData(companyId, key);
395
396 if (cacheResponseData == null) {
397 if (!isCacheableData(companyId, request)) {
398 if (_log.isDebugEnabled()) {
399 _log.debug("Request is not cacheable " + key);
400 }
401
402 processFilter(
403 CacheFilter.class, request, response, filterChain);
404
405 return;
406 }
407
408 if (_log.isInfoEnabled()) {
409 _log.info("Caching request " + key);
410 }
411
412 CacheResponse cacheResponse = new CacheResponse(
413 response, StringPool.UTF8);
414
415 processFilter(
416 CacheFilter.class, request, cacheResponse, filterChain);
417
418 cacheResponseData = new CacheResponseData(
419 cacheResponse.getData(), cacheResponse.getContentType(),
420 cacheResponse.getHeaders());
421
422 LastPath lastPath = (LastPath)request.getAttribute(
423 WebKeys.LAST_PATH);
424
425 if (lastPath != null) {
426 cacheResponseData.setAttribute(WebKeys.LAST_PATH, lastPath);
427 }
428
429
434 if (isCacheableRequest(request) &&
435 isCacheableResponse(cacheResponse)) {
436
437 CacheUtil.putCacheResponseData(
438 companyId, key, cacheResponseData);
439 }
440 }
441 else {
442 LastPath lastPath = (LastPath)cacheResponseData.getAttribute(
443 WebKeys.LAST_PATH);
444
445 if (lastPath != null) {
446 HttpSession session = request.getSession();
447
448 session.setAttribute(WebKeys.LAST_PATH, lastPath);
449 }
450 }
451
452 CacheResponseUtil.write(response, cacheResponseData);
453 }
454 else {
455 if (_log.isDebugEnabled()) {
456 _log.debug("Request is not cacheable");
457 }
458
459 processFilter(CacheFilter.class, request, response, filterChain);
460 }
461 }
462
463 private static final int _PATTERN_FRIENDLY = 0;
464
465 private static final int _PATTERN_LAYOUT = 1;
466
467 private static final int _PATTERN_RESOURCE = 2;
468
469 private static final String _BROWSER_TYPE_IE_7 = "ie_7";
470
471 private static final String _BROWSER_TYPE_IE = "ie";
472
473 private static final String _BROWSER_TYPE_OTHER = "other";
474
475 private static Log _log = LogFactoryUtil.getLog(CacheFilter.class);
476
477 private int _pattern;
478
479 }