/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.kernel.servlet.filters.invoker;
import com.liferay.portal.kernel.concurrent.ConcurrentLFUCache;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.servlet.HttpOnlyCookieServletResponse;
import com.liferay.portal.kernel.servlet.NonSerializableObjectRequestWrapper;
import com.liferay.portal.kernel.servlet.SanitizedServletResponse;
import com.liferay.portal.kernel.util.BasePortalLifecycle;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.HttpUtil;
import com.liferay.portal.kernel.util.JavaConstants;
import com.liferay.portal.kernel.util.PropsKeys;
import com.liferay.portal.kernel.util.PropsUtil;
import com.liferay.portal.kernel.util.ServerDetector;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.kernel.util.WebKeys;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author Mika Koivisto
* @author Brian Wing Shun Chan
* @author Shuyang Zhou
*/
public class InvokerFilter extends BasePortalLifecycle implements Filter {
@Override
public void destroy() {
portalDestroy();
}
@Override
public void doFilter(
ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
String originalURI = getOriginalRequestURI(request);
if (!handleLongRequestURL(request, response, originalURI)) {
return;
}
request = handleNonSerializableRequest(request);
response =
HttpOnlyCookieServletResponse.getHttpOnlyCookieServletResponse(
response);
response = secureResponseHeaders(request, response);
String uri = getURI(request, originalURI);
request.setAttribute(WebKeys.INVOKER_FILTER_URI, uri);
try {
InvokerFilterChain invokerFilterChain = getInvokerFilterChain(
request, uri, filterChain);
Thread currentThread = Thread.currentThread();
ClassLoader contextClassLoader =
currentThread.getContextClassLoader();
invokerFilterChain.setContextClassLoader(contextClassLoader);
invokerFilterChain.doFilter(request, response);
}
finally {
request.removeAttribute(WebKeys.INVOKER_FILTER_URI);
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
_filterConfig = filterConfig;
ServletContext servletContext = _filterConfig.getServletContext();
_contextPath = servletContext.getContextPath();
boolean registerPortalLifecycle = GetterUtil.getBoolean(
_filterConfig.getInitParameter("register-portal-lifecycle"), true);
if (registerPortalLifecycle) {
registerPortalLifecycle();
}
else {
try {
doPortalInit();
}
catch (Exception e) {
_log.error(e, e);
throw new ServletException(e);
}
}
}
protected void clearFilterChainsCache() {
if (_filterChains != null) {
_filterChains.clear();
}
}
@Override
protected void doPortalDestroy() {
ServletContext servletContext = _filterConfig.getServletContext();
InvokerFilterHelper invokerFilterHelper =
(InvokerFilterHelper)servletContext.getAttribute(
InvokerFilterHelper.class.getName());
if (invokerFilterHelper != null) {
servletContext.removeAttribute(InvokerFilterHelper.class.getName());
invokerFilterHelper.destroy();
}
}
@Override
protected void doPortalInit() throws Exception {
if (_INVOKER_FILTER_CHAIN_SIZE > 0) {
_filterChains = new ConcurrentLFUCache<>(
_INVOKER_FILTER_CHAIN_SIZE);
}
ServletContext servletContext = _filterConfig.getServletContext();
InvokerFilterHelper invokerFilterHelper =
(InvokerFilterHelper)servletContext.getAttribute(
InvokerFilterHelper.class.getName());
if (invokerFilterHelper == null) {
invokerFilterHelper = new InvokerFilterHelper();
servletContext.setAttribute(
InvokerFilterHelper.class.getName(), invokerFilterHelper);
invokerFilterHelper.init(_filterConfig);
}
_invokerFilterHelper = invokerFilterHelper;
_invokerFilterHelper.addInvokerFilter(this);
_dispatcher = Dispatcher.valueOf(
_filterConfig.getInitParameter("dispatcher"));
}
protected InvokerFilterChain getInvokerFilterChain(
HttpServletRequest request, String uri, FilterChain filterChain) {
if (_filterChains == null) {
return _invokerFilterHelper.createInvokerFilterChain(
request, _dispatcher, uri, filterChain);
}
InvokerFilterChain invokerFilterChain = _filterChains.get(uri);
if (invokerFilterChain == null) {
invokerFilterChain = _invokerFilterHelper.createInvokerFilterChain(
request, _dispatcher, uri, filterChain);
_filterChains.put(uri, invokerFilterChain);
}
return invokerFilterChain.clone(filterChain);
}
protected String getOriginalRequestURI(HttpServletRequest request) {
String uri = null;
if (_dispatcher == Dispatcher.ERROR) {
uri = (String)request.getAttribute(
JavaConstants.JAVAX_SERVLET_ERROR_REQUEST_URI);
}
else if (_dispatcher == Dispatcher.INCLUDE) {
uri = (String)request.getAttribute(
JavaConstants.JAVAX_SERVLET_INCLUDE_REQUEST_URI);
}
else {
uri = request.getRequestURI();
}
return uri;
}
/**
* @deprecated As of 7.0.0, replaced by {@link #getURI(HttpServletRequest,
* String)}
*/
@Deprecated
protected String getURI(HttpServletRequest request) {
return null;
}
protected String getURI(HttpServletRequest request, String originalURI) {
if (Validator.isNotNull(_contextPath) &&
!_contextPath.equals(StringPool.SLASH) &&
originalURI.startsWith(_contextPath)) {
originalURI = originalURI.substring(_contextPath.length());
}
return HttpUtil.normalizePath(originalURI);
}
/**
* @deprecated As of 7.0.0, with no direct replacement
*/
@Deprecated
protected String getURL(HttpServletRequest request) {
StringBuffer requestURL = request.getRequestURL();
if (requestURL == null) {
return StringPool.BLANK;
}
String queryString = request.getQueryString();
if (!Validator.isBlank(queryString)) {
requestURL.append(StringPool.QUESTION);
requestURL.append(request.getQueryString());
}
return requestURL.toString();
}
protected boolean handleLongRequestURL(
HttpServletRequest request, HttpServletResponse response,
String originalURI)
throws IOException {
String queryString = request.getQueryString();
int length = originalURI.length();
if (queryString != null) {
length += queryString.length();
}
if (length <= _INVOKER_FILTER_URI_MAX_LENGTH) {
return true;
}
response.sendError(HttpServletResponse.SC_REQUEST_URI_TOO_LONG);
if (_log.isWarnEnabled()) {
StringBundler sb = new StringBundler(5);
sb.append("Rejected ");
sb.append(
StringUtil.shorten(
originalURI, _INVOKER_FILTER_URI_MAX_LENGTH));
sb.append(" because it has more than ");
sb.append(_INVOKER_FILTER_URI_MAX_LENGTH);
sb.append(" characters");
_log.warn(sb.toString());
}
return false;
}
protected HttpServletRequest handleNonSerializableRequest(
HttpServletRequest request) {
if (ServerDetector.isWebLogic()) {
if (!NonSerializableObjectRequestWrapper.isWrapped(request)) {
request = new NonSerializableObjectRequestWrapper(request);
}
}
return request;
}
protected HttpServletResponse secureResponseHeaders(
HttpServletRequest request, HttpServletResponse response) {
if (Boolean.FALSE.equals(request.getAttribute(_SECURE_RESPONSE))) {
return response;
}
request.setAttribute(_SECURE_RESPONSE, Boolean.FALSE);
return SanitizedServletResponse.getSanitizedServletResponse(
request, response);
}
private static final int _INVOKER_FILTER_CHAIN_SIZE = GetterUtil.getInteger(
PropsUtil.get(PropsKeys.INVOKER_FILTER_CHAIN_SIZE));
private static final int _INVOKER_FILTER_URI_MAX_LENGTH =
GetterUtil.getInteger(
PropsUtil.get(PropsKeys.INVOKER_FILTER_URI_MAX_LENGTH));
private static final String _SECURE_RESPONSE =
InvokerFilter.class.getName() + "SECURE_RESPONSE";
private static final Log _log = LogFactoryUtil.getLog(InvokerFilter.class);
private String _contextPath;
private Dispatcher _dispatcher;
private ConcurrentLFUCache<String, InvokerFilterChain> _filterChains;
private FilterConfig _filterConfig;
private InvokerFilterHelper _invokerFilterHelper;
}