You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ja...@apache.org on 2015/03/10 11:45:03 UTC

svn commit: r1665462 - in /felix/trunk/http: base/src/main/java/org/apache/felix/http/base/internal/ base/src/main/java/org/apache/felix/http/base/internal/handler/ base/src/main/java/org/apache/felix/http/base/internal/runtime/ base/src/main/java/org/...

Author: jawi
Date: Tue Mar 10 10:45:02 2015
New Revision: 1665462

URL: http://svn.apache.org/r1665462
Log:
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.


Added:
    felix/trunk/http/itest/src/test/java/org/apache/felix/http/itest/HttpWhiteboardTargetTest.java   (with props)
    felix/trunk/http/itest/src/test/java/org/apache/felix/http/itest/ServletPatternTest.java   (with props)
Removed:
    felix/trunk/http/itest/src/test/java/org/apache/felix/http/itest/HttpWhiteboardTest.java
Modified:
    felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java
    felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerMapping.java
    felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java
    felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistry.java
    felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java
    felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java
    felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/util/PatternUtil.java
    felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java
    felix/trunk/http/base/src/test/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistryTest.java

Modified: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java?rev=1665462&r1=1665461&r2=1665462&view=diff
==============================================================================
--- felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java (original)
+++ felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java Tue Mar 10 10:45:02 2015
@@ -47,7 +47,7 @@ public final class HttpServiceController
     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);

Modified: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerMapping.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerMapping.java?rev=1665462&r1=1665461&r2=1665462&view=diff
==============================================================================
--- felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerMapping.java (original)
+++ felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerMapping.java Tue Mar 10 10:45:02 2015
@@ -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>>
 {
+    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.
+     */
+    HandlerMapping()
+    {
+        this(Collections.<Pattern, Collection<V>>emptyMap());
+    }
+
     /**
-     * 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>
+     * Creates a new {@link HandlerMapping} instance for the given elements.
+     *
+     * @param mappings the elements to map.
      */
-    static class PatternComparator implements Comparator<Pattern>
+    private HandlerMapping(Map<Pattern, Collection<V>> mappings)
     {
-        @Override
-        public int compare(Pattern p1, Pattern p2)
+        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 (Map.Entry<Pattern, Collection<V>> mapping : mappings.entrySet())
         {
-            String ps1 = p1.pattern();
-            String ps2 = p2.pattern();
+            Pattern pattern = mapping.getKey();
+            Collection<V> handlers = mapping.getValue();
 
-            // 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))
+            mappedHandlers.addAll(handlers);
+
+            if (PatternUtil.isWildcardPattern(pattern))
             {
-                if (isWildcardExtension(ps2))
-                {
-                    // Descending on length...
-                    r = ps2.length() - ps1.length();
-                }
-                else
+                Set<V> vs = this.wildcardMap.get(pattern);
+                if (vs == null)
                 {
-                    // Wildcard paths & exact matches go first...
-                    r = 1;
+                    vs = new TreeSet<V>();
+                    this.wildcardMap.put(pattern, vs);
                 }
+                vs.addAll(handlers);
             }
             else
             {
-                if (isWildcardExtension(ps2) || isWildcardPath(ps2))
-                {
-                    // Exact matches go first...
-                    r = -1;
-                }
-                else
+                Set<V> vs = this.exactMap.get(pattern);
+                if (vs == null)
                 {
-                    // Descending on length...
-                    r = ps2.length() - ps1.length();
+                    vs = new TreeSet<V>();
+                    this.exactMap.put(pattern, vs);
                 }
+                vs.addAll(handlers);
             }
-
-            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)
+    /**
+     * Returns a new {@link HandlerMapping} instance with a mapping for the
+     * given handler.
+     *
+     * @param handler the handler to be added to the mapping.
+     * @return a new {@link HandlerMapping} instance with a mapping for the
+     *         given handler.
+     */
+    HandlerMapping<V> add(V handler)
+    {
+        Map<Pattern, V> mappings = new TreeMap<Pattern, V>(PatternUtil.PatternComparator.INSTANCE);
+        for (Pattern pattern : handler.getPatterns())
         {
-            return p.startsWith("^(.*");
+            mappings.put(pattern, handler);
         }
+        return add(mappings);
+    }
 
-        private boolean isWildcardPath(String p)
+    HandlerMapping<V> add(Map<Pattern, V> mappings)
+    {
+        Map<Pattern, Collection<V>> newMappings = getAllMappings();
+        for (Map.Entry<Pattern, V> mapping : mappings.entrySet())
         {
-            return p.startsWith("^(/");
+            if (!newMappings.containsKey(mapping.getKey()))
+            {
+                newMappings.put(mapping.getKey(), new TreeSet<V>());
+            }
+            newMappings.get(mapping.getKey()).add(mapping.getValue());
         }
+        return new HandlerMapping<V>(newMappings);
     }
 
-    private final SortedMap<Pattern, List<V>> exactMap;
-    private final SortedMap<Pattern, List<V>> wildcardMap;
-    private final Set<V> all;
-
     /**
-     * Creates a new, empty, {@link HandlerMapping} instance.
+     * 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.
      */
-    public HandlerMapping()
+    HandlerMapping<V> remove(V handler)
     {
-        this(Collections.<V> emptyList());
+        Map<Pattern, V> mappings = new TreeMap<Pattern, V>(PatternUtil.PatternComparator.INSTANCE);
+        for (Pattern pattern : handler.getPatterns())
+        {
+            mappings.put(pattern, handler);
+        }
+        return remove(mappings);
     }
 
-    /**
-     * Creates a new {@link HandlerMapping} instance for the given elements.
-     *
-     * @param elements the elements to map, cannot be <code>null</code>.
-     */
-    public HandlerMapping(Collection<V> elements)
+    HandlerMapping<V> remove(Map<Pattern, 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);
-
-        for (V element : elements)
+        Map<Pattern, Collection<V>> newMappings = getAllMappings();
+        for (Map.Entry<Pattern, V> mapping : mappings.entrySet())
         {
-            for (Pattern pattern : element.getPatterns())
+            Collection<V> mappedHandlers = newMappings.get(mapping.getKey());
+            if (mappedHandlers == null)
             {
-                if (isWildcardPattern(pattern))
-                {
-                    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);
-                    }
-                }
-                else
-                {
-                    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);
-                    }
-                }
+                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 elements.
-     *
-     * @return a collection of mapped elements, never <code>null</code>.
+     * 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
      */
