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.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.Portlet;
37 import com.liferay.portal.model.PortletConstants;
38 import com.liferay.portal.model.impl.LayoutTypePortletImpl;
39 import com.liferay.portal.service.GroupLocalServiceUtil;
40 import com.liferay.portal.service.LayoutLocalServiceUtil;
41 import com.liferay.portal.service.PortletLocalServiceUtil;
42 import com.liferay.portal.servlet.filters.BasePortalFilter;
43 import com.liferay.portal.struts.LastPath;
44 import com.liferay.portal.util.PortalInstances;
45 import com.liferay.portal.util.PortalUtil;
46 import com.liferay.portal.util.PropsValues;
47 import com.liferay.portal.util.WebKeys;
48 import com.liferay.util.servlet.filters.CacheResponse;
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 LayoutTypePortletImpl.NESTED_COLUMN_IDS)) {
292
293 String[] columnIds = StringUtil.split(
294 properties.get(LayoutTypePortletImpl.NESTED_COLUMN_IDS));
295
296 for (String columnId : columnIds) {
297 String settings = properties.getProperty(
298 columnId, StringPool.BLANK);
299
300 if (!isCacheableColumn(companyId, settings)) {
301 return false;
302 }
303 }
304 }
305
306 return true;
307 }
308 catch (Exception e) {
309 return false;
310 }
311 }
312
313 protected boolean isCacheableRequest(HttpServletRequest request) {
314 String portletId = ParamUtil.getString(request, "p_p_id");
315
316 if (Validator.isNotNull(portletId)) {
317 return false;
318 }
319
320 if ((_pattern == _PATTERN_FRIENDLY) || (_pattern == _PATTERN_LAYOUT)) {
321 long userId = PortalUtil.getUserId(request);
322 String remoteUser = request.getRemoteUser();
323
324 if ((userId > 0) || Validator.isNotNull(remoteUser)) {
325 return false;
326 }
327 }
328
329 if (_pattern == _PATTERN_LAYOUT) {
330 String plid = ParamUtil.getString(request, "p_l_id");
331
332 if (Validator.isNull(plid)) {
333 return false;
334 }
335 }
336
337 return true;
338 }
339
340 protected boolean isCacheableResponse(CacheResponse cacheResponse) {
341 if (cacheResponse.getStatus() == HttpServletResponse.SC_OK) {
342 return true;
343 }
344 else {
345 return false;
346 }
347 }
348
349 protected boolean isInclude(HttpServletRequest request) {
350 String uri = (String)request.getAttribute(
351 JavaConstants.JAVAX_SERVLET_INCLUDE_REQUEST_URI);
352
353 if (uri == null) {
354 return false;
355 }
356 else {
357 return true;
358 }
359 }
360
361 protected void processFilter(
362 HttpServletRequest request, HttpServletResponse response,
363 FilterChain filterChain)
364 throws Exception {
365
366 if (isCacheableRequest(request) && !isInclude(request) &&
367 !isAlreadyFiltered(request)) {
368
369 request.setAttribute(SKIP_FILTER, Boolean.TRUE);
370
371 String key = getCacheKey(request);
372
373 long companyId = PortalInstances.getCompanyId(request);
374
375 CacheResponseData cacheResponseData =
376 CacheUtil.getCacheResponseData(companyId, key);
377
378 if (cacheResponseData == null) {
379 if (!isCacheableData(companyId, request)) {
380 if (_log.isDebugEnabled()) {
381 _log.debug("Request is not cacheable " + key);
382 }
383
384 processFilter(
385 CacheFilter.class, request, response, filterChain);
386
387 return;
388 }
389
390 if (_log.isInfoEnabled()) {
391 _log.info("Caching request " + key);
392 }
393
394 CacheResponse cacheResponse = new CacheResponse(
395 response, StringPool.UTF8);
396
397 processFilter(
398 CacheFilter.class, request, cacheResponse, filterChain);
399
400 cacheResponseData = new CacheResponseData(
401 cacheResponse.unsafeGetData(),
402 cacheResponse.getContentLength(),
403 cacheResponse.getContentType(), cacheResponse.getHeaders());
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(cacheResponse)) {
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 }