001    /**
002     * Copyright (c) 2000-present 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.StringPool;
035    import com.liferay.portal.kernel.util.Validator;
036    import com.liferay.portal.kernel.util.WebKeys;
037    
038    import java.io.IOException;
039    
040    import javax.servlet.Filter;
041    import javax.servlet.FilterChain;
042    import javax.servlet.FilterConfig;
043    import javax.servlet.ServletContext;
044    import javax.servlet.ServletException;
045    import javax.servlet.ServletRequest;
046    import javax.servlet.ServletResponse;
047    import javax.servlet.http.HttpServletRequest;
048    import javax.servlet.http.HttpServletResponse;
049    
050    /**
051     * @author Mika Koivisto
052     * @author Brian Wing Shun Chan
053     * @author Shuyang Zhou
054     */
055    public class InvokerFilter extends BasePortalLifecycle implements Filter {
056    
057            @Override
058            public void destroy() {
059                    portalDestroy();
060            }
061    
062            @Override
063            public void doFilter(
064                            ServletRequest servletRequest, ServletResponse servletResponse,
065                            FilterChain filterChain)
066                    throws IOException, ServletException {
067    
068                    HttpServletRequest request = (HttpServletRequest)servletRequest;
069    
070                    String uri = getURI(request);
071    
072                    request = handleNonSerializableRequest(request);
073    
074                    HttpServletResponse response = (HttpServletResponse)servletResponse;
075    
076                    if (ServletVersionDetector.is3_0()) {
077                            response =
078                                    HttpOnlyCookieServletResponse.getHttpOnlyCookieServletResponse(
079                                            response);
080                    }
081    
082                    response = secureResponseHeaders(request, response);
083    
084                    request.setAttribute(WebKeys.INVOKER_FILTER_URI, uri);
085    
086                    try {
087                            InvokerFilterChain invokerFilterChain = getInvokerFilterChain(
088                                    request, uri, filterChain);
089    
090                            Thread currentThread = Thread.currentThread();
091    
092                            ClassLoader contextClassLoader =
093                                    currentThread.getContextClassLoader();
094    
095                            invokerFilterChain.setContextClassLoader(contextClassLoader);
096    
097                            invokerFilterChain.doFilter(request, response);
098                    }
099                    finally {
100                            request.removeAttribute(WebKeys.INVOKER_FILTER_URI);
101                    }
102            }
103    
104            @Override
105            public void init(FilterConfig filterConfig) throws ServletException {
106                    _filterConfig = filterConfig;
107    
108                    ServletContext servletContext = _filterConfig.getServletContext();
109    
110                    _contextPath = ContextPathUtil.getContextPath(servletContext);
111    
112                    boolean registerPortalLifecycle = GetterUtil.getBoolean(
113                            _filterConfig.getInitParameter("register-portal-lifecycle"), true);
114    
115                    if (registerPortalLifecycle) {
116                            registerPortalLifecycle();
117                    }
118                    else {
119                            try {
120                                    doPortalInit();
121                            }
122                            catch (Exception e) {
123                                    _log.error(e, e);
124    
125                                    throw new ServletException(e);
126                            }
127                    }
128            }
129    
130            protected void clearFilterChainsCache() {
131                    if (_filterChains != null) {
132                            _filterChains.clear();
133                    }
134            }
135    
136            @Override
137            protected void doPortalDestroy() {
138                    ServletContext servletContext = _filterConfig.getServletContext();
139    
140                    InvokerFilterHelper invokerFilterHelper =
141                            (InvokerFilterHelper)servletContext.getAttribute(
142                                    InvokerFilterHelper.class.getName());
143    
144                    if (invokerFilterHelper != null) {
145                            servletContext.removeAttribute(InvokerFilterHelper.class.getName());
146    
147                            invokerFilterHelper.destroy();
148                    }
149            }
150    
151            @Override
152            protected void doPortalInit() throws Exception {
153                    _invokerFilterChainSize = GetterUtil.getInteger(
154                            PropsUtil.get(PropsKeys.INVOKER_FILTER_CHAIN_SIZE));
155    
156                    if (_invokerFilterChainSize > 0) {
157                            _filterChains = new ConcurrentLFUCache<String, InvokerFilterChain>(
158                                    _invokerFilterChainSize);
159                    }
160    
161                    ServletContext servletContext = _filterConfig.getServletContext();
162    
163                    InvokerFilterHelper invokerFilterHelper =
164                            (InvokerFilterHelper)servletContext.getAttribute(
165                                    InvokerFilterHelper.class.getName());
166    
167                    if (invokerFilterHelper == null) {
168                            invokerFilterHelper = new InvokerFilterHelper();
169    
170                            invokerFilterHelper.init(_filterConfig);
171    
172                            servletContext.setAttribute(
173                                    InvokerFilterHelper.class.getName(), invokerFilterHelper);
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 HttpUtil.removePathParameters(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 final 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    }