-    public Collection<V> getAllElements()
+    boolean contains(V handler)
     {
-        return this.all;
+        return mappedHandlers.contains(handler);
     }
 
     /**
@@ -195,7 +205,7 @@ public class HandlerMapping<V extends Ab
      * @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 @@ public class HandlerMapping<V extends Ab
      * @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 @@ public class HandlerMapping<V extends Ab
 
     /**
      * 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 @@ public class HandlerMapping<V extends Ab
     /**
      * 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 @@ public class HandlerMapping<V extends Ab
     {
         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);
     }
 }

Modified: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java?rev=1665462&r1=1665461&r2=1665462&view=diff
==============================================================================
--- felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java (original)
+++ felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java Tue Mar 10 10:45:02 2015
@@ -26,6 +26,7 @@ import javax.servlet.DispatcherType;
 
 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 @@ import org.apache.felix.http.base.intern
 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 final class HandlerRegistry
      */
     public void add(@Nonnull ServletContextHelperInfo info)
     {
-        this.add(new PerContextHandlerRegistry(info));
+        this.add(new PerContextHandlerRegistry(info, this.bundleContext));
     }
 
     /**

Modified: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistry.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistry.java?rev=1665462&r1=1665461&r2=1665462&view=diff
==============================================================================
--- felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistry.java (original)
+++ felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistry.java Tue Mar 10 10:45:02 2015
@@ -23,26 +23,43 @@ import java.util.HashMap;
 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 @@ public final class PerContextHandlerRegi
 
     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 final class PerContextHandlerRegi
 
     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 final class PerContextHandlerRegi
      */
     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;
