FELIX-4541 - Applied patch from Raluca:
- add integration tests for registrations with multiple patterns;
- implement support for registration of patterns using service rankings;
- some dings and dents fixed in the code.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1665462 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java b/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java
index 79a1958..1c1dbfe 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java
@@ -47,7 +47,7 @@
public HttpServiceController(final BundleContext bundleContext)
{
this.bundleContext = bundleContext;
- this.registry = new HandlerRegistry();
+ this.registry = new HandlerRegistry(this.bundleContext);
this.dispatcher = new Dispatcher(this.registry);
this.plugin = new HttpServicePlugin(bundleContext, registry);
this.httpServiceFactory = new HttpServiceFactory(this.bundleContext, this.registry);
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerMapping.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerMapping.java
index 94e025f..714e63d 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerMapping.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerMapping.java
@@ -18,175 +18,185 @@
*/
package org.apache.felix.http.base.internal.handler;
+import static java.util.Collections.unmodifiableCollection;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
+import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import org.apache.felix.http.base.internal.util.PatternUtil;
+
/**
* Represents a Map-like structure that can map path-patterns to servlet/filter handlers, allowing
* for easy access to those handlers, based on the match rules defined in section 12.1 of Servlet
* 3.0 specification.
- *
+ * <p>
+ * {@link HandlerMapping} instances are immutable.
+ *
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
-public class HandlerMapping<V extends AbstractHandler>
+final class HandlerMapping<V extends AbstractHandler<V>>
{
- /**
- * Compares {@link Pattern}s based on a set of simple rules:
- * <ol>
- * <li>exact matches go first;</li>
- * <li>followed by wildcard path matches;</li>
- * <li>lastly all wildcard extension matches.</li>
- * </ol>
- * <p>
- * Equal matches will first be sorted on length in descending order (longest patterns first),
- * and in case of equal lengths, they are sorted in natural (ascending) order.
- * </p>
- */
- static class PatternComparator implements Comparator<Pattern>
- {
- @Override
- public int compare(Pattern p1, Pattern p2)
- {
- String ps1 = p1.pattern();
- String ps2 = p2.pattern();
-
- // Sorts wildcard path matches before wildcard extension matches...
- int r;
- if (isWildcardPath(ps1))
- {
- if (isWildcardPath(ps2))
- {
- // Descending on length...
- r = ps2.length() - ps1.length();
- }
- else
- {
- // Exact matches go first...
- r = isWildcardExtension(ps2) ? -1 : 1;
- }
- }
- else if (isWildcardExtension(ps1))
- {
- if (isWildcardExtension(ps2))
- {
- // Descending on length...
- r = ps2.length() - ps1.length();
- }
- else
- {
- // Wildcard paths & exact matches go first...
- r = 1;
- }
- }
- else
- {
- if (isWildcardExtension(ps2) || isWildcardPath(ps2))
- {
- // Exact matches go first...
- r = -1;
- }
- else
- {
- // Descending on length...
- r = ps2.length() - ps1.length();
- }
- }
-
- if (r == 0)
- {
- // In case of a draw, ensure we sort in a predictable (ascending) order...
- r = ps1.compareTo(ps2);
- }
-
- return r;
- }
-
- private boolean isWildcardExtension(String p)
- {
- return p.startsWith("^(.*");
- }
-
- private boolean isWildcardPath(String p)
- {
- return p.startsWith("^(/");
- }
- }
-
- private final SortedMap<Pattern, List<V>> exactMap;
- private final SortedMap<Pattern, List<V>> wildcardMap;
- private final Set<V> all;
+ private final SortedMap<Pattern, Set<V>> exactMap;
+ private final SortedMap<Pattern, Set<V>> wildcardMap;
+ private final Set<V> mappedHandlers;
/**
* Creates a new, empty, {@link HandlerMapping} instance.
*/
- public HandlerMapping()
+ HandlerMapping()
{
- this(Collections.<V> emptyList());
+ this(Collections.<Pattern, Collection<V>>emptyMap());
}
/**
* Creates a new {@link HandlerMapping} instance for the given elements.
*
- * @param elements the elements to map, cannot be <code>null</code>.
+ * @param mappings the elements to map.
*/
- public HandlerMapping(Collection<V> elements)
+ private HandlerMapping(Map<Pattern, Collection<V>> mappings)
{
- this.exactMap = new TreeMap<Pattern, List<V>>(new PatternComparator());
- this.wildcardMap = new TreeMap<Pattern, List<V>>(new PatternComparator());
- this.all = new HashSet<V>(elements);
+ this.exactMap = new TreeMap<Pattern, Set<V>>(PatternUtil.PatternComparator.INSTANCE);
+ this.wildcardMap = new TreeMap<Pattern, Set<V>>(PatternUtil.PatternComparator.INSTANCE);
+ this.mappedHandlers = new TreeSet<V>();
- for (V element : elements)
+ for (Map.Entry<Pattern, Collection<V>> mapping : mappings.entrySet())
{
- for (Pattern pattern : element.getPatterns())
+ Pattern pattern = mapping.getKey();
+ Collection<V> handlers = mapping.getValue();
+
+ mappedHandlers.addAll(handlers);
+
+ if (PatternUtil.isWildcardPattern(pattern))
{
- if (isWildcardPattern(pattern))
+ Set<V> vs = this.wildcardMap.get(pattern);
+ if (vs == null)
{
- List<V> vs = this.wildcardMap.get(pattern);
- if (vs == null)
- {
- vs = new ArrayList<V>();
- this.wildcardMap.put(pattern, vs);
- }
- if (!vs.contains(element))
- {
- vs.add(element);
- }
+ vs = new TreeSet<V>();
+ this.wildcardMap.put(pattern, vs);
}
- else
+ vs.addAll(handlers);
+ }
+ else
+ {
+ Set<V> vs = this.exactMap.get(pattern);
+ if (vs == null)
{
- List<V> vs = this.exactMap.get(pattern);
- if (vs == null)
- {
- vs = new ArrayList<V>();
- this.exactMap.put(pattern, vs);
- }
- if (!vs.contains(element))
- {
- vs.add(element);
- }
+ vs = new TreeSet<V>();
+ this.exactMap.put(pattern, vs);
}
+ vs.addAll(handlers);
}
}
}
/**
- * Returns all mapped elements.
+ * Returns a new {@link HandlerMapping} instance with a mapping for the
+ * given handler.
*
- * @return a collection of mapped elements, never <code>null</code>.
+ * @param handler the handler to be added to the mapping.
+ * @return a new {@link HandlerMapping} instance with a mapping for the
+ * given handler.
*/
- public Collection<V> getAllElements()
+ HandlerMapping<V> add(V handler)
{
- return this.all;
+ Map<Pattern, V> mappings = new TreeMap<Pattern, V>(PatternUtil.PatternComparator.INSTANCE);
+ for (Pattern pattern : handler.getPatterns())
+ {
+ mappings.put(pattern, handler);
+ }
+ return add(mappings);
+ }
+
+ HandlerMapping<V> add(Map<Pattern, V> mappings)
+ {
+ Map<Pattern, Collection<V>> newMappings = getAllMappings();
+ for (Map.Entry<Pattern, V> mapping : mappings.entrySet())
+ {
+ if (!newMappings.containsKey(mapping.getKey()))
+ {
+ newMappings.put(mapping.getKey(), new TreeSet<V>());
+ }
+ newMappings.get(mapping.getKey()).add(mapping.getValue());
+ }
+ return new HandlerMapping<V>(newMappings);
+ }
+
+ /**
+ * Returns a new {@link HandlerMapping} instance without a mapping for the
+ * given handler.
+ *
+ * @param subject the handled element to be removed from the mapping
+ * @return a new {@link HandlerMapping} instance without a mapping for the
+ * given handler.
+ */
+ HandlerMapping<V> remove(V handler)
+ {
+ Map<Pattern, V> mappings = new TreeMap<Pattern, V>(PatternUtil.PatternComparator.INSTANCE);
+ for (Pattern pattern : handler.getPatterns())
+ {
+ mappings.put(pattern, handler);
+ }
+ return remove(mappings);
+ }
+
+ HandlerMapping<V> remove(Map<Pattern, V> mappings)
+ {
+ Map<Pattern, Collection<V>> newMappings = getAllMappings();
+ for (Map.Entry<Pattern, V> mapping : mappings.entrySet())
+ {
+ Collection<V> mappedHandlers = newMappings.get(mapping.getKey());
+ if (mappedHandlers == null)
+ {
+ continue;
+ }
+ mappedHandlers.remove(mapping.getValue());
+ if (mappedHandlers.isEmpty())
+ {
+ newMappings.remove(mapping.getKey());
+ }
+ }
+ return new HandlerMapping<V>(newMappings);
+ }
+
+ private Map<Pattern, Collection<V>> getAllMappings()
+ {
+ Map<Pattern, Collection<V>> newMappings = new TreeMap<Pattern, Collection<V>>(PatternUtil.PatternComparator.INSTANCE);
+ newMappings.putAll(exactMap);
+ newMappings.putAll(wildcardMap);
+ return newMappings;
+ }
+
+ /**
+ * Returns all mapped handlers.
+ *
+ * @return the handlers contained in this mapping. The returned
+ * <code>Collection</code> is unmodifiable and never
+ * <code>null</code>.
+ */
+ Collection<V> values()
+ {
+ return unmodifiableCollection(mappedHandlers);
+ }
+
+ /**
+ * Returns whether this mapping contains the specified handler.
+ *
+ * @return <code>true</code> if the handlers contains the specified handler,
+ * <code>false</code> otherwise
+ */
+ boolean contains(V handler)
+ {
+ return mappedHandlers.contains(handler);
}
/**
@@ -195,7 +205,7 @@
* @param path the path that should match, cannot be <code>null</code>.
* @return a {@link Collection} of all matching handlers, never <code>null</code>.
*/
- public List<V> getAllMatches(String path)
+ List<V> getAllMatches(String path)
{
return getAllMatches(path, false /* firstOnly */);
}
@@ -213,7 +223,7 @@
* @param path the path that should match, cannot be <code>null</code>.
* @return the best matching handler for the given path, or <code>null</code> in case no handler matched.
*/
- public V getBestMatch(String path)
+ V getBestMatch(String path)
{
List<V> allMatches = getAllMatches(path, true /* firstOnly */);
return allMatches.isEmpty() ? null : allMatches.get(0);
@@ -221,17 +231,18 @@
/**
* Returns the (first) handler identified by the given name.
+ *
* @param name the name of the handler to return, can be <code>null</code> in which case this method will return <code>null</code>.
* @return the element with the given name, or <code>null</code> if not found, or the given argument was <code>null</code>.
*/
- public V getByName(String name)
+ V getByName(String name)
{
if (name == null)
{
return null;
}
- for (V element : this.all)
+ for (V element : this.mappedHandlers)
{
if (name.equals(element.getName()))
{
@@ -245,11 +256,11 @@
/**
* Provides information on whether there are elements mapped or not.
*
- * @return <code>true</code> if there is at least one element mapped, <code>false</code> otherwise.
+ * @return <code>false</code> if there is at least one element mapped, <code>true</code> otherwise.
*/
- public boolean hasElements()
+ boolean isEmpty()
{
- return !this.all.isEmpty();
+ return this.mappedHandlers.isEmpty();
}
/**
@@ -265,60 +276,45 @@
{
path = (path == null) ? "" : path.trim();
- List<V> result = new ArrayList<V>();
+ Set<V> result = new TreeSet<V>();
// Look for exact matches only, that is, those patterns without wildcards...
- for (Entry<Pattern, List<V>> entry : this.exactMap.entrySet())
+ for (Entry<Pattern, Set<V>> entry : this.exactMap.entrySet())
{
Matcher matcher = entry.getKey().matcher(path);
// !!! we should always match the *entire* pattern, instead of the longest prefix...
if (matcher.matches())
{
- List<V> vs = entry.getValue();
+ Set<V> vs = entry.getValue();
for (V v : vs)
{
- if (!result.contains(v))
- {
- result.add(v);
- }
-
+ result.add(v);
if (firstOnly)
{
- return result;
+ return new ArrayList<V>(result);
}
}
}
}
// Try to apply the wildcard patterns...
- for (Entry<Pattern, List<V>> entry : this.wildcardMap.entrySet())
+ for (Entry<Pattern, Set<V>> entry : this.wildcardMap.entrySet())
{
Matcher matcher = entry.getKey().matcher(path);
if (matcher.find(0))
{
- List<V> vs = entry.getValue();
+ Set<V> vs = entry.getValue();
for (V v : vs)
{
- if (!result.contains(v))
- {
- result.add(v);
- }
+ result.add(v);
if (firstOnly)
{
- return result;
+ break;
}
}
}
}
- // Make sure the results are properly sorted...
- Collections.sort(result);
-
- return result;
- }
-
- static boolean isWildcardPattern(Pattern p)
- {
- return p.pattern().contains(".*");
+ return new ArrayList<V>(result);
}
}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java
index 9f5b614..58002db 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java
@@ -26,6 +26,7 @@
import org.apache.felix.http.base.internal.runtime.HandlerRuntime;
import org.apache.felix.http.base.internal.runtime.ServletContextHelperInfo;
+import org.osgi.framework.BundleContext;
/**
* Registry for all services.
@@ -36,16 +37,22 @@
public final class HandlerRegistry
{
private static FilterHandler[] EMPTY_FILTER_HANDLER = new FilterHandler[0];
+ private final BundleContext bundleContext;
/** Current list of context registrations. */
private volatile List<PerContextHandlerRegistry> registrations = Collections.emptyList();
+ public HandlerRegistry(BundleContext bundleContext)
+ {
+ this.bundleContext = bundleContext;
+ }
+
/**
* Register default context registry for Http Service
*/
public void init()
{
- this.add(new PerContextHandlerRegistry());
+ this.add(new PerContextHandlerRegistry(this.bundleContext));
}
/**
@@ -74,7 +81,7 @@
*/
public void add(@Nonnull ServletContextHelperInfo info)
{
- this.add(new PerContextHandlerRegistry(info));
+ this.add(new PerContextHandlerRegistry(info, this.bundleContext));
}
/**
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistry.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistry.java
index 8d27a96..e7b3a22 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistry.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistry.java
@@ -23,26 +23,43 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.regex.Pattern;
+import javax.annotation.Nonnull;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
+import org.apache.felix.http.base.internal.context.ExtServletContext;
import org.apache.felix.http.base.internal.runtime.FilterInfo;
import org.apache.felix.http.base.internal.runtime.HandlerRuntime;
import org.apache.felix.http.base.internal.runtime.HandlerRuntime.ErrorPage;
import org.apache.felix.http.base.internal.runtime.ServletContextHelperInfo;
import org.apache.felix.http.base.internal.runtime.ServletInfo;
+import org.apache.felix.http.base.internal.service.ResourceServlet;
+import org.apache.felix.http.base.internal.util.PatternUtil;
+import org.apache.felix.http.base.internal.whiteboard.ContextHandler;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceObjects;
public final class PerContextHandlerRegistry implements Comparable<PerContextHandlerRegistry>
{
- private final Map<Servlet, ServletHandler> servletMap = new HashMap<Servlet, ServletHandler>();
+ private final BundleContext bundleContext;
+
private final Map<Filter, FilterHandler> filterMap = new HashMap<Filter, FilterHandler>();
- private final Map<String, Servlet> servletPatternMap = new HashMap<String, Servlet>();
+
private volatile HandlerMapping<ServletHandler> servletMapping = new HandlerMapping<ServletHandler>();
private volatile HandlerMapping<FilterHandler> filterMapping = new HandlerMapping<FilterHandler>();
private final ErrorsMapping errorsMapping = new ErrorsMapping();
+
+ private SortedMap<Pattern, SortedSet<ServletHandler>> patternToServletHandler = new TreeMap<Pattern, SortedSet<ServletHandler>>(PatternUtil.PatternComparator.INSTANCE);
+ private Map<ServletHandler, Integer> servletHandlerToUses = new HashMap<ServletHandler, Integer>();
+ private final SortedSet<ServletHandler> allServletHandlers = new TreeSet<ServletHandler>();
private final long serviceId;
@@ -52,18 +69,20 @@
private final String prefix;
- public PerContextHandlerRegistry() {
+ public PerContextHandlerRegistry(BundleContext bundleContext) {
this.serviceId = 0;
this.ranking = Integer.MAX_VALUE;
this.path = "/";
this.prefix = null;
+ this.bundleContext = bundleContext;
}
- public PerContextHandlerRegistry(final ServletContextHelperInfo info)
+ public PerContextHandlerRegistry(final ServletContextHelperInfo info, BundleContext bundleContext)
{
this.serviceId = info.getServiceId();
this.ranking = info.getRanking();
this.path = info.getPath();
+ this.bundleContext = bundleContext;
if ( this.path.equals("/") )
{
prefix = null;
@@ -76,15 +95,14 @@
public synchronized void addFilter(FilterHandler handler) throws ServletException
{
- if (this.filterMap.containsKey(handler.getFilter()))
- {
- throw new ServletException("Filter instance already registered");
- }
+ if(this.filterMapping.contains(handler))
+ {
+ throw new ServletException("Filter instance already registered");
+ }
handler.init();
+ this.filterMapping = this.filterMapping.add(handler);
this.filterMap.put(handler.getFilter(), handler);
-
- updateFilterMapping();
}
@Override
@@ -109,35 +127,147 @@
*/
public synchronized void addServlet(final ServletHandler handler) throws ServletException
{
- // Can be null in case of error-handling servlets...
- String[] patterns = handler.getServletInfo().getPatterns();
- int length = patterns == null ? 0 : patterns.length;
+ Pattern[] patterns = handler.getPatterns();
+ String[] errorPages = handler.getServletInfo().getErrorPage();
+
+ if(patterns.length > 0 && errorPages != null)
+ {
+ throw new ServletException("Servlet instance " + handler.getName() + " has both patterns and errorPage set");
+ }
+
+ SortedMap<Pattern, ServletHandler> toAdd = new TreeMap<Pattern, ServletHandler>(PatternUtil.PatternComparator.INSTANCE);
+ SortedMap<Pattern, ServletHandler> toRemove = new TreeMap<Pattern, ServletHandler>(PatternUtil.PatternComparator.INSTANCE);
+
+ this.servletHandlerToUses.put(handler, new Integer(0));
+
+ for (Pattern p : patterns)
+ {
+ ServletHandler prevHandler = null;
- for (int i = 0; i < length; i++)
- {
- final String pattern = patterns[i];
- if (this.servletPatternMap.containsKey(pattern))
+ if( !this.patternToServletHandler.containsKey(p))
+ {
+ this.patternToServletHandler.put(p, new TreeSet<ServletHandler>());
+ }
+ else
+ {
+ prevHandler = this.patternToServletHandler.get(p).first();
+ }
+
+ this.patternToServletHandler.get(p).add(handler);
+
+ if ( handler.equals(this.patternToServletHandler.get(p).first()))
{
- throw new ServletException("Servlet instance " + handler.getName() + " already registered");
- }
- this.servletPatternMap.put(pattern, handler.getServlet());
- }
+ useServletHandler(handler);
+ if (!handler.isWhiteboardService())
+ {
+ handler.init();
+ }
+ increaseUseCount(handler);
- patterns = handler.getServletInfo().getErrorPage();
- if ( patterns != null )
- {
- for(final String errorPage : patterns)
- {
- this.errorsMapping.addErrorServlet(errorPage, handler);
+ if (prevHandler != null)
+ {
+ decreaseUseCount(prevHandler);
+ toRemove.put(p, prevHandler);
+ }
+ toAdd.put(p, handler);
}
- }
- handler.init();
- this.servletMap.put(handler.getServlet(), handler);
-
- updateServletMapping();
+ }
+
+ this.servletMapping = this.servletMapping.remove(toRemove);
+ this.servletMapping = this.servletMapping.add(toAdd);
+ this.allServletHandlers.add(handler);
+
+ if(errorPages != null)
+ {
+ for(String errorPage : errorPages)
+ {
+ this.errorsMapping.addErrorServlet(errorPage, handler);
+ }
+ }
}
- public ErrorsMapping getErrorsMapping()
+ /**
+ * Ensures the servlet handler contains a valid servlet object.
+ * It gets one from the ServiceRegistry if the servlet handler was added by the whiteboard implementation
+ * and the object was not yet retrieved.
+ *
+ * @param handler
+ * @throws ServletException
+ */
+ private void useServletHandler(ServletHandler handler) throws ServletException
+ {
+ if( (!handler.isWhiteboardService()) || (handler.getServlet() != null) )
+ {
+ return;
+ }
+
+ // isWhiteboardService && servlet == null
+ boolean isResource = handler.getServletInfo().isResource();
+ final ServiceObjects<Servlet> so = this.bundleContext.getServiceObjects(handler.getServletInfo().getServiceReference());
+
+ Servlet servlet = getServiceObject(so, handler, isResource);
+ handler.setServlet(servlet);
+
+ try {
+ handler.init();
+ } catch (ServletException e) {
+ ungetServiceObject(so, servlet, isResource);
+ throw e;
+ }
+ }
+
+ private Servlet getServiceObject(ServiceObjects<Servlet> so, ServletHandler handler, boolean isResource)
+ {
+ if(isResource)
+ {
+ return new ResourceServlet(handler.getServletInfo().getPrefix());
+ }
+ if(so != null)
+ {
+ return so.getService();
+ }
+ return null;
+ }
+
+ private void ungetServiceObject(ServiceObjects<Servlet> so, Servlet servlet, boolean isResource)
+ {
+ if(isResource || (so == null))
+ {
+ return;
+ }
+ so.ungetService(servlet);
+ }
+
+ private void increaseUseCount(ServletHandler handler)
+ {
+ Integer uses = this.servletHandlerToUses.get(handler);
+ if(uses != null)
+ {
+ int newUsesValue = uses.intValue() + 1;
+ this.servletHandlerToUses.put(handler, new Integer(newUsesValue));
+ }
+ }
+
+ private void decreaseUseCount(@Nonnull ServletHandler handler)
+ {
+ Integer uses = this.servletHandlerToUses.get(handler);
+ if(uses != null)
+ {
+ int newUsesValue = uses.intValue() - 1;
+ if(newUsesValue == 0 && handler.isWhiteboardService())
+ {
+ // if the servlet is no longer used and it is registered as a whiteboard service
+ // call destroy, unget the service object and set the servlet in the handler to null
+ handler.destroy();
+ ServiceObjects<Servlet> so = this.bundleContext.getServiceObjects(handler.getServletInfo().getServiceReference());
+ ungetServiceObject(so, handler.getServlet(), handler.getServletInfo().isResource());
+ handler.setServlet(null);
+ }
+ this.servletHandlerToUses.put(handler, new Integer(newUsesValue));
+ }
+ }
+
+ public ErrorsMapping getErrorsMapping()
{
return this.errorsMapping;
}
@@ -160,7 +290,7 @@
String servletName = (servletHandler != null) ? servletHandler.getName() : null;
// TODO this is not the most efficient/fastest way of doing this...
- for (FilterHandler filterHandler : this.filterMapping.getAllElements())
+ for (FilterHandler filterHandler : this.filterMapping.values())
{
if (referencesServletByName(filterHandler, servletName))
{
@@ -193,27 +323,26 @@
public synchronized void removeAll()
{
- for (Iterator<ServletHandler> it = servletMap.values().iterator(); it.hasNext(); )
+ Collection<ServletHandler> servletHandlers = servletMapping.values();
+ Collection<FilterHandler> filterHandlers = filterMapping.values();
+
+ this.servletMapping = new HandlerMapping<ServletHandler>();
+ this.filterMapping = new HandlerMapping<FilterHandler>();
+
+ for (ServletHandler handler : servletHandlers)
{
- ServletHandler handler = it.next();
- it.remove();
handler.destroy();
}
- for (Iterator<FilterHandler> it = filterMap.values().iterator(); it.hasNext(); )
+ for (FilterHandler handler : filterHandlers)
{
- FilterHandler handler = it.next();
- it.remove();
handler.destroy();
}
- this.servletMap.clear();
- this.filterMap.clear();
- this.servletPatternMap.clear();
this.errorsMapping.clear();
-
- updateServletMapping();
- updateFilterMapping();
+ this.allServletHandlers.clear();
+ //this.servletMap.clear();
+ this.filterMap.clear();
}
public synchronized void removeFilter(Filter filter, final boolean destroy)
@@ -221,7 +350,7 @@
FilterHandler handler = this.filterMap.remove(filter);
if (handler != null)
{
- updateFilterMapping();
+ this.filterMapping = this.filterMapping.remove(handler);
if (destroy)
{
handler.destroy();
@@ -231,76 +360,123 @@
public synchronized Filter removeFilter(final FilterInfo filterInfo, final boolean destroy)
{
+ FilterHandler handler = getFilterHandler(filterInfo);
+
+ if (handler == null)
+ {
+ return null;
+ }
+
+ this.filterMapping = this.filterMapping.remove(handler);
+
+ if (destroy)
+ {
+ handler.destroy();
+ }
+ return handler.getFilter();
+ }
+
+ private FilterHandler getFilterHandler(final FilterInfo filterInfo)
+ {
for(final FilterHandler handler : this.filterMap.values())
{
if ( handler.getFilterInfo().compareTo(filterInfo) == 0)
{
- this.filterMap.remove(handler.getFilter());
- updateFilterMapping();
- if (destroy)
- {
- handler.destroy();
- }
- return handler.getFilter();
+ return handler;
}
}
return null;
}
-
+
public synchronized Servlet removeServlet(ServletInfo servletInfo, final boolean destroy)
{
- for(final ServletHandler handler : this.servletMap.values())
- {
- if ( handler.getServletInfo().compareTo(servletInfo) == 0 )
- {
- this.servletMap.remove(handler.getServlet());
- updateServletMapping();
+ ServletHandler handler = getServletHandler(servletInfo);
+
+ Pattern[] patterns = (handler == null) ? new Pattern[0] : handler.getPatterns();
+ SortedMap<Pattern, ServletHandler> toAdd = new TreeMap<Pattern, ServletHandler>(PatternUtil.PatternComparator.INSTANCE);
+ SortedMap<Pattern, ServletHandler> toRemove = new TreeMap<Pattern, ServletHandler>(PatternUtil.PatternComparator.INSTANCE);
- // Can be null in case of error-handling servlets...
- String[] patterns = handler.getServletInfo().getPatterns();
- int length = patterns == null ? 0 : patterns.length;
+ for(Pattern p : patterns)
+ {
+ SortedSet<ServletHandler> handlers = this.patternToServletHandler.get(p);
+ if(handlers != null && (!handlers.isEmpty()))
+ {
+ if(handlers.first().equals(handler))
+ {
+ toRemove.put(p, handler);
+ }
+ handlers.remove(handler);
+
+ ServletHandler activeHandler = null;
+ if( !handlers.isEmpty() )
+ {
+ activeHandler = handlers.first();
+
+ try {
+ useServletHandler(activeHandler);
+ increaseUseCount(activeHandler);
+ toAdd.put(p, activeHandler);
+ } catch (ServletException e) {
+ // TODO: next servlet handling this pattern could not be initialized, it belongs to failure DTOs
+ }
+ }
+ else
+ {
+ this.patternToServletHandler.remove(p);
+ }
+ }
+ }
+
+ Servlet servlet = null;
+ if(handler != null)
+ {
+ servlet = handler.getServlet();
+ if(destroy)
+ {
+ servlet.destroy();
+ }
+ if(handler.isWhiteboardService())
+ {
+ ServiceObjects<Servlet> so = this.bundleContext.getServiceObjects(handler.getServletInfo().getServiceReference());
+ ungetServiceObject(so, servlet, servletInfo.isResource());
+ }
+ }
+
+ this.servletHandlerToUses.remove(handler);
+
+ this.servletMapping = this.servletMapping.remove(toRemove);
+ this.servletMapping = this.servletMapping.add(toAdd);
- for (int i = 0; i < length; i++)
- {
- this.servletPatternMap.remove(patterns[i]);
- }
-
- this.errorsMapping.removeServlet(handler.getServlet());
-
- if (destroy)
- {
- handler.destroy();
- }
- return handler.getServlet();
- }
- }
- return null;
+ return servlet;
}
+ private ServletHandler getServletHandler(final ServletInfo servletInfo)
+ {
+ Iterator<ServletHandler> it = this.allServletHandlers.iterator();
+ while(it.hasNext())
+ {
+ ServletHandler handler = it.next();
+ if(handler.getServletInfo().compareTo(servletInfo) == 0)
+ {
+ return handler;
+ }
+ }
+ return null;
+ }
+
public synchronized void removeServlet(Servlet servlet, final boolean destroy)
{
- ServletHandler handler = this.servletMap.remove(servlet);
- if (handler != null)
- {
- updateServletMapping();
-
- // Can be null in case of error-handling servlets...
- String[] patterns = handler.getServletInfo().getPatterns();
- int length = patterns == null ? 0 : patterns.length;
-
- for (int i = 0; i < length; i++)
- {
- this.servletPatternMap.remove(patterns[i]);
- }
-
- this.errorsMapping.removeServlet(servlet);
-
- if (destroy)
- {
- handler.destroy();
- }
- }
+ Iterator<ServletHandler> it = this.allServletHandlers.iterator();
+ while(it.hasNext())
+ {
+ ServletHandler handler = it.next();
+ if(handler.getServlet() == servlet)
+ {
+ removeServlet(handler.getServletInfo(), destroy);
+ }
+ }
}
+
private boolean referencesDispatcherType(FilterHandler handler, DispatcherType dispatcherType)
{
@@ -321,16 +497,6 @@
return false;
}
- private void updateFilterMapping()
- {
- this.filterMapping = new HandlerMapping<FilterHandler>(this.filterMap.values());
- }
-
- private void updateServletMapping()
- {
- this.servletMapping = new HandlerMapping<ServletHandler>(this.servletMap.values());
- }
-
public String isMatching(final String requestURI)
{
if ( requestURI.equals(this.path) )
@@ -365,9 +531,13 @@
List<ServletHandler> servletHandlers = new ArrayList<ServletHandler>();
List<ServletHandler> resourceHandlers = new ArrayList<ServletHandler>();
- for (ServletHandler servletHandler : servletMap.values())
+
+ Iterator<ServletHandler> it = this.allServletHandlers.iterator();
+ while(it.hasNext())
{
- if (servletHandler.getServletInfo().isResource())
+ ServletHandler servletHandler = it.next();
+
+ if (servletHandler.getServletInfo().isResource())
{
resourceHandlers.add(servletHandler);
}
@@ -378,5 +548,5 @@
}
return new HandlerRuntime(servletHandlers, filterHandlers, resourceHandlers, errorPages, serviceId);
- }
+ }
}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java
index 008bda3..d640c0a 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java
@@ -36,24 +36,37 @@
/**
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
-public final class ServletHandler extends AbstractHandler<ServletHandler>
+public class ServletHandler extends AbstractHandler<ServletHandler>
{
private final ServletInfo servletInfo;
- private final Servlet servlet;
+ private Servlet servlet;
private final Pattern[] patterns;
private final long contextServiceId;
+
+ private final boolean isWhiteboardService;
public ServletHandler(final ServletContextHelperInfo contextInfo,
final ExtServletContext context,
final ServletInfo servletInfo,
final Servlet servlet)
{
- super(context, servletInfo.getInitParameters(), servletInfo.getName());
+ this(contextInfo, context, servletInfo, servlet, false);
+ }
+
+ public ServletHandler(final ServletContextHelperInfo contextInfo,
+ final ExtServletContext context,
+ final ServletInfo servletInfo,
+ final Servlet servlet,
+ final boolean isWhiteboardService)
+ {
+ super(context, servletInfo.getInitParameters(), servletInfo.getName());
+
this.servlet = servlet;
this.servletInfo = servletInfo;
+ this.isWhiteboardService = isWhiteboardService;
// Can be null in case of error-handling servlets...
String[] patterns = this.servletInfo.getPatterns();
@@ -74,6 +87,7 @@
this.contextServiceId = 0;
}
}
+
@Override
public int compareTo(final ServletHandler other)
@@ -111,6 +125,16 @@
{
return this.servlet;
}
+
+ void setServlet(Servlet servlet)
+ {
+ this.servlet = servlet;
+ }
+
+ public boolean isWhiteboardService()
+ {
+ return this.isWhiteboardService;
+ }
@Override
public Pattern[] getPatterns()
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java
index 9c87be4..8f3d549 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java
@@ -118,14 +118,17 @@
}
else if (value instanceof String[])
{
- final String[] arr = (String[]) value;
- for(int i=0; i<arr.length; i++)
+ final String[] arr = (String[]) value;
+ String[] values = new String[arr.length];
+
+ for(int i=0, j=0; i<arr.length; i++)
{
if ( arr[i] != null )
{
- arr[i] = arr[i].trim();
+ values[j++] = arr[i].trim();
}
}
+ return values;
}
else if (value instanceof Collection<?>)
{
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/util/PatternUtil.java b/http/base/src/main/java/org/apache/felix/http/base/internal/util/PatternUtil.java
index 708c628..530dca7 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/util/PatternUtil.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/util/PatternUtil.java
@@ -18,7 +18,15 @@
*/
package org.apache.felix.http.base.internal.util;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.SortedSet;
import java.util.StringTokenizer;
+import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Some convenience utilities to deal with path patterns.
@@ -111,4 +119,93 @@
return valid;
}
+
+ /**
+ * Compares {@link Pattern}s based on a set of simple rules:
+ * <ol>
+ * <li>exact matches go first;</li>
+ * <li>followed by wildcard path matches;</li>
+ * <li>lastly all wildcard extension matches.</li>
+ * </ol>
+ * <p>
+ * Equal matches will first be sorted on length in descending order (longest patterns first),
+ * and in case of equal lengths, they are sorted in natural (ascending) order.
+ * </p>
+ */
+ public enum PatternComparator implements Comparator<Pattern>
+ {
+ INSTANCE;
+
+ @Override
+ public int compare(Pattern p1, Pattern p2)
+ {
+ String ps1 = p1.pattern();
+ String ps2 = p2.pattern();
+
+ // Sorts wildcard path matches before wildcard extension matches...
+ int r;
+ if (isWildcardPath(ps1))
+ {
+ if (isWildcardPath(ps2))
+ {
+ // Descending on length...
+ r = ps2.length() - ps1.length();
+ }
+ else
+ {
+ // Exact matches go first...
+ r = isWildcardExtension(ps2) ? -1 : 1;
+ }
+ }
+ else if (isWildcardExtension(ps1))
+ {
+ if (isWildcardExtension(ps2))
+ {
+ // Descending on length...
+ r = ps2.length() - ps1.length();
+ }
+ else
+ {
+ // Wildcard paths & exact matches go first...
+ r = 1;
+ }
+ }
+ else
+ {
+ if (isWildcardExtension(ps2) || isWildcardPath(ps2))
+ {
+ // Exact matches go first...
+ r = -1;
+ }
+ else
+ {
+ // Descending on length...
+ r = ps2.length() - ps1.length();
+ }
+ }
+
+ if (r == 0)
+ {
+ // In case of a draw, ensure we sort in a predictable (ascending) order...
+ r = ps1.compareTo(ps2);
+ }
+
+ return r;
+ }
+
+ private boolean isWildcardExtension(String p)
+ {
+ return p.startsWith("^(.*");
+ }
+
+ private boolean isWildcardPath(String p)
+ {
+ return p.startsWith("^(/");
+ }
+ }
+
+ public static boolean isWildcardPattern(Pattern p)
+ {
+ return p.pattern().contains(".*");
+ }
}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java
index b80ec80..10a6d40 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java
@@ -57,29 +57,21 @@
public void registerServlet(@Nonnull final ContextHandler contextHandler,
@Nonnull final ServletInfo servletInfo)
{
- final ServiceObjects<Servlet> so = this.bundleContext.getServiceObjects(servletInfo.getServiceReference());
- if ( so != null )
- {
- final Servlet servlet = so.getService();
- // TODO create failure DTO if null
- if ( servlet != null )
- {
- final ServletHandler handler = new ServletHandler(contextHandler.getContextInfo(),
- contextHandler.getServletContext(servletInfo.getServiceReference().getBundle()),
- servletInfo,
- servlet);
- try {
- final PerContextHandlerRegistry registry = this.handlerRegistry.getRegistry(contextHandler.getContextInfo());
- if (registry != null )
- {
- registry.addServlet(handler);
- }
- } catch (final ServletException e) {
- so.ungetService(servlet);
- // TODO create failure DTO
- }
- }
- }
+ final PerContextHandlerRegistry registry = this.handlerRegistry.getRegistry(contextHandler.getContextInfo());
+ if (registry != null)
+ {
+ try {
+ ServletHandler handler = new ServletHandler(contextHandler.getContextInfo(),
+ contextHandler.getServletContext(servletInfo.getServiceReference().getBundle()),
+ servletInfo,
+ null,
+ true);
+
+ registry.addServlet(handler);
+ } catch (ServletException e) {
+ // TODO create failure DTO
+ }
+ }
}
/**
@@ -92,11 +84,7 @@
final PerContextHandlerRegistry registry = this.handlerRegistry.getRegistry(contextHandler.getContextInfo());
if (registry != null )
{
- final Servlet instance = registry.removeServlet(servletInfo, true);
- if ( instance != null )
- {
- this.bundleContext.getServiceObjects(servletInfo.getServiceReference()).ungetService(instance);
- }
+ registry.removeServlet(servletInfo, true);
}
contextHandler.ungetServletContext(servletInfo.getServiceReference().getBundle());
}
@@ -156,22 +144,23 @@
public void registerResource(@Nonnull final ContextHandler contextHandler,
@Nonnull final ResourceInfo resourceInfo)
{
- final ServletInfo servletInfo = new ServletInfo(resourceInfo);
-
- final Servlet servlet = new ResourceServlet(resourceInfo.getPrefix());
- final ServletHandler handler = new ServletHandler(contextHandler.getContextInfo(),
- contextHandler.getServletContext(servletInfo.getServiceReference().getBundle()),
- servletInfo,
- servlet);
- try {
- final PerContextHandlerRegistry registry = this.handlerRegistry.getRegistry(contextHandler.getContextInfo());
- if (registry != null )
- {
- registry.addServlet(handler);
- }
- } catch (ServletException e) {
- // TODO create failure DTO
- }
+ final ServletInfo servletInfo = new ServletInfo(resourceInfo);
+
+ final ServletHandler handler = new ServletHandler(contextHandler.getContextInfo(),
+ contextHandler.getServletContext(servletInfo.getServiceReference().getBundle()),
+ servletInfo,
+ null,
+ true);
+
+ try {
+ final PerContextHandlerRegistry registry = this.handlerRegistry.getRegistry(contextHandler.getContextInfo());
+ if(registry != null)
+ {
+ registry.addServlet(handler);
+ }
+ } catch (ServletException e) {
+ // TODO create failure DTO
+ }
}
/**
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistryTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistryTest.java
index 9d89769..9dfdc84 100644
--- a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistryTest.java
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistryTest.java
@@ -35,10 +35,10 @@
@Test public void testPathOrdering()
{
final List<PerContextHandlerRegistry> list = new ArrayList<PerContextHandlerRegistry>();
- list.add(new PerContextHandlerRegistry(createServletContextHelperInfo("/", 1L, 0)));
- list.add(new PerContextHandlerRegistry(createServletContextHelperInfo("/foo", 2L, 0)));
- list.add(new PerContextHandlerRegistry(createServletContextHelperInfo("/", 3L, 0)));
- list.add(new PerContextHandlerRegistry(createServletContextHelperInfo("/bar", 4L, 0)));
+ list.add(new PerContextHandlerRegistry(createServletContextHelperInfo("/", 1L, 0), null));
+ list.add(new PerContextHandlerRegistry(createServletContextHelperInfo("/foo", 2L, 0), null));
+ list.add(new PerContextHandlerRegistry(createServletContextHelperInfo("/", 3L, 0), null));
+ list.add(new PerContextHandlerRegistry(createServletContextHelperInfo("/bar", 4L, 0), null));
Collections.sort(list);
@@ -51,10 +51,10 @@
@Test public void testRankingOrdering()
{
final List<PerContextHandlerRegistry> list = new ArrayList<PerContextHandlerRegistry>();
- list.add(new PerContextHandlerRegistry(createServletContextHelperInfo("/", 1L, 0)));
- list.add(new PerContextHandlerRegistry(createServletContextHelperInfo("/", 2L, 0)));
- list.add(new PerContextHandlerRegistry(createServletContextHelperInfo("/", 3L, -30)));
- list.add(new PerContextHandlerRegistry(createServletContextHelperInfo("/", 4L, 50)));
+ list.add(new PerContextHandlerRegistry(createServletContextHelperInfo("/", 1L, 0), null));
+ list.add(new PerContextHandlerRegistry(createServletContextHelperInfo("/", 2L, 0), null));
+ list.add(new PerContextHandlerRegistry(createServletContextHelperInfo("/", 3L, -30), null));
+ list.add(new PerContextHandlerRegistry(createServletContextHelperInfo("/", 4L, 50), null));
Collections.sort(list);
@@ -77,8 +77,8 @@
private void testSymetry(String path, String otherPath, long id, long otherId, int ranking, int otherRanking)
{
- PerContextHandlerRegistry handlerRegistry = new PerContextHandlerRegistry(createServletContextHelperInfo(path, id, ranking));
- PerContextHandlerRegistry other = new PerContextHandlerRegistry(createServletContextHelperInfo(otherPath, otherId, otherRanking));
+ PerContextHandlerRegistry handlerRegistry = new PerContextHandlerRegistry(createServletContextHelperInfo(path, id, ranking), null);
+ PerContextHandlerRegistry other = new PerContextHandlerRegistry(createServletContextHelperInfo(otherPath, otherId, otherRanking), null);
assertEquals(handlerRegistry.compareTo(other), -other.compareTo(handlerRegistry));
}
@@ -96,9 +96,9 @@
long highId, long midId, long lowId,
int highRanking, int midRanking, int lowRanking)
{
- PerContextHandlerRegistry high = new PerContextHandlerRegistry(createServletContextHelperInfo(highPath, highId, highRanking));
- PerContextHandlerRegistry mid = new PerContextHandlerRegistry(createServletContextHelperInfo(midPath, midId, midRanking));
- PerContextHandlerRegistry low = new PerContextHandlerRegistry(createServletContextHelperInfo(lowPath, lowId, lowRanking));
+ PerContextHandlerRegistry high = new PerContextHandlerRegistry(createServletContextHelperInfo(highPath, highId, highRanking), null);
+ PerContextHandlerRegistry mid = new PerContextHandlerRegistry(createServletContextHelperInfo(midPath, midId, midRanking), null);
+ PerContextHandlerRegistry low = new PerContextHandlerRegistry(createServletContextHelperInfo(lowPath, lowId, lowRanking), null);
assertEquals(1, high.compareTo(mid));
assertEquals(1, mid.compareTo(low));
diff --git a/http/itest/src/test/java/org/apache/felix/http/itest/HttpWhiteboardTest.java b/http/itest/src/test/java/org/apache/felix/http/itest/HttpWhiteboardTargetTest.java
similarity index 98%
rename from http/itest/src/test/java/org/apache/felix/http/itest/HttpWhiteboardTest.java
rename to http/itest/src/test/java/org/apache/felix/http/itest/HttpWhiteboardTargetTest.java
index b7628fe..e9ba91d 100644
--- a/http/itest/src/test/java/org/apache/felix/http/itest/HttpWhiteboardTest.java
+++ b/http/itest/src/test/java/org/apache/felix/http/itest/HttpWhiteboardTargetTest.java
@@ -47,7 +47,7 @@
import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
@RunWith(JUnit4TestRunner.class)
-public class HttpWhiteboardTest extends BaseIntegrationTest
+public class HttpWhiteboardTargetTest extends BaseIntegrationTest
{
private static final String SERVICE_HTTP_PORT = "org.osgi.service.http.port";
@@ -81,13 +81,13 @@
ServiceRegistration<?> reg = m_context.registerService(Servlet.class.getName(), servlet, props);
try {
- assertTrue(initLatch.await(600, TimeUnit.SECONDS));
+ assertTrue(initLatch.await(5, TimeUnit.SECONDS));
URL testURL = createURL("/servletAlias");
assertContent("It works!", testURL);
} finally {
reg.unregister();
}
- assertTrue(destroyLatch.await(600, TimeUnit.SECONDS));
+ assertTrue(destroyLatch.await(5, TimeUnit.SECONDS));
}
/**
diff --git a/http/itest/src/test/java/org/apache/felix/http/itest/ServletPatternTest.java b/http/itest/src/test/java/org/apache/felix/http/itest/ServletPatternTest.java
new file mode 100644
index 0000000..8bc7f02
--- /dev/null
+++ b/http/itest/src/test/java/org/apache/felix/http/itest/ServletPatternTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.felix.http.itest;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.Servlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+
+@RunWith(JUnit4TestRunner.class)
+public class ServletPatternTest extends BaseIntegrationTest
+{
+
+ @Test
+ public void testHighRankReplaces() throws Exception
+ {
+ CountDownLatch initLatch = new CountDownLatch(2);
+ CountDownLatch destroyLatch = new CountDownLatch(2);
+
+ TestServlet lowRankServlet = new TestServlet(initLatch, destroyLatch)
+ {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException
+ {
+ resp.getWriter().print("lowRankServlet");
+ resp.flushBuffer();
+ }
+ };
+
+ TestServlet highRankServlet = new TestServlet(initLatch, destroyLatch)
+ {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException
+ {
+ resp.getWriter().print("highRankServlet");
+ resp.flushBuffer();
+ }
+ };
+
+ Dictionary<String, Object> lowRankProps = new Hashtable<String, Object>();
+ String lowRankPattern[] = {"/foo", "/bar"};
+ lowRankProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, lowRankPattern);
+ lowRankProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_INIT_PARAM_PREFIX + ".myname", "lowRankServlet");
+ lowRankProps.put(Constants.SERVICE_RANKING, 1);
+
+ ServiceRegistration<?> lowRankReg = m_context.registerService(Servlet.class.getName(), lowRankServlet, lowRankProps);
+
+ Dictionary<String, Object> highRankProps = new Hashtable<String, Object>();
+ String highRankPattern[] = {"/foo", "/baz"};
+ highRankProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, highRankPattern);
+ highRankProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_INIT_PARAM_PREFIX + ".myname", "highRankServlet");
+ highRankProps.put(Constants.SERVICE_RANKING, 2);
+
+ ServiceRegistration<?> highRankReg = m_context.registerService(Servlet.class.getName(), highRankServlet, highRankProps);
+
+ try {
+ assertTrue(initLatch.await(5, TimeUnit.SECONDS));
+
+ assertContent("highRankServlet", createURL("/foo"));
+ assertContent("lowRankServlet", createURL("/bar"));
+ assertContent("highRankServlet", createURL("/baz"));
+
+ } finally {
+ lowRankReg.unregister();
+ highRankReg.unregister();
+ }
+
+ assertTrue(destroyLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testSameRankDoesNotReplace() throws Exception
+ {
+ CountDownLatch initLatch = new CountDownLatch(2);
+ CountDownLatch destroyLatch = new CountDownLatch(2);
+
+ TestServlet servlet1 = new TestServlet(initLatch, destroyLatch)
+ {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException
+ {
+ resp.getWriter().print("servlet1");
+ resp.flushBuffer();
+ }
+ };
+
+ TestServlet servlet2 = new TestServlet(initLatch, destroyLatch)
+ {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException
+ {
+ resp.getWriter().print("servlet2");
+ resp.flushBuffer();
+ }
+ };
+
+ Dictionary<String, Object> props1 = new Hashtable<String, Object>();
+ String lowRankPattern[] = {"/foo", "/bar"};
+ props1.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, lowRankPattern);
+ props1.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_INIT_PARAM_PREFIX + ".myname", "lowRankServlet");
+ props1.put(Constants.SERVICE_RANKING, 2);
+
+ ServiceRegistration<?> reg1 = m_context.registerService(Servlet.class.getName(), servlet1, props1);
+
+ Dictionary<String, Object> props2 = new Hashtable<String, Object>();
+ String highRankPattern[] = {"/foo", "/baz"};
+ props2.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, highRankPattern);
+ props2.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_INIT_PARAM_PREFIX + ".myname", "highRankServlet");
+ props2.put(Constants.SERVICE_RANKING, 2);
+
+ ServiceRegistration<?> reg2 = m_context.registerService(Servlet.class.getName(), servlet2, props2);
+
+ try {
+ assertTrue(initLatch.await(5, TimeUnit.SECONDS));
+
+ assertContent("servlet1", createURL("/foo"));
+ assertContent("servlet1", createURL("/bar"));
+ assertContent("servlet2", createURL("/baz"));
+
+ } finally {
+ reg1.unregister();
+ reg2.unregister();
+ }
+
+ assertTrue(destroyLatch.await(5, TimeUnit.SECONDS));
+ }
+}
+
+
+