/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.sling.engine.impl.filter; import java.util.Dictionary; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import javax.servlet.Filter; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import org.apache.sling.commons.osgi.OsgiUtil; import org.apache.sling.engine.EngineConstants; import org.apache.sling.engine.impl.helper.SlingFilterConfig; import org.apache.sling.engine.impl.helper.SlingServletContext; import org.apache.sling.engine.jmx.FilterProcessorMBean; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; import org.osgi.util.tracker.ServiceTracker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ServletFilterManager extends ServiceTracker<Filter, Filter> { public static enum FilterChainType { /** * Indicates request level filters. * * @see EngineConstants#FILTER_SCOPE_REQUEST */ REQUEST("Request"), /** * Indicates error level filters. * * @see EngineConstants#FILTER_SCOPE_ERROR */ ERROR("Error"), /** * Indicates include level filters. * * @see EngineConstants#FILTER_SCOPE_INCLUDE */ INCLUDE("Include"), /** * Indicates forward level filters. * * @see EngineConstants#FILTER_SCOPE_FORWARD */ FORWARD("Forward"), /** * Indicates component level filters. * * @see EngineConstants#FILTER_SCOPE_COMPONENT */ COMPONENT("Component"); private final String message; private FilterChainType(final String message) { this.message = message; } @Override public String toString() { return message; } } /** default log */ private final Logger log = LoggerFactory.getLogger(getClass()); private final SlingServletContext servletContext; private final SlingFilterChainHelper[] filterChains; private Map <Long, ServiceRegistration<FilterProcessorMBean>> mbeanMap; public ServletFilterManager(final BundleContext context, final SlingServletContext servletContext) { super(context, Filter.class, null); this.servletContext = servletContext; this.filterChains = new SlingFilterChainHelper[FilterChainType.values().length]; this.filterChains[FilterChainType.REQUEST.ordinal()] = new SlingFilterChainHelper(); this.filterChains[FilterChainType.ERROR.ordinal()] = new SlingFilterChainHelper(); this.filterChains[FilterChainType.INCLUDE.ordinal()] = new SlingFilterChainHelper(); this.filterChains[FilterChainType.FORWARD.ordinal()] = new SlingFilterChainHelper(); this.filterChains[FilterChainType.COMPONENT.ordinal()] = new SlingFilterChainHelper(); this.mbeanMap = new HashMap<Long, ServiceRegistration<FilterProcessorMBean>>(); } public SlingFilterChainHelper getFilterChain(final FilterChainType chain) { return filterChains[chain.ordinal()]; } public FilterHandle[] getFilters(final FilterChainType chain) { return getFilterChain(chain).getFilters(); } @Override public Filter addingService(ServiceReference<Filter> reference) { if ( this.excludeFilter(reference) ) { return null; } Filter service = super.addingService(reference); if (service != null) { initFilter(reference, service); } return service; } @Override public void modifiedService(ServiceReference<Filter> reference, Filter service) { destroyFilter(reference, service); if ( !this.excludeFilter(reference) ) { initFilter(reference, service); } } @Override public void removedService(ServiceReference<Filter> reference, Filter service) { if ( service != null ) { destroyFilter(reference, service); super.removedService(reference, service); } } /** * Check if the filter should be excluded. */ private boolean excludeFilter(final ServiceReference<Filter> reference) { boolean exclude = true; // if the service has a filter scope property, we include it if ( reference.getProperty(EngineConstants.SLING_FILTER_SCOPE) != null || reference.getProperty(EngineConstants.FILTER_SCOPE) != null ) { exclude = false; } if ( !exclude ) { final String filterName = SlingFilterConfig.getName(reference); if (filterName == null) { log.error("initFilter: Missing name for filter {}", reference); exclude = true; } } return exclude; } private void initFilter(final ServiceReference<Filter> reference, final Filter filter) { // we already checked name in excludeFilter() final String filterName = SlingFilterConfig.getName(reference); // initialize the filter first try { // service id final Long serviceId = (Long) reference.getProperty(Constants.SERVICE_ID); FilterProcessorMBeanImpl mbean; try { final Dictionary<String, String> mbeanProps = new Hashtable<String, String>(); mbeanProps.put("jmx.objectname", "org.apache.sling:type=engine-filter,service="+filterName); mbean = new FilterProcessorMBeanImpl(); ServiceRegistration<FilterProcessorMBean> filterProcessorMBeanRegistration = context.registerService(FilterProcessorMBean.class, mbean, mbeanProps); mbeanMap.put(serviceId,filterProcessorMBeanRegistration); } catch (Throwable t) { log.debug("Unable to register mbean", t); mbean = null; } final FilterConfig config = new SlingFilterConfig( servletContext, reference, filterName); filter.init(config); // get the order, Integer.MAX_VALUE by default final String orderSource; Object orderObj = reference.getProperty(Constants.SERVICE_RANKING); if (orderObj == null) { // filter order is defined as lower value has higher // priority while service ranking is the opposite In // addition we allow different types than Integer orderObj = reference.getProperty(EngineConstants.FILTER_ORDER); if (orderObj != null) { log.warn("Filter service {} is using deprecated property {}. Use {} instead.", new Object[] {reference, EngineConstants.FILTER_ORDER, Constants.SERVICE_RANKING}); // we can use 0 as the default as this will be applied // in the next step anyway if this props contains an // invalid value orderSource = EngineConstants.FILTER_ORDER + "=" + orderObj; orderObj = Integer.valueOf(-1 * OsgiUtil.toInteger(orderObj, 0)); } else { orderSource = "none"; } } else { orderSource = Constants.SERVICE_RANKING + "=" + orderObj; } final int order = (orderObj instanceof Integer) ? ((Integer) orderObj).intValue() : 0; // register by scope String[] scopes = OsgiUtil.toStringArray( reference.getProperty(EngineConstants.SLING_FILTER_SCOPE), null); String pattern = OsgiUtil.toString(reference.getProperty(EngineConstants.SLING_FILTER_PATTERN), ""); if ( scopes == null ) { scopes = OsgiUtil.toStringArray( reference.getProperty(EngineConstants.FILTER_SCOPE), null); log.warn("Filter service {} is using deprecated property {}. Use {} instead.", new Object[] {reference, EngineConstants.FILTER_SCOPE, EngineConstants.SLING_FILTER_SCOPE}); } if (scopes != null && scopes.length > 0) { for (String scope : scopes) { scope = scope.toUpperCase(); try { FilterChainType type = FilterChainType.valueOf(scope.toString()); getFilterChain(type).addFilter(filter, pattern, serviceId, order, orderSource, mbean); if (type == FilterChainType.COMPONENT) { getFilterChain(FilterChainType.INCLUDE).addFilter( filter, pattern, serviceId, order, orderSource, mbean); getFilterChain(FilterChainType.FORWARD).addFilter( filter, pattern, serviceId, order, orderSource, mbean); } } catch (IllegalArgumentException iae) { // TODO: log ... } } } else { log.warn(String.format( "A Filter (Service ID %s) has been registered without a filter.scope property.", reference.getProperty(Constants.SERVICE_ID))); getFilterChain(FilterChainType.REQUEST).addFilter(filter, pattern, serviceId, order, orderSource,mbean); } } catch (ServletException ce) { log.error("Filter " + filterName + " failed to initialize", ce); } catch (Throwable t) { log.error("Unexpected Problem initializing ComponentFilter " + "", t); } } private void destroyFilter(final ServiceReference<Filter> reference, final Filter filter) { // service id Object serviceId = reference.getProperty(Constants.SERVICE_ID); ServiceRegistration<FilterProcessorMBean> mbean = mbeanMap.remove(serviceId); if (mbean != null) { mbean.unregister(); } boolean removed = false; for (SlingFilterChainHelper filterChain : filterChains) { removed |= filterChain.removeFilterById(serviceId); } // destroy it if (removed) { try { filter.destroy(); } catch (Throwable t) { log.error("Unexpected problem destroying Filter {}", filter, t); } } } }