-
-        for (int i = 0; i < length; i++)
-        {
-            final String pattern = patterns[i];
-            if (this.servletPatternMap.containsKey(pattern))
-            {
-                throw new ServletException("Servlet instance " + handler.getName() + " already registered");
-            }
-            this.servletPatternMap.put(pattern, handler.getServlet());
-        }
-
-        patterns = handler.getServletInfo().getErrorPage();
-        if ( patterns != null )
-        {
-            for(final String errorPage : patterns)
-            {
-                this.errorsMapping.addErrorServlet(errorPage, handler);
-            }
-        }
-        handler.init();
-        this.servletMap.put(handler.getServlet(), handler);
-
-        updateServletMapping();
+    	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;
+
+    		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()))
+            {
+            	useServletHandler(handler);
+            	if (!handler.isWhiteboardService())
+            	{
+            		handler.init();
+            	}
+            	increaseUseCount(handler);
+
+            	if (prevHandler != null)
+            	{
+            		decreaseUseCount(prevHandler);
+            		toRemove.put(p, prevHandler);
+            	}
+            	toAdd.put(p, handler);
+            }
+    	}
+    	
+    	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 @@ public final class PerContextHandlerRegi
 
         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 final class PerContextHandlerRegi
 
     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 @@ public final class PerContextHandlerRegi
         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 final class PerContextHandlerRegi
 
     public synchronized Filter removeFilter(final FilterInfo filterInfo, final boolean destroy)
     {
-        for(final FilterHandler handler : this.filterMap.values())
+        FilterHandler handler = getFilterHandler(filterInfo);
+
+        if (handler == null)
         {
-            if ( handler.getFilterInfo().compareTo(filterInfo) == 0)
-            {
-                this.filterMap.remove(handler.getFilter());
-                updateFilterMapping();
-                if (destroy)
-                {
-                    handler.destroy();
-                }
-                return handler.getFilter();
-            }
+            return null;
         }
-        return null;
+
+        this.filterMapping = this.filterMapping.remove(handler);
+
+        if (destroy)
+        {
+            handler.destroy();
+        }
+        return handler.getFilter();
     }
 
-    public synchronized Servlet removeServlet(ServletInfo servletInfo, final boolean destroy)
+    private FilterHandler getFilterHandler(final FilterInfo filterInfo)
     {
-        for(final ServletHandler handler : this.servletMap.values())
+        for(final FilterHandler handler : this.filterMap.values())
         {
-            if ( handler.getServletInfo().compareTo(servletInfo) == 0 )
+            if ( handler.getFilterInfo().compareTo(filterInfo) == 0)
             {
-                this.servletMap.remove(handler.getServlet());
-                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(handler.getServlet());
-
-                if (destroy)
-                {
-                    handler.destroy();
-                }
-                return handler.getServlet();
+                return handler;
             }
         }
         return null;
     }
-
+    
+    public synchronized Servlet removeServlet(ServletInfo servletInfo, final boolean destroy)
+    {
+    	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);
+
+    	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);
+
+		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 @@ public final class PerContextHandlerRegi
         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 @@ public final class PerContextHandlerRegi
 
         List<ServletHandler> servletHandlers = new ArrayList<ServletHandler>();
         List<ServletHandler> resourceHandlers = new ArrayList<ServletHandler>();
-        for (ServletHandler servletHandler : servletMap.values())
-        {
-            if (servletHandler.getServletInfo().isResource())
+        
+        Iterator<ServletHandler> it = this.allServletHandlers.iterator();
+        while(it.hasNext())
+        {
+        	ServletHandler servletHandler = it.next();
+        	
+        	if (servletHandler.getServletInfo().isResource())
             {
                 resourceHandlers.add(servletHandler);
             }
@@ -378,5 +548,5 @@ public final class PerContextHandlerRegi
         }
 
         return new HandlerRuntime(servletHandlers, filterHandlers, resourceHandlers, errorPages, serviceId);
-    }
+    }    
 }

