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));
+	}
+}
+
+
+