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.util.GetterUtil;
25 import com.liferay.portal.kernel.util.Http;
26 import com.liferay.portal.kernel.util.HttpUtil;
27 import com.liferay.portal.kernel.util.JavaConstants;
28 import com.liferay.portal.kernel.util.ParamUtil;
29 import com.liferay.portal.kernel.util.StringBundler;
30 import com.liferay.portal.kernel.util.StringPool;
31 import com.liferay.portal.kernel.util.StringUtil;
32 import com.liferay.portal.kernel.util.UnicodeProperties;
33 import com.liferay.portal.kernel.util.Validator;
34 import com.liferay.portal.model.Group;
35 import com.liferay.portal.model.Layout;
36 import com.liferay.portal.model.LayoutConstants;
37 import com.liferay.portal.model.LayoutTypePortletConstants;
38 import com.liferay.portal.model.Portlet;
39 import com.liferay.portal.model.PortletConstants;
40 import com.liferay.portal.service.GroupLocalServiceUtil;
41 import com.liferay.portal.service.LayoutLocalServiceUtil;
42 import com.liferay.portal.service.PortletLocalServiceUtil;
43 import com.liferay.portal.servlet.filters.BasePortalFilter;
44 import com.liferay.portal.struts.LastPath;
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(StringPool.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.getType().equals(LayoutConstants.TYPE_PORTLET)) {
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 LayoutTypePortletConstants.NESTED_COLUMN_IDS)) {
293
294 String[] columnIds = StringUtil.split(
295 properties.get(
296 LayoutTypePortletConstants.NESTED_COLUMN_IDS));
297
298 for (String columnId : columnIds) {
299 String settings = properties.getProperty(
300 columnId, StringPool.BLANK);
301
302 if (!isCacheableColumn(companyId, settings)) {
303 return false;
304 }
305 }
306 }
307
308 return true;
309 }
310 catch (Exception e) {
311 return false;
312 }
313 }
314
315 protected boolean isCacheableRequest(HttpServletRequest request) {
316 String portletId = ParamUtil.getString(request, "p_p_id");
317
318 if (Validator.isNotNull(portletId)) {
319 return false;
320 }
321
322 if ((_pattern == _PATTERN_FRIENDLY) || (_pattern == _PATTERN_LAYOUT)) {
323 long userId = PortalUtil.getUserId(request);
324 String remoteUser = request.getRemoteUser();
325
326 if ((userId > 0) || Validator.isNotNull(remoteUser)) {
327 return false;
328 }
329 }
330
331 if (_pattern == _PATTERN_LAYOUT) {
332 String plid = ParamUtil.getString(request, "p_l_id");
333
334 if (Validator.isNull(plid)) {
335 return false;
336 }
337 }
338
339 return true;
340 }
341
342 protected boolean isCacheableResponse(CacheResponse cacheResponse) {
343 if (cacheResponse.getStatus() == HttpServletResponse.SC_OK) {
344 return true;
345 }
346 else {
347 return false;
348 }
349 }
350
351 protected boolean isInclude(HttpServletRequest request) {
352 String uri = (String)request.getAttribute(
353 JavaConstants.JAVAX_SERVLET_INCLUDE_REQUEST_URI);
354
355 if (uri == null) {
356 return false;
357 }
358 else {
359 return true;
360 }
361 }
362
363 protected void processFilter(
364 HttpServletRequest request, HttpServletResponse response,
365 FilterChain filterChain)
366 throws Exception {
367
368 if (isCacheableRequest(request) && !isInclude(request) &&
369 !isAlreadyFiltered(request)) {
370
371 request.setAttribute(SKIP_FILTER, Boolean.TRUE);
372
373 String key = getCacheKey(request);
374
375 long companyId = PortalInstances.getCompanyId(request);
376
377 CacheResponseData cacheResponseData =
378 CacheUtil.getCacheResponseData(companyId, key);
379
380 if (cacheResponseData == null) {
381 if (!isCacheableData(companyId, request)) {
382 if (_log.isDebugEnabled()) {
383 _log.debug("Request is not cacheable " + key);
384 }
385
386 processFilter(
387 CacheFilter.class, request, response, filterChain);
388
389 return;
390 }
391
392 if (_log.isInfoEnabled()) {
393 _log.info("Caching request " + key);
394 }
395
396 CacheResponse cacheResponse = new CacheResponse(
397 response, StringPool.UTF8);
398
399 processFilter(
400 CacheFilter.class, request, cacheResponse, filterChain);
401
402 cacheResponseData = new CacheResponseData(
403 cacheResponse.unsafeGetData(),
404 cacheResponse.getContentLength(),
405 cacheResponse.getContentType(), cacheResponse.getHeaders());
406
407 LastPath lastPath = (LastPath)request.getAttribute(
408 WebKeys.LAST_PATH);
409
410 if (lastPath != null) {
411 cacheResponseData.setAttribute(WebKeys.LAST_PATH, lastPath);
412 }
413
414
419 if (isCacheableRequest(request) &&
420 isCacheableResponse(cacheResponse)) {
421
422 CacheUtil.putCacheResponseData(
423 companyId, key, cacheResponseData);
424 }
425 }
426 else {
427 LastPath lastPath = (LastPath)cacheResponseData.getAttribute(
428 WebKeys.LAST_PATH);
429
430 if (lastPath != null) {
431 HttpSession session = request.getSession();
432
433 session.setAttribute(WebKeys.LAST_PATH, lastPath);
434 }
435 }
436
437 CacheResponseUtil.write(response, cacheResponseData);
438 }
439 else {
440 if (_log.isDebugEnabled()) {
441 _log.debug("Request is not cacheable");
442 }
443
444 processFilter(CacheFilter.class, request, response, filterChain);
445 }
446 }
447
448 private static final int _PATTERN_FRIENDLY = 0;
449
450 private static final int _PATTERN_LAYOUT = 1;
451
452 private static final int _PATTERN_RESOURCE = 2;
453
454 private static Log _log = LogFactoryUtil.getLog(CacheFilter.class);
455
456 private int _pattern;
457
458 }