Modified: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java?rev=1665462&r1=1665461&r2=1665462&view=diff
==============================================================================
--- felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java (original)
+++ felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java Tue Mar 10 10:45:02 2015
@@ -36,24 +36,37 @@ import org.apache.felix.http.base.intern
 /**
  * @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 @@ public final class ServletHandler extend
             this.contextServiceId = 0;
         }
     }
+    
 
     @Override
     public int compareTo(final ServletHandler other)
@@ -111,6 +125,16 @@ public final class ServletHandler extend
     {
         return this.servlet;
     }
+    
+    void setServlet(Servlet servlet)
+    {
+    	this.servlet = servlet;
+    }
+    
+    public boolean isWhiteboardService()
+    {
+    	return this.isWhiteboardService;
+    }
 
     @Override
     public Pattern[] getPatterns()

Modified: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java?rev=1665462&r1=1665461&r2=1665462&view=diff
==============================================================================
--- felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java (original)
+++ felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java Tue Mar 10 10:45:02 2015
@@ -118,14 +118,17 @@ public abstract class AbstractInfo<T> im
         }
         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<?>)
         {

Modified: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/util/PatternUtil.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/util/PatternUtil.java?rev=1665462&r1=1665461&r2=1665462&view=diff
==============================================================================
--- felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/util/PatternUtil.java (original)
+++ felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/util/PatternUtil.java Tue Mar 10 10:45:02 2015
@@ -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 @@ public class PatternUtil
 
         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(".*");
+    }
 }

Modified: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java?rev=1665462&r1=1665461&r2=1665462&view=diff
==============================================================================
--- felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java (original)
+++ felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java Tue Mar 10 10:45:02 2015
@@ -57,29 +57,21 @@ public final class WhiteboardHttpService
     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 @@ public final class WhiteboardHttpService
         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 final class WhiteboardHttpService
     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
+    	}
     }
 
     /**

Modified: felix/trunk/http/base/src/test/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistryTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/test/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistryTest.java?rev=1665462&r1=1665461&r2=1665462&view=diff
==============================================================================
--- felix/trunk/http/base/src/test/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistryTest.java (original)
+++ felix/trunk/http/base/src/test/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistryTest.java Tue Mar 10 10:45:02 2015
@@ -35,10 +35,10 @@ public class PerContextHandlerRegistryTe
     @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 @@ public class PerContextHandlerRegistryTe
     @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 @@ public class PerContextHandlerRegistryTe
 
     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 @@ public class PerContextHandlerRegistryTe
             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));

Added: felix/trunk/http/itest/src/test/java/org/apache/felix/http/itest/HttpWhiteboardTargetTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/itest/src/test/java/org/apache/felix/http/itest/HttpWhiteboardTargetTest.java?rev=1665462&view=auto
==============================================================================
--- felix/trunk/http/itest/src/test/java/org/apache/felix/http/itest/HttpWhiteboardTargetTest.java (added)
+++ felix/trunk/http/itest/src/test/java/org/apache/felix/http/itest/HttpWhiteboardTargetTest.java Tue Mar 10 10:45:02 2015
@@ -0,0 +1,391 @@
+/*
+ * 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.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
+import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
+import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
+import static javax.servlet.http.HttpServletResponse.SC_OK;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+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.ServiceRegistration;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+@RunWith(JUnit4TestRunner.class)
+public class HttpWhiteboardTargetTest extends BaseIntegrationTest
+{
+	
+	private static final String SERVICE_HTTP_PORT = "org.osgi.service.http.port"; 
+	
+	/**]
+	 * Test that a servlet with the org.osgi.http.whiteboard.target property not set
+	 * is registered with the whiteboard
+	 */
+	@Test
+	public void testServletNoTargetProperty() throws Exception
+	{
+		CountDownLatch initLatch = new CountDownLatch(1);
+		CountDownLatch destroyLatch = new CountDownLatch(1);
+		
+		TestServlet servlet = new TestServlet(initLatch, destroyLatch) 
+		{
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException 
+			{
+				resp.getWriter().print("It works!");
+				resp.flushBuffer();
+			}
+		};
+		
+		Dictionary<String, Object> props = new Hashtable<String, Object>();
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/servletAlias");
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_INIT_PARAM_PREFIX + ".myname", "servletName");
+		
+		ServiceRegistration<?> reg = m_context.registerService(Servlet.class.getName(), servlet, props);
+
+		try {
+			assertTrue(initLatch.await(5, TimeUnit.SECONDS));
+			URL testURL = createURL("/servletAlias");
+            assertContent("It works!", testURL);            
+		} finally {
+				reg.unregister();	
+		}
+		assertTrue(destroyLatch.await(5, TimeUnit.SECONDS));
+	}
+	
+	/**
+	 * Test that a servlet with the org.osgi.http.whiteboard.target property matching the 
+	 * HttpServiceRuntime properties is registered with the whiteboard. 
+	 * 
+	 * In the current implementation the HttpServiceRuntime properties are the same as the
+	 * HttpService properties. 
+	 * 
+	 */
+	@Test
+	public void testServletTargetMatchPort() throws Exception 
+	{
+		CountDownLatch initLatch = new CountDownLatch(1);
+		CountDownLatch destroyLatch = new CountDownLatch(1);
+		
+		TestServlet servlet = new TestServlet(initLatch, destroyLatch)
+		{
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException 
+			{
+				resp.getWriter().print("matchingServlet");
+				resp.flushBuffer();
+			}
+		};
+		
+		Dictionary<String, Object> props = new Hashtable<String, Object>();
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/servletAlias");
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_INIT_PARAM_PREFIX + ".myname", "servletName");
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_TARGET, "(" + SERVICE_HTTP_PORT + "=8080" + ")");
+		
+		ServiceRegistration<?> reg = m_context.registerService(Servlet.class.getName(), servlet, props);
+		
+		try {
+			assertTrue(initLatch.await(5, TimeUnit.SECONDS));
+			URL testURL = createURL("/servletAlias");
+            assertContent("matchingServlet", testURL);            
+		} finally {
+			reg.unregister();
+		}
+		
+		assertTrue(destroyLatch.await(5, TimeUnit.SECONDS));		
+	}
+	
+	/**
+	 * Test that a servlet with the org.osgi.http.whiteboard.target property not matching
+	 * the properties of the HttpServiceRuntime is not registered with the whiteboard.
+	 * 
+	 */
+	@Test
+	public void testServletTargetNotMatchPort() throws Exception 
+	{
+		CountDownLatch initLatch = new CountDownLatch(1);
+		CountDownLatch destroyLatch = new CountDownLatch(1);
+
+		TestServlet nonMatchingServlet = new TestServlet(initLatch, destroyLatch) 
+		{
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException 
+			{
+				resp.getWriter().print("nonMatchingServlet");
+				resp.flushBuffer();
+			}
+		};
+		
+		Dictionary<String, Object> props = new Hashtable<String, Object>();
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/servletAlias");
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_INIT_PARAM_PREFIX + ".myname", "servletName");
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_TARGET, "(" + SERVICE_HTTP_PORT + "=8282" + ")");
+		
+		ServiceRegistration<?> reg = m_context.registerService(Servlet.class.getName(), nonMatchingServlet, props);
+		
+		try {
+			// the servlet will not be registered, its init method will not be called, await must return false due to timeout
+			assertFalse(initLatch.await(5, TimeUnit.SECONDS));
+			URL testURL = createURL("/servletAlias");
+			assertResponseCode(404, testURL);
+		} finally {
+			reg.unregister();
+		}
+	}	
+	
+	/**
+	 * Test that a filter with no target property set is correctly registered with the whiteboard
+	 * 
+	 */
+	@Test
+	public void testFilterNoTargetProperty() throws Exception
+	{
+		CountDownLatch initLatch = new CountDownLatch(3);
+		CountDownLatch destroyLatch = new CountDownLatch(3);
+		
+		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();
+			}
+		};
+		Dictionary<String, Object> props1 = new Hashtable<String, Object>();
+		props1.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/servlet/1");
+		props1.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_INIT_PARAM_PREFIX + ".myname", "servlet1");
+		props1.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_TARGET, "(" + SERVICE_HTTP_PORT + "=8080" + ")");
+		
+		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> props2 = new Hashtable<String, Object>();
+		props2.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/servlet/2");
+		props2.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_INIT_PARAM_PREFIX + ".myname", "servle2");
+		props2.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_TARGET, "(" + SERVICE_HTTP_PORT + "=8080" + ")");
+		
+		TestFilter filter = new TestFilter(initLatch, destroyLatch) 
+		{
+			@Override
+			protected void filter(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) throws IOException, ServletException
+			{
+				String param = req.getParameter("param");
+				if("forbidden".equals(param))
+				{
+					resp.reset();
+					resp.sendError(SC_FORBIDDEN);
+					resp.flushBuffer();
+				} 
+				else 
+				{
+					chain.doFilter(req, resp);
+				}
+			}
+		};
+
+		Dictionary<String, Object> props = new Hashtable<String, Object>();
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN, "/servlet/1");
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_INIT_PARAM_PREFIX + ".myname", "servletName");
+
+		ServiceRegistration<?> reg1 = m_context.registerService(Servlet.class.getName(), servlet1, props1);
+		ServiceRegistration<?> reg2 = m_context.registerService(Servlet.class.getName(), servlet2, props2);
+		ServiceRegistration<?> reg = m_context.registerService(Filter.class.getName(), filter, props);
+		
+		assertTrue(initLatch.await(5, TimeUnit.SECONDS));
+		
+		assertResponseCode(SC_FORBIDDEN, createURL("/servlet/1?param=forbidden"));
+		assertContent("servlet1", createURL("/servlet/1?param=any"));
+		assertContent("servlet1", createURL("/servlet/1"));
+		
+		assertResponseCode(SC_OK, createURL("/servlet/2?param=forbidden"));
+		assertContent("servlet2", createURL("/servlet/2?param=forbidden"));
+		
+		reg1.unregister();
+		reg2.unregister();
+		reg.unregister();
+		
+		assertTrue(destroyLatch.await(5, TimeUnit.SECONDS));
+	}	
+	
+	@Test
+	public void testFilterTargetMatchPort() throws Exception
+	{
+		CountDownLatch initLatch = new CountDownLatch(2);
+		CountDownLatch destroyLatch = new CountDownLatch(2);
+		
+		TestServlet servlet = new TestServlet(initLatch, destroyLatch)
+		{
+			private static final long serialVersionUID = 1L;
+			
+			@Override
+			protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException
+			{
+				resp.getWriter().print("servlet");
+				resp.flushBuffer();
+			}
+		};
+		Dictionary<String, Object> sprops = new Hashtable<String, Object>();
+		sprops.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/servlet");
+		sprops.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_INIT_PARAM_PREFIX + ".myname", "servlet1");
+		sprops.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_TARGET, "(" + SERVICE_HTTP_PORT + "=8080" + ")");
+		
+		TestFilter filter = new TestFilter(initLatch, destroyLatch) 
+		{
+			@Override
+			protected void filter(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) throws IOException, ServletException
+			{
+				String param = req.getParameter("param");
+				if("forbidden".equals(param))
+				{
+					resp.reset();
+					resp.sendError(SC_FORBIDDEN);
+					resp.flushBuffer();
+				} 
+				else 
+				{
+					chain.doFilter(req, resp);
+				}
+			}
+		};
+
+		Dictionary<String, Object> fprops = new Hashtable<String, Object>();
+		fprops.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN, "/servlet");
+		fprops.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_INIT_PARAM_PREFIX + ".myname", "servletName");
+		fprops.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_TARGET, "(" + SERVICE_HTTP_PORT + "=8080" + ")");
+
+		ServiceRegistration<?> sreg = m_context.registerService(Servlet.class.getName(), servlet, sprops);
+		ServiceRegistration<?> freg = m_context.registerService(Filter.class.getName(), filter, fprops);
+		
+		assertTrue(initLatch.await(5, TimeUnit.SECONDS));
+		
+		assertResponseCode(SC_FORBIDDEN, createURL("/servlet?param=forbidden"));
+		assertContent("servlet", createURL("/servlet?param=any"));
+		assertContent("servlet", createURL("/servlet"));
+		
+		sreg.unregister();
+		freg.unregister();
+		
+		assertTrue(destroyLatch.await(5, TimeUnit.SECONDS));
+	}	
+
+	@Test
+	public void testFilterTargetNotMatchPort() throws Exception
+	{
+		CountDownLatch servletInitLatch = new CountDownLatch(1);
+		CountDownLatch servletDestroyLatch = new CountDownLatch(1);
+		
+		CountDownLatch filterInitLatch = new CountDownLatch(1);
+		CountDownLatch filterDestroyLatch = new CountDownLatch(1);
+		
+		TestServlet servlet = new TestServlet(servletInitLatch, servletDestroyLatch)
+		{
+			private static final long serialVersionUID = 1L;
+			
+			@Override
+			protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException
+			{
+				resp.getWriter().print("servlet");
+				resp.flushBuffer();
+			}
+		};
+		Dictionary<String, Object> sprops = new Hashtable<String, Object>();
+		sprops.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/servlet");
+		sprops.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_INIT_PARAM_PREFIX + ".myname", "servlet1");
+		sprops.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_TARGET, "(" + SERVICE_HTTP_PORT + "=8080" + ")");
+		
+		TestFilter filter = new TestFilter(filterInitLatch, filterDestroyLatch) 
+		{
+			@Override
+			protected void filter(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) throws IOException, ServletException
+			{
+				String param = req.getParameter("param");
+				if("forbidden".equals(param))
+				{
+					resp.reset();
+					resp.sendError(SC_FORBIDDEN);
+					resp.flushBuffer();
+				} 
+				else 
+				{
+					chain.doFilter(req, resp);
+				}
+			}
+		};
+
+		Dictionary<String, Object> fprops = new Hashtable<String, Object>();
+		fprops.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN, "/servlet");
+		fprops.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_INIT_PARAM_PREFIX + ".myname", "servletName");
+		fprops.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_TARGET, "(" + SERVICE_HTTP_PORT + "=8181" + ")");
+
+		ServiceRegistration<?> sreg = m_context.registerService(Servlet.class.getName(), servlet, sprops);
+		ServiceRegistration<?> freg = m_context.registerService(Filter.class.getName(), filter, fprops);
+		
+		// servlet is registered
+		assertTrue(servletInitLatch.await(5, TimeUnit.SECONDS));
+		// fitler is not registered, timeout occurs
+		assertFalse(filterInitLatch.await(5, TimeUnit.SECONDS));
+		
+		assertResponseCode(SC_OK, createURL("/servlet?param=forbidden"));
+		assertContent("servlet", createURL("/servlet?param=forbidden"));
+		assertContent("servlet", createURL("/servlet?param=any"));
+		assertContent("servlet", createURL("/servlet"));
+		
+		sreg.unregister();
+		freg.unregister();
+		
+		assertTrue(servletDestroyLatch.await(5, TimeUnit.SECONDS));
+		assertFalse(filterDestroyLatch.await(5, TimeUnit.SECONDS));
+	}	
+}

Propchange: felix/trunk/http/itest/src/test/java/org/apache/felix/http/itest/HttpWhiteboardTargetTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: felix/trunk/http/itest/src/test/java/org/apache/felix/http/itest/ServletPatternTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/itest/src/test/java/org/apache/felix/http/itest/ServletPatternTest.java?rev=1665462&view=auto
==============================================================================
--- felix/trunk/http/itest/src/test/java/org/apache/felix/http/itest/ServletPatternTest.java (added)
+++ felix/trunk/http/itest/src/test/java/org/apache/felix/http/itest/ServletPatternTest.java Tue Mar 10 10:45:02 2015
@@ -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));
+	}
+}
+
+
+

Propchange: felix/trunk/http/itest/src/test/java/org/apache/felix/http/itest/ServletPatternTest.java
------------------------------------------------------------------------------
    svn:eol-style = native