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.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.servlet.PluginContextListener;
020    import com.liferay.portal.kernel.servlet.ServletContextPool;
021    import com.liferay.portal.kernel.util.AggregateClassLoader;
022    import com.liferay.portal.kernel.util.GetterUtil;
023    import com.liferay.portal.kernel.util.InstanceFactory;
024    import com.liferay.portal.kernel.util.ObjectValuePair;
025    import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
026    import com.liferay.portal.kernel.util.StringUtil;
027    import com.liferay.portal.kernel.util.Validator;
028    import com.liferay.portal.kernel.xml.Document;
029    import com.liferay.portal.kernel.xml.Element;
030    import com.liferay.portal.kernel.xml.UnsecureSAXReaderUtil;
031    import com.liferay.registry.Registry;
032    import com.liferay.registry.RegistryUtil;
033    import com.liferay.registry.ServiceReference;
034    import com.liferay.registry.ServiceTracker;
035    import com.liferay.registry.ServiceTrackerCustomizer;
036    import com.liferay.registry.util.StringPlus;
037    
038    import java.io.InputStream;
039    
040    import java.util.ArrayList;
041    import java.util.HashMap;
042    import java.util.List;
043    import java.util.Map;
044    import java.util.concurrent.ConcurrentHashMap;
045    import java.util.concurrent.ConcurrentMap;
046    import java.util.concurrent.CopyOnWriteArrayList;
047    
048    import javax.servlet.Filter;
049    import javax.servlet.FilterChain;
050    import javax.servlet.FilterConfig;
051    import javax.servlet.ServletContext;
052    import javax.servlet.ServletException;
053    import javax.servlet.http.HttpServletRequest;
054    
055    /**
056     * @author Mika Koivisto
057     * @author Brian Wing Shun Chan
058     */
059    public class InvokerFilterHelper {
060    
061            public void destroy() {
062                    _serviceTracker.close();
063    
064                    for (List<FilterMapping> filterMappings : _filterMappingsMap.values()) {
065                            FilterMapping filterMapping = filterMappings.get(0);
066    
067                            Filter filter = filterMapping.getFilter();
068    
069                            try {
070                                    filter.destroy();
071                            }
072                            catch (Exception e) {
073                                    _log.error(e, e);
074                            }
075                    }
076    
077                    _filterMappingsMap.clear();
078                    _filterNames.clear();
079    
080                    clearFilterChainsCache();
081            }
082    
083            public void init(FilterConfig filterConfig) throws ServletException {
084                    try {
085                            ServletContext servletContext = filterConfig.getServletContext();
086    
087                            readLiferayFilterWebXML(servletContext, "/WEB-INF/liferay-web.xml");
088    
089                            Registry registry = RegistryUtil.getRegistry();
090    
091                            String servletContextName = GetterUtil.getString(
092                                    servletContext.getServletContextName());
093    
094                            com.liferay.registry.Filter filter = registry.getFilter(
095                                    "(&(objectClass=" + Filter.class.getName() +
096                                            ")(servlet-context-name=" + servletContextName +
097                                                    ")(servlet-filter-name=*))");
098    
099                            _serviceTracker = registry.trackServices(
100                                    filter, new FilterServiceTrackerCustomizer());
101    
102                            _serviceTracker.open();
103                    }
104                    catch (Exception e) {
105                            _log.error(e, e);
106    
107                            throw new ServletException(e);
108                    }
109            }
110    
111            public void registerFilterMapping(
112                    FilterMapping filterMapping, String positionFilterName, boolean after) {
113    
114                    String filterName = filterMapping.getFilterName();
115    
116                    while (true) {
117                            List<FilterMapping> oldFilterMappings = _filterMappingsMap.get(
118                                    filterName);
119    
120                            List<FilterMapping> newFilterMappings = null;
121    
122                            if (oldFilterMappings == null) {
123                                    newFilterMappings = new ArrayList<>();
124                            }
125                            else {
126                                    newFilterMappings = new ArrayList<>(oldFilterMappings);
127                            }
128    
129                            newFilterMappings.add(filterMapping);
130    
131                            if (newFilterMappings.size() == 1) {
132                                    if (_filterMappingsMap.putIfAbsent(
133                                                    filterName, newFilterMappings) == null) {
134    
135                                            int index = _filterNames.indexOf(positionFilterName);
136    
137                                            if (index == -1) {
138                                                    _filterNames.add(filterName);
139                                            }
140                                            else if (after) {
141                                                    _filterNames.add(index + 1, filterName);
142                                            }
143                                            else {
144                                                    _filterNames.add(index, filterName);
145                                            }
146    
147                                            break;
148                                    }
149                            }
150                            else if (_filterMappingsMap.replace(
151                                                    filterName, oldFilterMappings, newFilterMappings)) {
152    
153                                    break;
154                            }
155                    }
156            }
157    
158            public void unregisterFilterMapping(FilterMapping filterMapping) {
159                    String filterName = filterMapping.getFilterName();
160    
161                    while (true) {
162                            List<FilterMapping> oldFilterMappings = _filterMappingsMap.get(
163                                    filterName);
164    
165                            List<FilterMapping> newFilterMappings = new ArrayList<>(
166                                    oldFilterMappings);
167    
168                            newFilterMappings.remove(filterMapping);
169    
170                            if (newFilterMappings.isEmpty()) {
171                                    if (_filterMappingsMap.remove(filterName, oldFilterMappings)) {
172                                            _filterNames.remove(filterName);
173    
174                                            break;
175                                    }
176                            }
177                            else if (_filterMappingsMap.replace(
178                                                    filterName, oldFilterMappings, newFilterMappings)) {
179    
180                                    break;
181                            }
182                    }
183            }
184    
185            public void unregisterFilterMappings(String filterName) {
186                    List<FilterMapping> filterMappings = _filterMappingsMap.remove(
187                            filterName);
188    
189                    if (filterMappings == null) {
190                            return;
191                    }
192    
193                    FilterMapping filterMapping = filterMappings.get(0);
194    
195                    Filter filter = filterMapping.getFilter();
196    
197                    if (filter != null) {
198                            try {
199                                    filter.destroy();
200                            }
201                            catch (Exception e) {
202                                    _log.error(e, e);
203                            }
204                    }
205    
206                    _filterNames.remove(filterName);
207    
208                    clearFilterChainsCache();
209            }
210    
211            public void updateFilterMappings(String filterName, Filter filter) {
212                    while (true) {
213                            List<FilterMapping> oldFilterMappings = _filterMappingsMap.get(
214                                    filterName);
215    
216                            if (oldFilterMappings == null) {
217                                    if (_log.isWarnEnabled()) {
218                                            _log.warn(
219                                                    "No filter mappings for filter name " + filterName);
220                                    }
221    
222                                    return;
223                            }
224    
225                            List<FilterMapping> newFilterMappings = new ArrayList<>();
226    
227                            for (FilterMapping oldFilterMapping : oldFilterMappings) {
228                                    newFilterMappings.add(oldFilterMapping.replaceFilter(filter));
229                            }
230    
231                            if (_filterMappingsMap.replace(
232                                            filterName, oldFilterMappings, newFilterMappings)) {
233    
234                                    break;
235                            }
236                    }
237            }
238    
239            protected void addInvokerFilter(InvokerFilter invokerFilter) {
240                    _invokerFilters.add(invokerFilter);
241            }
242    
243            protected void clearFilterChainsCache() {
244                    for (InvokerFilter invokerFilter : _invokerFilters) {
245                            invokerFilter.clearFilterChainsCache();
246                    }
247            }
248    
249            protected InvokerFilterChain createInvokerFilterChain(
250                    HttpServletRequest request, Dispatcher dispatcher, String uri,
251                    FilterChain filterChain) {
252    
253                    InvokerFilterChain invokerFilterChain = new InvokerFilterChain(
254                            filterChain);
255    
256                    for (String filterName : _filterNames) {
257                            List<FilterMapping> filterMappings = _filterMappingsMap.get(
258                                    filterName);
259    
260                            if (filterMappings == null) {
261                                    continue;
262                            }
263    
264                            for (FilterMapping filterMapping : filterMappings) {
265                                    if (filterMapping.isMatch(request, dispatcher, uri)) {
266                                            invokerFilterChain.addFilter(filterMapping.getFilter());
267                                    }
268                            }
269                    }
270    
271                    return invokerFilterChain;
272            }
273    
274            protected Filter initFilter(
275                    ServletContext servletContext, String filterClassName,
276                    FilterConfig filterConfig) {
277    
278                    ClassLoader pluginClassLoader =
279                            (ClassLoader)servletContext.getAttribute(
280                                    PluginContextListener.PLUGIN_CLASS_LOADER);
281    
282                    Thread currentThread = Thread.currentThread();
283    
284                    ClassLoader contextClassLoader = currentThread.getContextClassLoader();
285    
286                    if (pluginClassLoader == null) {
287                            pluginClassLoader = contextClassLoader;
288                    }
289    
290                    ClassLoader portalClassLoader = PortalClassLoaderUtil.getClassLoader();
291    
292                    if (portalClassLoader != pluginClassLoader) {
293                            pluginClassLoader = AggregateClassLoader.getAggregateClassLoader(
294                                    portalClassLoader, pluginClassLoader);
295                    }
296    
297                    if (contextClassLoader != pluginClassLoader) {
298                            currentThread.setContextClassLoader(pluginClassLoader);
299                    }
300    
301                    try {
302                            Filter filter = (Filter)InstanceFactory.newInstance(
303                                    pluginClassLoader, filterClassName);
304    
305                            filter.init(filterConfig);
306    
307                            return filter;
308                    }
309                    catch (Exception e) {
310                            _log.error("Unable to initialize filter " + filterClassName, e);
311    
312                            return null;
313                    }
314                    finally {
315                            if (contextClassLoader != pluginClassLoader) {
316                                    currentThread.setContextClassLoader(contextClassLoader);
317                            }
318                    }
319            }
320    
321            /**
322             * @deprecated As of 7.0.0, replaced by {@link
323             *             #initFilter(ServletContext, String, FilterConfig)}
324             */
325            @Deprecated
326            protected Filter initFilter(
327                    ServletContext servletContext, String filterClassName,
328                    String filterName, FilterConfig filterConfig) {
329    
330                    return initFilter(servletContext, filterClassName, filterConfig);
331            }
332    
333            protected void readLiferayFilterWebXML(
334                            ServletContext servletContext, String path)
335                    throws Exception {
336    
337                    InputStream inputStream = servletContext.getResourceAsStream(path);
338    
339                    if (inputStream == null) {
340                            return;
341                    }
342    
343                    Document document = UnsecureSAXReaderUtil.read(inputStream, true);
344    
345                    Element rootElement = document.getRootElement();
346    
347                    Map<String, ObjectValuePair<Filter, FilterConfig>>
348                            filterObjectValuePairs = new HashMap<>();
349    
350                    for (Element filterElement : rootElement.elements("filter")) {
351                            String filterName = filterElement.elementText("filter-name");
352                            String filterClassName = filterElement.elementText("filter-class");
353    
354                            Map<String, String> initParameterMap = new HashMap<>();
355    
356                            List<Element> initParamElements = filterElement.elements(
357                                    "init-param");
358    
359                            for (Element initParamElement : initParamElements) {
360                                    String name = initParamElement.elementText("param-name");
361                                    String value = initParamElement.elementText("param-value");
362    
363                                    initParameterMap.put(name, value);
364                            }
365    
366                            FilterConfig filterConfig = new InvokerFilterConfig(
367                                    servletContext, filterName, initParameterMap);
368    
369                            Filter filter = initFilter(
370                                    servletContext, filterClassName, filterConfig);
371    
372                            if (filter != null) {
373                                    filterObjectValuePairs.put(
374                                            filterName, new ObjectValuePair<>(filter, filterConfig));
375                            }
376                    }
377    
378                    List<Element> filterMappingElements = rootElement.elements(
379                            "filter-mapping");
380    
381                    for (Element filterMappingElement : filterMappingElements) {
382                            String filterName = filterMappingElement.elementText("filter-name");
383    
384                            List<String> urlPatterns = new ArrayList<>();
385    
386                            List<Element> urlPatternElements = filterMappingElement.elements(
387                                    "url-pattern");
388    
389                            for (Element urlPatternElement : urlPatternElements) {
390                                    urlPatterns.add(urlPatternElement.getTextTrim());
391                            }
392    
393                            List<String> dispatchers = new ArrayList<>(4);
394    
395                            List<Element> dispatcherElements = filterMappingElement.elements(
396                                    "dispatcher");
397    
398                            for (Element dispatcherElement : dispatcherElements) {
399                                    String dispatcher = StringUtil.toUpperCase(
400                                            dispatcherElement.getTextTrim());
401    
402                                    dispatchers.add(dispatcher);
403                            }
404    
405                            ObjectValuePair<Filter, FilterConfig> filterObjectValuePair =
406                                    filterObjectValuePairs.get(filterName);
407    
408                            if (filterObjectValuePair == null) {
409                                    if (_log.isWarnEnabled()) {
410                                            _log.warn(
411                                                    "No filter and filter config for filter name " +
412                                                            filterName);
413                                    }
414    
415                                    continue;
416                            }
417    
418                            FilterMapping filterMapping = new FilterMapping(
419                                    filterName, filterObjectValuePair.getKey(),
420                                    filterObjectValuePair.getValue(), urlPatterns, dispatchers);
421    
422                            registerFilterMapping(filterMapping, null, true);
423                    }
424            }
425    
426            private static final Log _log = LogFactoryUtil.getLog(
427                    InvokerFilterHelper.class);
428    
429            private final ConcurrentMap<String, List<FilterMapping>>
430                    _filterMappingsMap = new ConcurrentHashMap<>();
431            private final List<String> _filterNames = new CopyOnWriteArrayList<>();
432            private final List<InvokerFilter> _invokerFilters = new ArrayList<>();
433            private ServiceTracker<Filter, FilterMapping> _serviceTracker;
434    
435            private class FilterServiceTrackerCustomizer
436                    implements ServiceTrackerCustomizer<Filter, FilterMapping> {
437    
438                    @Override
439                    public FilterMapping addingService(
440                            ServiceReference<Filter> serviceReference) {
441    
442                            Registry registry = RegistryUtil.getRegistry();
443    
444                            Filter filter = registry.getService(serviceReference);
445    
446                            String afterFilter = GetterUtil.getString(
447                                    serviceReference.getProperty("after-filter"));
448                            String beforeFilter = GetterUtil.getString(
449                                    serviceReference.getProperty("before-filter"));
450                            List<String> dispatchers = StringPlus.asList(
451                                    serviceReference.getProperty("dispatcher"));
452                            String servletContextName = GetterUtil.getString(
453                                    serviceReference.getProperty("servlet-context-name"));
454                            String servletFilterName = GetterUtil.getString(
455                                    serviceReference.getProperty("servlet-filter-name"));
456                            List<String> urlPatterns = StringPlus.asList(
457                                    serviceReference.getProperty("url-pattern"));
458    
459                            String positionFilterName = beforeFilter;
460                            boolean after = false;
461    
462                            if (Validator.isNotNull(afterFilter)) {
463                                    positionFilterName = afterFilter;
464                                    after = true;
465                            }
466    
467                            Map<String, String> initParameterMap = new HashMap<>();
468    
469                            Map<String, Object> properties = serviceReference.getProperties();
470    
471                            for (String key : properties.keySet()) {
472                                    if (!key.startsWith("init.param.")) {
473                                            continue;
474                                    }
475    
476                                    String value = GetterUtil.getString(
477                                            serviceReference.getProperty(key));
478    
479                                    initParameterMap.put(key, value);
480                            }
481    
482                            ServletContext servletContext = ServletContextPool.get(
483                                    servletContextName);
484    
485                            FilterConfig filterConfig = new InvokerFilterConfig(
486                                    servletContext, servletFilterName, initParameterMap);
487    
488                            try {
489                                    filter.init(filterConfig);
490                            }
491                            catch (ServletException se) {
492                                    _log.error(se, se);
493    
494                                    registry.ungetService(serviceReference);
495    
496                                    return null;
497                            }
498    
499                            updateFilterMappings(servletFilterName, filter);
500    
501                            FilterMapping filterMapping = new FilterMapping(
502                                    servletFilterName, filter, filterConfig, urlPatterns,
503                                    dispatchers);
504    
505                            registerFilterMapping(filterMapping, positionFilterName, after);
506    
507                            clearFilterChainsCache();
508    
509                            return filterMapping;
510                    }
511    
512                    @Override
513                    public void modifiedService(
514                            ServiceReference<Filter> serviceReference,
515                            FilterMapping filterMapping) {
516    
517                            removedService(serviceReference, filterMapping);
518    
519                            addingService(serviceReference);
520                    }
521    
522                    @Override
523                    public void removedService(
524                            ServiceReference<Filter> serviceReference,
525                            FilterMapping filterMapping) {
526    
527                            Registry registry = RegistryUtil.getRegistry();
528    
529                            registry.ungetService(serviceReference);
530    
531                            unregisterFilterMappings(
532                                    GetterUtil.getString(
533                                            serviceReference.getProperty("servlet-filter-name")));
534                    }
535    
536            }
537    
538    }