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