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.log.Log;
28 import com.liferay.portal.kernel.log.LogFactoryUtil;
29 import com.liferay.portal.kernel.servlet.BaseFilter;
30 import com.liferay.portal.kernel.servlet.BrowserSniffer;
31 import com.liferay.portal.kernel.util.GetterUtil;
32 import com.liferay.portal.kernel.util.HttpUtil;
33 import com.liferay.portal.kernel.util.JavaConstants;
34 import com.liferay.portal.kernel.util.ParamUtil;
35 import com.liferay.portal.kernel.util.PortalInitable;
36 import com.liferay.portal.kernel.util.PortalInitableUtil;
37 import com.liferay.portal.kernel.util.StringMaker;
38 import com.liferay.portal.kernel.util.StringPool;
39 import com.liferay.portal.kernel.util.StringUtil;
40 import com.liferay.portal.kernel.util.Validator;
41 import com.liferay.portal.model.Group;
42 import com.liferay.portal.model.Layout;
43 import com.liferay.portal.model.Portlet;
44 import com.liferay.portal.model.impl.LayoutImpl;
45 import com.liferay.portal.model.impl.PortletImpl;
46 import com.liferay.portal.service.GroupLocalServiceUtil;
47 import com.liferay.portal.service.LayoutLocalServiceUtil;
48 import com.liferay.portal.service.PortletLocalServiceUtil;
49 import com.liferay.portal.struts.LastPath;
50 import com.liferay.portal.util.PortalInstances;
51 import com.liferay.portal.util.PortalUtil;
52 import com.liferay.portal.util.PropsUtil;
53 import com.liferay.portal.util.PropsValues;
54 import com.liferay.portal.util.WebKeys;
55 import com.liferay.util.SystemProperties;
56 import com.liferay.util.servlet.filters.CacheResponse;
57 import com.liferay.util.servlet.filters.CacheResponseData;
58 import com.liferay.util.servlet.filters.CacheResponseUtil;
59
60 import java.io.IOException;
61
62 import java.util.Properties;
63
64 import javax.servlet.FilterChain;
65 import javax.servlet.FilterConfig;
66 import javax.servlet.ServletException;
67 import javax.servlet.ServletRequest;
68 import javax.servlet.ServletResponse;
69 import javax.servlet.http.HttpServletRequest;
70 import javax.servlet.http.HttpServletResponse;
71 import javax.servlet.http.HttpSession;
72
73
81 public class LayoutCacheFilter extends BaseFilter implements PortalInitable {
82
83 public static final boolean USE_FILTER = GetterUtil.getBoolean(
84 PropsUtil.get(LayoutCacheFilter.class.getName()), true);
85
86 public static final String ENCODING = GetterUtil.getString(
87 SystemProperties.get("file.encoding"), StringPool.UTF8);
88
89 public void portalInit() {
90 _pattern = GetterUtil.getInteger(
91 _config.getInitParameter("pattern"));
92
93 if ((_pattern != _PATTERN_FRIENDLY) &&
94 (_pattern != _PATTERN_LAYOUT) &&
95 (_pattern != _PATTERN_RESOURCE)) {
96
97 _log.error("Layout cache pattern is invalid");
98 }
99 }
100
101 public void init(FilterConfig config) throws ServletException {
102 super.init(config);
103
104 _config = config;
105
106 PortalInitableUtil.init(this);
107 }
108
109 public void doFilter(
110 ServletRequest req, ServletResponse res, FilterChain chain)
111 throws IOException, ServletException {
112
113 if (_log.isDebugEnabled()) {
114 if (USE_FILTER) {
115 _log.debug("Layout cache is enabled");
116 }
117 else {
118 _log.debug("Layout cache is disabled");
119 }
120 }
121
122 HttpServletRequest httpReq = (HttpServletRequest)req;
123 HttpServletResponse httpRes = (HttpServletResponse)res;
124
125 if (USE_FILTER && !isPortletRequest(httpReq) && isLayout(httpReq) &&
126 !isSignedIn(httpReq) && !isInclude(httpReq) &&
127 !isAlreadyFiltered(httpReq)) {
128
129 httpReq.setAttribute(_ALREADY_FILTERED, Boolean.TRUE);
130
131 String key = getCacheKey(httpReq);
132
133 long companyId = PortalInstances.getCompanyId(httpReq);
134
135 CacheResponseData data = LayoutCacheUtil.getCacheResponseData(
136 companyId, key);
137
138 if (data == null) {
139 if (!isCacheable(companyId, httpReq)) {
140 if (_log.isDebugEnabled()) {
141 _log.debug("Layout is not cacheable " + key);
142 }
143
144 doFilter(LayoutCacheFilter.class, req, res, chain);
145
146 return;
147 }
148
149 if (_log.isInfoEnabled()) {
150 _log.info("Caching layout " + key);
151 }
152
153 CacheResponse cacheResponse = new CacheResponse(
154 httpRes, ENCODING);
155
156 doFilter(LayoutCacheFilter.class, req, cacheResponse, chain);
157
158 data = new CacheResponseData(
159 cacheResponse.getData(), cacheResponse.getContentType(),
160 cacheResponse.getHeaders());
161
162 LastPath lastPath = (LastPath)httpReq.getAttribute(
163 WebKeys.LAST_PATH);
164
165 if (lastPath != null) {
166 data.setAttribute(WebKeys.LAST_PATH, lastPath);
167 }
168
169 if (data.getData().length > 0) {
170 LayoutCacheUtil.putCacheResponseData(companyId, key, data);
171 }
172 }
173 else {
174 LastPath lastPath = (LastPath)data.getAttribute(
175 WebKeys.LAST_PATH);
176
177 if (lastPath != null) {
178 HttpSession ses = httpReq.getSession();
179
180 ses.setAttribute(WebKeys.LAST_PATH, lastPath);
181 }
182 }
183
184 CacheResponseUtil.write(httpRes, data);
185 }
186 else {
187 if (_log.isDebugEnabled()) {
188 _log.debug("Did not request a layout");
189 }
190
191 doFilter(LayoutCacheFilter.class, req, res, chain);
192 }
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(HttpUtil.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 PropsValues.
280 LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING) ||
281 servletPath.startsWith(
282 PropsValues.
283 LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING)) {
284
285 privateLayout = true;
286 }
287 else if (servletPath.startsWith(
288 PropsValues.
289 LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING)) {
290
291 privateLayout = false;
292 }
293 }
294 catch (NoSuchLayoutException nsle) {
295 if (_log.isWarnEnabled()) {
296 _log.warn(nsle);
297 }
298 }
299 catch (Exception e) {
300 if (_log.isWarnEnabled()) {
301 _log.error(e);
302 }
303
304 return 0;
305 }
306
307
309 friendlyURL = null;
310
311 if ((pos != -1) && ((pos + 1) != pathInfo.length())) {
312 friendlyURL = pathInfo.substring(pos, pathInfo.length());
313 }
314
315 if (Validator.isNull(friendlyURL)) {
316 return 0;
317 }
318
319
321 try {
322 Layout layout = LayoutLocalServiceUtil.getFriendlyURLLayout(
323 groupId, privateLayout, friendlyURL);
324
325 return layout.getPlid();
326 }
327 catch (NoSuchLayoutException nsle) {
328 _log.warn(nsle);
329
330 return 0;
331 }
332 catch (Exception e) {
333 _log.error(e);
334
335 return 0;
336 }
337 }
338
339 protected boolean isAlreadyFiltered(HttpServletRequest req) {
340 if (req.getAttribute(_ALREADY_FILTERED) != null) {
341 return true;
342 }
343 else {
344 return false;
345 }
346 }
347
348 protected boolean isCacheable(long companyId, HttpServletRequest req) {
349 if (_pattern == _PATTERN_RESOURCE) {
350 return true;
351 }
352
353 try {
354 long plid = getPlid(
355 companyId, req.getPathInfo(), req.getServletPath(),
356 ParamUtil.getLong(req, "p_l_id"));
357
358 if (plid <= 0) {
359 return false;
360 }
361
362 Layout layout = LayoutLocalServiceUtil.getLayout(plid);
363
364 if (!layout.getType().equals(LayoutImpl.TYPE_PORTLET)) {
365 return false;
366 }
367
368 Properties props = layout.getTypeSettingsProperties();
369
370 for (int i = 0; i < 10; i++) {
371 String columnId = "column-" + i;
372
373 String settings = props.getProperty(columnId, StringPool.BLANK);
374
375 String[] portlets = StringUtil.split(settings);
376
377 for (int j = 0; j < portlets.length; j++) {
378 String portletId = StringUtil.extractFirst(
379 portlets[j], PortletImpl.INSTANCE_SEPARATOR);
380
381 Portlet portlet = PortletLocalServiceUtil.getPortletById(
382 companyId, portletId);
383
384 if (!portlet.isLayoutCacheable()) {
385 return false;
386 }
387 }
388 }
389 }
390 catch(Exception e) {
391 return false;
392 }
393
394 return true;
395 }
396
397 protected boolean isInclude(HttpServletRequest req) {
398 String uri = (String)req.getAttribute(
399 JavaConstants.JAVAX_SERVLET_INCLUDE_REQUEST_URI);
400
401 if (uri == null) {
402 return false;
403 }
404 else {
405 return true;
406 }
407 }
408
409 protected boolean isLayout(HttpServletRequest req) {
410 if ((_pattern == _PATTERN_FRIENDLY) ||
411 (_pattern == _PATTERN_RESOURCE)) {
412
413 return true;
414 }
415 else {
416 String plid = ParamUtil.getString(req, "p_l_id");
417
418 if (Validator.isNotNull(plid)) {
419 return true;
420 }
421 else {
422 return false;
423 }
424 }
425 }
426
427 protected boolean isPortletRequest(HttpServletRequest req) {
428 String portletId = ParamUtil.getString(req, "p_p_id");
429
430 if (Validator.isNull(portletId)) {
431 return false;
432 }
433 else {
434 return true;
435 }
436 }
437
438 protected boolean isSignedIn(HttpServletRequest req) {
439 long userId = PortalUtil.getUserId(req);
440 String remoteUser = req.getRemoteUser();
441
442 if ((userId <= 0) && (remoteUser == null)) {
443 return false;
444 }
445 else {
446 return true;
447 }
448 }
449
450 private static final String _ALREADY_FILTERED =
451 LayoutCacheFilter.class + "_ALREADY_FILTERED";
452
453 private static final int _PATTERN_FRIENDLY = 0;
454
455 private static final int _PATTERN_LAYOUT = 1;
456
457 private static final int _PATTERN_RESOURCE = 2;
458
459 private static final String _BROWSER_TYPE_IE_7 = "ie_7";
460
461 private static final String _BROWSER_TYPE_IE = "ie";
462
463 private static final String _BROWSER_TYPE_OTHER = "other";
464
465 private static Log _log = LogFactoryUtil.getLog(LayoutCacheFilter.class);
466
467 private FilterConfig _config;
468 private int _pattern;
469
470 }