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