001
014
015 package com.liferay.portal.servlet.filters.cache;
016
017 import com.liferay.portal.NoSuchLayoutException;
018 import com.liferay.portal.kernel.exception.SystemException;
019 import com.liferay.portal.kernel.language.LanguageUtil;
020 import com.liferay.portal.kernel.log.Log;
021 import com.liferay.portal.kernel.log.LogFactoryUtil;
022 import com.liferay.portal.kernel.servlet.BrowserSnifferUtil;
023 import com.liferay.portal.kernel.servlet.HttpHeaders;
024 import com.liferay.portal.kernel.servlet.StringServletResponse;
025 import com.liferay.portal.kernel.struts.LastPath;
026 import com.liferay.portal.kernel.util.GetterUtil;
027 import com.liferay.portal.kernel.util.Http;
028 import com.liferay.portal.kernel.util.HttpUtil;
029 import com.liferay.portal.kernel.util.JavaConstants;
030 import com.liferay.portal.kernel.util.ParamUtil;
031 import com.liferay.portal.kernel.util.StringBundler;
032 import com.liferay.portal.kernel.util.StringPool;
033 import com.liferay.portal.kernel.util.StringUtil;
034 import com.liferay.portal.kernel.util.UnicodeProperties;
035 import com.liferay.portal.kernel.util.Validator;
036 import com.liferay.portal.model.Group;
037 import com.liferay.portal.model.Layout;
038 import com.liferay.portal.model.LayoutTypePortletConstants;
039 import com.liferay.portal.model.Portlet;
040 import com.liferay.portal.model.PortletConstants;
041 import com.liferay.portal.service.GroupLocalServiceUtil;
042 import com.liferay.portal.service.LayoutLocalServiceUtil;
043 import com.liferay.portal.service.PortletLocalServiceUtil;
044 import com.liferay.portal.servlet.filters.BasePortalFilter;
045 import com.liferay.portal.util.PortalInstances;
046 import com.liferay.portal.util.PortalUtil;
047 import com.liferay.portal.util.PropsValues;
048 import com.liferay.portal.util.WebKeys;
049 import com.liferay.util.servlet.filters.CacheResponseData;
050 import com.liferay.util.servlet.filters.CacheResponseUtil;
051
052 import javax.servlet.FilterChain;
053 import javax.servlet.FilterConfig;
054 import javax.servlet.http.HttpServletRequest;
055 import javax.servlet.http.HttpServletResponse;
056 import javax.servlet.http.HttpSession;
057
058
063 public class CacheFilter extends BasePortalFilter {
064
065 public static final String SKIP_FILTER = CacheFilter.class + "SKIP_FILTER";
066
067 public void init(FilterConfig filterConfig) {
068 super.init(filterConfig);
069
070 _pattern = GetterUtil.getInteger(
071 filterConfig.getInitParameter("pattern"));
072
073 if ((_pattern != _PATTERN_FRIENDLY) &&
074 (_pattern != _PATTERN_LAYOUT) &&
075 (_pattern != _PATTERN_RESOURCE)) {
076
077 _log.error("Cache pattern is invalid");
078 }
079 }
080
081 protected String getCacheKey(HttpServletRequest request) {
082 StringBundler sb = new StringBundler(13);
083
084
085
086 sb.append(HttpUtil.getProtocol(request));
087 sb.append(Http.PROTOCOL_DELIMITER);
088 sb.append(request.getContextPath());
089 sb.append(request.getServletPath());
090 sb.append(request.getPathInfo());
091 sb.append(StringPool.QUESTION);
092 sb.append(request.getQueryString());
093
094
095
096 sb.append(StringPool.POUND);
097
098 String languageId = (String)request.getAttribute(
099 WebKeys.I18N_LANGUAGE_ID);
100
101 if (Validator.isNull(languageId)) {
102 languageId = LanguageUtil.getLanguageId(request);
103 }
104
105 sb.append(languageId);
106
107
108
109 String userAgent = GetterUtil.getString(
110 request.getHeader(HttpHeaders.USER_AGENT));
111
112 sb.append(StringPool.POUND);
113 sb.append(userAgent.toLowerCase().hashCode());
114
115
116
117 sb.append(StringPool.POUND);
118 sb.append(BrowserSnifferUtil.acceptsGzip(request));
119
120 return sb.toString().trim().toUpperCase();
121 }
122
123 protected long getPlid(
124 long companyId, String pathInfo, String servletPath, long defaultPlid) {
125
126 if (_pattern == _PATTERN_LAYOUT) {
127 return defaultPlid;
128 }
129
130 if (Validator.isNull(pathInfo) ||
131 !pathInfo.startsWith(StringPool.SLASH)) {
132
133 return 0;
134 }
135
136
137
138 String friendlyURL = null;
139
140 int pos = pathInfo.indexOf(StringPool.SLASH, 1);
141
142 if (pos != -1) {
143 friendlyURL = pathInfo.substring(0, pos);
144 }
145 else {
146 if (pathInfo.length() > 1) {
147 friendlyURL = pathInfo.substring(0, pathInfo.length());
148 }
149 }
150
151 if (Validator.isNull(friendlyURL)) {
152 return 0;
153 }
154
155 long groupId = 0;
156 boolean privateLayout = false;
157
158 try {
159 Group group = GroupLocalServiceUtil.getFriendlyURLGroup(
160 companyId, friendlyURL);
161
162 groupId = group.getGroupId();
163
164 if (servletPath.startsWith(
165 PropsValues.
166 LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING) ||
167 servletPath.startsWith(
168 PropsValues.
169 LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING)) {
170
171 privateLayout = true;
172 }
173 else if (servletPath.startsWith(
174 PropsValues.
175 LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING)) {
176
177 privateLayout = false;
178 }
179 }
180 catch (NoSuchLayoutException nsle) {
181 if (_log.isWarnEnabled()) {
182 _log.warn(nsle);
183 }
184 }
185 catch (Exception e) {
186 if (_log.isWarnEnabled()) {
187 _log.error(e);
188 }
189
190 return 0;
191 }
192
193
194
195 friendlyURL = null;
196
197 if ((pos != -1) && ((pos + 1) != pathInfo.length())) {
198 friendlyURL = pathInfo.substring(pos, pathInfo.length());
199 }
200
201 if (Validator.isNull(friendlyURL)) {
202 return 0;
203 }
204
205
206
207 try {
208 Layout layout = LayoutLocalServiceUtil.getFriendlyURLLayout(
209 groupId, privateLayout, friendlyURL);
210
211 return layout.getPlid();
212 }
213 catch (NoSuchLayoutException nsle) {
214 _log.warn(nsle);
215
216 return 0;
217 }
218 catch (Exception e) {
219 _log.error(e);
220
221 return 0;
222 }
223 }
224
225 protected boolean isAlreadyFiltered(HttpServletRequest request) {
226 if (request.getAttribute(SKIP_FILTER) != null) {
227 return true;
228 }
229 else {
230 return false;
231 }
232 }
233
234 protected boolean isCacheableColumn(long companyId, String columnSettings)
235 throws SystemException {
236
237 String[] portletIds = StringUtil.split(columnSettings);
238
239 for (String portletId : portletIds) {
240 portletId = PortletConstants.getRootPortletId(portletId);
241
242 Portlet portlet = PortletLocalServiceUtil.getPortletById(
243 companyId, portletId);
244
245 if (!portlet.isLayoutCacheable()) {
246 return false;
247 }
248 }
249
250 return true;
251 }
252
253 protected boolean isCacheableData(
254 long companyId, HttpServletRequest request) {
255
256 try {
257 if (_pattern == _PATTERN_RESOURCE) {
258 return true;
259 }
260
261 long plid = getPlid(
262 companyId, request.getPathInfo(), request.getServletPath(),
263 ParamUtil.getLong(request, "p_l_id"));
264
265 if (plid <= 0) {
266 return false;
267 }
268
269 Layout layout = LayoutLocalServiceUtil.getLayout(plid);
270
271 if (!layout.isTypePortlet()) {
272 return false;
273 }
274
275 UnicodeProperties properties = layout.getTypeSettingsProperties();
276
277 for (int i = 0; i < 10; i++) {
278 String columnId = "column-" + i;
279
280 String settings = properties.getProperty(
281 columnId, StringPool.BLANK);
282
283 if (!isCacheableColumn(companyId, settings)) {
284 return false;
285 }
286 }
287
288 if (properties.containsKey(
289 LayoutTypePortletConstants.NESTED_COLUMN_IDS)) {
290
291 String[] columnIds = StringUtil.split(
292 properties.get(
293 LayoutTypePortletConstants.NESTED_COLUMN_IDS));
294
295 for (String columnId : columnIds) {
296 String settings = properties.getProperty(
297 columnId, StringPool.BLANK);
298
299 if (!isCacheableColumn(companyId, settings)) {
300 return false;
301 }
302 }
303 }
304
305 return true;
306 }
307 catch (Exception e) {
308 return false;
309 }
310 }
311
312 protected boolean isCacheableRequest(HttpServletRequest request) {
313 String portletId = ParamUtil.getString(request, "p_p_id");
314
315 if (Validator.isNotNull(portletId)) {
316 return false;
317 }
318
319 if ((_pattern == _PATTERN_FRIENDLY) || (_pattern == _PATTERN_LAYOUT)) {
320 long userId = PortalUtil.getUserId(request);
321 String remoteUser = request.getRemoteUser();
322
323 if ((userId > 0) || Validator.isNotNull(remoteUser)) {
324 return false;
325 }
326 }
327
328 if (_pattern == _PATTERN_LAYOUT) {
329 String plid = ParamUtil.getString(request, "p_l_id");
330
331 if (Validator.isNull(plid)) {
332 return false;
333 }
334 }
335
336 return true;
337 }
338
339 protected boolean isCacheableResponse(
340 StringServletResponse stringResponse) {
341
342 if (stringResponse.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 StringServletResponse stringResponse =
396 new StringServletResponse(response);
397
398 processFilter(
399 CacheFilter.class, request, stringResponse, filterChain);
400
401 cacheResponseData = new CacheResponseData(stringResponse);
402
403 LastPath lastPath = (LastPath)request.getAttribute(
404 WebKeys.LAST_PATH);
405
406 if (lastPath != null) {
407 cacheResponseData.setAttribute(WebKeys.LAST_PATH, lastPath);
408 }
409
410
411
412
413
414
415 if (isCacheableRequest(request) &&
416 isCacheableResponse(stringResponse)) {
417
418 CacheUtil.putCacheResponseData(
419 companyId, key, cacheResponseData);
420 }
421 }
422 else {
423 LastPath lastPath = (LastPath)cacheResponseData.getAttribute(
424 WebKeys.LAST_PATH);
425
426 if (lastPath != null) {
427 HttpSession session = request.getSession();
428
429 session.setAttribute(WebKeys.LAST_PATH, lastPath);
430 }
431 }
432
433 CacheResponseUtil.write(response, cacheResponseData);
434 }
435 else {
436 if (_log.isDebugEnabled()) {
437 _log.debug("Request is not cacheable");
438 }
439
440 processFilter(CacheFilter.class, request, response, filterChain);
441 }
442 }
443
444 private static final int _PATTERN_FRIENDLY = 0;
445
446 private static final int _PATTERN_LAYOUT = 1;
447
448 private static final int _PATTERN_RESOURCE = 2;
449
450 private static Log _log = LogFactoryUtil.getLog(CacheFilter.class);
451
452 private int _pattern;
453
454 }