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.ByteBufferServletResponse;
024 import com.liferay.portal.kernel.servlet.HttpHeaders;
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 if (properties.containsKey(
290 LayoutTypePortletConstants.NESTED_COLUMN_IDS)) {
291
292 String[] columnIds = StringUtil.split(
293 properties.get(
294 LayoutTypePortletConstants.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(
341 ByteBufferServletResponse byteBufferResponse) {
342
343 if (byteBufferResponse.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 ByteBufferServletResponse byteBufferResponse =
397 new ByteBufferServletResponse(response);
398
399 processFilter(
400 CacheFilter.class, request, byteBufferResponse,
401 filterChain);
402
403 cacheResponseData = new CacheResponseData(byteBufferResponse);
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
413
414
415
416
417 if (isCacheableRequest(request) &&
418 isCacheableResponse(byteBufferResponse)) {
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 }