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.CharPool;
027 import com.liferay.portal.kernel.util.GetterUtil;
028 import com.liferay.portal.kernel.util.Http;
029 import com.liferay.portal.kernel.util.HttpUtil;
030 import com.liferay.portal.kernel.util.JavaConstants;
031 import com.liferay.portal.kernel.util.ParamUtil;
032 import com.liferay.portal.kernel.util.StringBundler;
033 import com.liferay.portal.kernel.util.StringPool;
034 import com.liferay.portal.kernel.util.StringUtil;
035 import com.liferay.portal.kernel.util.UnicodeProperties;
036 import com.liferay.portal.kernel.util.Validator;
037 import com.liferay.portal.model.Group;
038 import com.liferay.portal.model.Layout;
039 import com.liferay.portal.model.LayoutTypePortletConstants;
040 import com.liferay.portal.model.Portlet;
041 import com.liferay.portal.model.PortletConstants;
042 import com.liferay.portal.service.GroupLocalServiceUtil;
043 import com.liferay.portal.service.LayoutLocalServiceUtil;
044 import com.liferay.portal.service.PortletLocalServiceUtil;
045 import com.liferay.portal.servlet.filters.BasePortalFilter;
046 import com.liferay.portal.util.PortalInstances;
047 import com.liferay.portal.util.PortalUtil;
048 import com.liferay.portal.util.PropsValues;
049 import com.liferay.portal.util.WebKeys;
050 import com.liferay.util.servlet.filters.CacheResponseData;
051 import com.liferay.util.servlet.filters.CacheResponseUtil;
052
053 import javax.servlet.FilterChain;
054 import javax.servlet.FilterConfig;
055 import javax.servlet.http.HttpServletRequest;
056 import javax.servlet.http.HttpServletResponse;
057 import javax.servlet.http.HttpSession;
058
059
064 public class CacheFilter extends BasePortalFilter {
065
066 public static final String SKIP_FILTER = CacheFilter.class + "SKIP_FILTER";
067
068 public void init(FilterConfig filterConfig) {
069 super.init(filterConfig);
070
071 _pattern = GetterUtil.getInteger(
072 filterConfig.getInitParameter("pattern"));
073
074 if ((_pattern != _PATTERN_FRIENDLY) &&
075 (_pattern != _PATTERN_LAYOUT) &&
076 (_pattern != _PATTERN_RESOURCE)) {
077
078 _log.error("Cache pattern is invalid");
079 }
080 }
081
082 protected String getCacheKey(HttpServletRequest request) {
083 StringBundler sb = new StringBundler(13);
084
085
086
087 sb.append(HttpUtil.getProtocol(request));
088 sb.append(Http.PROTOCOL_DELIMITER);
089 sb.append(request.getContextPath());
090 sb.append(request.getServletPath());
091 sb.append(request.getPathInfo());
092 sb.append(StringPool.QUESTION);
093 sb.append(request.getQueryString());
094
095
096
097 sb.append(StringPool.POUND);
098
099 String languageId = (String)request.getAttribute(
100 WebKeys.I18N_LANGUAGE_ID);
101
102 if (Validator.isNull(languageId)) {
103 languageId = LanguageUtil.getLanguageId(request);
104 }
105
106 sb.append(languageId);
107
108
109
110 String userAgent = GetterUtil.getString(
111 request.getHeader(HttpHeaders.USER_AGENT));
112
113 sb.append(StringPool.POUND);
114 sb.append(userAgent.toLowerCase().hashCode());
115
116
117
118 sb.append(StringPool.POUND);
119 sb.append(BrowserSnifferUtil.acceptsGzip(request));
120
121 return sb.toString().trim().toUpperCase();
122 }
123
124 protected long getPlid(
125 long companyId, String pathInfo, String servletPath, long defaultPlid) {
126
127 if (_pattern == _PATTERN_LAYOUT) {
128 return defaultPlid;
129 }
130
131 if (Validator.isNull(pathInfo) ||
132 !pathInfo.startsWith(StringPool.SLASH)) {
133
134 return 0;
135 }
136
137
138
139 String friendlyURL = null;
140
141 int pos = pathInfo.indexOf(CharPool.SLASH, 1);
142
143 if (pos != -1) {
144 friendlyURL = pathInfo.substring(0, pos);
145 }
146 else {
147 if (pathInfo.length() > 1) {
148 friendlyURL = pathInfo.substring(0, pathInfo.length());
149 }
150 }
151
152 if (Validator.isNull(friendlyURL)) {
153 return 0;
154 }
155
156 long groupId = 0;
157 boolean privateLayout = false;
158
159 try {
160 Group group = GroupLocalServiceUtil.getFriendlyURLGroup(
161 companyId, friendlyURL);
162
163 groupId = group.getGroupId();
164
165 if (servletPath.startsWith(
166 PropsValues.
167 LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING) ||
168 servletPath.startsWith(
169 PropsValues.
170 LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING)) {
171
172 privateLayout = true;
173 }
174 else if (servletPath.startsWith(
175 PropsValues.
176 LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING)) {
177
178 privateLayout = false;
179 }
180 }
181 catch (NoSuchLayoutException nsle) {
182 if (_log.isWarnEnabled()) {
183 _log.warn(nsle);
184 }
185 }
186 catch (Exception e) {
187 if (_log.isWarnEnabled()) {
188 _log.error(e);
189 }
190
191 return 0;
192 }
193
194
195
196 friendlyURL = null;
197
198 if ((pos != -1) && ((pos + 1) != pathInfo.length())) {
199 friendlyURL = pathInfo.substring(pos, pathInfo.length());
200 }
201
202 if (Validator.isNull(friendlyURL)) {
203 return 0;
204 }
205
206
207
208 try {
209 Layout layout = LayoutLocalServiceUtil.getFriendlyURLLayout(
210 groupId, privateLayout, friendlyURL);
211
212 return layout.getPlid();
213 }
214 catch (NoSuchLayoutException nsle) {
215 _log.warn(nsle);
216
217 return 0;
218 }
219 catch (Exception e) {
220 _log.error(e);
221
222 return 0;
223 }
224 }
225
226 protected boolean isAlreadyFiltered(HttpServletRequest request) {
227 if (request.getAttribute(SKIP_FILTER) != null) {
228 return true;
229 }
230 else {
231 return false;
232 }
233 }
234
235 protected boolean isCacheableColumn(long companyId, String columnSettings)
236 throws SystemException {
237
238 String[] portletIds = StringUtil.split(columnSettings);
239
240 for (String portletId : portletIds) {
241 portletId = PortletConstants.getRootPortletId(portletId);
242
243 Portlet portlet = PortletLocalServiceUtil.getPortletById(
244 companyId, portletId);
245
246 if (!portlet.isLayoutCacheable()) {
247 return false;
248 }
249 }
250
251 return true;
252 }
253
254 protected boolean isCacheableData(
255 long companyId, HttpServletRequest request) {
256
257 try {
258 if (_pattern == _PATTERN_RESOURCE) {
259 return true;
260 }
261
262 long plid = getPlid(
263 companyId, request.getPathInfo(), request.getServletPath(),
264 ParamUtil.getLong(request, "p_l_id"));
265
266 if (plid <= 0) {
267 return false;
268 }
269
270 Layout layout = LayoutLocalServiceUtil.getLayout(plid);
271
272 if (!layout.isTypePortlet()) {
273 return false;
274 }
275
276 UnicodeProperties properties = layout.getTypeSettingsProperties();
277
278 for (int i = 0; i < 10; i++) {
279 String columnId = "column-" + i;
280
281 String settings = properties.getProperty(
282 columnId, StringPool.BLANK);
283
284 if (!isCacheableColumn(companyId, settings)) {
285 return false;
286 }
287 }
288
289 String columnIdsString = properties.get(
290 LayoutTypePortletConstants.NESTED_COLUMN_IDS);
291
292 if (columnIdsString != null) {
293 String[] columnIds = StringUtil.split(columnIdsString);
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 }