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.JavaConstants;
030    import com.liferay.portal.kernel.util.PropsKeys;
031    import com.liferay.portal.kernel.util.PropsUtil;
032    import com.liferay.portal.kernel.util.ServerDetector;
033    import com.liferay.portal.kernel.util.StringPool;
034    import com.liferay.portal.kernel.util.Validator;
035    import com.liferay.portal.kernel.util.WebKeys;
036    
037    import java.io.IOException;
038    
039    import javax.servlet.Filter;
040    import javax.servlet.FilterChain;
041    import javax.servlet.FilterConfig;
042    import javax.servlet.ServletContext;
043    import javax.servlet.ServletException;
044    import javax.servlet.ServletRequest;
045    import javax.servlet.ServletResponse;
046    import javax.servlet.http.HttpServletRequest;
047    import javax.servlet.http.HttpServletResponse;
048    
049    /**
050     * @author Mika Koivisto
051     * @author Brian Wing Shun Chan
052     * @author Shuyang Zhou
053     */
054    public class InvokerFilter extends BasePortalLifecycle implements Filter {
055    
056            @Override
057            public void destroy() {
058                    portalDestroy();
059            }
060    
061            @Override
062            public void doFilter(
063                            ServletRequest servletRequest, ServletResponse servletResponse,
064                            FilterChain filterChain)
065                    throws IOException, ServletException {
066    
067                    HttpServletRequest request = (HttpServletRequest)servletRequest;
068    
069                    String uri = getURI(request);
070    
071                    request = handleNonSerializableRequest(request);
072    
073                    HttpServletResponse response = (HttpServletResponse)servletResponse;
074    
075                    if (ServletVersionDetector.is3_0()) {
076                            response =
077                                    HttpOnlyCookieServletResponse.getHttpOnlyCookieServletResponse(
078                                            response);
079                    }
080    
081                    response = secureResponseHeaders(request, response);
082    
083                    request.setAttribute(WebKeys.INVOKER_FILTER_URI, uri);
084    
085                    try {
086                            InvokerFilterChain invokerFilterChain = getInvokerFilterChain(
087                                    request, uri, filterChain);
088    
089                            Thread currentThread = Thread.currentThread();
090    
091                            ClassLoader contextClassLoader =
092                                    currentThread.getContextClassLoader();
093    
094                            invokerFilterChain.setContextClassLoader(contextClassLoader);
095    
096                            invokerFilterChain.doFilter(request, response);
097                    }
098                    finally {
099                            request.removeAttribute(WebKeys.INVOKER_FILTER_URI);
100                    }
101            }
102    
103            @Override
104            public void init(FilterConfig filterConfig) throws ServletException {
105                    _filterConfig = filterConfig;
106    
107                    ServletContext servletContext = _filterConfig.getServletContext();
108    
109                    _contextPath = ContextPathUtil.getContextPath(servletContext);
110    
111                    boolean registerPortalLifecycle = GetterUtil.getBoolean(
112                            _filterConfig.getInitParameter("register-portal-lifecycle"), true);
113    
114                    if (registerPortalLifecycle) {
115                            registerPortalLifecycle();
116                    }
117                    else {
118                            try {
119                                    doPortalInit();
120                            }
121                            catch (Exception e) {
122                                    _log.error(e, e);
123    
124                                    throw new ServletException(e);
125                            }
126                    }
127            }
128    
129            protected void clearFilterChainsCache() {
130                    if (_filterChains != null) {
131                            _filterChains.clear();
132                    }
133            }
134    
135            @Override
136            protected void doPortalDestroy() {
137                    ServletContext servletContext = _filterConfig.getServletContext();
138    
139                    InvokerFilterHelper invokerFilterHelper =
140                            (InvokerFilterHelper)servletContext.getAttribute(
141                                    InvokerFilterHelper.class.getName());
142    
143                    if (invokerFilterHelper != null) {
144                            servletContext.removeAttribute(InvokerFilterHelper.class.getName());
145    
146                            invokerFilterHelper.destroy();
147                    }
148            }
149    
150            @Override
151            protected void doPortalInit() throws Exception {
152                    _invokerFilterChainSize = GetterUtil.getInteger(
153                            PropsUtil.get(PropsKeys.INVOKER_FILTER_CHAIN_SIZE));
154    
155                    if (_invokerFilterChainSize > 0) {
156                            _filterChains = new ConcurrentLFUCache<String, InvokerFilterChain>(
157                                    _invokerFilterChainSize);
158                    }
159    
160                    ServletContext servletContext = _filterConfig.getServletContext();
161    
162                    InvokerFilterHelper invokerFilterHelper =
163                            (InvokerFilterHelper)servletContext.getAttribute(
164                                    InvokerFilterHelper.class.getName());
165    
166                    if (invokerFilterHelper == null) {
167                            invokerFilterHelper = new InvokerFilterHelper();
168    
169                            servletContext.setAttribute(
170                                    InvokerFilterHelper.class.getName(), invokerFilterHelper);
171    
172                            invokerFilterHelper.readLiferayFilterWebXML(
173                                    servletContext, "/WEB-INF/liferay-web.xml");
174                    }
175    
176                    _invokerFilterHelper = invokerFilterHelper;
177    
178                    _invokerFilterHelper.addInvokerFilter(this);
179    
180                    String dispatcher = GetterUtil.getString(
181                            _filterConfig.getInitParameter("dispatcher"));
182    
183                    if (dispatcher.equals("ERROR")) {
184                            _dispatcher = Dispatcher.ERROR;
185                    }
186                    else if (dispatcher.equals("FORWARD")) {
187                            _dispatcher = Dispatcher.FORWARD;
188                    }
189                    else if (dispatcher.equals("INCLUDE")) {
190                            _dispatcher = Dispatcher.INCLUDE;
191                    }
192                    else if (dispatcher.equals("REQUEST")) {
193                            _dispatcher = Dispatcher.REQUEST;
194                    }
195                    else {
196                            throw new IllegalArgumentException(
197                                    "Invalid dispatcher " + dispatcher);
198                    }
199            }
200    
201            protected InvokerFilterChain getInvokerFilterChain(
202                    HttpServletRequest request, String uri, FilterChain filterChain) {
203    
204                    if (_filterChains == null) {
205                            return _invokerFilterHelper.createInvokerFilterChain(
206                                    request, _dispatcher, uri, filterChain);
207                    }
208    
209                    CacheKeyGenerator cacheKeyGenerator =
210                            CacheKeyGeneratorUtil.getCacheKeyGenerator(
211                                    InvokerFilter.class.getName());
212    
213                    String key = String.valueOf(cacheKeyGenerator.getCacheKey(uri));
214    
215                    InvokerFilterChain invokerFilterChain = _filterChains.get(key);
216    
217                    if (invokerFilterChain == null) {
218                            invokerFilterChain = _invokerFilterHelper.createInvokerFilterChain(
219                                    request, _dispatcher, uri, filterChain);
220    
221                            _filterChains.put(key, invokerFilterChain);
222                    }
223    
224                    return invokerFilterChain.clone(filterChain);
225            }
226    
227            protected String getURI(HttpServletRequest request) {
228                    String uri = null;
229    
230                    if (_dispatcher == Dispatcher.ERROR) {
231                            uri = (String)request.getAttribute(
232                                    JavaConstants.JAVAX_SERVLET_ERROR_REQUEST_URI);
233                    }
234                    else if (_dispatcher == Dispatcher.INCLUDE) {
235                            uri = (String)request.getAttribute(
236                                    JavaConstants.JAVAX_SERVLET_INCLUDE_REQUEST_URI);
237                    }
238                    else {
239                            uri = request.getRequestURI();
240                    }
241    
242                    if (Validator.isNotNull(_contextPath) &&
243                            !_contextPath.equals(StringPool.SLASH) &&
244                            uri.startsWith(_contextPath)) {
245    
246                            uri = uri.substring(_contextPath.length());
247                    }
248    
249                    return uri;
250            }
251    
252            protected HttpServletRequest handleNonSerializableRequest(
253                    HttpServletRequest request) {
254    
255                    if (ServerDetector.isWebLogic()) {
256                            if (!NonSerializableObjectRequestWrapper.isWrapped(request)) {
257                                    request = new NonSerializableObjectRequestWrapper(request);
258                            }
259                    }
260    
261                    return request;
262            }
263    
264            protected HttpServletResponse secureResponseHeaders(
265                    HttpServletRequest request, HttpServletResponse response) {
266    
267                    if (!GetterUtil.getBoolean(
268                                    request.getAttribute(_SECURE_RESPONSE), true)) {
269    
270                            return response;
271                    }
272    
273                    request.setAttribute(_SECURE_RESPONSE, Boolean.FALSE);
274    
275                    return SanitizedServletResponse.getSanitizedServletResponse(
276                            request, response);
277            }
278    
279            private static final String _SECURE_RESPONSE =
280                    InvokerFilter.class.getName() + "SECURE_RESPONSE";
281    
282            private static Log _log = LogFactoryUtil.getLog(InvokerFilter.class);
283    
284            private String _contextPath;
285            private Dispatcher _dispatcher;
286            private ConcurrentLFUCache<String, InvokerFilterChain> _filterChains;
287            private FilterConfig _filterConfig;
288            private int _invokerFilterChainSize;
289            private InvokerFilterHelper _invokerFilterHelper;
290    
291    }