SLING-8999 : Improve Sling filter handling
diff --git a/src/main/java/org/apache/sling/engine/impl/filter/FilterHandle.java b/src/main/java/org/apache/sling/engine/impl/filter/FilterHandle.java
index 7e6db26..4e281b1 100644
--- a/src/main/java/org/apache/sling/engine/impl/filter/FilterHandle.java
+++ b/src/main/java/org/apache/sling/engine/impl/filter/FilterHandle.java
@@ -28,7 +28,7 @@
private final Filter filter;
- private final Long filterId;
+ private final long filterId;
private final int order;
@@ -39,10 +39,11 @@
private AtomicLong time;
private FilterPredicate predicate;
-
+
FilterProcessorMBeanImpl mbean;
- FilterHandle(Filter filter, FilterPredicate predicate, Long filterId, int order, final String orderSource, FilterProcessorMBeanImpl mbean) {
+ FilterHandle(Filter filter, FilterPredicate predicate, long filterId, int order, final String orderSource,
+ FilterProcessorMBeanImpl mbean) {
this.filter = filter;
this.predicate = predicate;
this.filterId = filterId;
@@ -57,7 +58,7 @@
return filter;
}
- public Long getFilterId() {
+ public long getFilterId() {
return filterId;
}
@@ -103,6 +104,7 @@
* Note: this class has a natural ordering that is inconsistent with
* equals.
*/
+ @Override
public int compareTo(FilterHandle other) {
if (this == other || equals(other)) {
return 0;
@@ -113,17 +115,7 @@
} else if (order < other.order) {
return 1;
}
-
- // if the filterId is comparable and the other is of the same class
- if (filterId != null && other.filterId != null) {
- int comp = filterId.compareTo(other.filterId);
- if (comp != 0) {
- return comp;
- }
- }
-
- // consider equal ranking
- return 0;
+ return (this.filterId < other.filterId) ? -1 : ((this.filterId == other.filterId) ? 0 : 1);
}
@Override
@@ -134,6 +126,7 @@
return filter.hashCode();
}
+ @Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
diff --git a/src/main/java/org/apache/sling/engine/impl/filter/ServletFilterManager.java b/src/main/java/org/apache/sling/engine/impl/filter/ServletFilterManager.java
index 82b847c..53ef9f2 100644
--- a/src/main/java/org/apache/sling/engine/impl/filter/ServletFilterManager.java
+++ b/src/main/java/org/apache/sling/engine/impl/filter/ServletFilterManager.java
@@ -19,9 +19,9 @@
package org.apache.sling.engine.impl.filter;
import java.util.Dictionary;
-import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
@@ -34,6 +34,8 @@
import org.apache.sling.engine.jmx.FilterProcessorMBean;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.ServiceTracker;
@@ -42,6 +44,8 @@
public class ServletFilterManager extends ServiceTracker<Filter, Filter> {
+ private static final String JMX_OBJECTNAME = "jmx.objectname";
+
public static enum FilterChainType {
/**
* Indicates request level filters.
@@ -97,19 +101,28 @@
private final SlingFilterChainHelper[] filterChains;
- private Map <Long, ServiceRegistration<FilterProcessorMBean>> mbeanMap;
+ private final Map<Long, MBeanReg> mbeanMap = new ConcurrentHashMap<>();
+
+ private static final org.osgi.framework.Filter SERVICE_FILTER;
+ static {
+ org.osgi.framework.Filter f = null;
+ try {
+ f = FrameworkUtil.createFilter("(&(" + Constants.OBJECTCLASS + "=" + Filter.class.getName()
+ + ")(|(" + EngineConstants.SLING_FILTER_SCOPE + "=*)(" + EngineConstants.FILTER_SCOPE + "=*)))");
+ } catch (InvalidSyntaxException e) {
+ // we ignore this here as the above is a constant expression
+ }
+ SERVICE_FILTER = f;
+ }
public ServletFilterManager(final BundleContext context,
final SlingServletContext servletContext) {
- super(context, Filter.class, null);
+ super(context, SERVICE_FILTER, 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>>();
+ for (final FilterChainType type : FilterChainType.values()) {
+ this.filterChains[type.ordinal()] = new SlingFilterChainHelper();
+ }
}
public SlingFilterChainHelper getFilterChain(final FilterChainType chain) {
@@ -122,9 +135,6 @@
@Override
public Filter addingService(ServiceReference<Filter> reference) {
- if ( this.excludeFilter(reference) ) {
- return null;
- }
Filter service = super.addingService(reference);
if (service != null) {
initFilter(reference, service);
@@ -134,8 +144,13 @@
@Override
public void modifiedService(ServiceReference<Filter> reference, Filter service) {
- destroyFilter(reference, service);
- if ( !this.excludeFilter(reference) ) {
+ // only if the filter name has changed, we need to do a service re-registration
+ final String newFilterName = SlingFilterConfig.getName(reference);
+ if (newFilterName.equals(getUsedFilterName(reference))) {
+ removeFilterFromChains((Long) reference.getProperty(Constants.SERVICE_ID));
+ addFilterToChains(service, reference);
+ } else {
+ destroyFilter(reference, service);
initFilter(reference, service);
}
}
@@ -148,140 +163,68 @@
}
}
- /**
- * 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);
+ final Long serviceId = (Long) reference.getProperty(Constants.SERVICE_ID);
- // initialize the filter first
try {
- // service id
- final Long serviceId = (Long) reference.getProperty(Constants.SERVICE_ID);
- FilterProcessorMBeanImpl mbean;
+ MBeanReg reg;
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);
+ mbeanProps.put(JMX_OBJECTNAME, "org.apache.sling:type=engine-filter,service=" + filterName);
+ reg = new MBeanReg();
+ reg.mbean = new FilterProcessorMBeanImpl();
+
+ reg.registration = reference.getBundle().getBundleContext().registerService(FilterProcessorMBean.class,
+ reg.mbean, mbeanProps);
+
+ mbeanMap.put(serviceId, reg);
} catch (Throwable t) {
log.debug("Unable to register mbean", t);
- mbean = null;
+ reg = null;
}
- final FilterConfig config = new SlingFilterConfig(
- servletContext, reference, filterName);
+ // initialize the filter first
+ 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);
-
- FilterPredicate predicate = new FilterPredicate(reference);
-
- 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, predicate, serviceId,
- order, orderSource, mbean);
-
- if (type == FilterChainType.COMPONENT) {
- getFilterChain(FilterChainType.INCLUDE).addFilter(
- filter, predicate, serviceId, order, orderSource, mbean);
- getFilterChain(FilterChainType.FORWARD).addFilter(
- filter, predicate, 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, predicate,
- serviceId, order, orderSource,mbean);
- }
-
+ // add to chains
+ addFilterToChains(filter, reference);
} catch (ServletException ce) {
log.error("Filter " + filterName + " failed to initialize", ce);
} catch (Throwable t) {
- log.error("Unexpected Problem initializing ComponentFilter "
- + "", t);
+ log.error("Unexpected problem initializing filter " + filterName, t);
}
}
+ private String getUsedFilterName(final ServiceReference<Filter> reference) {
+ final MBeanReg reg = mbeanMap.get(reference.getProperty(Constants.SERVICE_ID));
+ if (reg != null) {
+ final String objectName = (String) reg.registration.getReference().getProperty(JMX_OBJECTNAME);
+ if (objectName != null) {
+ final int pos = objectName.indexOf(",service=");
+ if (pos != -1) {
+ return objectName.substring(pos + 9);
+ }
+ }
+ }
+ return null;
+ }
+
private void destroyFilter(final ServiceReference<Filter> reference,
final Filter filter) {
// service id
- Object serviceId = reference.getProperty(Constants.SERVICE_ID);
+ Long serviceId = (Long) 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);
+ final MBeanReg reg = mbeanMap.remove(serviceId);
+ if (reg != null) {
+ reg.registration.unregister();
}
// destroy it
- if (removed) {
+ if (removeFilterFromChains(serviceId)) {
try {
filter.destroy();
} catch (Throwable t) {
@@ -289,4 +232,83 @@
}
}
}
+
+ @SuppressWarnings("deprecation")
+ private void addFilterToChains(final Filter filter, final ServiceReference<Filter> reference) {
+ final Long serviceId = (Long) reference.getProperty(Constants.SERVICE_ID);
+ final MBeanReg mbeanReg = mbeanMap.get(serviceId);
+ final FilterProcessorMBeanImpl mbean = mbeanReg == null ? null : mbeanReg.mbean;
+
+ // 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);
+
+ FilterPredicate predicate = new FilterPredicate(reference);
+
+ 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, predicate, serviceId, order, orderSource, mbean);
+
+ if (type == FilterChainType.COMPONENT) {
+ getFilterChain(FilterChainType.INCLUDE).addFilter(filter, predicate, serviceId, order,
+ orderSource, mbean);
+ getFilterChain(FilterChainType.FORWARD).addFilter(filter, predicate, serviceId, order,
+ orderSource, mbean);
+ }
+
+ } catch (IllegalArgumentException iae) {
+ // TODO: log ...
+ }
+ }
+ } else {
+ log.warn(String.format("A Filter (Service ID %s) has been registered without a %s property.", serviceId,
+ EngineConstants.SLING_FILTER_SCOPE));
+ getFilterChain(FilterChainType.REQUEST).addFilter(filter, predicate, serviceId, order, orderSource, mbean);
+ }
+
+ }
+
+ private boolean removeFilterFromChains(final Long serviceId) {
+ boolean removed = false;
+ for (SlingFilterChainHelper filterChain : filterChains) {
+ removed |= filterChain.removeFilterById(serviceId);
+ }
+ return removed;
+ }
+
+ private static final class MBeanReg {
+ FilterProcessorMBeanImpl mbean;
+ ServiceRegistration<FilterProcessorMBean> registration;
+ }
}
diff --git a/src/main/java/org/apache/sling/engine/impl/filter/SlingFilterChainHelper.java b/src/main/java/org/apache/sling/engine/impl/filter/SlingFilterChainHelper.java
index 01bc9ed..9402b38 100644
--- a/src/main/java/org/apache/sling/engine/impl/filter/SlingFilterChainHelper.java
+++ b/src/main/java/org/apache/sling/engine/impl/filter/SlingFilterChainHelper.java
@@ -43,7 +43,7 @@
}
public synchronized Filter addFilter(final Filter filter, FilterPredicate pattern,
- final Long filterId, final int order, final String orderSource, FilterProcessorMBeanImpl mbean) {
+ final long filterId, final int order, final String orderSource, FilterProcessorMBeanImpl mbean) {
if (filterList == null) {
filterList = new TreeSet<FilterHandle>();
}
@@ -52,13 +52,11 @@
return filter;
}
- public synchronized boolean removeFilterById(final Object filterId) {
+ public synchronized boolean removeFilterById(final long filterId) {
if (filterList != null) {
for (Iterator<FilterHandle> fi = filterList.iterator(); fi.hasNext();) {
FilterHandle test = fi.next();
- if (test.getFilterId() == filterId
- || (test.getFilterId() != null && test.getFilterId().equals(
- filterId))) {
+ if (test.getFilterId() == filterId) {
fi.remove();
filters = getFiltersInternal();
return true;
diff --git a/src/main/java/org/apache/sling/engine/impl/helper/SlingFilterConfig.java b/src/main/java/org/apache/sling/engine/impl/helper/SlingFilterConfig.java
index 2dccbed..66e41e7 100644
--- a/src/main/java/org/apache/sling/engine/impl/helper/SlingFilterConfig.java
+++ b/src/main/java/org/apache/sling/engine/impl/helper/SlingFilterConfig.java
@@ -27,6 +27,7 @@
import java.util.Enumeration;
import java.util.List;
+import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
@@ -42,7 +43,7 @@
private ServletContext servletContext;
/** The <code>ServiceReference</code> providing the properties */
- private ServiceReference reference;
+ private ServiceReference<Filter> reference;
/** The name of this configuration object */
private String name;
@@ -58,7 +59,7 @@
* @param filterName The name of this configuration.
*/
public SlingFilterConfig(final ServletContext servletContext,
- final ServiceReference reference,
+ final ServiceReference<Filter> reference,
final String filterName) {
this.servletContext = servletContext;
this.reference = reference;
@@ -68,6 +69,7 @@
/**
* @see javax.servlet.FilterConfig#getInitParameter(java.lang.String)
*/
+ @Override
public String getInitParameter(String name) {
Object prop = reference.getProperty(name);
return (prop == null) ? null : String.valueOf(prop);
@@ -76,6 +78,7 @@
/**
* @see javax.servlet.FilterConfig#getInitParameterNames()
*/
+ @Override
public Enumeration<String> getInitParameterNames() {
List<String> keys = Arrays.asList(reference.getPropertyKeys());
return Collections.enumeration(keys);
@@ -84,16 +87,18 @@
/**
* @see javax.servlet.FilterConfig#getServletContext()
*/
+ @Override
public ServletContext getServletContext() {
return servletContext;
}
/**
- * Looks for a name value in the service reference properties. See the
- * class comment at the top for the list of properties checked by this
- * method.
+ * Looks for a name value in the service reference properties. See the class
+ * comment at the top for the list of properties checked by this method. As the
+ * service id is part of the checked property list this method always returns a
+ * name.
*/
- public static String getName(ServiceReference reference) {
+ public static String getName(ServiceReference<Filter> reference) {
String servletName = null;
for (int i = 0; i < NAME_PROPERTIES.length
&& (servletName == null || servletName.length() == 0); i++) {
@@ -108,6 +113,7 @@
/**
* @see javax.servlet.FilterConfig#getFilterName()
*/
+ @Override
public String getFilterName() {
return name;
}