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