1
22
23 package com.liferay.portal.servlet.filters.layoutcache;
24
25 import com.liferay.portal.NoSuchLayoutException;
26 import com.liferay.portal.kernel.language.LanguageUtil;
27 import com.liferay.portal.kernel.servlet.BrowserSniffer;
28 import com.liferay.portal.kernel.util.GetterUtil;
29 import com.liferay.portal.kernel.util.JavaConstants;
30 import com.liferay.portal.kernel.util.ParamUtil;
31 import com.liferay.portal.kernel.util.PortalInitable;
32 import com.liferay.portal.kernel.util.PortalInitableUtil;
33 import com.liferay.portal.kernel.util.StringMaker;
34 import com.liferay.portal.kernel.util.StringPool;
35 import com.liferay.portal.kernel.util.StringUtil;
36 import com.liferay.portal.kernel.util.Validator;
37 import com.liferay.portal.model.Group;
38 import com.liferay.portal.model.Layout;
39 import com.liferay.portal.model.Portlet;
40 import com.liferay.portal.model.impl.LayoutImpl;
41 import com.liferay.portal.model.impl.PortletImpl;
42 import com.liferay.portal.service.GroupLocalServiceUtil;
43 import com.liferay.portal.service.LayoutLocalServiceUtil;
44 import com.liferay.portal.service.PortletLocalServiceUtil;
45 import com.liferay.portal.struts.LastPath;
46 import com.liferay.portal.util.PortalInstances;
47 import com.liferay.portal.util.PortalUtil;
48 import com.liferay.portal.util.PropsUtil;
49 import com.liferay.portal.util.WebKeys;
50 import com.liferay.util.Http;
51 import com.liferay.util.SystemProperties;
52 import com.liferay.util.servlet.filters.CacheResponse;
53 import com.liferay.util.servlet.filters.CacheResponseData;
54 import com.liferay.util.servlet.filters.CacheResponseUtil;
55
56 import java.io.IOException;
57
58 import java.util.Properties;
59
60 import javax.servlet.Filter;
61 import javax.servlet.FilterChain;
62 import javax.servlet.FilterConfig;
63 import javax.servlet.ServletException;
64 import javax.servlet.ServletRequest;
65 import javax.servlet.ServletResponse;
66 import javax.servlet.http.HttpServletRequest;
67 import javax.servlet.http.HttpServletResponse;
68 import javax.servlet.http.HttpSession;
69
70 import org.apache.commons.logging.Log;
71 import org.apache.commons.logging.LogFactory;
72
73
80 public class LayoutCacheFilter implements Filter, PortalInitable {
81
82 public static final boolean USE_FILTER = GetterUtil.getBoolean(
83 SystemProperties.get(LayoutCacheFilter.class.getName()), true);
84
85 public static final String ENCODING = GetterUtil.getString(
86 SystemProperties.get("file.encoding"), "UTF-8");
87
88 public void portalInit() {
89 _pattern = GetterUtil.getInteger(
90 _config.getInitParameter("pattern"));
91
92 if ((_pattern != _PATTERN_FRIENDLY) &&
93 (_pattern != _PATTERN_LAYOUT) &&
94 (_pattern != _PATTERN_RESOURCE)) {
95
96 _log.error("Layout cache pattern is invalid");
97 }
98 }
99
100 public void init(FilterConfig config) throws ServletException {
101 _config = config;
102
103 PortalInitableUtil.init(this);
104 }
105
106 public void doFilter(
107 ServletRequest req, ServletResponse res, FilterChain chain)
108 throws IOException, ServletException {
109
110 if (_log.isDebugEnabled()) {
111 if (USE_FILTER) {
112 _log.debug("Layout cache is enabled");
113 }
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) && isLayout(httpReq) &&
123 !isSignedIn(httpReq) && !isInclude(httpReq) &&
124 !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.getCacheResponseData(
133 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 chain.doFilter(req, res);
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 chain.doFilter(req, cacheResponse);
154
155 data = new CacheResponseData(
156 cacheResponse.getData(), cacheResponse.getContentType(),
157 cacheResponse.getHeaders());
158
159 LastPath lastPath = (LastPath)httpReq.getAttribute(
160 WebKeys.LAST_PATH);
161
162 if (lastPath != null) {
163 data.setAttribute(WebKeys.LAST_PATH, lastPath);
164 }
165
166 if (data.getData().length > 0) {
167 LayoutCacheUtil.putCacheResponseData(companyId, key, data);
168 }
169 }
170 else {
171 LastPath lastPath = (LastPath)data.getAttribute(
172 WebKeys.LAST_PATH);
173
174 if (lastPath != null) {
175 HttpSession ses = httpReq.getSession();
176
177 ses.setAttribute(WebKeys.LAST_PATH, lastPath);
178 }
179 }
180
181 CacheResponseUtil.write(httpRes, data);
182 }
183 else {
184 if (_log.isDebugEnabled()) {
185 _log.debug("Did not request a layout");
186 }
187
188 chain.doFilter(req, res);
189 }
190 }
191
192 public void destroy() {
193 }
194
195 protected String getBrowserType(HttpServletRequest req) {
196 if (BrowserSniffer.is_ie_7(req)) {
197 return _BROWSER_TYPE_IE_7;
198 }
199 else if (BrowserSniffer.is_ie(req)) {
200 return _BROWSER_TYPE_IE;
201 }
202 else {
203 return _BROWSER_TYPE_OTHER;
204 }
205 }
206
207 protected String getCacheKey(HttpServletRequest req) {
208 StringMaker sm = new StringMaker();
209
210
212 sm.append(Http.getProtocol(req));
213 sm.append("://");
214 sm.append(req.getServletPath());
215 sm.append(req.getPathInfo());
216 sm.append(StringPool.QUESTION);
217 sm.append(req.getQueryString());
218
219
221 sm.append(StringPool.POUND);
222 sm.append(LanguageUtil.getLanguageId(req));
223
224
226 sm.append(StringPool.POUND);
227 sm.append(getBrowserType(req));
228
229
231 sm.append(StringPool.POUND);
232 sm.append(BrowserSniffer.acceptsGzip(req));
233
234 return sm.toString().trim().toUpperCase();
235 }
236
237 protected long getPlid(
238 long companyId, String pathInfo, String servletPath, long defaultPlid) {
239
240 if (_pattern == _PATTERN_LAYOUT) {
241 return defaultPlid;
242 }
243
244 if (Validator.isNull(pathInfo) ||
245 !pathInfo.startsWith(StringPool.SLASH)) {
246
247 return 0;
248 }
249
250
252 String friendlyURL = null;
253
254 int pos = pathInfo.indexOf(StringPool.SLASH, 1);
255
256 if (pos != -1) {
257 friendlyURL = pathInfo.substring(0, pos);
258 }
259 else {
260 if (pathInfo.length() > 1) {
261 friendlyURL = pathInfo.substring(0, pathInfo.length());
262 }
263 }
264
265 if (Validator.isNull(friendlyURL)) {
266 return 0;
267 }
268
269 long groupId = 0;
270 boolean privateLayout = false;
271
272 try {
273 Group group = GroupLocalServiceUtil.getFriendlyURLGroup(
274 companyId, friendlyURL);
275
276 groupId = group.getGroupId();
277
278 if (servletPath.startsWith(
279 _LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING) ||
280 servletPath.startsWith(
281 _LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING)) {
282
283 privateLayout = true;
284 }
285 else if (servletPath.startsWith(
286 _LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING)) {
287
288 privateLayout = false;
289 }
290 }
291 catch (NoSuchLayoutException nsle) {
292 if (_log.isWarnEnabled()) {
293 _log.warn(nsle);
294 }
295 }
296 catch (Exception e) {
297 if (_log.isWarnEnabled()) {
298 _log.error(e);
299 }
300
301 return 0;
302 }
303
304
306 friendlyURL = null;
307
308 if ((pos != -1) && ((pos + 1) != pathInfo.length())) {
309 friendlyURL = pathInfo.substring(pos, pathInfo.length());
310 }
311
312 if (Validator.isNull(friendlyURL)) {
313 return 0;
314 }
315
316
318 try {
319 Layout layout = LayoutLocalServiceUtil.getFriendlyURLLayout(
320 groupId, privateLayout, friendlyURL);
321
322 return layout.getPlid();
323 }
324 catch (NoSuchLayoutException nsle) {
325 _log.warn(nsle);
326
327 return 0;
328 }
329 catch (Exception e) {
330 _log.error(e);
331
332 return 0;
333 }
334 }
335
336 protected boolean isAlreadyFiltered(HttpServletRequest req) {
337 if (req.getAttribute(_ALREADY_FILTERED) != null) {
338 return true;
339 }
340 else {
341 return false;
342 }
343 }
344
345 protected boolean isCacheable(long companyId, HttpServletRequest req) {
346 if (_pattern == _PATTERN_RESOURCE) {
347 return true;
348 }
349
350 try {
351 long plid = getPlid(
352 companyId, req.getPathInfo(), req.getServletPath(),
353 ParamUtil.getLong(req, "p_l_id"));
354
355 if (plid <= 0) {
356 return false;
357 }
358
359 Layout layout = LayoutLocalServiceUtil.getLayout(plid);
360
361 if (!layout.getType().equals(LayoutImpl.TYPE_PORTLET)) {
362 return false;
363 }
364
365 Properties props = layout.getTypeSettingsProperties();
366
367 for (int i = 0; i < 10; i++) {
368 String columnId = "column-" + i;
369
370 String settings = props.getProperty(columnId, StringPool.BLANK);
371
372 String[] portlets = StringUtil.split(settings);
373
374 for (int j = 0; j < portlets.length; j++) {
375 String portletId = StringUtil.extractFirst(
376 portlets[j], PortletImpl.INSTANCE_SEPARATOR);
377
378 Portlet portlet = PortletLocalServiceUtil.getPortletById(
379 companyId, portletId);
380
381 if (!portlet.isLayoutCacheable()) {
382 return false;
383 }
384 }
385 }
386 }
387 catch(Exception e) {
388 return false;
389 }
390
391 return true;
392 }
393
394 protected boolean isInclude(HttpServletRequest req) {
395 String uri = (String)req.getAttribute(
396 JavaConstants.JAVAX_SERVLET_INCLUDE_REQUEST_URI);
397
398 if (uri == null) {
399 return false;
400 }
401 else {
402 return true;
403 }
404 }
405
406 protected boolean isLayout(HttpServletRequest req) {
407 if ((_pattern == _PATTERN_FRIENDLY) ||
408 (_pattern == _PATTERN_RESOURCE)) {
409
410 return true;
411 }
412 else {
413 String plid = ParamUtil.getString(req, "p_l_id");
414
415 if (Validator.isNotNull(plid)) {
416 return true;
417 }
418 else {
419 return false;
420 }
421 }
422 }
423
424 protected boolean isPortletRequest(HttpServletRequest req) {
425 String portletId = ParamUtil.getString(req, "p_p_id");
426
427 if (Validator.isNull(portletId)) {
428 return false;
429 }
430 else {
431 return true;
432 }
433 }
434
435 protected boolean isSignedIn(HttpServletRequest req) {
436 long userId = PortalUtil.getUserId(req);
437 String remoteUser = req.getRemoteUser();
438
439 if ((userId <= 0) && (remoteUser == null)) {
440 return false;
441 }
442 else {
443 return true;
444 }
445 }
446
447 private static final String _ALREADY_FILTERED =
448 LayoutCacheFilter.class + "_ALREADY_FILTERED";
449
450 private static final String
451 _LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING =
452 PropsUtil.get(
453 PropsUtil.LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING);
454
455 private static final String
456 _LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING =
457 PropsUtil.get(
458 PropsUtil.LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING);
459
460 private static final String _LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING =
461 PropsUtil.get(PropsUtil.LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING);
462
463 private static final int _PATTERN_FRIENDLY = 0;
464
465 private static final int _PATTERN_LAYOUT = 1;
466
467 private static final int _PATTERN_RESOURCE = 2;
468
469 private static final String _BROWSER_TYPE_IE_7 = "ie_7";
470
471 private static final String _BROWSER_TYPE_IE = "ie";
472
473 private static final String _BROWSER_TYPE_OTHER = "other";
474
475 private static Log _log = LogFactory.getLog(LayoutCacheFilter.class);
476
477 private FilterConfig _config;
478 private int _pattern;
479
480 }