001: /**
002: * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
003: *
004: * Permission is hereby granted, free of charge, to any person obtaining a copy
005: * of this software and associated documentation files (the "Software"), to deal
006: * in the Software without restriction, including without limitation the rights
007: * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
008: * copies of the Software, and to permit persons to whom the Software is
009: * furnished to do so, subject to the following conditions:
010: *
011: * The above copyright notice and this permission notice shall be included in
012: * all copies or substantial portions of the Software.
013: *
014: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
015: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
016: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
017: * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
018: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
019: * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
020: * SOFTWARE.
021: */package com.liferay.portal.servlet.filters.layoutcache;
022:
023: import com.liferay.portal.NoSuchLayoutException;
024: import com.liferay.portal.kernel.language.LanguageUtil;
025: import com.liferay.portal.kernel.log.Log;
026: import com.liferay.portal.kernel.log.LogFactoryUtil;
027: import com.liferay.portal.kernel.servlet.BaseFilter;
028: import com.liferay.portal.kernel.servlet.BrowserSniffer;
029: import com.liferay.portal.kernel.util.GetterUtil;
030: import com.liferay.portal.kernel.util.JavaConstants;
031: import com.liferay.portal.kernel.util.ParamUtil;
032: import com.liferay.portal.kernel.util.PortalInitable;
033: import com.liferay.portal.kernel.util.PortalInitableUtil;
034: import com.liferay.portal.kernel.util.StringMaker;
035: import com.liferay.portal.kernel.util.StringPool;
036: import com.liferay.portal.kernel.util.StringUtil;
037: import com.liferay.portal.kernel.util.Validator;
038: import com.liferay.portal.model.Group;
039: import com.liferay.portal.model.Layout;
040: import com.liferay.portal.model.Portlet;
041: import com.liferay.portal.model.impl.LayoutImpl;
042: import com.liferay.portal.model.impl.PortletImpl;
043: import com.liferay.portal.service.GroupLocalServiceUtil;
044: import com.liferay.portal.service.LayoutLocalServiceUtil;
045: import com.liferay.portal.service.PortletLocalServiceUtil;
046: import com.liferay.portal.struts.LastPath;
047: import com.liferay.portal.util.PortalInstances;
048: import com.liferay.portal.util.PortalUtil;
049: import com.liferay.portal.util.PropsValues;
050: import com.liferay.portal.util.WebKeys;
051: import com.liferay.util.Http;
052: import com.liferay.util.SystemProperties;
053: import com.liferay.util.servlet.filters.CacheResponse;
054: import com.liferay.util.servlet.filters.CacheResponseData;
055: import com.liferay.util.servlet.filters.CacheResponseUtil;
056:
057: import java.io.IOException;
058:
059: import java.util.Properties;
060:
061: import javax.servlet.FilterChain;
062: import javax.servlet.FilterConfig;
063: import javax.servlet.ServletException;
064: import javax.servlet.ServletRequest;
065: import javax.servlet.ServletResponse;
066: import javax.servlet.http.HttpServletRequest;
067: import javax.servlet.http.HttpServletResponse;
068: import javax.servlet.http.HttpSession;
069:
070: /**
071: * <a href="LayoutCacheFilter.java.html"><b><i>View Source</i></b></a>
072: *
073: * @author Alexander Chow
074: * @author Javier de Ros
075: * @author Raymond Augé
076: *
077: */
078: public class LayoutCacheFilter extends BaseFilter implements
079: PortalInitable {
080:
081: public static final boolean USE_FILTER = GetterUtil.getBoolean(
082: SystemProperties.get(LayoutCacheFilter.class.getName()),
083: true);
084:
085: public static final String ENCODING = GetterUtil.getString(
086: SystemProperties.get("file.encoding"), "UTF-8");
087:
088: public void portalInit() {
089: _pattern = GetterUtil.getInteger(_config
090: .getInitParameter("pattern"));
091:
092: if ((_pattern != _PATTERN_FRIENDLY)
093: && (_pattern != _PATTERN_LAYOUT)
094: && (_pattern != _PATTERN_RESOURCE)) {
095:
096: _log.error("Layout cache pattern is invalid");
097: }
098: }
099:
100: public void init(FilterConfig config) throws ServletException {
101: super .init(config);
102:
103: _config = config;
104:
105: PortalInitableUtil.init(this );
106: }
107:
108: public void doFilter(ServletRequest req, ServletResponse res,
109: FilterChain chain) throws IOException, ServletException {
110:
111: if (_log.isDebugEnabled()) {
112: if (USE_FILTER) {
113: _log.debug("Layout cache is enabled");
114: } else {
115: _log.debug("Layout cache is disabled");
116: }
117: }
118:
119: HttpServletRequest httpReq = (HttpServletRequest) req;
120: HttpServletResponse httpRes = (HttpServletResponse) res;
121:
122: if (USE_FILTER && !isPortletRequest(httpReq)
123: && isLayout(httpReq) && !isSignedIn(httpReq)
124: && !isInclude(httpReq) && !isAlreadyFiltered(httpReq)) {
125:
126: httpReq.setAttribute(_ALREADY_FILTERED, Boolean.TRUE);
127:
128: String key = getCacheKey(httpReq);
129:
130: long companyId = PortalInstances.getCompanyId(httpReq);
131:
132: CacheResponseData data = LayoutCacheUtil
133: .getCacheResponseData(companyId, key);
134:
135: if (data == null) {
136: if (!isCacheable(companyId, httpReq)) {
137: if (_log.isDebugEnabled()) {
138: _log.debug("Layout is not cacheable " + key);
139: }
140:
141: doFilter(LayoutCacheFilter.class, req, res, chain);
142:
143: return;
144: }
145:
146: if (_log.isInfoEnabled()) {
147: _log.info("Caching layout " + key);
148: }
149:
150: CacheResponse cacheResponse = new CacheResponse(
151: httpRes, ENCODING);
152:
153: doFilter(LayoutCacheFilter.class, req, cacheResponse,
154: chain);
155:
156: data = new CacheResponseData(cacheResponse.getData(),
157: cacheResponse.getContentType(), cacheResponse
158: .getHeaders());
159:
160: LastPath lastPath = (LastPath) httpReq
161: .getAttribute(WebKeys.LAST_PATH);
162:
163: if (lastPath != null) {
164: data.setAttribute(WebKeys.LAST_PATH, lastPath);
165: }
166:
167: if (data.getData().length > 0) {
168: LayoutCacheUtil.putCacheResponseData(companyId,
169: key, data);
170: }
171: } else {
172: LastPath lastPath = (LastPath) data
173: .getAttribute(WebKeys.LAST_PATH);
174:
175: if (lastPath != null) {
176: HttpSession ses = httpReq.getSession();
177:
178: ses.setAttribute(WebKeys.LAST_PATH, lastPath);
179: }
180: }
181:
182: CacheResponseUtil.write(httpRes, data);
183: } else {
184: if (_log.isDebugEnabled()) {
185: _log.debug("Did not request a layout");
186: }
187:
188: doFilter(LayoutCacheFilter.class, req, res, chain);
189: }
190: }
191:
192: protected String getBrowserType(HttpServletRequest req) {
193: if (BrowserSniffer.is_ie_7(req)) {
194: return _BROWSER_TYPE_IE_7;
195: } else if (BrowserSniffer.is_ie(req)) {
196: return _BROWSER_TYPE_IE;
197: } else {
198: return _BROWSER_TYPE_OTHER;
199: }
200: }
201:
202: protected String getCacheKey(HttpServletRequest req) {
203: StringMaker sm = new StringMaker();
204:
205: // Url
206:
207: sm.append(Http.getProtocol(req));
208: sm.append("://");
209: sm.append(req.getServletPath());
210: sm.append(req.getPathInfo());
211: sm.append(StringPool.QUESTION);
212: sm.append(req.getQueryString());
213:
214: // Language
215:
216: sm.append(StringPool.POUND);
217: sm.append(LanguageUtil.getLanguageId(req));
218:
219: // Browser type
220:
221: sm.append(StringPool.POUND);
222: sm.append(getBrowserType(req));
223:
224: // Gzip compression
225:
226: sm.append(StringPool.POUND);
227: sm.append(BrowserSniffer.acceptsGzip(req));
228:
229: return sm.toString().trim().toUpperCase();
230: }
231:
232: protected long getPlid(long companyId, String pathInfo,
233: String servletPath, long defaultPlid) {
234:
235: if (_pattern == _PATTERN_LAYOUT) {
236: return defaultPlid;
237: }
238:
239: if (Validator.isNull(pathInfo)
240: || !pathInfo.startsWith(StringPool.SLASH)) {
241:
242: return 0;
243: }
244:
245: // Group friendly URL
246:
247: String friendlyURL = null;
248:
249: int pos = pathInfo.indexOf(StringPool.SLASH, 1);
250:
251: if (pos != -1) {
252: friendlyURL = pathInfo.substring(0, pos);
253: } else {
254: if (pathInfo.length() > 1) {
255: friendlyURL = pathInfo.substring(0, pathInfo.length());
256: }
257: }
258:
259: if (Validator.isNull(friendlyURL)) {
260: return 0;
261: }
262:
263: long groupId = 0;
264: boolean privateLayout = false;
265:
266: try {
267: Group group = GroupLocalServiceUtil.getFriendlyURLGroup(
268: companyId, friendlyURL);
269:
270: groupId = group.getGroupId();
271:
272: if (servletPath
273: .startsWith(PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING)
274: || servletPath
275: .startsWith(PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING)) {
276:
277: privateLayout = true;
278: } else if (servletPath
279: .startsWith(PropsValues.LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING)) {
280:
281: privateLayout = false;
282: }
283: } catch (NoSuchLayoutException nsle) {
284: if (_log.isWarnEnabled()) {
285: _log.warn(nsle);
286: }
287: } catch (Exception e) {
288: if (_log.isWarnEnabled()) {
289: _log.error(e);
290: }
291:
292: return 0;
293: }
294:
295: // Layout friendly URL
296:
297: friendlyURL = null;
298:
299: if ((pos != -1) && ((pos + 1) != pathInfo.length())) {
300: friendlyURL = pathInfo.substring(pos, pathInfo.length());
301: }
302:
303: if (Validator.isNull(friendlyURL)) {
304: return 0;
305: }
306:
307: // If there is no layout path take the first from the group or user
308:
309: try {
310: Layout layout = LayoutLocalServiceUtil
311: .getFriendlyURLLayout(groupId, privateLayout,
312: friendlyURL);
313:
314: return layout.getPlid();
315: } catch (NoSuchLayoutException nsle) {
316: _log.warn(nsle);
317:
318: return 0;
319: } catch (Exception e) {
320: _log.error(e);
321:
322: return 0;
323: }
324: }
325:
326: protected boolean isAlreadyFiltered(HttpServletRequest req) {
327: if (req.getAttribute(_ALREADY_FILTERED) != null) {
328: return true;
329: } else {
330: return false;
331: }
332: }
333:
334: protected boolean isCacheable(long companyId, HttpServletRequest req) {
335: if (_pattern == _PATTERN_RESOURCE) {
336: return true;
337: }
338:
339: try {
340: long plid = getPlid(companyId, req.getPathInfo(), req
341: .getServletPath(), ParamUtil.getLong(req, "p_l_id"));
342:
343: if (plid <= 0) {
344: return false;
345: }
346:
347: Layout layout = LayoutLocalServiceUtil.getLayout(plid);
348:
349: if (!layout.getType().equals(LayoutImpl.TYPE_PORTLET)) {
350: return false;
351: }
352:
353: Properties props = layout.getTypeSettingsProperties();
354:
355: for (int i = 0; i < 10; i++) {
356: String columnId = "column-" + i;
357:
358: String settings = props.getProperty(columnId,
359: StringPool.BLANK);
360:
361: String[] portlets = StringUtil.split(settings);
362:
363: for (int j = 0; j < portlets.length; j++) {
364: String portletId = StringUtil
365: .extractFirst(portlets[j],
366: PortletImpl.INSTANCE_SEPARATOR);
367:
368: Portlet portlet = PortletLocalServiceUtil
369: .getPortletById(companyId, portletId);
370:
371: if (!portlet.isLayoutCacheable()) {
372: return false;
373: }
374: }
375: }
376: } catch (Exception e) {
377: return false;
378: }
379:
380: return true;
381: }
382:
383: protected boolean isInclude(HttpServletRequest req) {
384: String uri = (String) req
385: .getAttribute(JavaConstants.JAVAX_SERVLET_INCLUDE_REQUEST_URI);
386:
387: if (uri == null) {
388: return false;
389: } else {
390: return true;
391: }
392: }
393:
394: protected boolean isLayout(HttpServletRequest req) {
395: if ((_pattern == _PATTERN_FRIENDLY)
396: || (_pattern == _PATTERN_RESOURCE)) {
397:
398: return true;
399: } else {
400: String plid = ParamUtil.getString(req, "p_l_id");
401:
402: if (Validator.isNotNull(plid)) {
403: return true;
404: } else {
405: return false;
406: }
407: }
408: }
409:
410: protected boolean isPortletRequest(HttpServletRequest req) {
411: String portletId = ParamUtil.getString(req, "p_p_id");
412:
413: if (Validator.isNull(portletId)) {
414: return false;
415: } else {
416: return true;
417: }
418: }
419:
420: protected boolean isSignedIn(HttpServletRequest req) {
421: long userId = PortalUtil.getUserId(req);
422: String remoteUser = req.getRemoteUser();
423:
424: if ((userId <= 0) && (remoteUser == null)) {
425: return false;
426: } else {
427: return true;
428: }
429: }
430:
431: private static final String _ALREADY_FILTERED = LayoutCacheFilter.class
432: + "_ALREADY_FILTERED";
433:
434: private static final int _PATTERN_FRIENDLY = 0;
435:
436: private static final int _PATTERN_LAYOUT = 1;
437:
438: private static final int _PATTERN_RESOURCE = 2;
439:
440: private static final String _BROWSER_TYPE_IE_7 = "ie_7";
441:
442: private static final String _BROWSER_TYPE_IE = "ie";
443:
444: private static final String _BROWSER_TYPE_OTHER = "other";
445:
446: private static Log _log = LogFactoryUtil
447: .getLog(LayoutCacheFilter.class);
448:
449: private FilterConfig _config;
450: private int _pattern;
451:
452: }
|