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, boolean after) {
354    
355                    Filter filter = getFilter(filterName);
356    
357                    FilterConfig filterConfig = _filterConfigs.get(filterName);
358    
359                    if (filterConfig == null) {
360                            filterConfig = getFilterConfig(filterName);
361                    }
362    
363                    if (filter == null) {
364                            if (_log.isWarnEnabled()) {
365                                    _log.warn("No filter exists with filter mapping " + filterName);
366                            }
367    
368                            return;
369                    }
370    
371                    FilterMapping filterMapping = new FilterMapping(
372                            filter, filterConfig, urlPatterns, dispatchers);
373    
374                    registerFilterMapping(filterMapping, positionFilterName, after);
375            }
376    
377            private static Log _log = LogFactoryUtil.getLog(InvokerFilterHelper.class);
378    
379            private Map<String, FilterConfig> _filterConfigs =
380                    new HashMap<String, FilterConfig>();
381            private List<FilterMapping> _filterMappings =
382                    new CopyOnWriteArrayList<FilterMapping>();
383            private Map<String, Filter> _filters = new HashMap<String, Filter>();
384            private List<InvokerFilter> _invokerFilters =
385                    new ArrayList<InvokerFilter>();
386            private ServiceTracker<Filter, FilterMapping> _serviceTracker;
387    
388            private class FilterServiceTrackerCustomizer
389                    implements ServiceTrackerCustomizer<Filter, FilterMapping> {
390    
391                    @Override
392                    public FilterMapping addingService(
393                            ServiceReference<Filter> serviceReference) {
394    
395                            Registry registry = RegistryUtil.getRegistry();
396    
397                            Filter filter = registry.getService(serviceReference);
398    
399                            String afterFilter = GetterUtil.getString(
400                                    serviceReference.getProperty("after-filter"));
401                            String beforeFilter = GetterUtil.getString(
402                                    serviceReference.getProperty("before-filter"));
403                            List<String> dispatchers = StringPlus.asList(
404                                    serviceReference.getProperty("dispatcher"));
405                            String servletContextName = GetterUtil.getString(
406                                    serviceReference.getProperty("servlet-context-name"),
407                                    StringPool.BLANK);
408                            String servletFilterName = GetterUtil.getString(
409                                    serviceReference.getProperty("servlet-filter-name"));
410                            List<String> urlPatterns = StringPlus.asList(
411                                    serviceReference.getProperty("url-pattern"));
412    
413                            String positionFilterName = beforeFilter;
414                            boolean after = false;
415    
416                            if (Validator.isNotNull(afterFilter)) {
417                                    positionFilterName = afterFilter;
418                                    after = true;
419                            }
420    
421                            Map<String, String> initParameterMap =
422                                    new HashMap<String, String>();
423    
424                            Map<String, Object> properties = serviceReference.getProperties();
425    
426                            for (String key : properties.keySet()) {
427                                    if (!key.startsWith("init.param.")) {
428                                            continue;
429                                    }
430    
431                                    String value = GetterUtil.getString(
432                                            serviceReference.getProperty(key));
433    
434                                    initParameterMap.put(key, value);
435                            }
436    
437                            ServletContext servletContext = ServletContextPool.get(
438                                    servletContextName);
439    
440                            FilterConfig filterConfig = new InvokerFilterConfig(
441                                    servletContext, servletFilterName, initParameterMap);
442    
443                            try {
444                                    filter.init(filterConfig);
445                            }
446                            catch (ServletException se) {
447                                    _log.error(se, se);
448    
449                                    registry.ungetService(serviceReference);
450    
451                                    return null;
452                            }
453    
454                            _filterConfigs.put(servletFilterName, filterConfig);
455    
456                            registerFilter(servletFilterName, filter);
457    
458                            FilterMapping filterMapping = new FilterMapping(
459                                    filter, filterConfig, urlPatterns, dispatchers);
460    
461                            registerFilterMapping(filterMapping, positionFilterName, after);
462    
463                            return filterMapping;
464                    }
465    
466                    @Override
467                    public void modifiedService(
468                            ServiceReference<Filter> serviceReference,
469                            FilterMapping filterMapping) {
470    
471                            removedService(serviceReference, filterMapping);
472    
473                            addingService(serviceReference);
474                    }
475    
476                    @Override
477                    public void removedService(
478                            ServiceReference<Filter> serviceReference,
479                            FilterMapping filterMapping) {
480    
481                            Registry registry = RegistryUtil.getRegistry();
482    
483                            registry.ungetService(serviceReference);
484    
485                            String servletFilterName = GetterUtil.getString(
486                                    serviceReference.getProperty("servlet-filter-name"));
487    
488                            unregisterFilterMapping(filterMapping);
489    
490                            _filterConfigs.remove(servletFilterName);
491    
492                            Filter filter = _filters.remove(servletFilterName);
493    
494                            if (filter == null) {
495                                    return;
496                            }
497    
498                            try {
499                                    filter.destroy();
500                            }
501                            catch (Exception e) {
502                                    _log.error(e, e);
503                            }
504                    }
505    
506            }
507    
508    }