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