/**
* 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.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.servlet.PluginContextListener;
import com.liferay.portal.kernel.servlet.ServletContextPool;
import com.liferay.portal.kernel.util.AggregateClassLoader;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.InstanceFactory;
import com.liferay.portal.kernel.util.ObjectValuePair;
import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.kernel.xml.Document;
import com.liferay.portal.kernel.xml.Element;
import com.liferay.portal.kernel.xml.UnsecureSAXReaderUtil;
import com.liferay.registry.Registry;
import com.liferay.registry.RegistryUtil;
import com.liferay.registry.ServiceReference;
import com.liferay.registry.ServiceTracker;
import com.liferay.registry.ServiceTrackerCustomizer;
import com.liferay.registry.util.StringPlus;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
/**
* @author Mika Koivisto
* @author Brian Wing Shun Chan
*/
public class InvokerFilterHelper {
public void destroy() {
_serviceTracker.close();
for (List<FilterMapping> filterMappings : _filterMappingsMap.values()) {
FilterMapping filterMapping = filterMappings.get(0);
Filter filter = filterMapping.getFilter();
try {
filter.destroy();
}
catch (Exception e) {
_log.error(e, e);
}
}
_filterMappingsMap.clear();
_filterNames.clear();
clearFilterChainsCache();
}
public void init(FilterConfig filterConfig) throws ServletException {
try {
ServletContext servletContext = filterConfig.getServletContext();
readLiferayFilterWebXML(servletContext, "/WEB-INF/liferay-web.xml");
Registry registry = RegistryUtil.getRegistry();
String servletContextName = GetterUtil.getString(
servletContext.getServletContextName());
com.liferay.registry.Filter filter = registry.getFilter(
"(&(objectClass=" + Filter.class.getName() +
")(servlet-context-name=" + servletContextName +
")(servlet-filter-name=*))");
_serviceTracker = registry.trackServices(
filter, new FilterServiceTrackerCustomizer());
_serviceTracker.open();
}
catch (Exception e) {
_log.error(e, e);
throw new ServletException(e);
}
}
public void registerFilterMapping(
FilterMapping filterMapping, String positionFilterName, boolean after) {
String filterName = filterMapping.getFilterName();
while (true) {
List<FilterMapping> oldFilterMappings = _filterMappingsMap.get(
filterName);
List<FilterMapping> newFilterMappings = null;
if (oldFilterMappings == null) {
newFilterMappings = new ArrayList<>();
}
else {
newFilterMappings = new ArrayList<>(oldFilterMappings);
}
newFilterMappings.add(filterMapping);
if (newFilterMappings.size() == 1) {
if (_filterMappingsMap.putIfAbsent(
filterName, newFilterMappings) == null) {
int index = _filterNames.indexOf(positionFilterName);
if (index == -1) {
_filterNames.add(filterName);
}
else if (after) {
_filterNames.add(index + 1, filterName);
}
else {
_filterNames.add(index, filterName);
}
break;
}
}
else if (_filterMappingsMap.replace(
filterName, oldFilterMappings, newFilterMappings)) {
break;
}
}
}
public void unregisterFilterMapping(FilterMapping filterMapping) {
String filterName = filterMapping.getFilterName();
while (true) {
List<FilterMapping> oldFilterMappings = _filterMappingsMap.get(
filterName);
List<FilterMapping> newFilterMappings = new ArrayList<>(
oldFilterMappings);
newFilterMappings.remove(filterMapping);
if (newFilterMappings.isEmpty()) {
if (_filterMappingsMap.remove(filterName, oldFilterMappings)) {
_filterNames.remove(filterName);
break;
}
}
else if (_filterMappingsMap.replace(
filterName, oldFilterMappings, newFilterMappings)) {
break;
}
}
}
public void unregisterFilterMappings(String filterName) {
List<FilterMapping> filterMappings = _filterMappingsMap.remove(
filterName);
if (filterMappings == null) {
return;
}
FilterMapping filterMapping = filterMappings.get(0);
Filter filter = filterMapping.getFilter();
if (filter != null) {
try {
filter.destroy();
}
catch (Exception e) {
_log.error(e, e);
}
}
_filterNames.remove(filterName);
clearFilterChainsCache();
}
public void updateFilterMappings(String filterName, Filter filter) {
while (true) {
List<FilterMapping> oldFilterMappings = _filterMappingsMap.get(
filterName);
if (oldFilterMappings == null) {
if (_log.isWarnEnabled()) {
_log.warn(
"No filter mappings for filter name " + filterName);
}
return;
}
List<FilterMapping> newFilterMappings = new ArrayList<>();
for (FilterMapping oldFilterMapping : oldFilterMappings) {
newFilterMappings.add(oldFilterMapping.replaceFilter(filter));
}
if (_filterMappingsMap.replace(
filterName, oldFilterMappings, newFilterMappings)) {
break;
}
}
}
protected void addInvokerFilter(InvokerFilter invokerFilter) {
_invokerFilters.add(invokerFilter);
}
protected void clearFilterChainsCache() {
for (InvokerFilter invokerFilter : _invokerFilters) {
invokerFilter.clearFilterChainsCache();
}
}
protected InvokerFilterChain createInvokerFilterChain(
HttpServletRequest request, Dispatcher dispatcher, String uri,
FilterChain filterChain) {
InvokerFilterChain invokerFilterChain = new InvokerFilterChain(
filterChain);
for (String filterName : _filterNames) {
List<FilterMapping> filterMappings = _filterMappingsMap.get(
filterName);
if (filterMappings == null) {
continue;
}
for (FilterMapping filterMapping : filterMappings) {
if (filterMapping.isMatch(request, dispatcher, uri)) {
invokerFilterChain.addFilter(filterMapping.getFilter());
}
}
}
return invokerFilterChain;
}
protected Filter initFilter(
ServletContext servletContext, String filterClassName,
FilterConfig filterConfig) {
ClassLoader pluginClassLoader =
(ClassLoader)servletContext.getAttribute(
PluginContextListener.PLUGIN_CLASS_LOADER);
Thread currentThread = Thread.currentThread();
ClassLoader contextClassLoader = currentThread.getContextClassLoader();
if (pluginClassLoader == null) {
pluginClassLoader = contextClassLoader;
}
ClassLoader portalClassLoader = PortalClassLoaderUtil.getClassLoader();
if (portalClassLoader != pluginClassLoader) {
pluginClassLoader = AggregateClassLoader.getAggregateClassLoader(
portalClassLoader, pluginClassLoader);
}
if (contextClassLoader != pluginClassLoader) {
currentThread.setContextClassLoader(pluginClassLoader);
}
try {
Filter filter = (Filter)InstanceFactory.newInstance(
pluginClassLoader, filterClassName);
filter.init(filterConfig);
return filter;
}
catch (Exception e) {
_log.error("Unable to initialize filter " + filterClassName, e);
return null;
}
finally {
if (contextClassLoader != pluginClassLoader) {
currentThread.setContextClassLoader(contextClassLoader);
}
}
}
/**
* @deprecated As of 7.0.0, replaced by {@link #initFilter(ServletContext,
* String, FilterConfig)}
*/
@Deprecated
protected Filter initFilter(
ServletContext servletContext, String filterClassName,
String filterName, FilterConfig filterConfig) {
return initFilter(servletContext, filterClassName, filterConfig);
}
protected void readLiferayFilterWebXML(
ServletContext servletContext, String path)
throws Exception {
InputStream inputStream = servletContext.getResourceAsStream(path);
if (inputStream == null) {
return;
}
Document document = UnsecureSAXReaderUtil.read(inputStream, true);
Element rootElement = document.getRootElement();
Map<String, ObjectValuePair<Filter, FilterConfig>>
filterObjectValuePairs = new HashMap<>();
for (Element filterElement : rootElement.elements("filter")) {
String filterName = filterElement.elementText("filter-name");
String filterClassName = filterElement.elementText("filter-class");
Map<String, String> initParameterMap = new HashMap<>();
List<Element> initParamElements = filterElement.elements(
"init-param");
for (Element initParamElement : initParamElements) {
String name = initParamElement.elementText("param-name");
String value = initParamElement.elementText("param-value");
initParameterMap.put(name, value);
}
FilterConfig filterConfig = new InvokerFilterConfig(
servletContext, filterName, initParameterMap);
Filter filter = initFilter(
servletContext, filterClassName, filterConfig);
if (filter != null) {
filterObjectValuePairs.put(
filterName, new ObjectValuePair<>(filter, filterConfig));
}
}
List<Element> filterMappingElements = rootElement.elements(
"filter-mapping");
for (Element filterMappingElement : filterMappingElements) {
String filterName = filterMappingElement.elementText("filter-name");
List<String> urlPatterns = new ArrayList<>();
List<Element> urlPatternElements = filterMappingElement.elements(
"url-pattern");
for (Element urlPatternElement : urlPatternElements) {
urlPatterns.add(urlPatternElement.getTextTrim());
}
List<String> dispatchers = new ArrayList<>(4);
List<Element> dispatcherElements = filterMappingElement.elements(
"dispatcher");
for (Element dispatcherElement : dispatcherElements) {
String dispatcher = StringUtil.toUpperCase(
dispatcherElement.getTextTrim());
dispatchers.add(dispatcher);
}
ObjectValuePair<Filter, FilterConfig> filterObjectValuePair =
filterObjectValuePairs.get(filterName);
if (filterObjectValuePair == null) {
if (_log.isWarnEnabled()) {
_log.warn(
"No filter and filter config for filter name " +
filterName);
}
continue;
}
FilterMapping filterMapping = new FilterMapping(
filterName, filterObjectValuePair.getKey(),
filterObjectValuePair.getValue(), urlPatterns, dispatchers);
registerFilterMapping(filterMapping, null, true);
}
}
private static final Log _log = LogFactoryUtil.getLog(
InvokerFilterHelper.class);
private final ConcurrentMap<String, List<FilterMapping>>
_filterMappingsMap = new ConcurrentHashMap<>();
private final List<String> _filterNames = new CopyOnWriteArrayList<>();
private final List<InvokerFilter> _invokerFilters = new ArrayList<>();
private ServiceTracker<Filter, FilterMapping> _serviceTracker;
private class FilterServiceTrackerCustomizer
implements ServiceTrackerCustomizer<Filter, FilterMapping> {
@Override
public FilterMapping addingService(
ServiceReference<Filter> serviceReference) {
Registry registry = RegistryUtil.getRegistry();
Filter filter = registry.getService(serviceReference);
String afterFilter = GetterUtil.getString(
serviceReference.getProperty("after-filter"));
String beforeFilter = GetterUtil.getString(
serviceReference.getProperty("before-filter"));
List<String> dispatchers = StringPlus.asList(
serviceReference.getProperty("dispatcher"));
String servletContextName = GetterUtil.getString(
serviceReference.getProperty("servlet-context-name"));
String servletFilterName = GetterUtil.getString(
serviceReference.getProperty("servlet-filter-name"));
List<String> urlPatterns = StringPlus.asList(
serviceReference.getProperty("url-pattern"));
String positionFilterName = beforeFilter;
boolean after = false;
if (Validator.isNotNull(afterFilter)) {
positionFilterName = afterFilter;
after = true;
}
Map<String, String> initParameterMap = new HashMap<>();
Map<String, Object> properties = serviceReference.getProperties();
for (String key : properties.keySet()) {
if (!key.startsWith("init.param.")) {
continue;
}
String value = GetterUtil.getString(
serviceReference.getProperty(key));
initParameterMap.put(key, value);
}
ServletContext servletContext = ServletContextPool.get(
servletContextName);
FilterConfig filterConfig = new InvokerFilterConfig(
servletContext, servletFilterName, initParameterMap);
try {
filter.init(filterConfig);
}
catch (ServletException se) {
_log.error(se, se);
registry.ungetService(serviceReference);
return null;
}
updateFilterMappings(servletFilterName, filter);
FilterMapping filterMapping = new FilterMapping(
servletFilterName, filter, filterConfig, urlPatterns,
dispatchers);
registerFilterMapping(filterMapping, positionFilterName, after);
clearFilterChainsCache();
return filterMapping;
}
@Override
public void modifiedService(
ServiceReference<Filter> serviceReference,
FilterMapping filterMapping) {
removedService(serviceReference, filterMapping);
addingService(serviceReference);
}
@Override
public void removedService(
ServiceReference<Filter> serviceReference,
FilterMapping filterMapping) {
Registry registry = RegistryUtil.getRegistry();
registry.ungetService(serviceReference);
unregisterFilterMappings(
GetterUtil.getString(
serviceReference.getProperty("servlet-filter-name")));
}
}
}