001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portal.kernel.servlet.filters.invoker;
016    
017    import com.liferay.portal.kernel.concurrent.ConcurrentLFUCache;
018    import com.liferay.portal.kernel.log.Log;
019    import com.liferay.portal.kernel.log.LogFactoryUtil;
020    import com.liferay.portal.kernel.servlet.HttpOnlyCookieServletResponse;
021    import com.liferay.portal.kernel.servlet.NonSerializableObjectRequestWrapper;
022    import com.liferay.portal.kernel.servlet.SanitizedServletResponse;
023    import com.liferay.portal.kernel.servlet.ServletVersionDetector;
024    import com.liferay.portal.kernel.util.BasePortalLifecycle;
025    import com.liferay.portal.kernel.util.ContextPathUtil;
026    import com.liferay.portal.kernel.util.GetterUtil;
027    import com.liferay.portal.kernel.util.HttpUtil;
028    import com.liferay.portal.kernel.util.JavaConstants;
029    import com.liferay.portal.kernel.util.PropsKeys;
030    import com.liferay.portal.kernel.util.PropsUtil;
031    import com.liferay.portal.kernel.util.ServerDetector;
032    import com.liferay.portal.kernel.util.StringBundler;
033    import com.liferay.portal.kernel.util.StringPool;
034    import com.liferay.portal.kernel.util.StringUtil;
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                    HttpServletResponse response = (HttpServletResponse)servletResponse;
073    
074                    String requestURL = getURL(request);
075    
076                    if (requestURL.length() > _invokerFilterURIMaxLength) {
077                            response.sendError(HttpServletResponse.SC_REQUEST_URI_TOO_LONG);
078    
079                            if (_log.isWarnEnabled()) {
080                                    StringBundler sb = new StringBundler(7);
081    
082                                    sb.append("Rejected ");
083                                    sb.append(StringUtil.shorten(uri, _invokerFilterURIMaxLength));
084                                    sb.append(" because it has more than ");
085                                    sb.append(_invokerFilterURIMaxLength);
086                                    sb.append(" characters");
087    
088                                    _log.warn(sb.toString());
089                            }
090    
091                            return;
092                    }
093    
094                    request = handleNonSerializableRequest(request);
095    
096                    if (ServletVersionDetector.is3_0()) {
097                            response =
098                                    HttpOnlyCookieServletResponse.getHttpOnlyCookieServletResponse(
099                                            response);
100                    }
101    
102                    response = secureResponseHeaders(request, response);
103    
104                    request.setAttribute(WebKeys.INVOKER_FILTER_URI, uri);
105    
106                    try {
107                            InvokerFilterChain invokerFilterChain = getInvokerFilterChain(
108                                    request, uri, filterChain);
109    
110                            Thread currentThread = Thread.currentThread();
111    
112                            ClassLoader contextClassLoader =
113                                    currentThread.getContextClassLoader();
114    
115                            invokerFilterChain.setContextClassLoader(contextClassLoader);
116    
117                            invokerFilterChain.doFilter(request, response);
118                    }
119                    finally {
120                            request.removeAttribute(WebKeys.INVOKER_FILTER_URI);
121                    }
122            }
123    
124            @Override
125            public void init(FilterConfig filterConfig) throws ServletException {
126                    _filterConfig = filterConfig;
127    
128                    ServletContext servletContext = _filterConfig.getServletContext();
129    
130                    _contextPath = ContextPathUtil.getContextPath(servletContext);
131    
132                    boolean registerPortalLifecycle = GetterUtil.getBoolean(
133                            _filterConfig.getInitParameter("register-portal-lifecycle"), true);
134    
135                    if (registerPortalLifecycle) {
136                            registerPortalLifecycle();
137                    }
138                    else {
139                            try {
140                                    doPortalInit();
141                            }
142                            catch (Exception e) {
143                                    _log.error(e, e);
144    
145                                    throw new ServletException(e);
146                            }
147                    }
148            }
149    
150            protected void clearFilterChainsCache() {
151                    if (_filterChains != null) {
152                            _filterChains.clear();
153                    }
154            }
155    
156            @Override
157            protected void doPortalDestroy() {
158                    ServletContext servletContext = _filterConfig.getServletContext();
159    
160                    InvokerFilterHelper invokerFilterHelper =
161                            (InvokerFilterHelper)servletContext.getAttribute(
162                                    InvokerFilterHelper.class.getName());
163    
164                    if (invokerFilterHelper != null) {
165                            servletContext.removeAttribute(InvokerFilterHelper.class.getName());
166    
167                            invokerFilterHelper.destroy();
168                    }
169            }
170    
171            @Override
172            protected void doPortalInit() throws Exception {
173                    _invokerFilterChainSize = GetterUtil.getInteger(
174                            PropsUtil.get(PropsKeys.INVOKER_FILTER_CHAIN_SIZE));
175    
176                    if (_invokerFilterChainSize > 0) {
177                            _filterChains = new ConcurrentLFUCache<String, InvokerFilterChain>(
178                                    _invokerFilterChainSize);
179                    }
180    
181                    _invokerFilterURIMaxLength = GetterUtil.getInteger(
182                            PropsUtil.get(PropsKeys.INVOKER_FILTER_URI_MAX_LENGTH));
183    
184                    ServletContext servletContext = _filterConfig.getServletContext();
185    
186                    InvokerFilterHelper invokerFilterHelper =
187                            (InvokerFilterHelper)servletContext.getAttribute(
188                                    InvokerFilterHelper.class.getName());
189    
190                    if (invokerFilterHelper == null) {
191                            invokerFilterHelper = new InvokerFilterHelper();
192    
193                            servletContext.setAttribute(
194                                    InvokerFilterHelper.class.getName(), invokerFilterHelper);
195    
196                            invokerFilterHelper.readLiferayFilterWebXML(
197                                    servletContext, "/WEB-INF/liferay-web.xml");
198                    }
199    
200                    _invokerFilterHelper = invokerFilterHelper;
201    
202                    _invokerFilterHelper.addInvokerFilter(this);
203    
204                    String dispatcher = GetterUtil.getString(
205                            _filterConfig.getInitParameter("dispatcher"));
206    
207                    if (dispatcher.equals("ERROR")) {
208                            _dispatcher = Dispatcher.ERROR;
209                    }
210                    else if (dispatcher.equals("FORWARD")) {
211                            _dispatcher = Dispatcher.FORWARD;
212                    }
213                    else if (dispatcher.equals("INCLUDE")) {
214                            _dispatcher = Dispatcher.INCLUDE;
215                    }
216                    else if (dispatcher.equals("REQUEST")) {
217                            _dispatcher = Dispatcher.REQUEST;
218                    }
219                    else {
220                            throw new IllegalArgumentException(
221                                    "Invalid dispatcher " + dispatcher);
222                    }
223            }
224    
225            protected InvokerFilterChain getInvokerFilterChain(
226                    HttpServletRequest request, String uri, FilterChain filterChain) {
227    
228                    if (_filterChains == null) {
229                            return _invokerFilterHelper.createInvokerFilterChain(
230                                    request, _dispatcher, uri, filterChain);
231                    }
232    
233                    InvokerFilterChain invokerFilterChain = _filterChains.get(uri);
234    
235                    if (invokerFilterChain == null) {
236                            invokerFilterChain = _invokerFilterHelper.createInvokerFilterChain(
237                                    request, _dispatcher, uri, filterChain);
238    
239                            _filterChains.put(uri, invokerFilterChain);
240                    }
241    
242                    return invokerFilterChain.clone(filterChain);
243            }
244    
245            protected String getURI(HttpServletRequest request) {
246                    String uri = null;
247    
248                    if (_dispatcher == Dispatcher.ERROR) {
249                            uri = (String)request.getAttribute(
250                                    JavaConstants.JAVAX_SERVLET_ERROR_REQUEST_URI);
251                    }
252                    else if (_dispatcher == Dispatcher.INCLUDE) {
253                            uri = (String)request.getAttribute(
254                                    JavaConstants.JAVAX_SERVLET_INCLUDE_REQUEST_URI);
255                    }
256                    else {
257                            uri = request.getRequestURI();
258                    }
259    
260                    if (Validator.isNotNull(_contextPath) &&
261                            !_contextPath.equals(StringPool.SLASH) &&
262                            uri.startsWith(_contextPath)) {
263    
264                            uri = uri.substring(_contextPath.length());
265                    }
266    
267                    return HttpUtil.normalizePath(uri);
268            }
269    
270            protected String getURL(HttpServletRequest request) {
271                    StringBuffer requestURL = request.getRequestURL();
272    
273                    if (requestURL == null) {
274                            return StringPool.BLANK;
275                    }
276    
277                    String queryString = request.getQueryString();
278    
279                    if (!Validator.isBlank(queryString)) {
280                            requestURL.append(StringPool.QUESTION);
281                            requestURL.append(request.getQueryString());
282                    }
283    
284                    return requestURL.toString();
285            }
286    
287            protected HttpServletRequest handleNonSerializableRequest(
288                    HttpServletRequest request) {
289    
290                    if (ServerDetector.isWebLogic()) {
291                            if (!NonSerializableObjectRequestWrapper.isWrapped(request)) {
292                                    request = new NonSerializableObjectRequestWrapper(request);
293                            }
294                    }
295    
296                    return request;
297            }
298    
299            protected HttpServletResponse secureResponseHeaders(
300                    HttpServletRequest request, HttpServletResponse response) {
301    
302                    if (Boolean.FALSE.equals(request.getAttribute(_SECURE_RESPONSE))) {
303                            return response;
304                    }
305    
306                    request.setAttribute(_SECURE_RESPONSE, Boolean.FALSE);
307    
308                    return SanitizedServletResponse.getSanitizedServletResponse(
309                            request, response);
310            }
311    
312            private static final String _SECURE_RESPONSE =
313                    InvokerFilter.class.getName() + "SECURE_RESPONSE";
314    
315            private static Log _log = LogFactoryUtil.getLog(InvokerFilter.class);
316    
317            private String _contextPath;
318            private Dispatcher _dispatcher;
319            private ConcurrentLFUCache<String, InvokerFilterChain> _filterChains;
320            private FilterConfig _filterConfig;
321            private int _invokerFilterChainSize;
322            private InvokerFilterHelper _invokerFilterHelper;
323            private int _invokerFilterURIMaxLength;
324    
325    }