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