001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.servlet.filters.invoker;
016    
017    import com.liferay.portal.kernel.cache.key.CacheKeyGenerator;
018    import com.liferay.portal.kernel.cache.key.CacheKeyGeneratorUtil;
019    import com.liferay.portal.kernel.concurrent.ConcurrentLFUCache;
020    import com.liferay.portal.kernel.log.Log;
021    import com.liferay.portal.kernel.log.LogFactoryUtil;
022    import com.liferay.portal.kernel.servlet.HttpOnlyCookieServletResponse;
023    import com.liferay.portal.kernel.servlet.NonSerializableObjectRequestWrapper;
024    import com.liferay.portal.kernel.servlet.SanitizedServletResponse;
025    import com.liferay.portal.kernel.servlet.ServletVersionDetector;
026    import com.liferay.portal.kernel.util.BasePortalLifecycle;
027    import com.liferay.portal.kernel.util.ContextPathUtil;
028    import com.liferay.portal.kernel.util.GetterUtil;
029    import com.liferay.portal.kernel.util.HttpUtil;
030    import com.liferay.portal.kernel.util.JavaConstants;
031    import com.liferay.portal.kernel.util.PropsKeys;
032    import com.liferay.portal.kernel.util.PropsUtil;
033    import com.liferay.portal.kernel.util.ServerDetector;
034    import com.liferay.portal.kernel.util.StringBundler;
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.kernel.util.WebKeys;
039    
040    import java.io.IOException;
041    
042    import javax.servlet.Filter;
043    import javax.servlet.FilterChain;
044    import javax.servlet.FilterConfig;
045    import javax.servlet.ServletContext;
046    import javax.servlet.ServletException;
047    import javax.servlet.ServletRequest;
048    import javax.servlet.ServletResponse;
049    import javax.servlet.http.HttpServletRequest;
050    import javax.servlet.http.HttpServletResponse;
051    
052    /**
053     * @author Mika Koivisto
054     * @author Brian Wing Shun Chan
055     * @author Shuyang Zhou
056     */
057    public class InvokerFilter extends BasePortalLifecycle implements Filter {
058    
059            @Override
060            public void destroy() {
061                    portalDestroy();
062            }
063    
064            @Override
065            public void doFilter(
066                            ServletRequest servletRequest, ServletResponse servletResponse,
067                            FilterChain filterChain)
068                    throws IOException, ServletException {
069    
070                    HttpServletRequest request = (HttpServletRequest)servletRequest;
071    
072                    String uri = getURI(request);
073    
074                    HttpServletResponse response = (HttpServletResponse)servletResponse;
075    
076                    String requestURL = getURL(request);
077    
078                    if (requestURL.length() > _invokerFilterURIMaxLength) {
079                            response.sendError(HttpServletResponse.SC_REQUEST_URI_TOO_LONG);
080    
081                            if (_log.isWarnEnabled()) {
082                                    StringBundler sb = new StringBundler(7);
083    
084                                    sb.append("Rejected ");
085                                    sb.append(StringUtil.shorten(uri, _invokerFilterURIMaxLength));
086                                    sb.append(" because it has more than ");
087                                    sb.append(_invokerFilterURIMaxLength);
088                                    sb.append(" characters");
089    
090                                    _log.warn(sb.toString());
091                            }
092    
093                            return;
094                    }
095    
096                    request = handleNonSerializableRequest(request);
097    
098                    if (ServletVersionDetector.is3_0()) {
099                            response =
100                                    HttpOnlyCookieServletResponse.getHttpOnlyCookieServletResponse(
101                                            response);
102                    }
103    
104                    response = secureResponseHeaders(request, response);
105    
106                    request.setAttribute(WebKeys.INVOKER_FILTER_URI, uri);
107    
108                    try {
109                            InvokerFilterChain invokerFilterChain = getInvokerFilterChain(
110                                    request, uri, filterChain);
111    
112                            Thread currentThread = Thread.currentThread();
113    
114                            ClassLoader contextClassLoader =
115                                    currentThread.getContextClassLoader();
116    
117                            invokerFilterChain.setContextClassLoader(contextClassLoader);
118    
119                            invokerFilterChain.doFilter(request, response);
120                    }
121                    finally {
122                            request.removeAttribute(WebKeys.INVOKER_FILTER_URI);
123                    }
124            }
125    
126            @Override
127            public void init(FilterConfig filterConfig) throws ServletException {
128                    _filterConfig = filterConfig;
129    
130                    ServletContext servletContext = _filterConfig.getServletContext();
131    
132                    _contextPath = ContextPathUtil.getContextPath(servletContext);
133    
134                    boolean registerPortalLifecycle = GetterUtil.getBoolean(
135                            _filterConfig.getInitParameter("register-portal-lifecycle"), true);
136    
137                    if (registerPortalLifecycle) {
138                            registerPortalLifecycle();
139                    }
140                    else {
141                            try {
142                                    doPortalInit();
143                            }
144                            catch (Exception e) {
145                                    _log.error(e, e);
146    
147                                    throw new ServletException(e);
148                            }
149                    }
150            }
151    
152            protected void clearFilterChainsCache() {
153                    if (_filterChains != null) {
154                            _filterChains.clear();
155                    }
156            }
157    
158            @Override
159            protected void doPortalDestroy() {
160                    ServletContext servletContext = _filterConfig.getServletContext();
161    
162                    InvokerFilterHelper invokerFilterHelper =
163                            (InvokerFilterHelper)servletContext.getAttribute(
164                                    InvokerFilterHelper.class.getName());
165    
166                    if (invokerFilterHelper != null) {
167                            servletContext.removeAttribute(InvokerFilterHelper.class.getName());
168    
169                            invokerFilterHelper.destroy();
170                    }
171            }
172    
173            @Override
174            protected void doPortalInit() throws Exception {
175                    _invokerFilterChainSize = GetterUtil.getInteger(
176                            PropsUtil.get(PropsKeys.INVOKER_FILTER_CHAIN_SIZE));
177    
178                    if (_invokerFilterChainSize > 0) {
179                            _filterChains = new ConcurrentLFUCache<String, InvokerFilterChain>(
180                                    _invokerFilterChainSize);
181                    }
182    
183                    _invokerFilterURIMaxLength = GetterUtil.getInteger(
184                            PropsUtil.get(PropsKeys.INVOKER_FILTER_URI_MAX_LENGTH));
185    
186                    ServletContext servletContext = _filterConfig.getServletContext();
187    
188                    InvokerFilterHelper invokerFilterHelper =
189                            (InvokerFilterHelper)servletContext.getAttribute(
190                                    InvokerFilterHelper.class.getName());
191    
192                    if (invokerFilterHelper == null) {
193                            invokerFilterHelper = new InvokerFilterHelper();
194    
195                            servletContext.setAttribute(
196                                    InvokerFilterHelper.class.getName(), invokerFilterHelper);
197    
198                            invokerFilterHelper.readLiferayFilterWebXML(
199                                    servletContext, "/WEB-INF/liferay-web.xml");
200                    }
201    
202                    _invokerFilterHelper = invokerFilterHelper;
203    
204                    _invokerFilterHelper.addInvokerFilter(this);
205    
206                    String dispatcher = GetterUtil.getString(
207                            _filterConfig.getInitParameter("dispatcher"));
208    
209                    if (dispatcher.equals("ERROR")) {
210                            _dispatcher = Dispatcher.ERROR;
211                    }
212                    else if (dispatcher.equals("FORWARD")) {
213                            _dispatcher = Dispatcher.FORWARD;
214                    }
215                    else if (dispatcher.equals("INCLUDE")) {
216                            _dispatcher = Dispatcher.INCLUDE;
217                    }
218                    else if (dispatcher.equals("REQUEST")) {
219                            _dispatcher = Dispatcher.REQUEST;
220                    }
221                    else {
222                            throw new IllegalArgumentException(
223                                    "Invalid dispatcher " + dispatcher);
224                    }
225            }
226    
227            protected InvokerFilterChain getInvokerFilterChain(
228                    HttpServletRequest request, String uri, FilterChain filterChain) {
229    
230                    if (_filterChains == null) {
231                            return _invokerFilterHelper.createInvokerFilterChain(
232                                    request, _dispatcher, uri, filterChain);
233                    }
234    
235                    CacheKeyGenerator cacheKeyGenerator =
236                            CacheKeyGeneratorUtil.getCacheKeyGenerator(
237                                    InvokerFilter.class.getName());
238    
239                    String key = String.valueOf(cacheKeyGenerator.getCacheKey(uri));
240    
241                    InvokerFilterChain invokerFilterChain = _filterChains.get(key);
242    
243                    if (invokerFilterChain == null) {
244                            invokerFilterChain = _invokerFilterHelper.createInvokerFilterChain(
245                                    request, _dispatcher, uri, filterChain);
246    
247                            _filterChains.put(key, invokerFilterChain);
248                    }
249    
250                    return invokerFilterChain.clone(filterChain);
251            }
252    
253            protected String getURI(HttpServletRequest request) {
254                    String uri = null;
255    
256                    if (_dispatcher == Dispatcher.ERROR) {
257                            uri = (String)request.getAttribute(
258                                    JavaConstants.JAVAX_SERVLET_ERROR_REQUEST_URI);
259                    }
260                    else if (_dispatcher == Dispatcher.INCLUDE) {
261                            uri = (String)request.getAttribute(
262                                    JavaConstants.JAVAX_SERVLET_INCLUDE_REQUEST_URI);
263                    }
264                    else {
265                            uri = request.getRequestURI();
266                    }
267    
268                    if (Validator.isNotNull(_contextPath) &&
269                            !_contextPath.equals(StringPool.SLASH) &&
270                            uri.startsWith(_contextPath)) {
271    
272                            uri = uri.substring(_contextPath.length());
273                    }
274    
275                    return HttpUtil.normalizePath(uri);
276            }
277    
278            protected String getURL(HttpServletRequest request) {
279                    StringBuffer requestURL = request.getRequestURL();
280    
281                    if (requestURL == null) {
282                            return StringPool.BLANK;
283                    }
284    
285                    String queryString = request.getQueryString();
286    
287                    if (!Validator.isBlank(queryString)) {
288                            requestURL.append(StringPool.QUESTION);
289                            requestURL.append(request.getQueryString());
290                    }
291    
292                    return requestURL.toString();
293            }
294    
295            protected HttpServletRequest handleNonSerializableRequest(
296                    HttpServletRequest request) {
297    
298                    if (ServerDetector.isWebLogic()) {
299                            if (!NonSerializableObjectRequestWrapper.isWrapped(request)) {
300                                    request = new NonSerializableObjectRequestWrapper(request);
301                            }
302                    }
303    
304                    return request;
305            }
306    
307            protected HttpServletResponse secureResponseHeaders(
308                    HttpServletRequest request, HttpServletResponse response) {
309    
310                    if (!GetterUtil.getBoolean(
311                                    request.getAttribute(_SECURE_RESPONSE), true)) {
312    
313                            return response;
314                    }
315    
316                    request.setAttribute(_SECURE_RESPONSE, Boolean.FALSE);
317    
318                    return SanitizedServletResponse.getSanitizedServletResponse(
319                            request, response);
320            }
321    
322            private static final String _SECURE_RESPONSE =
323                    InvokerFilter.class.getName() + "SECURE_RESPONSE";
324    
325            private static Log _log = LogFactoryUtil.getLog(InvokerFilter.class);
326    
327            private String _contextPath;
328            private Dispatcher _dispatcher;
329            private ConcurrentLFUCache<String, InvokerFilterChain> _filterChains;
330            private FilterConfig _filterConfig;
331            private int _invokerFilterChainSize;
332            private InvokerFilterHelper _invokerFilterHelper;
333            private int _invokerFilterURIMaxLength;
334    
335    }