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