FELIX-4541 : Add support for multiple patterns in servlet/filter registration. Apply patch from Thomas Baier

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1672256 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/http/base/pom.xml b/http/base/pom.xml
index b5731b8..b579905 100644
--- a/http/base/pom.xml
+++ b/http/base/pom.xml
@@ -85,5 +85,11 @@
             <artifactId>jsr305</artifactId>
             <version>3.0.0</version>
         </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <version>1.3</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
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 1c1dbfe..79a1958 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.bundleContext);
+        this.registry = new HandlerRegistry();
         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/dispatch/Dispatcher.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java
index 8750079..c675987 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java
@@ -51,7 +51,6 @@
 import javax.servlet.http.HttpSession;
 
 import org.apache.felix.http.base.internal.context.ExtServletContext;
-import org.apache.felix.http.base.internal.handler.ErrorsMapping;
 import org.apache.felix.http.base.internal.handler.FilterHandler;
 import org.apache.felix.http.base.internal.handler.HandlerRegistry;
 import org.apache.felix.http.base.internal.handler.HttpSessionWrapper;
@@ -167,65 +166,49 @@
                     code != SC_PARTIAL_CONTENT &&
                     code >= SC_OK)
                 {
+                    final String exceptionType = (String)request.getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE);
+                    final ServletHandler errorHandler = handlerRegistry.getErrorsHandler(request.getRequestURI(), this.serviceId, code, exceptionType);
 
-                    final ErrorsMapping errorsMapping = handlerRegistry.getErrorsMapping(request.getRequestURI(), this.serviceId);
-                    if ( errorsMapping != null )
+                    if ( errorHandler != null )
                     {
-                        final String exceptionType = (String)request.getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE);
-
-                        ServletHandler errorHandler = null;
-
-                        if (exceptionType != null)
+                        try
                         {
-                            errorHandler = errorsMapping.get(exceptionType);
+                            request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, new Integer(code));
+                            if ( message != null )
+                            {
+                                request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message);
+                            }
+                            request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, request.getRequestURI());
+                            if ( this.servletName != null )
+                            {
+                                request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME, this.servletName);
+                            }
+
+                            final String servletPath = null;
+                            final String pathInfo = request.getRequestURI();
+                            final String queryString = null; // XXX
+
+                            final RequestInfo requestInfo = new RequestInfo(servletPath, pathInfo, queryString);
+
+                            final FilterHandler[] filterHandlers = handlerRegistry.getFilterHandlers(errorHandler, DispatcherType.ERROR, request.getRequestURI());
+
+                            // TODO - is async = false correct?
+                            invokeChain(filterHandlers, errorHandler, new ServletRequestWrapper(request, errorHandler.getContext(), requestInfo, this.serviceId, false), this);
+
+                            invokeSuper = false;
                         }
-
-                        if ( errorHandler == null )
+                        catch (final ServletException e)
                         {
-                            errorHandler = errorsMapping.get(code);
+                            // ignore
                         }
-
-                        if ( errorHandler != null )
+                        finally
                         {
-                            try
-                            {
-                                request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, new Integer(code));
-                                if ( message != null )
-                                {
-                                    request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message);
-                                }
-                                request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, request.getRequestURI());
-                                if ( this.servletName != null )
-                                {
-                                    request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME, this.servletName);
-                                }
-
-                                final String servletPath = null;
-                                final String pathInfo = request.getRequestURI();
-                                final String queryString = null; // XXX
-
-                                final RequestInfo requestInfo = new RequestInfo(servletPath, pathInfo, queryString);
-
-                                final FilterHandler[] filterHandlers = handlerRegistry.getFilterHandlers(errorHandler, DispatcherType.ERROR, request.getRequestURI());
-
-                                // TODO - is async = false correct?
-                                invokeChain(filterHandlers, errorHandler, new ServletRequestWrapper(request, errorHandler.getContext(), requestInfo, this.serviceId, false), this);
-
-                                invokeSuper = false;
-                            }
-                            catch (final ServletException e)
-                            {
-                                // ignore
-                            }
-                            finally
-                            {
-                                request.removeAttribute(RequestDispatcher.ERROR_STATUS_CODE);
-                                request.removeAttribute(RequestDispatcher.ERROR_MESSAGE);
-                                request.removeAttribute(RequestDispatcher.ERROR_REQUEST_URI);
-                                request.removeAttribute(RequestDispatcher.ERROR_SERVLET_NAME);
-                                request.removeAttribute(RequestDispatcher.ERROR_EXCEPTION);
-                                request.removeAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE);
-                            }
+                            request.removeAttribute(RequestDispatcher.ERROR_STATUS_CODE);
+                            request.removeAttribute(RequestDispatcher.ERROR_MESSAGE);
+                            request.removeAttribute(RequestDispatcher.ERROR_REQUEST_URI);
+                            request.removeAttribute(RequestDispatcher.ERROR_SERVLET_NAME);
+                            request.removeAttribute(RequestDispatcher.ERROR_EXCEPTION);
+                            request.removeAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE);
                         }
                     }
                 }
@@ -601,7 +584,7 @@
         }
 
         // Determine which servlets we should forward the request to...
-        final ServletHandler servletHandler = this.handlerRegistry.getServletHander(requestURI);
+        final ServletHandler servletHandler = this.handlerRegistry.getServletHandler(requestURI);
 
         final HttpServletResponse wrappedResponse = new ServletResponseWrapper(req, res, servletHandler);
         if ( servletHandler == null )
@@ -678,7 +661,7 @@
             requestURI = "";
         }
 
-        ServletHandler handler = this.handlerRegistry.getServletHander(requestURI);
+        ServletHandler handler = this.handlerRegistry.getServletHandler(requestURI);
         if (handler == null)
         {
             return null;
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ErrorsMapping.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ErrorsMapping.java
index e8b545b..b319c52 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ErrorsMapping.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ErrorsMapping.java
@@ -18,77 +18,83 @@
 
 import static org.apache.felix.http.base.internal.util.CollectionUtils.sortedUnion;
 
-import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
 import java.util.Map;
-import java.util.regex.Pattern;
 
-import javax.servlet.Servlet;
-import javax.servlet.ServletException;
-
-import org.apache.felix.http.base.internal.runtime.dto.ErrorPageRuntime;
+import org.apache.felix.http.base.internal.util.ErrorPageUtil;
+import org.apache.felix.http.base.internal.whiteboard.RegistrationFailureException;
+import org.osgi.service.http.runtime.dto.DTOConstants;
 
 public final class ErrorsMapping
 {
-    private static final Pattern ERROR_CODE_PATTERN = Pattern.compile("\\d{3}");
-
     private final Map<Integer, ServletHandler> errorCodesMap;
     private final Map<String, ServletHandler> exceptionsMap;
 
-    // inverted indexes
-
-    private final Map<Servlet, Collection<Integer>> invertedErrorCodesMap;
-    private final Map<Servlet, Collection<String>> invertedExceptionsMap;
-
-    public ErrorsMapping()
+    ErrorsMapping()
     {
-        this.errorCodesMap = new HashMap<Integer, ServletHandler>();
-        this.exceptionsMap = new HashMap<String, ServletHandler>();
-        this.invertedErrorCodesMap = new HashMap<Servlet, Collection<Integer>>();
-        this.invertedExceptionsMap = new HashMap<Servlet, Collection<String>>();
+        this(new HashMap<Integer, ServletHandler>(), new HashMap<String, ServletHandler>());
     }
 
-    void addErrorServlet(String errorPage, ServletHandler handler) throws ServletException
+    ErrorsMapping(Map<Integer, ServletHandler> errorCodesMap, Map<String, ServletHandler> exceptionsMap)
     {
-        // TODO Handle special values 4xx and 5xx
-        if (ERROR_CODE_PATTERN.matcher(errorPage).matches())
-        {
-            Integer errorCode = Integer.valueOf(errorPage);
-            addErrorServlet(handler, errorCode, this.errorCodesMap, this.invertedErrorCodesMap);
-        }
-        else
-        {
-            addErrorServlet(handler, errorPage, this.exceptionsMap, this.invertedExceptionsMap);
-        }
+        this.errorCodesMap = errorCodesMap;
+        this.exceptionsMap = exceptionsMap;
     }
 
-    private static <E> void addErrorServlet(ServletHandler handler, E error, Map<E, ServletHandler> index, Map<Servlet, Collection<E>> invertedIndex) throws ServletException
+    ErrorsMapping update(Map<String, ServletHandler> add, Map<String, ServletHandler> remove) throws RegistrationFailureException
     {
-        if (!index.containsKey(error))
+        Map<Integer, ServletHandler> newErrorCodesMap = new HashMap<Integer, ServletHandler>(this.errorCodesMap);
+        Map<String, ServletHandler> newExceptionsMap = new HashMap<String, ServletHandler>(this.exceptionsMap);;
+
+        for (Map.Entry<String, ServletHandler> errorPage : remove.entrySet())
         {
-            index.put(error, handler);
-        }
-        else
-        {
-            throw new ServletException("Handler for error " + error + " already registered");
+            String errorString = errorPage.getKey();
+            if (ErrorPageUtil.isErrorCode(errorString))
+            {
+                Integer errorCode = Integer.valueOf(errorString);
+                newErrorCodesMap.remove(errorCode);
+            }
+            else
+            {
+                newExceptionsMap.remove(errorString);
+            }
         }
 
-        Servlet servlet = handler.getServlet();
-        Collection<E> values = invertedIndex.get(servlet);
-
-        if (values == null)
+        for (Map.Entry<String, ServletHandler> errorPage : add.entrySet())
         {
-            values = new LinkedList<E>();
-            invertedIndex.put(servlet, values);
+            String errorString = errorPage.getKey();
+            if (ErrorPageUtil.isErrorCode(errorString))
+            {
+                Integer errorCode = Integer.valueOf(errorString);
+                addErrorServlet(errorCode, errorPage.getValue(), newErrorCodesMap);
+            }
+            else
+            {
+                addErrorServlet(errorString, errorPage.getValue(), newExceptionsMap);
+            }
         }
 
-        values.add(error);
+        return new ErrorsMapping(newErrorCodesMap, newExceptionsMap);
     }
 
+    private <E> void addErrorServlet(E error, ServletHandler handler, Map<E, ServletHandler> index) throws RegistrationFailureException
+    {
+        if (index.containsKey(error))
+        {
+            throw new RegistrationFailureException(handler.getServletInfo(), DTOConstants.FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE,
+                "Handler for error " + error + " already registered");
+        }
+        index.put(error, handler);
+    }
+
+    void clear()
+    {
+        this.errorCodesMap.clear();
+        this.exceptionsMap.clear();
+    }
+
+
     public ServletHandler get(int errorCode)
     {
         return this.errorCodesMap.get(errorCode);
@@ -99,51 +105,10 @@
         return this.exceptionsMap.get(exception);
     }
 
-    void removeServlet(Servlet servlet)
-    {
-        removeMapping(servlet, this.errorCodesMap, this.invertedErrorCodesMap);
-        removeMapping(servlet, this.exceptionsMap, this.invertedExceptionsMap);
-    }
-
-    private static <E> void removeMapping(Servlet servlet, Map<E, ServletHandler> index, Map<Servlet, Collection<E>> invertedIndex)
-    {
-        Collection<E> keys = invertedIndex.remove(servlet);
-        if (keys != null && !keys.isEmpty())
-        {
-            for (E key : keys)
-            {
-                index.remove(key);
-            }
-        }
-    }
-
-    void clear()
-    {
-        this.errorCodesMap.clear();
-        this.exceptionsMap.clear();
-    }
 
     @SuppressWarnings("unchecked")
-    public Collection<ServletHandler> getMappedHandlers()
+    Collection<ServletHandler> values()
     {
         return sortedUnion(errorCodesMap.values(), exceptionsMap.values());
     }
-
-    public ErrorPageRuntime getErrorPage(ServletHandler servletHandler)
-    {
-        Collection<Integer> errorCodes = getCopy(servletHandler, invertedErrorCodesMap);
-        Collection<String> exceptions = getCopy(servletHandler, invertedExceptionsMap);
-        return new ErrorPageRuntime(servletHandler, errorCodes, exceptions);
-    }
-
-    private static <T> List<T> getCopy(ServletHandler key, Map<Servlet, Collection<T>> map)
-    {
-        Collection<T> result = map.get(key.getServlet());
-        if (result != null)
-        {
-            return new ArrayList<T>(result);
-        }
-
-        return Collections.emptyList();
-    }
 }
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 714e63d..9a7210d 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
@@ -34,6 +34,7 @@
 import java.util.regex.Pattern;
 
 import org.apache.felix.http.base.internal.util.PatternUtil;
+import org.apache.felix.http.base.internal.util.PatternUtil.PatternComparator;
 
 /**
  * Represents a Map-like structure that can map path-patterns to servlet/filter handlers, allowing
@@ -65,8 +66,8 @@
      */
     private HandlerMapping(Map<Pattern, Collection<V>> mappings)
     {
-        this.exactMap = new TreeMap<Pattern, Set<V>>(PatternUtil.PatternComparator.INSTANCE);
-        this.wildcardMap = new TreeMap<Pattern, Set<V>>(PatternUtil.PatternComparator.INSTANCE);
+        this.exactMap = new TreeMap<Pattern, Set<V>>(PatternComparator.INSTANCE);
+        this.wildcardMap = new TreeMap<Pattern, Set<V>>(PatternComparator.INSTANCE);
         this.mappedHandlers = new TreeSet<V>();
 
         for (Map.Entry<Pattern, Collection<V>> mapping : mappings.entrySet())
@@ -109,7 +110,7 @@
      */
     HandlerMapping<V> add(V handler)
     {
-        Map<Pattern, V> mappings = new TreeMap<Pattern, V>(PatternUtil.PatternComparator.INSTANCE);
+        Map<Pattern, V> mappings = new TreeMap<Pattern, V>(PatternComparator.INSTANCE);
         for (Pattern pattern : handler.getPatterns())
         {
             mappings.put(pattern, handler);
@@ -120,14 +121,7 @@
     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());
-        }
+        addMappings(mappings, newMappings);
         return new HandlerMapping<V>(newMappings);
     }
 
@@ -141,7 +135,7 @@
      */
     HandlerMapping<V> remove(V handler)
     {
-        Map<Pattern, V> mappings = new TreeMap<Pattern, V>(PatternUtil.PatternComparator.INSTANCE);
+        Map<Pattern, V> mappings = new TreeMap<Pattern, V>(PatternComparator.INSTANCE);
         for (Pattern pattern : handler.getPatterns())
         {
             mappings.put(pattern, handler);
@@ -152,9 +146,35 @@
     HandlerMapping<V> remove(Map<Pattern, V> mappings)
     {
         Map<Pattern, Collection<V>> newMappings = getAllMappings();
+        removeMappings(mappings, newMappings);
+        return new HandlerMapping<V>(newMappings);
+    }
+
+    HandlerMapping<V> update(Map<Pattern, V> add, Map<Pattern, V> remove)
+    {
+        Map<Pattern, Collection<V>> newMappings = getAllMappings();
+        removeMappings(remove, newMappings);
+        addMappings(add, newMappings);
+        return new HandlerMapping<V>(newMappings);
+    }
+
+    private void addMappings(Map<Pattern, V> mappings, Map<Pattern, Collection<V>> target)
+    {
         for (Map.Entry<Pattern, V> mapping : mappings.entrySet())
         {
-            Collection<V> mappedHandlers = newMappings.get(mapping.getKey());
+            if (!target.containsKey(mapping.getKey()))
+            {
+                target.put(mapping.getKey(), new TreeSet<V>());
+            }
+            target.get(mapping.getKey()).add(mapping.getValue());
+        }
+    }
+
+    private void removeMappings(Map<Pattern, V> mappings, Map<Pattern, Collection<V>> target)
+    {
+        for (Map.Entry<Pattern, V> mapping : mappings.entrySet())
+        {
+            Collection<V> mappedHandlers = target.get(mapping.getKey());
             if (mappedHandlers == null)
             {
                 continue;
@@ -162,15 +182,14 @@
             mappedHandlers.remove(mapping.getValue());
             if (mappedHandlers.isEmpty())
             {
-                newMappings.remove(mapping.getKey());
+                target.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);
+        Map<Pattern, Collection<V>> newMappings = new TreeMap<Pattern, Collection<V>>(PatternComparator.INSTANCE);
         newMappings.putAll(exactMap);
         newMappings.putAll(wildcardMap);
         return newMappings;
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRankingMultimap.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRankingMultimap.java
new file mode 100644
index 0000000..e9a426a
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRankingMultimap.java
@@ -0,0 +1,322 @@
+/*
+ * 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.base.internal.handler;
+
+import static java.util.Arrays.asList;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.PriorityQueue;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+
+final class HandlerRankingMultimap<K>
+{
+    private final Map<ServletHandler, Integer> useCounts = new TreeMap<ServletHandler, Integer>();
+
+    private final Map<K, PriorityQueue<ServletHandler>> handlerMultimap;
+    private final Comparator<ServletHandler> handlerComparator;
+    private final Comparator<K> keyComparator;
+
+    private int size = 0;
+
+    HandlerRankingMultimap()
+    {
+        this.handlerMultimap = new HashMap<K, PriorityQueue<ServletHandler>>();
+        this.keyComparator = null;
+        this.handlerComparator = null;
+    }
+
+    HandlerRankingMultimap(Comparator<K> keyComparator)
+    {
+        this(keyComparator, null);
+    }
+
+    HandlerRankingMultimap(Comparator<K> keyComparator, Comparator<ServletHandler> handlerComparator)
+    {
+        this.keyComparator = keyComparator;
+        this.handlerMultimap = new TreeMap<K, PriorityQueue<ServletHandler>>(keyComparator);
+        this.handlerComparator = handlerComparator;
+    }
+
+    boolean isActive(ServletHandler handler)
+    {
+        return useCounts.containsKey(handler);
+    }
+
+    Update<K> add(K[] keys, ServletHandler handler)
+    {
+        return add(asList(keys), handler);
+    }
+
+    Update<K> add(Collection<K> keys, ServletHandler handler)
+    {
+        if (handler == null)
+        {
+            throw new NullPointerException("handler must not be null");
+        }
+        Map<K, ServletHandler> activate = createMap();
+        Map<K, ServletHandler> deactivate = createMap();
+        Set<ServletHandler> destroy = new TreeSet<ServletHandler>();
+        for (K key : keys)
+        {
+            PriorityQueue<ServletHandler> queue = getQueue(key);
+
+            if (queue.isEmpty() || compareHandlers(queue.peek(), handler) > 0)
+            {
+                activateEntry(key, handler, activate, null);
+
+                if (!queue.isEmpty())
+                {
+                    ServletHandler currentHead = queue.peek();
+                    deactivateEntry(key, currentHead, deactivate, destroy);
+                }
+            }
+
+            queue.add(handler);
+        }
+
+        size += 1;
+
+        return Update.forAdd(activate, deactivate, destroy);
+    }
+
+    Update<K> remove(K[] keys, ServletHandler handler)
+    {
+        return remove(asList(keys), handler);
+    }
+
+    Update<K> remove(Collection<K> keys, ServletHandler handler)
+    {
+        Map<K, ServletHandler> activate = createMap();
+        Map<K, ServletHandler> deactivate = createMap();
+        Set<ServletHandler> init = new TreeSet<ServletHandler>();
+        for (K key : keys)
+        {
+            PriorityQueue<ServletHandler> queue = handlerMultimap.get(key);
+            if (queue == null)
+            {
+                throw new IllegalArgumentException("Map does not contain key: " + key);
+            }
+
+            boolean isDeactivate = !queue.isEmpty() && compareHandlers(queue.peek(), handler) == 0;
+            queue.remove(handler);
+
+            if (isDeactivate)
+            {
+                deactivateEntry(key, handler, deactivate, null);
+
+                if (!queue.isEmpty())
+                {
+                    ServletHandler newHead = queue.peek();
+                    activateEntry(key, newHead, activate, init);
+                }
+            }
+
+            if (queue.isEmpty())
+            {
+                handlerMultimap.remove(key);
+            }
+        }
+
+        size -= 1;
+
+        return Update.forRemove(activate, deactivate, init);
+    }
+
+    private PriorityQueue<ServletHandler> getQueue(K key)
+    {
+        PriorityQueue<ServletHandler> queue = handlerMultimap.get(key);
+        if (queue == null)
+        {
+            queue = new PriorityQueue<ServletHandler>(1, handlerComparator);
+            handlerMultimap.put(key, queue);
+        }
+        return queue;
+    }
+
+    private void activateEntry(K key, ServletHandler handler, Map<K, ServletHandler> activate, Set<ServletHandler> init)
+    {
+        activate.put(key, handler);
+        if (incrementUseCount(handler) == 1 && init != null)
+        {
+            init.add(handler);
+        };
+    }
+
+    private void deactivateEntry(K key, ServletHandler handler, Map<K, ServletHandler> deactivate, Set<ServletHandler> destroy)
+    {
+        deactivate.put(key, handler);
+        if (decrementUseCount(handler) == 0 && destroy != null)
+        {
+            destroy.add(handler);
+        }
+    }
+
+    private int incrementUseCount(ServletHandler handler)
+    {
+        Integer currentCount = useCounts.get(handler);
+        Integer newCount = currentCount == null ? 1 : currentCount + 1;
+
+        useCounts.put(handler, newCount);
+
+        return newCount;
+    }
+
+    private int decrementUseCount(ServletHandler handler)
+    {
+        int currentCount = useCounts.get(handler);
+        if (currentCount == 1)
+        {
+            useCounts.remove(handler);
+            return 0;
+        }
+
+        int newCount = currentCount - 1;
+        useCounts.put(handler, newCount);
+        return newCount;
+    }
+
+    void clear()
+    {
+        handlerMultimap.clear();
+        useCounts.clear();
+    }
+
+    Collection<ServletHandler> getActiveValues()
+    {
+        TreeSet<ServletHandler> activeValues = new TreeSet<ServletHandler>();
+        for (PriorityQueue<ServletHandler> queue : handlerMultimap.values())
+        {
+            activeValues.add(queue.peek());
+        }
+        return activeValues;
+    }
+
+    Collection<ServletHandler> getShadowedValues()
+    {
+        TreeSet<ServletHandler> shadowedValues = new TreeSet<ServletHandler>();
+        for (PriorityQueue<ServletHandler> queue : handlerMultimap.values())
+        {
+            ServletHandler head = queue.element();
+            for (ServletHandler value : queue)
+            {
+                if (compareHandlers(value, head) != 0)
+                {
+                    shadowedValues.add(value);
+                }
+            }
+        }
+        return shadowedValues;
+    }
+
+    int size()
+    {
+        return size;
+    }
+
+    private int compareHandlers(ServletHandler one, ServletHandler two)
+    {
+        if (handlerComparator == null)
+        {
+            return one.compareTo(two);
+        }
+        return handlerComparator.compare(one, two);
+    }
+
+    private Map<K,ServletHandler> createMap()
+    {
+        return keyComparator == null ? new HashMap<K, ServletHandler>() : new TreeMap<K, ServletHandler>(keyComparator);
+    }
+
+    static final class Update<K>
+    {
+        private final Map<K, ServletHandler> activate;
+        private final Map<K, ServletHandler> deactivate;
+        private final Collection<ServletHandler> init;
+        private final Collection<ServletHandler> destroy;
+
+        Update(Map<K, ServletHandler> activate,
+                Map<K, ServletHandler> deactivate,
+                Collection<ServletHandler> init,
+                Collection<ServletHandler> destroy)
+        {
+            this.activate = activate;
+            this.deactivate = deactivate;
+            this.init = init;
+            this.destroy = destroy;
+        }
+
+        private static <K> Update<K> forAdd(Map<K, ServletHandler> activate,
+                Map<K, ServletHandler> deactivate,
+                Collection<ServletHandler> destroy)
+        {
+            // activate contains at most one value, mapped to multiple keys
+            Collection<ServletHandler> init = valueAsCollection(activate);
+            return new Update<K>(activate, deactivate, init, destroy);
+        }
+
+        private static <K> Update<K> forRemove(Map<K, ServletHandler> activate,
+                Map<K, ServletHandler> deactivate,
+                Collection<ServletHandler> init)
+        {
+            // deactivate contains at most one value, mapped to multiple keys
+            Collection<ServletHandler> destroy = valueAsCollection(deactivate);
+            return new Update<K>(activate, deactivate, init, destroy);
+        }
+
+        private static <K> Collection<ServletHandler> valueAsCollection(Map<K, ServletHandler> valueMap)
+        {
+            if (valueMap.isEmpty())
+            {
+                return Collections.emptyList();
+            }
+
+            Collection<ServletHandler> valueSet = new ArrayList<ServletHandler>(1);
+            valueSet.add(valueMap.values().iterator().next());
+            return valueSet;
+        }
+
+        Map<K, ServletHandler> getActivated()
+        {
+            return activate;
+        }
+
+        Map<K, ServletHandler> getDeactivated()
+        {
+            return deactivate;
+        }
+
+        Collection<ServletHandler> getInit()
+        {
+            return init;
+        }
+
+        Collection<ServletHandler> getDestroy()
+        {
+            return destroy;
+        }
+    }
+}
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 e872de1..02f68ea 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
@@ -1,5 +1,5 @@
 /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
+ * Licensed to the Apaanche 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
@@ -16,18 +16,32 @@
  */
 package org.apache.felix.http.base.internal.handler;
 
+import static org.osgi.service.http.runtime.dto.DTOConstants.FAILURE_REASON_SERVLET_CONTEXT_FAILURE;
+import static org.osgi.service.http.runtime.dto.DTOConstants.FAILURE_REASON_VALIDATION_FAILED;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+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.runtime.FilterInfo;
 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.runtime.WhiteboardServiceInfo;
 import org.apache.felix.http.base.internal.runtime.dto.ContextRuntime;
 import org.apache.felix.http.base.internal.runtime.dto.FailureRuntime;
-import org.osgi.framework.BundleContext;
+import org.apache.felix.http.base.internal.runtime.dto.HandlerRegistryRuntime;
+import org.apache.felix.http.base.internal.runtime.dto.InfoServletContextHelperRuntime;
+import org.apache.felix.http.base.internal.runtime.dto.ServletContextHelperRuntime;
+import org.apache.felix.http.base.internal.runtime.dto.ServletRegistryRuntime;
+import org.apache.felix.http.base.internal.whiteboard.RegistrationFailureException;
 
 /**
  * Registry for all services.
@@ -37,23 +51,22 @@
  */
 public final class HandlerRegistry
 {
+    private static final String HTTP_SERVICE_CONTEXT_NAME = "Http service context";
+
     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;
-    }
-    
+    private final ServletHandlerRegistry servletRegistry = new ServletHandlerRegistry();
+
     /**
      * Register default context registry for Http Service
      */
     public void init()
     {
-        this.add(new PerContextHandlerRegistry(this.bundleContext));
+        this.add(new PerContextHandlerRegistry());
+        servletRegistry.init();
     }
 
     /**
@@ -70,6 +83,7 @@
 
         }
 
+        servletRegistry.shutdown();
         for(final PerContextHandlerRegistry r : list)
         {
             r.removeAll();
@@ -82,7 +96,11 @@
      */
     public void add(@Nonnull ServletContextHelperInfo info)
     {
-        this.add(new PerContextHandlerRegistry(info, this.bundleContext));
+        synchronized ( this )
+        {
+            this.add(new PerContextHandlerRegistry(info));
+            this.servletRegistry.add(info);
+        }
     }
 
     /**
@@ -93,6 +111,7 @@
     {
         synchronized ( this )
         {
+            this.servletRegistry.remove(info);
             final List<PerContextHandlerRegistry> updatedList = new ArrayList<PerContextHandlerRegistry>(this.registrations);
             final Iterator<PerContextHandlerRegistry> i = updatedList.iterator();
             while ( i.hasNext() )
@@ -124,15 +143,58 @@
         }
     }
 
+    public void addFilter(FilterHandler handler) throws ServletException
+    {
+        getRegistryChecked(null, null).addFilter(handler);
+    }
+
+    public void addFilter(ServletContextHelperInfo contextInfo, FilterHandler handler) throws ServletException
+    {
+        getRegistryChecked(contextInfo, handler.getFilterInfo()).addFilter(handler);
+    }
+
+    public void removeFilter(Filter filter, boolean destroy)
+    {
+        try
+        {
+            getRegistryChecked(null, null).removeFilter(filter, destroy);
+        }
+        catch (RegistrationFailureException e)
+        {
+            // TODO
+        }
+    }
+
+    public Filter removeFilter(ServletContextHelperInfo contextInfo, FilterInfo filterInfo)
+    {
+        return getRegistry(contextInfo).removeFilter(filterInfo, true);
+    }
+
+    private PerContextHandlerRegistry getRegistryChecked(ServletContextHelperInfo info, WhiteboardServiceInfo<?> serviceInfo)
+        throws RegistrationFailureException
+    {
+        PerContextHandlerRegistry registry = getRegistry(info);
+        if (registry == null)
+        {
+            throw new RegistrationFailureException(serviceInfo, FAILURE_REASON_SERVLET_CONTEXT_FAILURE);
+        }
+        return registry;
+    }
+
     /**
      * Get the per context registry.
      * @param info The servlet context helper info or {@code null} for the Http Service context.
      * @return A per context registry or {@code null}
      */
-    public PerContextHandlerRegistry getRegistry(final ServletContextHelperInfo info)
+    private PerContextHandlerRegistry getRegistry(final ServletContextHelperInfo info)
     {
         final long key = (info == null ? 0 : info.getServiceId());
 
+        return getRegistry(key);
+    }
+
+    private PerContextHandlerRegistry getRegistry(final long key)
+    {
         synchronized ( this )
         {
             for(final PerContextHandlerRegistry r : this.registrations)
@@ -143,11 +205,28 @@
                 }
             }
         }
-
         return null;
     }
 
-    public ErrorsMapping getErrorsMapping(final String requestURI, final Long serviceId)
+    public ServletHandler getErrorsHandler(String requestURI, Long serviceId, int code, String exceptionType)
+    {
+        ErrorsMapping errorsMapping = getErrorsMapping(requestURI, serviceId);
+        if (errorsMapping == null)
+        {
+            return null;
+        }
+
+        // TODO check exception hierarchy
+        ServletHandler errorHandler = errorsMapping.get(exceptionType);
+        if (errorHandler != null)
+        {
+            return errorHandler;
+        }
+
+        return errorsMapping.get(code);
+    }
+
+    private ErrorsMapping getErrorsMapping(final String requestURI, final Long serviceId)
     {
         final List<PerContextHandlerRegistry> regs = this.registrations;
         for(final PerContextHandlerRegistry r : regs)
@@ -181,6 +260,76 @@
         return EMPTY_FILTER_HANDLER;
     }
 
+    public ServletContextHelperRuntime getHttpServiceContextRuntime()
+    {
+        ServletContextHelperInfo info = new ServletContextHelperInfo(Integer.MAX_VALUE, 0, HTTP_SERVICE_CONTEXT_NAME, "/", null);
+        return new InfoServletContextHelperRuntime(info);
+    }
+
+    public synchronized void addServlet(ServletHandler handler) throws RegistrationFailureException
+    {
+        Pattern[] patterns = handler.getPatterns();
+        String[] errorPages = handler.getServletInfo().getErrorPage();
+        if (patterns != null && patterns.length > 0)
+        {
+            servletRegistry.addServlet(handler);
+        }
+        else if (errorPages != null && errorPages.length > 0)
+        {
+            getRegistry(handler.getContextServiceId()).addErrorPage(handler, errorPages);
+        }
+        else
+        {
+            throw new RegistrationFailureException(handler.getServletInfo(), FAILURE_REASON_VALIDATION_FAILED,
+                "Neither patterns nor errorPages specified for " + handler.getName());
+        }
+    }
+
+    public synchronized void removeServlet(Servlet servlet, boolean destroy)
+    {
+        try
+        {
+            servletRegistry.removeServlet(servlet, destroy);
+        }
+        catch (RegistrationFailureException e)
+        {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
+
+    public synchronized Servlet removeServlet(ServletInfo servletInfo)
+    {
+        try
+        {
+            return servletRegistry.removeServlet(servletInfo);
+        }
+        catch (RegistrationFailureException e)
+        {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public synchronized void removeServlet(long contextId, ServletInfo servletInfo) throws RegistrationFailureException
+    {
+        String[] patterns = servletInfo.getPatterns();
+        if (patterns != null && patterns.length > 0)
+        {
+            servletRegistry.removeServlet(contextId, servletInfo);
+        }
+        else
+        {
+            getRegistry(contextId).removeErrorPage(servletInfo);
+        }
+    }
+
+    public ServletHandler getServletHandler(String requestURI)
+    {
+        return servletRegistry.getServletHandler(requestURI);
+    }
+
     /**
      * Get the servlet handler for a servlet by name
      * @param contextId The context id or {@code null}
@@ -189,50 +338,17 @@
      */
     public ServletHandler getServletHandlerByName(final Long contextId, @Nonnull final String name)
     {
-        final long key = (contextId == null ? 0 : contextId);
-        final List<PerContextHandlerRegistry> regs = this.registrations;
-        for(final PerContextHandlerRegistry r : regs)
-        {
-            if ( key == r.getContextServiceId() )
-            {
-                return r.getServletHandlerByName(name);
-            }
-        }
-
-        return null;
+        return servletRegistry.getServletHandlerByName(contextId, name);
     }
 
-    /**
-     * Search the servlet handler for the request uri
-     * @param requestURI The request uri
-     * @return
-     */
-    public ServletHandler getServletHander(@Nonnull final String requestURI)
-    {
-        // search the first matching context registry
-        final List<PerContextHandlerRegistry> regs = this.registrations;
-        for(final PerContextHandlerRegistry r : regs)
-        {
-        	final String pathInContext = r.isMatching(requestURI);
-        	if ( pathInContext != null )
-        	{
-                final ServletHandler handler = r.getServletHander(pathInContext);
-                if ( handler != null )
-                {
-                    return handler;
-                }
-            }
-        }
-        return null;
-    }
-
-    public synchronized List<ContextRuntime> getRuntime(FailureRuntime.Builder failureRuntimeBuilder)
+    public synchronized HandlerRegistryRuntime getRuntime(FailureRuntime.Builder failureRuntimeBuilder)
     {
         List<ContextRuntime> handlerRuntimes = new ArrayList<ContextRuntime>();
         for (PerContextHandlerRegistry contextRegistry : this.registrations)
         {
             handlerRuntimes.add(contextRegistry.getRuntime(failureRuntimeBuilder));
         }
-        return handlerRuntimes;
+        ServletRegistryRuntime servletRegistryRuntime = servletRegistry.getRuntime(failureRuntimeBuilder);
+        return new HandlerRegistryRuntime(handlerRuntimes, servletRegistryRuntime);
     }
 }
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 3ead1b6..6fc17e5 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
@@ -27,19 +27,13 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
-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.handler.HandlerRankingMultimap.Update;
 import org.apache.felix.http.base.internal.runtime.FilterInfo;
 import org.apache.felix.http.base.internal.runtime.ServletContextHelperInfo;
 import org.apache.felix.http.base.internal.runtime.ServletInfo;
@@ -48,25 +42,16 @@
 import org.apache.felix.http.base.internal.runtime.dto.FailureRuntime;
 import org.apache.felix.http.base.internal.runtime.dto.FilterRuntime;
 import org.apache.felix.http.base.internal.runtime.dto.ServletRuntime;
-import org.apache.felix.http.base.internal.whiteboard.ResourceServlet;
-import org.apache.felix.http.base.internal.util.PatternUtil;
 import org.apache.felix.http.base.internal.whiteboard.RegistrationFailureException;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceObjects;
 
 public final class PerContextHandlerRegistry implements Comparable<PerContextHandlerRegistry>
 {
-    private final BundleContext bundleContext;
-
     private final Map<Filter, FilterHandler> filterMap = new HashMap<Filter, FilterHandler>();
 
-    private volatile HandlerMapping<ServletHandler> servletMapping = new HandlerMapping<ServletHandler>();
     private volatile HandlerMapping<FilterHandler> filterMapping = new HandlerMapping<FilterHandler>();
-    private final ErrorsMapping errorsMapping = new ErrorsMapping();
+    private volatile ErrorsMapping errorsMapping = new ErrorsMapping();
 
-    private final SortedMap<Pattern, SortedSet<ServletHandler>> patternToServletHandler = new TreeMap<Pattern, SortedSet<ServletHandler>>(PatternUtil.PatternComparator.INSTANCE);
-    private final Map<ServletHandler, Integer> servletHandlerToUses = new HashMap<ServletHandler, Integer>();
-    private final SortedSet<ServletHandler> allServletHandlers = new TreeSet<ServletHandler>();
+    private final HandlerRankingMultimap<String> registeredErrorPages = new HandlerRankingMultimap<String>();
 
     private final long serviceId;
 
@@ -76,53 +61,44 @@
 
     private final String prefix;
 
-    public PerContextHandlerRegistry(BundleContext bundleContext)
+    public PerContextHandlerRegistry()
     {
         this.serviceId = 0;
         this.ranking = Integer.MAX_VALUE;
         this.path = "/";
         this.prefix = null;
-        this.bundleContext = bundleContext;
     }
 
-    public PerContextHandlerRegistry(final ServletContextHelperInfo info, BundleContext bundleContext)
+    public PerContextHandlerRegistry(final ServletContextHelperInfo info)
     {
         this.serviceId = info.getServiceId();
         this.ranking = info.getRanking();
         this.path = info.getPath();
-        this.bundleContext = bundleContext;
-        if (this.path.equals("/"))
+        if ( this.path.equals("/") )
         {
-            prefix = null;
+            this.prefix = null;
         }
         else
         {
-            prefix = this.path + "/";
+            this.prefix = this.path + "/";
         }
     }
 
-    public synchronized void addFilter(FilterHandler handler) throws ServletException
-    {
-        if (this.filterMapping.contains(handler))
-        {
-            throw new RegistrationFailureException(handler.getFilterInfo(), FAILURE_REASON_SERVICE_ALREAY_USED, "Filter instance " + handler.getName() + " already registered");
-        }
-
-        handler.init();
-        this.filterMapping = this.filterMapping.add(handler);
-        this.filterMap.put(handler.getFilter(), handler);
-    }
-
     @Override
     public int compareTo(final PerContextHandlerRegistry other)
     {
-        final int result = Integer.compare(other.path.length(), this.path.length());
-        if (result == 0)
+        // the context of the HttpService is the least element
+        if (this.serviceId == 0 ^ other.serviceId == 0)
         {
+            return this.serviceId == 0 ? -1 : 1;
+        }
+
+        final int result = Integer.compare(other.path.length(), this.path.length());
+        if ( result == 0 ) {
             if (this.ranking == other.ranking)
             {
                 // Service id's can be negative. Negative id's follow the reverse natural ordering of integers.
-                int reverseOrder = (this.serviceId >= 0 && other.serviceId >= 0) ? 1 : -1;
+                int reverseOrder = ( this.serviceId <= 0 && other.serviceId <= 0 ) ? -1 : 1;
                 return reverseOrder * Long.compare(this.serviceId, other.serviceId);
             }
 
@@ -131,231 +107,49 @@
         return result;
     }
 
-    /**
-     * Add a new servlet.
-     */
-    public synchronized void addServlet(final ServletHandler handler) throws ServletException
+    public synchronized void addFilter(FilterHandler handler) throws RegistrationFailureException
     {
-        Pattern[] patterns = handler.getPatterns();
-        String[] errorPages = handler.getServletInfo().getErrorPage();
-
-        if (patterns.length > 0 && errorPages != null)
+        if (this.filterMapping.contains(handler))
         {
-            throw new ServletException("Servlet instance " + handler.getName() + " has both patterns and errorPage set");
+            throw new RegistrationFailureException(handler.getFilterInfo(), FAILURE_REASON_SERVICE_ALREAY_USED,
+                "Filter instance " + handler.getName() + " already registered");
         }
 
-        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)
-            {
-                useServletHandler(handler);
-                this.errorsMapping.addErrorServlet(errorPage, handler);
-            }
-        }
-    }
-
-    /**
-     * 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;
+            throw new RegistrationFailureException(handler.getFilterInfo(), FAILURE_REASON_EXCEPTION_ON_INIT, e);
         }
+
+        this.filterMapping = this.filterMapping.add(handler);
+        this.filterMap.put(handler.getFilter(), handler);
     }
 
-    private Servlet getServiceObject(ServiceObjects<Servlet> so, ServletHandler handler, boolean isResource)
+
+    void addErrorPage(ServletHandler handler, String[] errorPages) throws RegistrationFailureException
     {
-        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;
-    }
-
-    public FilterHandler[] getFilterHandlers(ServletHandler servletHandler, DispatcherType dispatcherType, String requestURI)
-    {
-        // See Servlet 3.0 specification, section 6.2.4...
-        List<FilterHandler> result = new ArrayList<FilterHandler>();
-        result.addAll(this.filterMapping.getAllMatches(requestURI));
-
-        // TODO this is not the most efficient/fastest way of doing this...
-        Iterator<FilterHandler> iter = result.iterator();
-        while (iter.hasNext())
-        {
-            if (!referencesDispatcherType(iter.next(), dispatcherType))
-            {
-                iter.remove();
-            }
-        }
-
-        String servletName = (servletHandler != null) ? servletHandler.getName() : null;
-        // TODO this is not the most efficient/fastest way of doing this...
-        for (FilterHandler filterHandler : this.filterMapping.values())
-        {
-            if (referencesServletByName(filterHandler, servletName))
-            {
-                result.add(filterHandler);
-            }
-        }
-
-        // TODO - we should already check for the context when building up the result set
-        final Iterator<FilterHandler> i = result.iterator();
-        while (i.hasNext())
-        {
-            final FilterHandler handler = i.next();
-            if (handler.getContextServiceId() != servletHandler.getContextServiceId())
-            {
-                i.remove();
-            }
-        }
-        return result.toArray(new FilterHandler[result.size()]);
-    }
-
-    public ServletHandler getServletHandlerByName(String name)
-    {
-        return this.servletMapping.getByName(name);
-    }
-
-    public ServletHandler getServletHander(String requestURI)
-    {
-        return this.servletMapping.getBestMatch(requestURI);
+        Update<String> update = this.registeredErrorPages.add(errorPages, handler);
+        initHandlers(update.getInit());
+        this.errorsMapping = this.errorsMapping.update(update.getActivated(), update.getDeactivated());
+        destroyHandlers(update.getDestroy());
     }
 
     public synchronized void removeAll()
     {
-        Collection<ServletHandler> servletHandlers = servletMapping.values();
-        Collection<FilterHandler> filterHandlers = filterMapping.values();
+        Collection<ServletHandler> errorPageHandlers = this.errorsMapping.values();
+        Collection<FilterHandler> filterHandlers = this.filterMapping.values();
 
-        this.servletMapping = new HandlerMapping<ServletHandler>();
         this.filterMapping = new HandlerMapping<FilterHandler>();
+        this.errorsMapping = new ErrorsMapping();
 
-        for (ServletHandler handler : servletHandlers)
-        {
-            handler.destroy();
-        }
+        destroyHandlers(filterHandlers);
+        destroyHandlers(errorPageHandlers);
 
-        for (FilterHandler handler : filterHandlers)
-        {
-            handler.destroy();
-        }
-
-        this.errorsMapping.clear();
-        this.allServletHandlers.clear();
-        //this.servletMap.clear();
         this.filterMap.clear();
+        this.registeredErrorPages.clear();
     }
 
     public synchronized void removeFilter(Filter filter, final boolean destroy)
@@ -391,7 +185,7 @@
 
     private FilterHandler getFilterHandler(final FilterInfo filterInfo)
     {
-        for (final FilterHandler handler : this.filterMap.values())
+        for(final FilterHandler handler : this.filterMap.values())
         {
             if (handler.getFilterInfo().compareTo(filterInfo) == 0)
             {
@@ -401,74 +195,33 @@
         return null;
     }
 
-    public synchronized Servlet removeServlet(ServletInfo servletInfo, final boolean destroy) throws RegistrationFailureException
+    void removeErrorPage(ServletInfo info) throws RegistrationFailureException
     {
-        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)
+        ServletHandler handler = getServletHandler(info);
+        if (handler == null)
         {
-            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)
-                    {
-                        throw new RegistrationFailureException(activeHandler.getServletInfo(), FAILURE_REASON_EXCEPTION_ON_INIT, e);
-                    }
-                }
-                else
-                {
-                    this.patternToServletHandler.remove(p);
-                }
-            }
+            return;
         }
-
-        Servlet servlet = null;
-        if (handler != null && handler.getServlet() != 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;
+        String[] errorPages = handler.getServletInfo().getErrorPage();
+        Update<String> update = this.registeredErrorPages.remove(errorPages, handler);
+        initHandlers(update.getInit());
+        this.errorsMapping = this.errorsMapping.update(update.getActivated(), update.getDeactivated());
+        destroyHandlers(update.getDestroy());
     }
 
     private ServletHandler getServletHandler(final ServletInfo servletInfo)
     {
-        Iterator<ServletHandler> it = this.allServletHandlers.iterator();
+        ServletHandler servletHandler = getServletHandler(servletInfo, this.registeredErrorPages.getActiveValues());
+        if (servletHandler == null)
+        {
+            return getServletHandler(servletInfo, this.registeredErrorPages.getShadowedValues());
+        }
+        return servletHandler;
+    }
+
+    private ServletHandler getServletHandler(final ServletInfo servletInfo, Collection<ServletHandler> values)
+    {
+        Iterator<ServletHandler> it = values.iterator();
         while (it.hasNext())
         {
             ServletHandler handler = it.next();
@@ -480,19 +233,69 @@
         return null;
     }
 
-    public synchronized void removeServlet(Servlet servlet, final boolean destroy) throws RegistrationFailureException
+    private void initHandlers(Collection<ServletHandler> handlers) throws RegistrationFailureException
     {
-        Iterator<ServletHandler> it = this.allServletHandlers.iterator();
-        while (it.hasNext())
+        for (ServletHandler servletHandler : handlers)
         {
-            ServletHandler handler = it.next();
-            if (handler.getServlet() == servlet)
+            try
             {
-                removeServlet(handler.getServletInfo(), destroy);
+                servletHandler.init();
+            }
+            catch (ServletException e)
+            {
+                // TODO we should collect this cases and not throw an exception immediately
+                throw new RegistrationFailureException(servletHandler.getServletInfo(), FAILURE_REASON_EXCEPTION_ON_INIT, e);
             }
         }
     }
 
+    private void destroyHandlers(Collection<? extends AbstractHandler<?>> servletHandlers)
+    {
+        for (AbstractHandler<?> handler : servletHandlers)
+        {
+            handler.destroy();
+        }
+    }
+
+    public FilterHandler[] getFilterHandlers(ServletHandler servletHandler, DispatcherType dispatcherType, String requestURI)
+    {
+        // See Servlet 3.0 specification, section 6.2.4...
+        List<FilterHandler> result = new ArrayList<FilterHandler>();
+        result.addAll(this.filterMapping.getAllMatches(requestURI));
+
+        // TODO this is not the most efficient/fastest way of doing this...
+        Iterator<FilterHandler> iter = result.iterator();
+        while (iter.hasNext())
+        {
+            if (!referencesDispatcherType(iter.next(), dispatcherType))
+            {
+                iter.remove();
+            }
+        }
+
+        String servletName = (servletHandler != null) ? servletHandler.getName() : null;
+        // TODO this is not the most efficient/fastest way of doing this...
+        for (FilterHandler filterHandler : this.filterMapping.values())
+        {
+            if (referencesServletByName(filterHandler, servletName))
+            {
+                result.add(filterHandler);
+            }
+        }
+
+        // TODO - we should already check for the context when building up the result set
+        final Iterator<FilterHandler> i = result.iterator();
+        while ( i.hasNext() )
+        {
+            final FilterHandler handler = i.next();
+            if ( handler.getContextServiceId() != servletHandler.getContextServiceId() )
+            {
+                i.remove();
+            }
+        }
+        return result.toArray(new FilterHandler[result.size()]);
+    }
+
     private boolean referencesDispatcherType(FilterHandler handler, DispatcherType dispatcherType)
     {
         return Arrays.asList(handler.getFilterInfo().getDispatcher()).contains(dispatcherType);
@@ -512,6 +315,11 @@
         return false;
     }
 
+    public ErrorsMapping getErrorsMapping()
+    {
+        return this.errorsMapping;
+    }
+
     public String isMatching(final String requestURI)
     {
         if (requestURI.equals(this.path))
@@ -534,42 +342,31 @@
         return this.serviceId;
     }
 
-    public synchronized ContextRuntime getRuntime(FailureRuntime.Builder failureRuntimeBuilder)
-    {
-        Collection<ErrorPageRuntime> errorPages = new TreeSet<ErrorPageRuntime>(ServletRuntime.COMPARATOR);
-        Collection<ServletHandler> errorHandlers = errorsMapping.getMappedHandlers();
-        for (ServletHandler servletHandler : errorHandlers)
-        {
-            errorPages.add(errorsMapping.getErrorPage(servletHandler));
-        }
-
+    public synchronized ContextRuntime getRuntime(FailureRuntime.Builder failureRuntimeBuilder) {
         Collection<FilterRuntime> filterRuntimes = new TreeSet<FilterRuntime>(FilterRuntime.COMPARATOR);
-        for (FilterRuntime filterRuntime : filterMap.values())
+        for (FilterRuntime filterRuntime : this.filterMap.values())
         {
             filterRuntimes.add(filterRuntime);
         }
 
-        Collection<ServletRuntime> servletRuntimes = new TreeSet<ServletRuntime>(ServletRuntime.COMPARATOR);
-        Collection<ServletRuntime> resourceRuntimes = new TreeSet<ServletRuntime>(ServletRuntime.COMPARATOR);
-
-        for (Set<ServletHandler> patternHandlers : patternToServletHandler.values())
+        Collection<ErrorPageRuntime> errorPages = new TreeSet<ErrorPageRuntime>(ServletRuntime.COMPARATOR);
+        for (ServletHandler servletHandler : this.registeredErrorPages.getActiveValues())
         {
-            Iterator<ServletHandler> itr = patternHandlers.iterator();
-            ServletHandler activeHandler = itr.next();
-            if (activeHandler.getServletInfo().isResource())
-            {
-                resourceRuntimes.add(activeHandler);
-            }
-            else
-            {
-                servletRuntimes.add(activeHandler);
-            }
-            while (itr.hasNext())
-            {
-                failureRuntimeBuilder.add(itr.next().getServletInfo(), FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE);
-            }
+            errorPages.add(ErrorPageRuntime.fromServletRuntime(servletHandler));
         }
 
-        return new ContextRuntime(servletRuntimes, filterRuntimes, resourceRuntimes, errorPages, serviceId);
+        addShadowedHandlers(failureRuntimeBuilder, this.registeredErrorPages.getShadowedValues());
+
+        return new ContextRuntime(filterRuntimes,
+                errorPages,
+                this.serviceId);
+    }
+
+    private void addShadowedHandlers(FailureRuntime.Builder failureRuntimeBuilder, Collection<ServletHandler> handlers)
+    {
+        for (ServletHandler handler : handlers)
+        {
+            failureRuntimeBuilder.add(handler.getServletInfo(), FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE);
+        }
     }
 }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ResourceServletHandler.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ResourceServletHandler.java
new file mode 100644
index 0000000..a4c2de4
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ResourceServletHandler.java
@@ -0,0 +1,67 @@
+/*
+ * 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.base.internal.handler;
+
+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.ServletContextHelperInfo;
+import org.apache.felix.http.base.internal.runtime.ServletInfo;
+import org.apache.felix.http.base.internal.whiteboard.ResourceServlet;
+
+public final class ResourceServletHandler extends ServletHandler
+{
+    private Servlet servlet;
+
+    public ResourceServletHandler(ServletContextHelperInfo contextInfo,
+            ExtServletContext context,
+            ServletInfo servletInfo)
+    {
+        super(contextInfo.getServiceId(), context, checkIsResource(servletInfo, true));
+    }
+
+    @Override
+    public Servlet getServlet()
+    {
+        return servlet;
+    }
+
+    @Override
+    public void init() throws ServletException
+    {
+        if (servlet != null)
+        {
+            return;
+        }
+
+        servlet = new ResourceServlet(getServletInfo().getPrefix());
+        servlet.init(new ServletConfigImpl(getName(), getContext(), getInitParams()));
+    }
+
+    @Override
+    public void destroy()
+    {
+        if (servlet == null)
+        {
+            return;
+        }
+
+        servlet.destroy();
+        servlet = null;
+    }
+}
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 feeb3e7..10398f8 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
@@ -23,13 +23,11 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import javax.servlet.Servlet;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.felix.http.base.internal.context.ExtServletContext;
-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.runtime.dto.ServletRuntime;
 import org.apache.felix.http.base.internal.util.PatternUtil;
@@ -37,37 +35,19 @@
 /**
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
-public final class ServletHandler extends AbstractHandler<ServletHandler> implements ServletRuntime
+public abstract class ServletHandler extends AbstractHandler<ServletHandler> implements ServletRuntime
 {
     private final ServletInfo servletInfo;
-
-    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)
-    {
-    	this(contextInfo, context, servletInfo, servlet, false);
-    }
-    
-    public ServletHandler(final ServletContextHelperInfo contextInfo,
+    public ServletHandler(final long contextServiceId,
             final ExtServletContext context,
-            final ServletInfo servletInfo,
-            final Servlet servlet, 
-            final boolean isWhiteboardService)
+            final ServletInfo servletInfo)
     {
-    	super(context, servletInfo.getInitParameters(), servletInfo.getName());
-    	
-        this.servlet = servlet;
+        super(context, servletInfo.getInitParameters(), servletInfo.getName());
+
         this.servletInfo = servletInfo;
-        this.isWhiteboardService = isWhiteboardService;
 
         // Can be null in case of error-handling servlets...
         String[] patterns = this.servletInfo.getPatterns();
@@ -79,16 +59,9 @@
             final String pattern = patterns[i];
             this.patterns[i] = Pattern.compile(PatternUtil.convertToRegEx(pattern));
         }
-        if ( contextInfo != null )
-        {
-            this.contextServiceId = contextInfo.getServiceId();
-        }
-        else
-        {
-            this.contextServiceId = 0;
-        }
+
+        this.contextServiceId = contextServiceId;
     }
-    
 
     @Override
     public int compareTo(final ServletHandler other)
@@ -96,6 +69,25 @@
         return this.servletInfo.compareTo(other.servletInfo);
     }
 
+    public boolean handle(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
+    {
+        if (getContext().handleSecurity(req, res))
+        {
+            getServlet().service(req, res);
+
+            return true;
+        }
+
+        // FELIX-3988: If the response is not yet committed and still has the default
+        // status, we're going to override this and send an error instead.
+        if (!res.isCommitted() && (res.getStatus() == SC_OK || res.getStatus() == 0))
+        {
+            res.sendError(SC_FORBIDDEN);
+        }
+
+        return false;
+    }
+
     public String determineServletPath(String uri)
     {
         if (uri == null)
@@ -117,68 +109,17 @@
     }
 
     @Override
-    public void destroy()
-    {
-        this.servlet.destroy();
-    }
-
-    public Servlet getServlet()
-    {
-        return this.servlet;
-    }
-    
-    void setServlet(Servlet servlet)
-    {
-    	this.servlet = servlet;
-    }
-    
-    public boolean isWhiteboardService()
-    {
-    	return this.isWhiteboardService;
-    }
-
-    @Override
     public Pattern[] getPatterns()
     {
         return this.patterns;
     }
 
-    @Override
-    public void init() throws ServletException
-    {
-        this.servlet.init(new ServletConfigImpl(getName(), getContext(), getInitParams()));
-    }
-
-    public boolean handle(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
-    {
-        if (getContext().handleSecurity(req, res))
-        {
-            this.servlet.service(req, res);
-
-            return true;
-        }
-
-        // FELIX-3988: If the response is not yet committed and still has the default
-        // status, we're going to override this and send an error instead.
-        if (!res.isCommitted() && (res.getStatus() == SC_OK || res.getStatus() == 0))
-        {
-            res.sendError(SC_FORBIDDEN);
-        }
-
-        return false;
-    }
-
     public ServletInfo getServletInfo()
     {
         return this.servletInfo;
     }
 
     @Override
-    protected Object getSubject()
-    {
-        return this.servlet;
-    }
-
     public long getContextServiceId()
     {
         return this.contextServiceId;
@@ -189,4 +130,20 @@
     {
         return this.servletInfo.getServiceId();
     }
+
+    @Override
+    protected Object getSubject()
+    {
+        return getServlet();
+    }
+
+    protected static ServletInfo checkIsResource(ServletInfo servletInfo, boolean checkTrue)
+    {
+        if (checkTrue != servletInfo.isResource())
+        {
+            String message = "ServletInfo must " + (checkTrue ? "" : "not") + " represent a resource";
+            throw new IllegalArgumentException(message);
+        }
+        return servletInfo;
+    }
 }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandlerRegistry.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandlerRegistry.java
new file mode 100644
index 0000000..7d7e5d0
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandlerRegistry.java
@@ -0,0 +1,358 @@
+/*
+ * 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.base.internal.handler;
+
+import static java.util.Arrays.asList;
+import static org.osgi.service.http.runtime.dto.DTOConstants.FAILURE_REASON_EXCEPTION_ON_INIT;
+import static org.osgi.service.http.runtime.dto.DTOConstants.FAILURE_REASON_SERVICE_ALREAY_USED;
+import static org.osgi.service.http.runtime.dto.DTOConstants.FAILURE_REASON_SERVLET_CONTEXT_FAILURE;
+import static org.osgi.service.http.runtime.dto.DTOConstants.FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.regex.Pattern;
+
+import javax.annotation.Nonnull;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+
+import org.apache.felix.http.base.internal.handler.HandlerRankingMultimap.Update;
+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.runtime.dto.FailureRuntime;
+import org.apache.felix.http.base.internal.runtime.dto.ServletRegistryRuntime;
+import org.apache.felix.http.base.internal.runtime.dto.ServletRuntime;
+import org.apache.felix.http.base.internal.util.PatternUtil;
+import org.apache.felix.http.base.internal.util.PatternUtil.PatternComparator;
+import org.apache.felix.http.base.internal.whiteboard.RegistrationFailureException;
+
+public final class ServletHandlerRegistry
+{
+    private volatile HandlerMapping<ServletHandler> servletMapping = new HandlerMapping<ServletHandler>();
+    private final HandlerRankingMultimap<String> registeredServletHandlers;
+    private final SortedSet<ServletHandler> allServletHandlers = new TreeSet<ServletHandler>();
+
+    private final ContextServletHandlerComparator handlerComparator;
+
+    public ServletHandlerRegistry()
+    {
+        PatternComparator keyComparator = PatternComparator.INSTANCE;
+        handlerComparator = new ContextServletHandlerComparator();
+        this.registeredServletHandlers = new HandlerRankingMultimap<String>(null, handlerComparator);
+    }
+
+    /**
+     * Register default context registry for Http Service
+     */
+    public void init()
+    {
+        handlerComparator.contextRankings.put(0L, new ContextRanking());
+    }
+
+    public void shutdown()
+    {
+        removeAll();
+    }
+
+    public void add(@Nonnull ServletContextHelperInfo info)
+    {
+        handlerComparator.contextRankings.put(info.getServiceId(), new ContextRanking(info));
+    }
+
+    public void remove(@Nonnull ServletContextHelperInfo info)
+    {
+        handlerComparator.contextRankings.remove(info.getServiceId());
+    }
+
+    public ServletHandler getServletHandlerByName(final Long contextId, @Nonnull final String name)
+    {
+        return servletMapping.getByName(name);
+    }
+
+    public synchronized void addServlet(final ServletHandler handler) throws RegistrationFailureException
+    {
+        if (this.allServletHandlers.contains(handler))
+        {
+            throw new RegistrationFailureException(handler.getServletInfo(), FAILURE_REASON_SERVICE_ALREAY_USED,
+                "Filter instance " + handler.getName() + " already registered");
+        }
+
+        registerServlet(handler);
+
+        this.allServletHandlers.add(handler);
+    }
+
+    private void registerServlet(ServletHandler handler) throws RegistrationFailureException
+    {
+        String contextPath = handlerComparator.getPath(handler.getContextServiceId());
+        if (contextPath == null)
+        {
+            throw new RegistrationFailureException(handler.getServletInfo(), FAILURE_REASON_SERVLET_CONTEXT_FAILURE);
+        }
+        contextPath = contextPath.equals("/") ? "" : contextPath;
+        List<String> patterns = new ArrayList<String>(asList(handler.getServletInfo().getPatterns()));
+        for (int i = 0; i < patterns.size(); i++)
+        {
+            patterns.set(i, contextPath + patterns.get(i));
+        }
+        Update<String> update = this.registeredServletHandlers.add(patterns, handler);
+        initHandlers(update.getInit());
+        this.servletMapping = this.servletMapping.update(convert(update.getActivated()), convert(update.getDeactivated()));
+        destroyHandlers(update.getDestroy());
+    }
+
+    private Map<Pattern, ServletHandler> convert(Map<String, ServletHandler> mapping)
+    {
+        TreeMap<Pattern, ServletHandler> converted = new TreeMap<Pattern, ServletHandler>(PatternComparator.INSTANCE);
+        for (Map.Entry<String, ServletHandler> entry : mapping.entrySet())
+        {
+            Pattern pattern = Pattern.compile(PatternUtil.convertToRegEx(entry.getKey()));
+            converted.put(pattern, entry.getValue());
+        }
+        return converted;
+    }
+
+    public synchronized void removeAll()
+    {
+        Collection<ServletHandler> servletHandlers = this.servletMapping.values();
+
+        this.servletMapping = new HandlerMapping<ServletHandler>();
+
+        destroyHandlers(servletHandlers);
+
+        this.allServletHandlers.clear();
+        this.registeredServletHandlers.clear();
+    }
+
+    synchronized Servlet removeServlet(ServletInfo servletInfo) throws RegistrationFailureException
+    {
+        return removeServlet(0L, servletInfo, true);
+    }
+
+    public synchronized Servlet removeServlet(Long contextId, ServletInfo servletInfo) throws RegistrationFailureException
+    {
+        return removeServlet(contextId, servletInfo, true);
+    }
+
+    public synchronized Servlet removeServlet(Long contextId, ServletInfo servletInfo, final boolean destroy) throws RegistrationFailureException
+    {
+        ServletHandler handler = getServletHandler(servletInfo);
+        if (handler == null)
+        {
+            return null;
+        }
+
+        Servlet servlet = handler.getServlet();
+
+        removeServlet(handler, destroy);
+
+        if (destroy)
+        {
+            handler.destroy();
+        }
+
+
+        return servlet;
+    }
+
+    synchronized void removeServlet(Servlet servlet, final boolean destroy) throws RegistrationFailureException
+    {
+        Iterator<ServletHandler> it = this.allServletHandlers.iterator();
+        List<ServletHandler> removals = new ArrayList<ServletHandler>();
+        while (it.hasNext())
+        {
+            ServletHandler handler = it.next();
+            if (handler.getServlet() != null && handler.getServlet() == servlet)
+            {
+                removals.add(handler);
+            }
+        }
+
+        for (ServletHandler servletHandler : removals)
+        {
+            removeServlet(0L, servletHandler.getServletInfo(), destroy);
+        }
+    }
+
+    private void removeServlet(ServletHandler handler, boolean destroy) throws RegistrationFailureException
+    {
+        String contextPath = handlerComparator.getPath(handler.getContextServiceId());
+        contextPath = contextPath.equals("/") ? "" : contextPath;
+        List<String> patterns = new ArrayList<String>(asList(handler.getServletInfo().getPatterns()));
+        for (int i = 0; i < patterns.size(); i++)
+        {
+            patterns.set(i, contextPath + patterns.get(i));
+        }
+        Update<String> update = this.registeredServletHandlers.remove(patterns, handler);
+        initHandlers(update.getInit());
+        this.servletMapping = this.servletMapping.update(convert(update.getActivated()), convert(update.getDeactivated()));
+        if (destroy)
+        {
+            destroyHandlers(update.getDestroy());
+        }
+        this.allServletHandlers.remove(handler);
+    }
+
+    private void initHandlers(Collection<ServletHandler> handlers) throws RegistrationFailureException
+    {
+        for (ServletHandler servletHandler : handlers)
+        {
+            try
+            {
+                servletHandler.init();
+            }
+            catch (ServletException e)
+            {
+                // TODO we should collect this cases and not throw an exception immediately
+                throw new RegistrationFailureException(servletHandler.getServletInfo(), FAILURE_REASON_EXCEPTION_ON_INIT, e);
+            }
+        }
+    }
+
+    private void destroyHandlers(Collection<? extends AbstractHandler<?>> servletHandlers)
+    {
+        for (AbstractHandler<?> handler : servletHandlers)
+        {
+            handler.destroy();
+        }
+    }
+
+    public ServletHandler getServletHandler(String requestURI)
+    {
+        return this.servletMapping.getBestMatch(requestURI);
+    }
+
+    public ServletHandler getServletHandlerByName(String name)
+    {
+        return this.servletMapping.getByName(name);
+    }
+
+    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 ServletRegistryRuntime getRuntime(FailureRuntime.Builder failureRuntimeBuilder)
+    {
+        addShadowedHandlers(failureRuntimeBuilder, this.registeredServletHandlers.getShadowedValues());
+
+        Collection<ServletRuntime> servletRuntimes = new TreeSet<ServletRuntime>(ServletRuntime.COMPARATOR);
+        Collection<ServletRuntime> resourceRuntimes = new TreeSet<ServletRuntime>(ServletRuntime.COMPARATOR);
+        for (ServletHandler activeHandler : this.registeredServletHandlers.getActiveValues())
+        {
+            if (activeHandler.getServletInfo().isResource())
+            {
+                resourceRuntimes.add(activeHandler);
+            }
+            else
+            {
+                servletRuntimes.add(activeHandler);
+            }
+        }
+        return new ServletRegistryRuntime(servletRuntimes, resourceRuntimes);
+    }
+
+    private void addShadowedHandlers(FailureRuntime.Builder failureRuntimeBuilder, Collection<ServletHandler> handlers)
+    {
+        for (ServletHandler handler : handlers)
+        {
+            failureRuntimeBuilder.add(handler.getServletInfo(), FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE);
+        }
+    }
+
+    private static class ContextServletHandlerComparator implements Comparator<ServletHandler>
+    {
+        private final Map<Long, ContextRanking> contextRankings = new HashMap<Long, ContextRanking>();
+
+        @Override
+        public int compare(ServletHandler o1, ServletHandler o2)
+        {
+            ContextRanking contextRankingOne = contextRankings.get(o1.getContextServiceId());
+            ContextRanking contextRankingTwo = contextRankings.get(o2.getContextServiceId());
+            int contextComparison = contextRankingOne.compareTo(contextRankingTwo);
+            return contextComparison == 0 ? o1.compareTo(o2) : contextComparison;
+        }
+
+       String getPath(long contextId)
+       {
+           return contextRankings.get(contextId).path;
+       }
+    }
+
+    // TODO combine with PerContextHandlerRegistry
+    private static class ContextRanking implements Comparable<ContextRanking>
+    {
+        private final long serviceId;
+        private final int ranking;
+        private final String path;
+
+        ContextRanking()
+        {
+            this.serviceId = 0;
+            this.ranking = Integer.MAX_VALUE;
+            this.path = "/";
+        }
+
+        ContextRanking(ServletContextHelperInfo info)
+        {
+            this.serviceId = info.getServiceId();
+            this.ranking = info.getRanking();
+            this.path = info.getPath();
+        }
+
+        @Override
+        public int compareTo(ContextRanking other)
+        {
+            // the context of the HttpService is the least element
+            if (this.serviceId == 0 ^ other.serviceId == 0)
+            {
+                return this.serviceId == 0 ? -1 : 1;
+            }
+
+            final int result = Integer.compare(other.path.length(), this.path.length());
+            if ( result == 0 ) {
+                if (this.ranking == other.ranking)
+                {
+                    // Service id's can be negative. Negative id's follow the reverse natural ordering of integers.
+                    int reverseOrder = ( this.serviceId <= 0 && other.serviceId <= 0 ) ? -1 : 1;
+                    return reverseOrder * Long.compare(this.serviceId, other.serviceId);
+                }
+
+                return Integer.compare(other.ranking, this.ranking);
+            }
+            return result;
+        }
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/SimpleServletHandler.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/SimpleServletHandler.java
new file mode 100644
index 0000000..fe1d962
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/SimpleServletHandler.java
@@ -0,0 +1,68 @@
+/*
+ * 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.base.internal.handler;
+
+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.ServletInfo;
+
+public final class SimpleServletHandler extends ServletHandler
+{
+    private final Servlet servlet;
+
+    public SimpleServletHandler(ExtServletContext context,
+            ServletInfo servletInfo,
+            Servlet servlet)
+    {
+        this(0, context, servletInfo, servlet);
+    }
+
+    public SimpleServletHandler(long contextServiceId,
+            ExtServletContext context,
+            ServletInfo servletInfo,
+            Servlet servlet)
+    {
+        super(contextServiceId, context, servletInfo);
+        this.servlet = servlet;
+    }
+
+    @Override
+    public Servlet getServlet()
+    {
+        return servlet;
+    }
+
+    @Override
+    public void init() throws ServletException
+    {
+        servlet.init(new ServletConfigImpl(getName(), getContext(), getInitParams()));
+    }
+
+    @Override
+    public void destroy()
+    {
+        servlet.destroy();
+    }
+
+    @Override
+    protected Object getSubject()
+    {
+        return servlet;
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/WhiteboardServletHandler.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/WhiteboardServletHandler.java
new file mode 100644
index 0000000..920f7b7
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/WhiteboardServletHandler.java
@@ -0,0 +1,101 @@
+/*
+ * 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.base.internal.handler;
+
+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.ServletContextHelperInfo;
+import org.apache.felix.http.base.internal.runtime.ServletInfo;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceObjects;
+import org.osgi.framework.ServiceReference;
+
+public final class WhiteboardServletHandler extends ServletHandler
+{
+    private final BundleContext bundleContext;
+
+    private Servlet servlet;
+
+    public WhiteboardServletHandler(ServletContextHelperInfo contextInfo,
+            ExtServletContext context,
+            ServletInfo servletInfo,
+            BundleContext bundleContext)
+    {
+        super(contextInfo.getServiceId(), context, checkIsResource(servletInfo, false));
+        this.bundleContext = bundleContext;
+    }
+
+    @Override
+    public Servlet getServlet()
+    {
+        return servlet;
+    }
+
+    @Override
+    protected Object getSubject()
+    {
+        return getServlet();
+    }
+
+    @Override
+    public void init() throws ServletException
+    {
+        if (servlet != null)
+        {
+            return;
+        }
+
+        ServiceReference<Servlet> serviceReference = getServletInfo().getServiceReference();
+        ServiceObjects<Servlet> so = this.bundleContext.getServiceObjects(serviceReference);
+
+        servlet = so.getService();
+
+        if (servlet == null)
+        {
+            // TODO throw Exception - service ungettable ?
+            return;
+        }
+
+        try {
+            servlet.init(new ServletConfigImpl(getName(), getContext(), getInitParams()));
+        } catch (ServletException e) {
+            so.ungetService(servlet);
+            throw e;
+        }
+    }
+
+    @Override
+    public void destroy()
+    {
+        if (servlet == null)
+        {
+            return;
+        }
+
+        servlet.destroy();
+
+        ServiceObjects<Servlet> so = this.bundleContext.getServiceObjects(getServletInfo().getServiceReference());
+        // TODO check if this is needed
+        if (so != null)
+        {
+            so.ungetService(servlet);
+        }
+        servlet = null;
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletContextHelperInfo.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletContextHelperInfo.java
index 2d0aa11..05d468d 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletContextHelperInfo.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletContextHelperInfo.java
@@ -54,8 +54,7 @@
         this.initParams = getInitParams(ref, CONTEXT_INIT_PREFIX);
     }
 
-    @SuppressWarnings("unchecked")
-    ServletContextHelperInfo(int serviceRanking,
+    public ServletContextHelperInfo(int serviceRanking,
             long serviceId,
             String name,
             String path,
@@ -64,19 +63,19 @@
         super(serviceRanking, serviceId);
         this.name = name;
         this.path = path;
-        this.initParams = initParams == null ? (Map<String, String>)Collections.EMPTY_MAP : Collections.unmodifiableMap(initParams);
+        this.initParams = initParams == null ? Collections.<String, String>emptyMap(): Collections.unmodifiableMap(initParams);
     }
 
     private boolean isValidPath()
     {
-        if ( !this.isEmpty(path) )
+        if (!this.isEmpty(path))
         {
-        	if ( path.equals("/") )
-        	{
-        		return true;
-        	}
+            if (path.equals("/"))
+            {
+                return true;
+            }
             // TODO we need more validation
-            if ( path.startsWith("/") && !path.endsWith("/") )
+            if (path.startsWith("/") && !path.endsWith("/"))
             {
                 return true;
             }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ContextRuntime.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ContextRuntime.java
index 083dcc2..177628b 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ContextRuntime.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ContextRuntime.java
@@ -23,55 +23,37 @@
 
 public final class ContextRuntime
 {
-    private final Collection<ServletRuntime> servletRuntimes;
     private final Collection<FilterRuntime> filterRuntimes;
-    private final Collection<ServletRuntime> resourceRuntimes;
     private final Collection<ErrorPageRuntime> errorPageRuntimes;
     private final long serviceId;
 
-    public ContextRuntime(Collection<ServletRuntime> servletRuntimes,
-            Collection<FilterRuntime> filterRuntimes,
-            Collection<ServletRuntime> resourceRuntimes,
+    public ContextRuntime(Collection<FilterRuntime> filterRuntimes,
             Collection<ErrorPageRuntime> errorPageRuntimes,
             long serviceId)
     {
-        this.servletRuntimes = servletRuntimes;
         this.filterRuntimes = filterRuntimes;
-        this.resourceRuntimes = resourceRuntimes;
         this.errorPageRuntimes = errorPageRuntimes;
         this.serviceId = serviceId;
     }
 
     public static ContextRuntime empty(long serviceId)
     {
-        return new ContextRuntime(Collections.<ServletRuntime>emptyList(),
-                Collections.<FilterRuntime>emptyList(),
-                Collections.<ServletRuntime>emptyList(),
+        return new ContextRuntime(Collections.<FilterRuntime>emptyList(),
                 Collections.<ErrorPageRuntime> emptyList(),
                 serviceId);
     }
 
-    public Collection<ServletRuntime> getServletRuntimes()
-    {
-        return servletRuntimes;
-    }
-
-    public Collection<FilterRuntime> getFilterRuntimes()
+    Collection<FilterRuntime> getFilterRuntimes()
     {
         return filterRuntimes;
     }
 
-    public Collection<ServletRuntime> getResourceRuntimes()
-    {
-        return resourceRuntimes;
-    }
-
-    public Collection<ErrorPageRuntime> getErrorPageRuntimes()
+    Collection<ErrorPageRuntime> getErrorPageRuntimes()
     {
         return errorPageRuntimes;
     }
 
-    public long getServiceId()
+    long getServiceId()
     {
         return serviceId;
     }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ErrorPageRuntime.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ErrorPageRuntime.java
index 3155ede..d32af7a 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ErrorPageRuntime.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ErrorPageRuntime.java
@@ -21,16 +21,14 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
-import java.util.regex.Pattern;
 
 import javax.servlet.Servlet;
 
 import org.apache.felix.http.base.internal.runtime.ServletInfo;
+import org.apache.felix.http.base.internal.util.ErrorPageUtil;
 
 public class ErrorPageRuntime implements ServletRuntime
 {
-    private static final Pattern ERROR_CODE_PATTERN = Pattern.compile("\\d{3}");
-
     private final ServletRuntime servletRuntime;
     private final Collection<Integer> errorCodes;
     private final Collection<String> exceptions;
@@ -44,14 +42,14 @@
         this.exceptions = exceptions;
     }
 
-    public static ErrorPageRuntime fromRuntime(ServletRuntime servletRuntime)
+    public static ErrorPageRuntime fromServletRuntime(ServletRuntime servletRuntime)
     {
         List<Integer> errorCodes = new ArrayList<Integer>();
         List<String> exceptions = new ArrayList<String>();
 
         for (String string : servletRuntime.getServletInfo().getErrorPage())
         {
-            if (ERROR_CODE_PATTERN.matcher(string).matches())
+            if (ErrorPageUtil.isErrorCode(string))
             {
                 errorCodes.add(Integer.valueOf(string));
             }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/FailureRuntime.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/FailureRuntime.java
index d156f83..3e2dec3 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/FailureRuntime.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/FailureRuntime.java
@@ -227,13 +227,13 @@
         {
             if (info instanceof ServletContextHelperInfo)
             {
-                ServletContextHelperRuntime servletRuntime = new FailureServletContextHelperRuntime((ServletContextHelperInfo) info);
+                ServletContextHelperRuntime servletRuntime = new InfoServletContextHelperRuntime((ServletContextHelperInfo) info);
                 contextRuntimes.add(new Failure<ServletContextHelperRuntime>(servletRuntime, failureCode));
             }
             else if (info instanceof ServletInfo && ((ServletInfo) info).getErrorPage() != null)
             {
                 FailureServletRuntime servletRuntime = new FailureServletRuntime((ServletInfo) info);
-                ErrorPageRuntime errorPageRuntime = ErrorPageRuntime.fromRuntime(servletRuntime);
+                ErrorPageRuntime errorPageRuntime = ErrorPageRuntime.fromServletRuntime(servletRuntime);
                 errorPageRuntimes.add(new Failure<ErrorPageRuntime>(errorPageRuntime, failureCode));
             }
             else if (info instanceof ServletInfo)
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/HandlerRegistryRuntime.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/HandlerRegistryRuntime.java
new file mode 100644
index 0000000..735b960
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/HandlerRegistryRuntime.java
@@ -0,0 +1,41 @@
+/*
+ * 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.base.internal.runtime.dto;
+
+import java.util.List;
+
+public final class HandlerRegistryRuntime
+{
+    private final List<ContextRuntime> contextRuntimes;
+    private final ServletRegistryRuntime servletRegistryRuntime;
+
+    public HandlerRegistryRuntime(List<ContextRuntime> contextRuntimes, ServletRegistryRuntime servletRegistryRuntime)
+    {
+        this.contextRuntimes = contextRuntimes;
+        this.servletRegistryRuntime = servletRegistryRuntime;
+    }
+
+    public List<ContextRuntime> getContextRuntimes()
+    {
+        return contextRuntimes;
+    }
+
+    public ServletRegistryRuntime getServletRegistryRuntime()
+    {
+        return servletRegistryRuntime;
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/FailureServletContextHelperRuntime.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/InfoServletContextHelperRuntime.java
similarity index 82%
rename from http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/FailureServletContextHelperRuntime.java
rename to http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/InfoServletContextHelperRuntime.java
index 11b3f75..8110c41 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/FailureServletContextHelperRuntime.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/InfoServletContextHelperRuntime.java
@@ -22,11 +22,11 @@
 
 import org.apache.felix.http.base.internal.runtime.ServletContextHelperInfo;
 
-public class FailureServletContextHelperRuntime implements ServletContextHelperRuntime, Comparable<FailureServletContextHelperRuntime>
+public class InfoServletContextHelperRuntime implements ServletContextHelperRuntime, Comparable<InfoServletContextHelperRuntime>
 {
     private final ServletContextHelperInfo info;
 
-    public FailureServletContextHelperRuntime(ServletContextHelperInfo info)
+    public InfoServletContextHelperRuntime(ServletContextHelperInfo info)
     {
         this.info = info;
     }
@@ -44,7 +44,7 @@
     }
 
     @Override
-    public int compareTo(FailureServletContextHelperRuntime other)
+    public int compareTo(InfoServletContextHelperRuntime other)
     {
         return info.compareTo(other.info);
     }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/RegistryRuntime.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/RegistryRuntime.java
index cfd2621..4e115bb 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/RegistryRuntime.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/RegistryRuntime.java
@@ -24,6 +24,7 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.TreeSet;
 
 import org.osgi.framework.ServiceReference;
 
@@ -32,16 +33,21 @@
     private final Collection<ServletContextHelperRuntime> contexts;
     private final Map<Long, Collection<ServiceReference<?>>> listenerRuntimes;
     private final Map<Long, ContextRuntime> handlerRuntimes;
+    private final Map<Long, Collection<ServletRuntime>> servletRuntimes;
+    private final Map<Long, Collection<ServletRuntime>> resourceRuntimes;
     private final FailureRuntime failureRuntime;
 
     public RegistryRuntime(Collection<ServletContextHelperRuntime> contexts,
             Collection<ContextRuntime> contextRuntimes,
+            ServletRegistryRuntime servletRegistryRuntime,
             Map<Long, Collection<ServiceReference<?>>> listenerRuntimes,
             FailureRuntime failureRuntime)
     {
         this.contexts = contexts;
         this.failureRuntime = failureRuntime;
         this.handlerRuntimes = createServiceIdMap(contextRuntimes);
+        this.servletRuntimes = createServletServiceIdMap(servletRegistryRuntime.getServletRuntimes());
+        this.resourceRuntimes = createServletServiceIdMap(servletRegistryRuntime.getResourceRuntimes());
         this.listenerRuntimes = listenerRuntimes;
     }
 
@@ -55,6 +61,21 @@
         return runtimesMap;
     }
 
+    private static Map<Long, Collection<ServletRuntime>> createServletServiceIdMap(Collection<ServletRuntime> runtimes)
+    {
+        Map<Long, Collection<ServletRuntime>> runtimesMap = new HashMap<Long, Collection<ServletRuntime>>();
+        for (ServletRuntime runtime : runtimes)
+        {
+            long contextServiceId = runtime.getContextServiceId();
+            if (!runtimesMap.containsKey(contextServiceId))
+            {
+                runtimesMap.put(contextServiceId, new TreeSet<ServletRuntime>(ServletRuntime.COMPARATOR));
+            }
+            runtimesMap.get(contextServiceId).add(runtime);
+        }
+        return runtimesMap;
+    }
+
     public ContextRuntime getHandlerRuntime(ServletContextHelperRuntime contextRuntime)
     {
         long serviceId = contextRuntime.getContextInfo().getServiceId();
@@ -71,6 +92,24 @@
         return ContextRuntime.empty(serviceId);
     }
 
+    public Collection<ServletRuntime> getServletRuntimes(ServletContextHelperRuntime contextRuntime)
+    {
+        if (servletRuntimes.containsKey(contextRuntime.getContextInfo().getServiceId()))
+        {
+            return servletRuntimes.get(contextRuntime.getContextInfo().getServiceId());
+        }
+        return Collections.emptyList();
+    }
+
+    public Collection<ServletRuntime> getResourceRuntimes(ServletContextHelperRuntime contextRuntime)
+    {
+        if (resourceRuntimes.containsKey(contextRuntime.getContextInfo().getServiceId()))
+        {
+            return resourceRuntimes.get(contextRuntime.getContextInfo().getServiceId());
+        }
+        return Collections.emptyList();
+    }
+
     private boolean isDefaultContext(ServletContextHelperRuntime contextRuntime)
     {
         return contextRuntime.getContextInfo().getName().equals(HTTP_WHITEBOARD_DEFAULT_CONTEXT_NAME);
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/RuntimeDTOBuilder.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/RuntimeDTOBuilder.java
index b381af6..ac6c302 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/RuntimeDTOBuilder.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/RuntimeDTOBuilder.java
@@ -78,6 +78,8 @@
         {
             contextDTOs.add(createContextDTO(context,
                     registry.getHandlerRuntime(context),
+                    registry.getServletRuntimes(context),
+                    registry.getResourceRuntimes(context),
                     registry.getListenerRuntimes(context)));
         }
         return contextDTOs.toArray(BuilderConstants.CONTEXT_DTO_ARRAY);
@@ -85,10 +87,10 @@
 
     private ServletContextDTO createContextDTO(ServletContextHelperRuntime context,
             ContextRuntime contextRuntime,
+            Collection<ServletRuntime> servletRuntimes,
+            Collection<ServletRuntime> resourceRuntimes,
             Collection<ServiceReference<?>> listenerRuntimes)
     {
-        Collection<ServletRuntime> servletRuntimes = contextRuntime.getServletRuntimes();
-        Collection<ServletRuntime> resourceRuntimes = contextRuntime.getResourceRuntimes();
         Collection<FilterRuntime> filterRuntimes = contextRuntime.getFilterRuntimes();
         Collection<ErrorPageRuntime> errorPageRuntimes = contextRuntime.getErrorPageRuntimes();
         long servletContextId = contextRuntime.getServiceId();
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ServletRegistryRuntime.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ServletRegistryRuntime.java
new file mode 100644
index 0000000..a63b5a6
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ServletRegistryRuntime.java
@@ -0,0 +1,51 @@
+/*
+ * 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.base.internal.runtime.dto;
+
+import java.util.Collection;
+import java.util.Collections;
+
+public final class ServletRegistryRuntime
+{
+    private final Collection<ServletRuntime> servletRuntimes;
+    private final Collection<ServletRuntime> resourceRuntimes;
+
+    public ServletRegistryRuntime(Collection<ServletRuntime> servletRuntimes,
+            Collection<ServletRuntime> resourceRuntimes)
+    {
+        this.servletRuntimes = servletRuntimes;
+        this.resourceRuntimes = resourceRuntimes;
+    }
+
+    public static ServletRegistryRuntime empty()
+    {
+        return new ServletRegistryRuntime(Collections.<ServletRuntime>emptyList(),
+                Collections.<ServletRuntime>emptyList());
+    }
+
+    Collection<ServletRuntime> getServletRuntimes()
+    {
+        return servletRuntimes;
+    }
+
+    Collection<ServletRuntime> getResourceRuntimes()
+    {
+        return resourceRuntimes;
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ServletRuntime.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ServletRuntime.java
index e994be3..0b7ee11 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ServletRuntime.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ServletRuntime.java
@@ -40,4 +40,7 @@
     Servlet getServlet();
 
     ServletInfo getServletInfo();
+
+    long getContextServiceId();
+
 }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceFactory.java b/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceFactory.java
index 1d3c057..f0b1fb2 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceFactory.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceFactory.java
@@ -102,7 +102,7 @@
         this.sessionListenerManager.open();
         this.sessionAttributeListenerManager.open();
 
-        this.sharedHttpService = new SharedHttpServiceImpl(handlerRegistry.getRegistry(null));
+        this.sharedHttpService = new SharedHttpServiceImpl(handlerRegistry);
 
         final String[] ifaces = new String[] { HttpService.class.getName(), ExtHttpService.class.getName() };
         this.httpServiceReg = bundleContext.registerService(ifaces, this, this.httpServiceProps);
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/service/SharedHttpServiceImpl.java b/http/base/src/main/java/org/apache/felix/http/base/internal/service/SharedHttpServiceImpl.java
index 9f58a64..ce12148 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/service/SharedHttpServiceImpl.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/service/SharedHttpServiceImpl.java
@@ -27,20 +27,20 @@
 
 import org.apache.felix.http.base.internal.context.ExtServletContext;
 import org.apache.felix.http.base.internal.handler.FilterHandler;
-import org.apache.felix.http.base.internal.handler.PerContextHandlerRegistry;
+import org.apache.felix.http.base.internal.handler.HandlerRegistry;
 import org.apache.felix.http.base.internal.handler.ServletHandler;
+import org.apache.felix.http.base.internal.handler.SimpleServletHandler;
 import org.apache.felix.http.base.internal.runtime.FilterInfo;
 import org.apache.felix.http.base.internal.runtime.ServletInfo;
-import org.apache.felix.http.base.internal.whiteboard.RegistrationFailureException;
 import org.osgi.service.http.NamespaceException;
 
 public final class SharedHttpServiceImpl
 {
-    private final PerContextHandlerRegistry handlerRegistry;
+    private final HandlerRegistry handlerRegistry;
 
     private final Map<String, ServletHandler> aliasMap = new HashMap<String, ServletHandler>();
 
-    public SharedHttpServiceImpl(final PerContextHandlerRegistry handlerRegistry)
+    public SharedHttpServiceImpl(final HandlerRegistry handlerRegistry)
     {
         if (handlerRegistry == null)
         {
@@ -78,7 +78,7 @@
             @Nonnull final Servlet servlet,
             @Nonnull final ServletInfo servletInfo) throws ServletException, NamespaceException
     {
-        final ServletHandler handler = new ServletHandler(null, httpContext, servletInfo, servlet);
+        final ServletHandler handler = new SimpleServletHandler(httpContext, servletInfo, servlet);
 
         synchronized (this.aliasMap)
         {
@@ -105,14 +105,7 @@
                 throw new IllegalArgumentException("Nothing registered at " + alias);
             }
 
-            try
-            {
-                return this.handlerRegistry.removeServlet(handler.getServletInfo(), true);
-            } catch (RegistrationFailureException e)
-            {
-                // TODO create FailureDTO
-                return null;
-            }
+            return this.handlerRegistry.removeServlet(handler.getServletInfo());
         }
     }
 
@@ -120,13 +113,7 @@
     {
         if (servlet != null)
         {
-            try
-            {
-                this.handlerRegistry.removeServlet(servlet, destroy);
-            } catch (RegistrationFailureException e)
-            {
-                // TODO create FailureDTO
-            }
+            this.handlerRegistry.removeServlet(servlet, destroy);
 
             synchronized (this.aliasMap)
             {
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/util/ErrorPageUtil.java b/http/base/src/main/java/org/apache/felix/http/base/internal/util/ErrorPageUtil.java
new file mode 100644
index 0000000..57022af
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/util/ErrorPageUtil.java
@@ -0,0 +1,30 @@
+/*
+ * 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.base.internal.util;
+
+import java.util.regex.Pattern;
+
+public final class ErrorPageUtil
+{
+    // TODO Handle special values 4xx and 5xx
+    public static final Pattern ERROR_CODE_PATTERN = Pattern.compile("\\d{3}");
+
+    public static boolean isErrorCode(String string)
+    {
+        return ERROR_CODE_PATTERN.matcher(string).matches();
+    }
+}
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 f31a463..65f6863 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
@@ -25,11 +25,11 @@
 
 import org.apache.felix.http.base.internal.handler.FilterHandler;
 import org.apache.felix.http.base.internal.handler.HandlerRegistry;
-import org.apache.felix.http.base.internal.handler.PerContextHandlerRegistry;
+import org.apache.felix.http.base.internal.handler.ResourceServletHandler;
 import org.apache.felix.http.base.internal.handler.ServletHandler;
+import org.apache.felix.http.base.internal.handler.WhiteboardServletHandler;
 import org.apache.felix.http.base.internal.runtime.FilterInfo;
 import org.apache.felix.http.base.internal.runtime.ResourceInfo;
-import org.apache.felix.http.base.internal.runtime.ServletContextHelperInfo;
 import org.apache.felix.http.base.internal.runtime.ServletInfo;
 import org.osgi.framework.BundleContext;
 
@@ -61,31 +61,22 @@
             @Nonnull final ServletInfo servletInfo)
             throws RegistrationFailureException
     {
-        ServletContextHelperInfo contextInfo = contextHandler.getContextInfo();
-        final PerContextHandlerRegistry registry = this.handlerRegistry.getRegistry(contextInfo);
-        if (registry != null)
+        try
         {
-            try
-            {
-                ServletHandler handler = new ServletHandler(contextHandler.getContextInfo(),
-                        contextHandler.getServletContext(servletInfo.getServiceReference().getBundle()),
-                        servletInfo,
-                        null,
-                        true);
+            ServletHandler handler = new WhiteboardServletHandler(contextHandler.getContextInfo(),
+                contextHandler.getServletContext(servletInfo.getServiceReference().getBundle()),
+                servletInfo,
+                bundleContext);
 
-                registry.addServlet(handler);
-            }
-            catch (final RegistrationFailureException e)
-            {
-                throw e;
-            }
-            catch (final ServletException e)
-            {
-                throw new RegistrationFailureException(servletInfo, FAILURE_REASON_EXCEPTION_ON_INIT, e);
-            }
-        } else
+            handlerRegistry.addServlet(handler);
+        }
+        catch (final RegistrationFailureException e)
         {
-            throw new RegistrationFailureException(servletInfo, FAILURE_REASON_SERVICE_NOT_GETTABLE);
+            throw e;
+        }
+        catch (final ServletException e)
+        {
+            throw new RegistrationFailureException(servletInfo, FAILURE_REASON_EXCEPTION_ON_INIT, e);
         }
     }
 
@@ -97,11 +88,7 @@
      */
     public void unregisterServlet(@Nonnull final ContextHandler contextHandler, @Nonnull final ServletInfo servletInfo) throws RegistrationFailureException
     {
-        final PerContextHandlerRegistry registry = this.handlerRegistry.getRegistry(contextHandler.getContextInfo());
-        if (registry != null )
-        {
-            registry.removeServlet(servletInfo, true);
-        }
+        handlerRegistry.removeServlet(contextHandler.getContextInfo().getServiceId(), servletInfo);
         contextHandler.ungetServletContext(servletInfo.getServiceReference().getBundle());
     }
 
@@ -122,11 +109,7 @@
                     filter,
                     filterInfo);
             try {
-                final PerContextHandlerRegistry registry = this.handlerRegistry.getRegistry(contextHandler.getContextInfo());
-                if (registry != null )
-                {
-                    registry.addFilter(handler);
-                }
+                handlerRegistry.addFilter(contextHandler.getContextInfo(), handler);
             }
             catch (final RegistrationFailureException e)
             {
@@ -150,14 +133,10 @@
      */
     public void unregisterFilter(@Nonnull final ContextHandler contextHandler, @Nonnull final FilterInfo filterInfo)
     {
-        final PerContextHandlerRegistry registry = this.handlerRegistry.getRegistry(contextHandler.getContextInfo());
-        if (registry != null )
+        final Filter instance = handlerRegistry.removeFilter(contextHandler.getContextInfo(), filterInfo);
+        if ( instance != null )
         {
-            final Filter instance = registry.removeFilter(filterInfo, true);
-            if ( instance != null )
-            {
-                this.bundleContext.getServiceObjects(filterInfo.getServiceReference()).ungetService(instance);
-            }
+            this.bundleContext.getServiceObjects(filterInfo.getServiceReference()).ungetService(instance);
         }
         contextHandler.ungetServletContext(filterInfo.getServiceReference().getBundle());
     }
@@ -171,23 +150,20 @@
     public void registerResource(@Nonnull final ContextHandler contextHandler,
             @Nonnull final ResourceInfo resourceInfo) throws RegistrationFailureException
     {
-    	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) {
+        final ServletInfo servletInfo = new ServletInfo(resourceInfo);
+
+        final ServletHandler handler = new ResourceServletHandler(contextHandler.getContextInfo(),
+            contextHandler.getServletContext(servletInfo.getServiceReference().getBundle()),
+            servletInfo);
+
+        try
+        {
+            handlerRegistry.addServlet(handler);
+        }
+        catch (ServletException e)
+        {
             throw new RegistrationFailureException(resourceInfo, FAILURE_REASON_EXCEPTION_ON_INIT, e);
-    	}
+        }
     }
 
     /**
@@ -199,11 +175,7 @@
     public void unregisterResource(@Nonnull final ContextHandler contextHandler, @Nonnull final ResourceInfo resourceInfo) throws RegistrationFailureException
     {
         final ServletInfo servletInfo = new ServletInfo(resourceInfo);
-        final PerContextHandlerRegistry registry = this.handlerRegistry.getRegistry(contextHandler.getContextInfo());
-        if (registry != null )
-        {
-            registry.removeServlet(servletInfo, true);
-        }
+        handlerRegistry.removeServlet(contextHandler.getContextInfo().getServiceId(), servletInfo);
         contextHandler.ungetServletContext(servletInfo.getServiceReference().getBundle());
     }
 
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardManager.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardManager.java
index 025f0b8..d3e6a49 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardManager.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardManager.java
@@ -57,8 +57,8 @@
 import org.apache.felix.http.base.internal.runtime.ServletRequestAttributeListenerInfo;
 import org.apache.felix.http.base.internal.runtime.ServletRequestListenerInfo;
 import org.apache.felix.http.base.internal.runtime.WhiteboardServiceInfo;
-import org.apache.felix.http.base.internal.runtime.dto.ContextRuntime;
 import org.apache.felix.http.base.internal.runtime.dto.FailureRuntime;
+import org.apache.felix.http.base.internal.runtime.dto.HandlerRegistryRuntime;
 import org.apache.felix.http.base.internal.runtime.dto.RegistryRuntime;
 import org.apache.felix.http.base.internal.runtime.dto.ServletContextHelperRuntime;
 import org.apache.felix.http.base.internal.service.HttpServiceFactory;
@@ -108,8 +108,6 @@
 
     private volatile ServletContext webContext;
 
-    private volatile ServiceReference<HttpServiceRuntime> httpServiceRuntime;
-
     private volatile ServiceRegistration<ServletContextHelper> defaultContextRegistration;
 
     private final List<ServiceTracker<?, ?>> trackers = new ArrayList<ServiceTracker<?, ?>>();
@@ -141,8 +139,7 @@
                 serviceRuntime,
                 this.serviceRuntime.getAttributes());
 
-        this.webContext = webContext;
-        this.httpServiceRuntime = httpServiceRuntime;
+        this.webContext = context;
 
         final Dictionary<String, Object> props = new Hashtable<String, Object>();
         props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, HttpWhiteboardConstants.HTTP_WHITEBOARD_DEFAULT_CONTEXT_NAME);
@@ -645,7 +642,7 @@
             try
             {
                 final Filter f = this.bundleContext.createFilter(target);
-                return f.match(this.httpServiceRuntime);
+                return f.match(this.runtimeServiceReg.getReference());
             }
             catch ( final InvalidSyntaxException ise)
             {
@@ -689,10 +686,10 @@
 
     public RegistryRuntime getRuntime(HandlerRegistry registry)
     {
-        Collection<ServletContextHelperRuntime> contextRuntimes = new TreeSet<ServletContextHelperRuntime>(ServletContextHelperRuntime.COMPARATOR);
-        List<ContextRuntime> handlerRuntimes;
+        final Collection<ServletContextHelperRuntime> contextRuntimes = new TreeSet<ServletContextHelperRuntime>(ServletContextHelperRuntime.COMPARATOR);
+        HandlerRegistryRuntime handlerRuntimes;
         final Map<Long, Collection<ServiceReference<?>>> listenerRuntimes = new HashMap<Long, Collection<ServiceReference<?>>>();
-        FailureRuntime.Builder failureRuntime = FailureRuntime.builder();
+        final FailureRuntime.Builder failureRuntime = FailureRuntime.builder();
         synchronized ( this.contextMap )
         {
             for (List<ContextHandler> contextHandlerList : this.contextMap.values())
@@ -706,9 +703,15 @@
                     listenerRuntimes.put(serviceId, handler.getListenerRegistry().getRuntime());
                 }
             }
+            contextRuntimes.add(registry.getHttpServiceContextRuntime());
             handlerRuntimes = registry.getRuntime(failureRuntime);
             failureRuntime.add(serviceFailures);
         }
-        return new RegistryRuntime(contextRuntimes, handlerRuntimes, listenerRuntimes, failureRuntime.build());
+
+        return new RegistryRuntime(contextRuntimes,
+            handlerRuntimes.getContextRuntimes(),
+            handlerRuntimes.getServletRegistryRuntime(),
+            listenerRuntimes,
+            failureRuntime.build());
     }
 }
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/HandlerRankingMultimapTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/HandlerRankingMultimapTest.java
new file mode 100644
index 0000000..5fc66b6
--- /dev/null
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/HandlerRankingMultimapTest.java
@@ -0,0 +1,385 @@
+/*
+ * 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.base.internal.handler;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+
+import org.apache.felix.http.base.internal.handler.HandlerRankingMultimap.Update;
+import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Test;
+
+@SuppressWarnings("unchecked")
+public class HandlerRankingMultimapTest
+{
+    private HandlerRankingMultimap<String> handlerMultimap;
+
+    @Before
+    public void setup()
+    {
+        handlerMultimap = new HandlerRankingMultimap<String>();
+    }
+
+    @Test
+    public void addedInfoIsUsed() throws ServletException
+    {
+        TestHandler<String> handler = TestHandler.create(asList("a"), 0);
+        Update<String> update = handlerMultimap.add(handler.getKeys(), handler);
+
+        assertEquals(1, handlerMultimap.size());
+        assertTrue(handlerMultimap.isActive(handler));
+
+        assertEquals(1, update.getActivated().size());
+        assertThat(update.getActivated(), hasEntry("a", handler));
+        assertTrue(update.getDeactivated().isEmpty());
+
+        assertEquals(1, update.getInit().size());
+        assertThat(update.getInit(), contains(handler));
+        assertTrue(update.getDestroy().isEmpty());
+    }
+
+    @Test
+    public void highestPriorityServiceIsUsed() throws ServletException
+    {
+        TestHandler<String> higher = TestHandler.create(asList("a"), 1);
+        TestHandler<String> lower = TestHandler.create(asList("a"), 0);
+
+        handlerMultimap.add(lower.getKeys(), lower);
+
+        Update<String> updateAddingHigher = handlerMultimap.add(higher.getKeys(), higher);
+
+        assertTrue(handlerMultimap.isActive(higher));
+        assertFalse(handlerMultimap.isActive(lower));
+
+        assertEquals(1, updateAddingHigher.getActivated().size());
+        assertThat(updateAddingHigher.getActivated(), hasEntry("a", higher));
+        assertEquals(1, updateAddingHigher.getDeactivated().size());
+        assertThat(updateAddingHigher.getDeactivated(), hasEntry("a", lower));
+
+        assertEquals(1, updateAddingHigher.getInit().size());
+        assertThat(updateAddingHigher.getInit(), contains(higher));
+        assertEquals(1, updateAddingHigher.getDestroy().size());
+        assertThat(updateAddingHigher.getDestroy(), contains(lower));
+    }
+
+    @Test
+    public void removeHighestPriorityService() throws ServletException
+    {
+        TestHandler<String> higher = TestHandler.create(asList("a"), 1);
+        TestHandler<String> lower = TestHandler.create(asList("a"), 0);
+
+        handlerMultimap.add(lower.getKeys(), lower);
+        handlerMultimap.add(higher.getKeys(), higher);
+
+        Update<String> update = handlerMultimap.remove(higher.getKeys(), higher);
+
+        assertFalse(handlerMultimap.isActive(higher));
+        assertTrue(handlerMultimap.isActive(lower));
+
+        assertEquals(1, update.getActivated().size());
+        assertThat(update.getActivated(), hasEntry("a", lower));
+        assertEquals(1, update.getDeactivated().size());
+        assertThat(update.getDeactivated(), hasEntry("a", higher));
+
+        assertEquals(1, update.getInit().size());
+        assertThat(update.getInit(), contains(lower));
+        assertEquals(1, update.getDestroy().size());
+        assertThat(update.getDestroy(), contains(higher));
+    }
+
+    @Test
+    public void removeLowerPriorityService() throws ServletException
+    {
+        TestHandler<String> higher = TestHandler.create(asList("a"), 1);
+        TestHandler<String> lower = TestHandler.create(asList("a"), 0);
+
+        handlerMultimap.add(lower.getKeys(), higher);
+        handlerMultimap.add(higher.getKeys(), lower);
+
+        Update<String> update = handlerMultimap.remove(lower.getKeys(), lower);
+
+        assertTrue(handlerMultimap.isActive(higher));
+        assertFalse(handlerMultimap.isActive(lower));
+
+        assertTrue(update.getActivated().isEmpty());
+        assertTrue(update.getDeactivated().isEmpty());
+
+        assertTrue(update.getInit().isEmpty());
+        assertTrue(update.getDestroy().isEmpty());
+    }
+
+    @Test
+    public void addServiceWithMultipleKeys() throws ServletException
+    {
+        TestHandler<String> handler = TestHandler.create(asList("a", "b"), 0);
+
+        Update<String> update = handlerMultimap.add(handler.getKeys(), handler);
+
+        assertTrue(handlerMultimap.isActive(handler));
+
+        assertEquals(2, update.getActivated().size());
+        assertThat(update.getActivated(), hasEntry("a", handler));
+        assertThat(update.getActivated(), hasEntry("b", handler));
+        assertTrue(update.getDeactivated().isEmpty());
+
+        assertEquals(1, update.getInit().size());
+        assertThat(update.getInit(), contains(handler));
+    }
+
+    @Test
+    public void addServiceWithMultipleKeysShadowsAllKeys() throws ServletException
+    {
+        TestHandler<String> higher = TestHandler.create(asList("a", "b", "c"), 1);
+        TestHandler<String> lower = TestHandler.create(asList("a", "b"), 0);
+
+        handlerMultimap.add(lower.getKeys(), lower);
+
+        Update<String> updateWithHigher = handlerMultimap.add(higher.getKeys(), higher);
+
+        assertTrue(handlerMultimap.isActive(higher));
+        assertFalse(handlerMultimap.isActive(lower));
+
+        assertEquals(3, updateWithHigher.getActivated().size());
+        assertThat(updateWithHigher.getActivated(), hasEntry("a", higher));
+        assertThat(updateWithHigher.getActivated(), hasEntry("b", higher));
+        assertThat(updateWithHigher.getActivated(), hasEntry("c", higher));
+        assertEquals(2, updateWithHigher.getDeactivated().size());
+        assertThat(updateWithHigher.getDeactivated(), hasEntry("a", lower));
+        assertThat(updateWithHigher.getDeactivated(), hasEntry("b", lower));
+
+        assertEquals(1, updateWithHigher.getInit().size());
+        assertThat(updateWithHigher.getInit(), contains(higher));
+        assertEquals(1, updateWithHigher.getDestroy().size());
+        assertThat(updateWithHigher.getDestroy(), contains(lower));
+    }
+
+    @Test
+    public void addServiceWithMultipleKeysShadowsPartially() throws ServletException
+    {
+        TestHandler<String> higher = TestHandler.create(asList("a", "c"), 1);
+        TestHandler<String> lower = TestHandler.create(asList("a", "b"), 0);
+
+        handlerMultimap.add(lower.getKeys(), lower);
+
+        Update<String> updateWithHigher = handlerMultimap.add(higher.getKeys(), higher);
+
+        assertTrue(handlerMultimap.isActive(higher));
+        assertTrue(handlerMultimap.isActive(lower));
+
+        assertEquals(2, updateWithHigher.getActivated().size());
+        assertThat(updateWithHigher.getActivated(), hasEntry("a", higher));
+        assertThat(updateWithHigher.getActivated(), hasEntry("c", higher));
+        assertEquals(1, updateWithHigher.getDeactivated().size());
+        assertThat(updateWithHigher.getDeactivated(), hasEntry("a", lower));
+
+        assertEquals(1, updateWithHigher.getInit().size());
+        assertThat(updateWithHigher.getInit(), contains(higher));
+        assertTrue(updateWithHigher.getDestroy().isEmpty());
+    }
+
+    @Test
+    public void addServiceWithMultipleKeysIsCompletelyShadowed() throws ServletException
+    {
+        TestHandler<String> higher = TestHandler.create(asList("a", "b", "c"), 1);
+        TestHandler<String> lower = TestHandler.create(asList("a", "b"), 0);
+
+        handlerMultimap.add(higher.getKeys(), higher);
+
+        Update<String> updateWithLower = handlerMultimap.add(lower.getKeys(), lower);
+
+        assertTrue(handlerMultimap.isActive(higher));
+        assertFalse(handlerMultimap.isActive(lower));
+
+        assertTrue(updateWithLower.getActivated().isEmpty());
+        assertTrue(updateWithLower.getDeactivated().isEmpty());
+
+        assertTrue(updateWithLower.getInit().isEmpty());
+        assertTrue(updateWithLower.getDestroy().isEmpty());
+    }
+
+    @Test
+    public void sizeReturnsAllEntries() throws ServletException
+    {
+        TestHandler<String> higher = TestHandler.create(asList("a", "b", "c"), 1);
+        TestHandler<String> lower = TestHandler.create(asList("a", "b"), 0);
+        TestHandler<String> third = TestHandler.create(asList("d"), 3);
+
+        assertEquals(0 , handlerMultimap.size());
+
+        handlerMultimap.add(lower.getKeys(), lower);
+
+        assertEquals(1, handlerMultimap.size());
+
+        handlerMultimap.add(higher.getKeys(), higher);
+
+        assertEquals(2, handlerMultimap.size());
+
+        handlerMultimap.add(third.getKeys(), third);
+
+        assertEquals(3, handlerMultimap.size());
+
+        handlerMultimap.remove(lower.getKeys(), lower);
+
+        assertEquals(2, handlerMultimap.size());
+
+        handlerMultimap.remove(higher.getKeys(), higher);
+
+        assertEquals(1, handlerMultimap.size());
+
+        handlerMultimap.remove(third.getKeys(), third);
+
+        assertEquals(0, handlerMultimap.size());
+    }
+
+    public void getActiveValuesContainsAllHeads()
+    {
+        TestHandler<String> one = TestHandler.create(asList("a", "b", "c"), 1);
+        TestHandler<String> two = TestHandler.create(asList("a", "b"), 0);
+        TestHandler<String> three = TestHandler.create(asList("c, e"), 3);
+        TestHandler<String> four = TestHandler.create(asList("d"), 3);
+
+        handlerMultimap.add(one.getKeys(), one);
+        handlerMultimap.add(two.getKeys(), two);
+        handlerMultimap.add(three.getKeys(), three);
+        handlerMultimap.add(four.getKeys(), four);
+
+        assertEquals(3, handlerMultimap.getActiveValues());
+        assertTrue(handlerMultimap.getActiveValues().contains(one));
+        assertTrue(handlerMultimap.getActiveValues().contains(three));
+        assertTrue(handlerMultimap.getActiveValues().contains(four));
+    }
+
+    public void getShadowedValuesContainsAllTails()
+    {
+        TestHandler<String> one = TestHandler.create(asList("a", "b", "c"), 1);
+        TestHandler<String> two = TestHandler.create(asList("a", "b"), 0);
+        TestHandler<String> three = TestHandler.create(asList("a"), -1);
+        TestHandler<String> four = TestHandler.create(asList("c, e"), 3);
+        TestHandler<String> five = TestHandler.create(asList("d"), 3);
+
+        handlerMultimap.add(one.getKeys(), one);
+        handlerMultimap.add(two.getKeys(), two);
+        handlerMultimap.add(three.getKeys(), three);
+        handlerMultimap.add(four.getKeys(), four);
+        handlerMultimap.add(five.getKeys(), five);
+
+        assertEquals(3, handlerMultimap.getActiveValues());
+        assertTrue(handlerMultimap.getActiveValues().contains(one));
+        assertTrue(handlerMultimap.getActiveValues().contains(two));
+        assertTrue(handlerMultimap.getActiveValues().contains(three));
+    }
+
+    public void keyComparatorIsUsed()
+    {
+        final Object keyOne = new Object();
+        final Object keyTwo = new Object();
+
+        Comparator<Object> keyComparator = new Comparator<Object>()
+        {
+            @Override
+            public int compare(Object o1, Object o2)
+            {
+                if (o1 == o2)
+                {
+                    return 0;
+                }
+                if (o1 == keyOne && o2 == keyTwo)
+                {
+                    return 1;
+                }
+                if (o1 == keyTwo && o2 == keyOne)
+                {
+                    return -1;
+                }
+                throw new IllegalArgumentException();
+            }
+        };
+
+        HandlerRankingMultimap<Object> multimap = new HandlerRankingMultimap<Object>(keyComparator);
+
+        TestHandler<Object> handlerOne = TestHandler.create(asList(keyOne), 1);
+        TestHandler<Object> handlerTwo = TestHandler.create(asList(keyOne, keyTwo), 0);
+
+        multimap.add(handlerOne.getKeys(), handlerOne);
+        multimap.add(handlerTwo.getKeys(), handlerTwo);
+
+        assertEquals(2, multimap.size());
+        assertEquals(2, multimap.getActiveValues().size());
+        assertTrue(multimap.getActiveValues().contains(keyOne));
+        assertTrue(multimap.getActiveValues().contains(keyTwo));
+        assertEquals(1, multimap.getShadowedValues().size());
+        assertTrue(multimap.getShadowedValues().contains(keyTwo));
+    }
+
+    private static Matcher<Map<? extends String, ? extends ServletHandler>> hasEntry(String key, ServletHandler servletHandler)
+    {
+        return Matchers.hasEntry(key, servletHandler);
+    }
+
+    private static Matcher<Iterable<? extends ServletHandler>> contains(ServletHandler servletHandler)
+    {
+        return Matchers.contains(servletHandler);
+    }
+
+    private static abstract class TestHandler<T> extends ServletHandler
+    {
+        static int idCount = 0;
+
+        TestHandler(List<T> keys, int ranking)
+        {
+            super(0L, null, null);
+        }
+
+        static <T> TestHandler<T> create(List<T> keys, int ranking)
+        {
+            TestHandler<T> testHandler = mock(TestHandler.class);
+            when(testHandler.getId()).thenReturn(++idCount);
+            when(testHandler.getRanking()).thenReturn(ranking);
+            when(testHandler.getKeys()).thenReturn(keys);
+            when(testHandler.compareTo(any(TestHandler.class))).thenCallRealMethod();
+            return testHandler;
+        }
+
+        @Override
+        public int compareTo(ServletHandler o)
+        {
+            TestHandler<T> other = (TestHandler<T>) o;
+            int rankCompare = Integer.compare(other.getRanking(), getRanking());
+            return rankCompare != 0 ? rankCompare : Integer.compare(getId(), other.getId());
+        }
+
+        abstract int getRanking();
+
+        abstract int getId();
+
+        abstract List<T> getKeys();
+    }
+}
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 a2d767c..b81a916 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
@@ -37,10 +37,10 @@
     public void testPathOrdering()
     {
         final List<PerContextHandlerRegistry> list = new ArrayList<PerContextHandlerRegistry>();
-        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));
+        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)));
 
         Collections.sort(list);
 
@@ -54,10 +54,10 @@
     public void testRankingOrdering()
     {
         final List<PerContextHandlerRegistry> list = new ArrayList<PerContextHandlerRegistry>();
-        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));
+        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)));
 
         Collections.sort(list);
 
@@ -81,8 +81,8 @@
 
     private void testSymetry(String path, String otherPath, long id, long otherId, int ranking, int otherRanking)
     {
-        PerContextHandlerRegistry handlerRegistry = new PerContextHandlerRegistry(createServletContextHelperInfo(path, id, ranking), null);
-        PerContextHandlerRegistry other = new PerContextHandlerRegistry(createServletContextHelperInfo(otherPath, otherId, otherRanking), null);
+        PerContextHandlerRegistry handlerRegistry = new PerContextHandlerRegistry(createServletContextHelperInfo(path, id, ranking));
+        PerContextHandlerRegistry other = new PerContextHandlerRegistry(createServletContextHelperInfo(otherPath, otherId, otherRanking));
 
         assertEquals(handlerRegistry.compareTo(other), -other.compareTo(handlerRegistry));
     }
@@ -91,17 +91,18 @@
     public void testOrderingTransitivity()
     {
         testTransitivity("/", "/foo", "/barrr", 1L, 2L, 3L, 0, 0, 0);
-        testTransitivity("/", "/", "/", 0L, 1L, 2L, 1, 2, 3);
+        testTransitivity("/", "/", "/", 1L, 2L, 3L, 1, 2, 3);
+        testTransitivity("/", "/", "/", 2L, 1L, 0L, 1, 2, 3);
         testTransitivity("/", "/", "/", 2L, 1L, 0L, 0, 0, 0);
-        testTransitivity("/", "/", "/", -1L, 1L, 0L, 0, 0, 0);
+        testTransitivity("/", "/", "/", 1L, -1L, 0L, 0, 0, 0);
         testTransitivity("/", "/", "/", -2L, -1L, 0L, 0, 0, 0);
     }
 
     private void testTransitivity(String highPath, String midPath, String lowPath, long highId, long midId, long lowId, int highRanking, int midRanking, int 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);
+        PerContextHandlerRegistry high = new PerContextHandlerRegistry(createServletContextHelperInfo(highPath, highId, highRanking));
+        PerContextHandlerRegistry mid = new PerContextHandlerRegistry(createServletContextHelperInfo(midPath, midId, midRanking));
+        PerContextHandlerRegistry low = new PerContextHandlerRegistry(createServletContextHelperInfo(lowPath, lowId, lowRanking));
 
         assertTrue(high.compareTo(mid) > 0);
         assertTrue(mid.compareTo(low) > 0);
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/ServletHandlerTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/SimpleServletHandlerTest.java
similarity index 98%
rename from http/base/src/test/java/org/apache/felix/http/base/internal/handler/ServletHandlerTest.java
rename to http/base/src/test/java/org/apache/felix/http/base/internal/handler/SimpleServletHandlerTest.java
index d8a94aa..d1eea17 100644
--- a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/ServletHandlerTest.java
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/SimpleServletHandlerTest.java
@@ -39,7 +39,7 @@
 import org.junit.Before;
 import org.junit.Test;
 
-public class ServletHandlerTest extends AbstractHandlerTest
+public class SimpleServletHandlerTest extends AbstractHandlerTest
 {
     private Servlet servlet;
 
@@ -225,6 +225,6 @@
             map = Collections.emptyMap();
         }
         final ServletInfo info = new ServletInfo(null, alias, 0, map);
-        return new ServletHandler(null, this.context, info, this.servlet);
+        return new SimpleServletHandler(this.context, info, this.servlet);
     }
 }
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/runtime/WhiteboardServiceHelper.java b/http/base/src/test/java/org/apache/felix/http/base/internal/runtime/WhiteboardServiceHelper.java
index 8e01e05..243d3c9 100644
--- a/http/base/src/test/java/org/apache/felix/http/base/internal/runtime/WhiteboardServiceHelper.java
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/runtime/WhiteboardServiceHelper.java
@@ -33,7 +33,7 @@
 
 import org.apache.felix.http.base.internal.context.ExtServletContext;
 import org.apache.felix.http.base.internal.handler.FilterHandler;
-import org.apache.felix.http.base.internal.handler.ServletHandler;
+import org.apache.felix.http.base.internal.handler.SimpleServletHandler;
 import org.apache.felix.http.base.internal.runtime.dto.ErrorPageRuntime;
 import org.apache.felix.http.base.internal.runtime.dto.FilterRuntime;
 import org.apache.felix.http.base.internal.runtime.dto.ServletRuntime;
@@ -98,24 +98,26 @@
     }
 
     public static ServletRuntime createTestServletWithServiceId(String identifier,
-            ExtServletContext context)
+            ExtServletContext context,
+            long contextServiceId)
     {
-        return createTestServlet(identifier, context, ID_COUNTER.incrementAndGet());
+        return createTestServlet(identifier, context, ID_COUNTER.incrementAndGet(), contextServiceId);
     }
 
-    public static ServletRuntime createTestServlet(String identifier, ExtServletContext context)
+    public static ServletRuntime createTestServlet(String identifier, ExtServletContext context, long contextServiceId)
     {
-        return createTestServlet(identifier, context, -ID_COUNTER.incrementAndGet());
+        return createTestServlet(identifier, context, -ID_COUNTER.incrementAndGet(), contextServiceId);
     }
 
     private static ServletRuntime createTestServlet(String identifier,
             ExtServletContext context,
-            Long serviceId)
+            Long serviceId,
+            Long contextServiceId)
     {
         ServletInfo servletInfo = createServletInfo(identifier, serviceId);
         Servlet servlet = mock(Servlet.class);
         when(servlet.getServletInfo()).thenReturn("info_" + identifier);
-        return new ServletHandler(null, context, servletInfo, servlet);
+        return new SimpleServletHandler(contextServiceId, context, servletInfo, servlet);
     }
 
     private static ServletInfo createServletInfo(String identifier, Long serviceId)
@@ -156,21 +158,22 @@
                 };
     }
 
-    public static ErrorPageRuntime createErrorPageWithServiceId(String identifier, ExtServletContext context)
+    public static ErrorPageRuntime createErrorPageWithServiceId(String identifier, ExtServletContext context, long contextServiceId)
     {
-        return createErrorPage(identifier, context, ID_COUNTER.incrementAndGet());
+        return createErrorPage(identifier, context, ID_COUNTER.incrementAndGet(), contextServiceId);
     }
 
-    public static ErrorPageRuntime createErrorPage(String identifier, ExtServletContext context)
+    public static ErrorPageRuntime createErrorPage(String identifier, ExtServletContext context, long contextServiceId)
     {
-        return createErrorPage(identifier, context, -ID_COUNTER.incrementAndGet());
+        return createErrorPage(identifier, context, -ID_COUNTER.incrementAndGet(), contextServiceId);
     }
 
     private static ErrorPageRuntime createErrorPage(String identifier,
             ExtServletContext context,
-            Long serviceId)
+            Long serviceId,
+            long contextServiceId)
     {
-        ServletRuntime servletHandler = createTestServlet(identifier, context, serviceId);
+        ServletRuntime servletHandler = createTestServlet(identifier, context, serviceId, contextServiceId);
         Collection<Integer> errorCodes = Arrays.asList(400, 500);
         Collection<String> exceptions = Arrays.asList("Bad request", "Error");
 
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/runtime/dto/RuntimeDTOBuilderTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/runtime/dto/RuntimeDTOBuilderTest.java
index a107415..d989b38 100644
--- a/http/base/src/test/java/org/apache/felix/http/base/internal/runtime/dto/RuntimeDTOBuilderTest.java
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/runtime/dto/RuntimeDTOBuilderTest.java
@@ -57,7 +57,7 @@
 
 import org.apache.felix.http.base.internal.context.ExtServletContext;
 import org.apache.felix.http.base.internal.handler.FilterHandler;
-import org.apache.felix.http.base.internal.handler.ServletHandler;
+import org.apache.felix.http.base.internal.handler.SimpleServletHandler;
 import org.apache.felix.http.base.internal.runtime.AbstractInfo;
 import org.apache.felix.http.base.internal.runtime.FilterInfo;
 import org.apache.felix.http.base.internal.runtime.ServletContextHelperInfo;
@@ -208,10 +208,15 @@
 
     public void setupRegistry(List<ServletContextHelperRuntime> contexts,
             List<ContextRuntime> contextRuntimes,
+            ServletRegistryRuntime servletRegistryRuntime,
             Map<Long, Collection<ServiceReference<?>>> listenerRuntimes,
             FailureRuntime failures)
     {
-        registry = new RegistryRuntime(contexts, contextRuntimes, listenerRuntimes, failures);
+        registry = new RegistryRuntime(contexts,
+            contextRuntimes,
+            servletRegistryRuntime,
+            listenerRuntimes,
+            failures);
     }
 
     @Test
@@ -221,32 +226,36 @@
         ServletContextHelperRuntime contextHelper_A = setupContext(context_A, "A", ID_A);
         ServletContextHelperRuntime contextHelper_B = setupContext(context_B, "B", ID_B);
 
-        List<ServletRuntime> servlets_0 = asList(createTestServlet("1", context_0));
+        List<ServletRuntime> servlets = new ArrayList<ServletRuntime>();
+        List<ServletRuntime> resources = new ArrayList<ServletRuntime>();
+
+        servlets.add(createTestServlet("1", context_0, ID_0));
+        resources.add(createTestServlet("1", context_0, ID_0));
         List<FilterRuntime> filters_0 = asList(createTestFilter("1", context_0));
-        List<ServletRuntime> resources_0 = asList(createTestServlet("1", context_0));
-        List<ErrorPageRuntime> errorPages_0 = asList(createErrorPage("E_1", context_0));
-        ContextRuntime contextRuntime_0 = new ContextRuntime(servlets_0, filters_0, resources_0, errorPages_0, ID_0);
+        List<ErrorPageRuntime> errorPages_0 = asList(createErrorPage("E_1", context_0, ID_0));
+        ContextRuntime contextRuntime_0 = new ContextRuntime(filters_0, errorPages_0, ID_0);
 
-        List<ServletRuntime> servlets_A = asList(createTestServlet("A_1", context_A));
+        servlets.add(createTestServlet("A_1", context_A, ID_A));
+        resources.add(createTestServlet("A_1", context_A, ID_A));
         List<FilterRuntime> filters_A = asList(createTestFilter("A_1", context_A));
-        List<ServletRuntime> resources_A = asList(createTestServlet("A_1", context_A));
-        List<ErrorPageRuntime> errorPages_A = asList(createErrorPage("E_A_1", context_A));
-        ContextRuntime contextRuntime_A = new ContextRuntime(servlets_A, filters_A, resources_A, errorPages_A, ID_A);
+        List<ErrorPageRuntime> errorPages_A = asList(createErrorPage("E_A_1", context_A, ID_A));
+        ContextRuntime contextRuntime_A = new ContextRuntime(filters_A, errorPages_A, ID_A);
 
-        List<ServletRuntime> servlets_B = asList(createTestServletWithServiceId("B_1", context_B),
-                createTestServletWithServiceId("B_2", context_B));
+        servlets.addAll(asList(createTestServletWithServiceId("B_1", context_B, ID_B),
+                createTestServletWithServiceId("B_2", context_B, ID_B)));
+        resources.addAll(asList(createTestServletWithServiceId("B_1", context_B, ID_B),
+            createTestServletWithServiceId("B_2", context_B, ID_B)));
         List<FilterRuntime> filters_B = asList(createTestFilterWithServiceId("B_1", context_B),
                 createTestFilterWithServiceId("B_2", context_B));
-        List<ServletRuntime> resources_B = asList(createTestServletWithServiceId("B_1", context_B),
-                createTestServletWithServiceId("B_2", context_B));
-        List<ErrorPageRuntime> errorPages_B = asList(createErrorPageWithServiceId("E_B_1", context_B),
-                createErrorPageWithServiceId("E_B_2", context_B));
-        ContextRuntime contextRuntime_B = new ContextRuntime(servlets_B, filters_B, resources_B, errorPages_B, ID_B);
+        List<ErrorPageRuntime> errorPages_B = asList(createErrorPageWithServiceId("E_B_1", context_B, ID_B),
+                createErrorPageWithServiceId("E_B_2", context_B, ID_B));
+        ContextRuntime contextRuntime_B = new ContextRuntime(filters_B, errorPages_B, ID_B);
 
         Map<Long, Collection<ServiceReference<?>>> listenerRuntimes = setupListeners();
 
         setupRegistry(asList(contextHelper_0, contextHelper_A, contextHelper_B),
                 asList(contextRuntime_0, contextRuntime_A, contextRuntime_B),
+                new ServletRegistryRuntime(servlets, resources),
                 listenerRuntimes,
                 FailureRuntime.empty());
 
@@ -565,7 +574,7 @@
                 true,
                 Collections.<String, String>emptyMap());
         Servlet servlet = mock(Servlet.class);
-        ServletRuntime servletHandler = new ServletHandler(null, context_0, servletInfo, servlet);
+        ServletRuntime servletHandler = new SimpleServletHandler(context_0, servletInfo, servlet);
         when(servlet.getServletInfo()).thenReturn("info_0");
 
         FilterInfo filterInfo = createFilterInfo(0,
@@ -587,14 +596,13 @@
                 true,
                 Collections.<String, String>emptyMap());
         Servlet resource = mock(Servlet.class);
-        ServletRuntime resourceHandler = new ServletHandler(null, context_0, resourceInfo, resource);
+        ServletRuntime resourceHandler = new SimpleServletHandler(ID_0, context_0, resourceInfo, resource);
 
-        ContextRuntime contextRuntime = new ContextRuntime(asList(servletHandler),
-                asList(filterHandler),
-                asList(resourceHandler),
+        ContextRuntime contextRuntime = new ContextRuntime(asList(filterHandler),
                 Collections.<ErrorPageRuntime>emptyList(),
                 ID_0);
         setupRegistry(asList(contextHandler), asList(contextRuntime),
+                new ServletRegistryRuntime(asList(resourceHandler), asList(servletHandler)),
                 Collections.<Long, Collection<ServiceReference<?>>>emptyMap(),
                 FailureRuntime.empty());
 
@@ -618,6 +626,7 @@
 
         setupRegistry(asList(contextHandler_0, contextHandler_A),
                 asList(ContextRuntime.empty(ID_0), ContextRuntime.empty(ID_A)),
+                ServletRegistryRuntime.empty(),
                 Collections.<Long, Collection<ServiceReference<?>>>emptyMap(),
                 FailureRuntime.empty());
 
@@ -646,15 +655,14 @@
                 true,
                 Collections.<String, String>emptyMap());
         Servlet servlet = mock(Servlet.class);
-        ServletRuntime servletHandler = new ServletHandler(null, context_0, servletInfo, servlet);
+        ServletRuntime servletHandler = new SimpleServletHandler(ID_0, context_0, servletInfo, servlet);
         when(servlet.getServletInfo()).thenReturn("info_0");
 
-        ContextRuntime contextRuntime = new ContextRuntime(asList(servletHandler),
-                Collections.<FilterRuntime>emptyList(),
-                Collections.<ServletRuntime>emptyList(),
+        ContextRuntime contextRuntime = new ContextRuntime(Collections.<FilterRuntime>emptyList(),
                 Collections.<ErrorPageRuntime>emptyList(),
                 ID_0);
         setupRegistry(asList(contextHandler), asList(contextRuntime),
+                new ServletRegistryRuntime(asList(servletHandler), Collections.<ServletRuntime>emptyList()),
                 Collections.<Long, Collection<ServiceReference<?>>>emptyMap(),
                 FailureRuntime.empty());
 
@@ -732,6 +740,7 @@
     {
         setupRegistry(Collections.<ServletContextHelperRuntime>emptyList(),
                 Collections.<ContextRuntime>emptyList(),
+                ServletRegistryRuntime.empty(),
                 Collections.<Long, Collection<ServiceReference<?>>>emptyMap(),
                 setupFailures());
 
diff --git a/http/itest/src/test/java/org/apache/felix/http/itest/BaseIntegrationTest.java b/http/itest/src/test/java/org/apache/felix/http/itest/BaseIntegrationTest.java
index 562f09a..d88e35b 100644
--- a/http/itest/src/test/java/org/apache/felix/http/itest/BaseIntegrationTest.java
+++ b/http/itest/src/test/java/org/apache/felix/http/itest/BaseIntegrationTest.java
@@ -313,7 +313,7 @@
             bootDelegationPackage("sun.*"),
             cleanCaches(),
             CoreOptions.systemProperty("logback.configurationFile").value("file:src/test/resources/logback.xml"), //
-            //            CoreOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8787"),
+//                        CoreOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8787"),
 
             mavenBundle("org.slf4j", "slf4j-api").version("1.6.5").startLevel(START_LEVEL_SYSTEM_BUNDLES),
             mavenBundle("ch.qos.logback", "logback-core").version("1.0.6").startLevel(START_LEVEL_SYSTEM_BUNDLES),
diff --git a/http/itest/src/test/java/org/apache/felix/http/itest/HttpServiceRuntimeTest.java b/http/itest/src/test/java/org/apache/felix/http/itest/HttpServiceRuntimeTest.java
index 98f20c0..2c022a6 100644
--- a/http/itest/src/test/java/org/apache/felix/http/itest/HttpServiceRuntimeTest.java
+++ b/http/itest/src/test/java/org/apache/felix/http/itest/HttpServiceRuntimeTest.java
@@ -68,7 +68,6 @@
 import javax.servlet.http.HttpSessionListener;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.ops4j.pax.exam.junit.JUnit4TestRunner;
@@ -78,6 +77,7 @@
 import org.osgi.service.http.HttpService;
 import org.osgi.service.http.context.ServletContextHelper;
 import org.osgi.service.http.runtime.HttpServiceRuntime;
+import org.osgi.service.http.runtime.dto.FailedErrorPageDTO;
 import org.osgi.service.http.runtime.dto.FailedServletDTO;
 import org.osgi.service.http.runtime.dto.RuntimeDTO;
 import org.osgi.service.http.runtime.dto.ServletContextDTO;
@@ -237,20 +237,20 @@
         assertEquals(0, runtimeDTO.failedServletContextDTOs.length);
         assertEquals(0, runtimeDTO.failedServletDTOs.length);
 
-        assertEquals(1, runtimeDTO.servletContextDTOs.length);
-        assertEquals("default", runtimeDTO.servletContextDTOs[0].name);
-        assertEquals(0, runtimeDTO.servletContextDTOs[0].attributes.size());
+        ServletContextDTO defaultContext = assertDefaultContext(runtimeDTO);
 
+        assertEquals(0, defaultContext.attributes.size());
         // TODO The default context should have a negative service Id
 //        assertTrue(0 > runtimeDTO.servletContextDTOs[0].serviceId);
-        assertEquals("", runtimeDTO.servletContextDTOs[0].contextPath);
-        assertEquals(0, runtimeDTO.servletContextDTOs[0].initParams.size());
+        // TODO Should be "/" ?
+        assertEquals("", defaultContext.contextPath);
+        assertEquals(0, defaultContext.initParams.size());
 
-        assertEquals(0, runtimeDTO.servletContextDTOs[0].filterDTOs.length);
-        assertEquals(0, runtimeDTO.servletContextDTOs[0].servletDTOs.length);
-        assertEquals(0, runtimeDTO.servletContextDTOs[0].resourceDTOs.length);
-        assertEquals(0, runtimeDTO.servletContextDTOs[0].errorPageDTOs.length);
-        assertEquals(0, runtimeDTO.servletContextDTOs[0].listenerDTOs.length);
+        assertEquals(0, defaultContext.filterDTOs.length);
+        assertEquals(0, defaultContext.servletDTOs.length);
+        assertEquals(0, defaultContext.resourceDTOs.length);
+        assertEquals(0, defaultContext.errorPageDTOs.length);
+        assertEquals(0, defaultContext.listenerDTOs.length);
     }
 
     @Test
@@ -360,7 +360,7 @@
         assertEquals(0, runtimeDTOWithFirstErrorPage.failedServletDTOs.length);
         assertEquals(0, runtimeDTOWithFirstErrorPage.failedErrorPageDTOs.length);
 
-        ServletContextDTO contextDTO = runtimeDTOWithFirstErrorPage.servletContextDTOs[0];
+        ServletContextDTO contextDTO = assertDefaultContext(runtimeDTOWithFirstErrorPage);
         assertEquals(1, contextDTO.errorPageDTOs.length);
         assertEquals("error page 1", contextDTO.errorPageDTOs[0].name);
         assertArrayEquals(new String[] { NoSuchElementException.class.getName() }, contextDTO.errorPageDTOs[0].exceptions);
@@ -395,9 +395,8 @@
         RuntimeDTO runtimeDTOWithFirstListener = serviceRuntime.getRuntimeDTO();
 
         assertEquals(0, runtimeDTOWithFirstListener.failedListenerDTOs.length);
-        assertEquals(1, runtimeDTOWithFirstListener.servletContextDTOs.length);
 
-        ServletContextDTO contextDTO = runtimeDTOWithFirstListener.servletContextDTOs[0];
+        ServletContextDTO contextDTO = assertDefaultContext(runtimeDTOWithFirstListener);
         // TODO fix : servlet context listener is only added when registerd before context activation
         assertEquals(0, contextDTO.listenerDTOs.length);
         // TODO
@@ -419,9 +418,8 @@
         RuntimeDTO runtimeDTOWithAllListeners = serviceRuntime.getRuntimeDTO();
 
         assertEquals(0, runtimeDTOWithAllListeners.failedListenerDTOs.length);
-        assertEquals(1, runtimeDTOWithAllListeners.servletContextDTOs.length);
 
-        contextDTO = runtimeDTOWithAllListeners.servletContextDTOs[0];
+        contextDTO = assertDefaultContext(runtimeDTOWithAllListeners);
         // TODO
         assertEquals(5, contextDTO.listenerDTOs.length);
 //        assertEquals(ServletContextListener.class.getName(), contextDTO.listenerDTOs[0].types[0]);
@@ -444,14 +442,16 @@
         RuntimeDTO runtimeDTOWithAdditionalContext = serviceRuntime.getRuntimeDTO();
 
         assertEquals(0, runtimeDTOWithAdditionalContext.failedServletContextDTOs.length);
-        assertEquals(2, runtimeDTOWithAdditionalContext.servletContextDTOs.length);
+        assertEquals(3, runtimeDTOWithAdditionalContext.servletContextDTOs.length);
 
         // default context is last, as it has the lowest service ranking
-        assertEquals("contextA", runtimeDTOWithAdditionalContext.servletContextDTOs[0].name);
-        assertEquals("/contextA", runtimeDTOWithAdditionalContext.servletContextDTOs[0].contextPath);
-        assertEquals("default", runtimeDTOWithAdditionalContext.servletContextDTOs[1].name);
+        assertEquals("Http service context", runtimeDTOWithAdditionalContext.servletContextDTOs[0].name);
+        assertEquals("/", runtimeDTOWithAdditionalContext.servletContextDTOs[0].contextPath);
+        assertEquals("contextA", runtimeDTOWithAdditionalContext.servletContextDTOs[1].name);
+        assertEquals("/contextA", runtimeDTOWithAdditionalContext.servletContextDTOs[1].contextPath);
+        assertEquals("default", runtimeDTOWithAdditionalContext.servletContextDTOs[2].name);
         // TODO should this be "/" ?
-        assertEquals("", runtimeDTOWithAdditionalContext.servletContextDTOs[1].contextPath);
+        assertEquals("", runtimeDTOWithAdditionalContext.servletContextDTOs[2].contextPath);
 
         // register second additional context
         registerContext("contextB", "/contextB");
@@ -459,15 +459,17 @@
         RuntimeDTO runtimeDTOWithAllContexts = serviceRuntime.getRuntimeDTO();
 
         assertEquals(0, runtimeDTOWithAllContexts.failedServletContextDTOs.length);
-        assertEquals(3, runtimeDTOWithAllContexts.servletContextDTOs.length);
+        assertEquals(4, runtimeDTOWithAllContexts.servletContextDTOs.length);
 
         // default context is last, as it has the lowest service ranking
-        assertEquals("contextA", runtimeDTOWithAllContexts.servletContextDTOs[0].name);
-        assertEquals("/contextA", runtimeDTOWithAllContexts.servletContextDTOs[0].contextPath);
-        assertEquals("contextB", runtimeDTOWithAllContexts.servletContextDTOs[1].name);
-        assertEquals("/contextB", runtimeDTOWithAllContexts.servletContextDTOs[1].contextPath);
-        assertEquals("default", runtimeDTOWithAllContexts.servletContextDTOs[2].name);
-        assertEquals("", runtimeDTOWithAllContexts.servletContextDTOs[2].contextPath);
+        assertEquals("Http service context", runtimeDTOWithAdditionalContext.servletContextDTOs[0].name);
+        assertEquals("/", runtimeDTOWithAdditionalContext.servletContextDTOs[0].contextPath);
+        assertEquals("contextA", runtimeDTOWithAllContexts.servletContextDTOs[1].name);
+        assertEquals("/contextA", runtimeDTOWithAllContexts.servletContextDTOs[1].contextPath);
+        assertEquals("contextB", runtimeDTOWithAllContexts.servletContextDTOs[2].name);
+        assertEquals("/contextB", runtimeDTOWithAllContexts.servletContextDTOs[2].contextPath);
+        assertEquals("default", runtimeDTOWithAllContexts.servletContextDTOs[3].name);
+        assertEquals("", runtimeDTOWithAllContexts.servletContextDTOs[3].contextPath);
     }
 
     @Test
@@ -503,11 +505,12 @@
         assertEquals(0, runtimeDTO.failedServletContextDTOs.length);
         assertEquals(0, runtimeDTO.failedServletDTOs.length);
 
-        assertEquals(2, runtimeDTO.servletContextDTOs.length);
-        assertEquals("test-context", runtimeDTO.servletContextDTOs[0].name);
-        assertEquals("default", runtimeDTO.servletContextDTOs[1].name);
+        assertEquals(3, runtimeDTO.servletContextDTOs.length);
+        assertEquals("Http service context", runtimeDTO.servletContextDTOs[0].name);
+        assertEquals("test-context", runtimeDTO.servletContextDTOs[1].name);
+        assertEquals("default", runtimeDTO.servletContextDTOs[2].name);
 
-        ServletContextDTO defaultContextDTO = runtimeDTO.servletContextDTOs[1];
+        ServletContextDTO defaultContextDTO = runtimeDTO.servletContextDTOs[2];
         long contextServiceId = defaultContextDTO.serviceId;
 
         assertEquals(1, defaultContextDTO.servletDTOs.length);
@@ -523,7 +526,7 @@
         assertEquals(ServletRequestListener.class.getName(), defaultContextDTO.listenerDTOs[0].types[0]);
         assertEquals(contextServiceId, defaultContextDTO.listenerDTOs[0].servletContextId);
 
-        ServletContextDTO testContextDTO = runtimeDTO.servletContextDTOs[0];
+        ServletContextDTO testContextDTO = runtimeDTO.servletContextDTOs[1];
         contextServiceId = testContextDTO.serviceId;
 
         assertEquals(1, testContextDTO.servletDTOs.length);
@@ -633,11 +636,12 @@
         assertEquals("/second", runtimeDTO.failedServletContextDTOs[0].contextPath);
         assertEquals(FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE, runtimeDTO.failedServletContextDTOs[0].failureReason);
 
-        assertEquals(2, runtimeDTO.servletContextDTOs.length);
-        assertEquals("default", runtimeDTO.servletContextDTOs[1].name);
+        assertEquals(3, runtimeDTO.servletContextDTOs.length);
+        assertEquals("Http service context", runtimeDTO.servletContextDTOs[0].name);
+        assertEquals("default", runtimeDTO.servletContextDTOs[2].name);
 
-        assertEquals("contextA", runtimeDTO.servletContextDTOs[0].name);
-        assertEquals("/first", runtimeDTO.servletContextDTOs[0].contextPath);
+        assertEquals("contextA", runtimeDTO.servletContextDTOs[1].name);
+        assertEquals("/first", runtimeDTO.servletContextDTOs[1].contextPath);
 
         firstContextReg.unregister();
 
@@ -645,11 +649,12 @@
 
         assertEquals(0, runtimeDTO.failedServletContextDTOs.length);
 
-        assertEquals(2, runtimeDTO.servletContextDTOs.length);
-        assertEquals("default", runtimeDTO.servletContextDTOs[1].name);
+        assertEquals(3, runtimeDTO.servletContextDTOs.length);
+        assertEquals("Http service context", runtimeDTO.servletContextDTOs[0].name);
+        assertEquals("default", runtimeDTO.servletContextDTOs[2].name);
 
-        assertEquals("contextA", runtimeDTO.servletContextDTOs[0].name);
-        assertEquals("/second", runtimeDTO.servletContextDTOs[0].contextPath);
+        assertEquals("contextA", runtimeDTO.servletContextDTOs[1].name);
+        assertEquals("/second", runtimeDTO.servletContextDTOs[1].contextPath);
     }
 
     // As specified in OSGi Compendium Release 6, Chapter 140.1
@@ -723,10 +728,10 @@
         runtimeDTO = serviceRuntime.getRuntimeDTO();
 
         assertEquals(0, runtimeDTO.failedServletDTOs.length);
-        assertEquals(2, runtimeDTO.servletContextDTOs.length);
-        assertEquals("contextA", runtimeDTO.servletContextDTOs[0].name);
-        assertEquals(1, runtimeDTO.servletContextDTOs[0].servletDTOs.length);
-        assertEquals("servlet 1", runtimeDTO.servletContextDTOs[0].servletDTOs[0].name);
+        assertEquals(3, runtimeDTO.servletContextDTOs.length);
+        assertEquals("contextA", runtimeDTO.servletContextDTOs[1].name);
+        assertEquals(1, runtimeDTO.servletContextDTOs[1].servletDTOs.length);
+        assertEquals("servlet 1", runtimeDTO.servletContextDTOs[1].servletDTOs[0].name);
     }
 
     // As specified in OSGi Compendium Release 6, Chapter 140.3
@@ -848,18 +853,105 @@
 
     // As specified in OSGi Compendium Release 6, Chapter 140.4.1
     @Test
-    @Ignore
-    public void multipleErrorPagesForSameExceptionsChoosenByServiceRankingRules()
+    public void multipleErrorPagesForSameExceptionsChoosenByServiceRankingRules() throws InterruptedException
     {
-        // TODO
+        registerErrorPage("error page 1", asList(NullPointerException.class.getName(), "500"));
+
+        HttpServiceRuntime serviceRuntime = (HttpServiceRuntime) getService(HttpServiceRuntime.class.getName());
+        assertNotNull("HttpServiceRuntime unavailable", serviceRuntime);
+
+        RuntimeDTO runtimeDTO = serviceRuntime.getRuntimeDTO();
+
+        ServletContextDTO defaultContext = assertDefaultContext(runtimeDTO);
+
+        assertEquals(0, runtimeDTO.failedErrorPageDTOs.length);
+        assertEquals(1, defaultContext.errorPageDTOs.length);
+
+        Dictionary<String, ?> properties = createDictionary(
+                HTTP_WHITEBOARD_SERVLET_ERROR_PAGE, asList("500", IllegalArgumentException.class.getName()),
+                HTTP_WHITEBOARD_SERVLET_NAME, "error page 2",
+                SERVICE_RANKING, Integer.MAX_VALUE);
+
+        CountDownLatch initLatch = new CountDownLatch(1);
+        CountDownLatch destroyLatch = new CountDownLatch(1);
+        TestServlet testServlet = new TestServlet(initLatch, destroyLatch);
+        ServiceRegistration<?> higherRankingServlet = m_context.registerService(Servlet.class.getName(), testServlet, properties);
+        awaitServiceRegistration(initLatch);
+
+        RuntimeDTO runtimeWithShadowedErrorPage = serviceRuntime.getRuntimeDTO();
+
+        defaultContext = assertDefaultContext(runtimeWithShadowedErrorPage);
+
+        assertEquals(2, defaultContext.errorPageDTOs.length);
+        assertEquals("error page 2", defaultContext.errorPageDTOs[0].name);
+        assertArrayEquals(new long[] { 500 }, defaultContext.errorPageDTOs[0].errorCodes);
+        assertArrayEquals(new String[] { IllegalArgumentException.class.getName() }, defaultContext.errorPageDTOs[0].exceptions);
+        assertEquals("error page 1", defaultContext.errorPageDTOs[1].name);
+        assertArrayEquals(new long[] { 500 }, defaultContext.errorPageDTOs[1].errorCodes);
+        assertArrayEquals(new String[] { NullPointerException.class.getName() }, defaultContext.errorPageDTOs[1].exceptions);
+
+        assertEquals(1, runtimeWithShadowedErrorPage.failedErrorPageDTOs.length);
+        FailedErrorPageDTO failedErrorPageDTO = runtimeWithShadowedErrorPage.failedErrorPageDTOs[0];
+        assertEquals("error page 1", failedErrorPageDTO.name);
+        assertArrayEquals(new long[] { 500 }, failedErrorPageDTO.errorCodes);
+        assertArrayEquals(new String[] { NullPointerException.class.getName() }, failedErrorPageDTO.exceptions);
+        assertEquals(FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE, failedErrorPageDTO.failureReason);
+
+        higherRankingServlet.unregister();
+        awaitServiceRegistration(destroyLatch);
+
+        runtimeDTO = serviceRuntime.getRuntimeDTO();
+
+        defaultContext = assertDefaultContext(runtimeDTO);
+
+        assertEquals(0, runtimeDTO.failedErrorPageDTOs.length);
+        assertEquals(1, defaultContext.errorPageDTOs.length);
+        assertEquals("error page 1", defaultContext.errorPageDTOs[0].name);
     }
 
     // As specified in OSGi Compendium Release 6, Chapter 140.4
     @Test
-    @Ignore
-    public void mulitpleServletsWithSamePatternHttpServiceRegistrationWins()
+    public void mulitpleServletsWithSamePatternHttpServiceRegistrationWins() throws Exception
     {
-        // TODO
+        registerServlet("servlet 1", "/pathcollision");
+
+        HttpServiceRuntime serviceRuntime = (HttpServiceRuntime) getService(HttpServiceRuntime.class.getName());
+        assertNotNull("HttpServiceRuntime unavailable", serviceRuntime);
+
+        RuntimeDTO runtimeDTO = serviceRuntime.getRuntimeDTO();
+
+        assertEquals(0, runtimeDTO.failedServletDTOs.length);
+        ServletContextDTO defaultContext = assertDefaultContext(runtimeDTO);
+        assertEquals(1, defaultContext.servletDTOs.length);
+
+        CountDownLatch initLatch = new CountDownLatch(1);
+        CountDownLatch destroyLatch = new CountDownLatch(1);
+        TestServlet testServlet = new TestServlet(initLatch, destroyLatch);
+        register("/pathcollision", testServlet);
+
+        RuntimeDTO runtimeWithShadowedServlet = serviceRuntime.getRuntimeDTO();
+        awaitServiceRegistration(initLatch);
+
+        defaultContext = assertDefaultContext(runtimeWithShadowedServlet);
+        ServletContextDTO httpServiceContext = runtimeWithShadowedServlet.servletContextDTOs[0];
+        assertEquals("Http service context", httpServiceContext.name);
+        assertEquals(1, httpServiceContext.servletDTOs.length);
+        assertArrayEquals(new String[] {"/pathcollision", "/pathcollision/*"}, httpServiceContext.servletDTOs[0].patterns);
+
+        assertEquals(1, runtimeWithShadowedServlet.failedServletDTOs.length);
+        FailedServletDTO failedServletDTO = runtimeWithShadowedServlet.failedServletDTOs[0];
+        assertEquals("servlet 1", failedServletDTO.name);
+        assertEquals(FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE, failedServletDTO.failureReason);
+
+        unregister(testServlet);
+        awaitServiceRegistration(destroyLatch);
+
+        runtimeDTO = serviceRuntime.getRuntimeDTO();
+
+        assertEquals(0, runtimeDTO.failedServletDTOs.length);
+        defaultContext = assertDefaultContext(runtimeDTO);
+        assertEquals(1, defaultContext.servletDTOs.length);
+        assertEquals("servlet 1", defaultContext.servletDTOs[0].name);
     }
 
     // As specified in OSGi Compendium Release 6, Chapter 140.7
@@ -896,14 +988,15 @@
         RuntimeDTO runtimeDTO = serviceRuntime.getRuntimeDTO();
 
         assertEquals(0, runtimeDTO.failedServletContextDTOs.length);
-        assertEquals(2, runtimeDTO.servletContextDTOs.length);
-        assertEquals(firstContextId.longValue(), runtimeDTO.servletContextDTOs[0].serviceId);
-        assertEquals("test-context", runtimeDTO.servletContextDTOs[0].name);
-        assertEquals("/first", runtimeDTO.servletContextDTOs[0].contextPath);
-        assertEquals("default", runtimeDTO.servletContextDTOs[1].name);
+        assertEquals(3, runtimeDTO.servletContextDTOs.length);
+        assertEquals(firstContextId.longValue(), runtimeDTO.servletContextDTOs[1].serviceId);
+        assertEquals("test-context", runtimeDTO.servletContextDTOs[1].name);
+        assertEquals("/first", runtimeDTO.servletContextDTOs[1].contextPath);
+        assertEquals("default", runtimeDTO.servletContextDTOs[2].name);
+        assertEquals("Http service context", runtimeDTO.servletContextDTOs[0].name);
 
-        assertEquals(1, runtimeDTO.servletContextDTOs[0].servletDTOs.length);
-        assertEquals("servlet", runtimeDTO.servletContextDTOs[0].servletDTOs[0].name);
+        assertEquals(1, runtimeDTO.servletContextDTOs[1].servletDTOs.length);
+        assertEquals("servlet", runtimeDTO.servletContextDTOs[1].servletDTOs[0].name);
 
         Dictionary<String, ?> properties = createDictionary(
                 HTTP_WHITEBOARD_CONTEXT_NAME, "test-context",
@@ -920,34 +1013,36 @@
         assertEquals("test-context", runtimeDTO.failedServletContextDTOs[0].name);
         assertEquals("/first", runtimeDTO.failedServletContextDTOs[0].contextPath);
 
-        assertEquals(2, runtimeDTO.servletContextDTOs.length);
+        assertEquals(3, runtimeDTO.servletContextDTOs.length);
 
-        assertEquals(secondContextId.longValue(), runtimeDTO.servletContextDTOs[0].serviceId);
-        assertEquals("test-context", runtimeDTO.servletContextDTOs[0].name);
-        assertEquals("/second", runtimeDTO.servletContextDTOs[0].contextPath);
-        assertEquals("default", runtimeDTO.servletContextDTOs[1].name);
+        assertEquals(secondContextId.longValue(), runtimeDTO.servletContextDTOs[1].serviceId);
+        assertEquals("test-context", runtimeDTO.servletContextDTOs[1].name);
+        assertEquals("/second", runtimeDTO.servletContextDTOs[1].contextPath);
+        assertEquals("default", runtimeDTO.servletContextDTOs[2].name);
+        assertEquals("Http service context", runtimeDTO.servletContextDTOs[0].name);
 
-        assertEquals(1, runtimeDTO.servletContextDTOs[0].servletDTOs.length);
-        assertEquals("servlet", runtimeDTO.servletContextDTOs[0].servletDTOs[0].name);
+        assertEquals(1, runtimeDTO.servletContextDTOs[1].servletDTOs.length);
+        assertEquals("servlet", runtimeDTO.servletContextDTOs[1].servletDTOs[0].name);
 
         secondContext.unregister();
 
         runtimeDTO = serviceRuntime.getRuntimeDTO();
 
         assertEquals(0, runtimeDTO.failedServletContextDTOs.length);
-        assertEquals(2, runtimeDTO.servletContextDTOs.length);
-        assertEquals(firstContextId.longValue(), runtimeDTO.servletContextDTOs[0].serviceId);
-        assertEquals("test-context", runtimeDTO.servletContextDTOs[0].name);
-        assertEquals("/first", runtimeDTO.servletContextDTOs[0].contextPath);
-        assertEquals("default", runtimeDTO.servletContextDTOs[1].name);
+        assertEquals(3, runtimeDTO.servletContextDTOs.length);
+        assertEquals(firstContextId.longValue(), runtimeDTO.servletContextDTOs[1].serviceId);
+        assertEquals("test-context", runtimeDTO.servletContextDTOs[1].name);
+        assertEquals("/first", runtimeDTO.servletContextDTOs[1].contextPath);
+        assertEquals("default", runtimeDTO.servletContextDTOs[2].name);
+        assertEquals("Http service context", runtimeDTO.servletContextDTOs[0].name);
 
-        assertEquals(1, runtimeDTO.servletContextDTOs[0].servletDTOs.length);
-        assertEquals("servlet", runtimeDTO.servletContextDTOs[0].servletDTOs[0].name);
+        assertEquals(1, runtimeDTO.servletContextDTOs[1].servletDTOs.length);
+        assertEquals("servlet", runtimeDTO.servletContextDTOs[1].servletDTOs[0].name);
     }
 
     // As specified in OSGi Compendium Release 6, Chapter 140.9
     @Test
-    public void httServiceIdIsSet()
+    public void httpServiceIdIsSet()
     {
         ServiceReference<?> httpServiceRef = m_context.getServiceReference(HttpService.class.getName());
         ServiceReference<?> httpServiceRuntimeRef = m_context.getServiceReference(HttpServiceRuntime.class.getName());
@@ -960,7 +1055,6 @@
 
     // As specified in OSGi Compendium Release 6, Chapter 140.9
     @Test
-    @Ignore // This is still broken
     public void serviceRegisteredWithHttpServiceHasNegativeServiceId() throws Exception
     {
         CountDownLatch initLatch = new CountDownLatch(1);
@@ -972,7 +1066,7 @@
 
         RuntimeDTO runtimeDTO = serviceRuntime.getRuntimeDTO();
 
-        assertEquals(1, runtimeDTO.servletContextDTOs.length);
+        assertEquals(2, runtimeDTO.servletContextDTOs.length);
         assertEquals(1, runtimeDTO.servletContextDTOs[0].servletDTOs.length);
         assertTrue(0 > runtimeDTO.servletContextDTOs[0].servletDTOs[0].serviceId);
     }
@@ -1030,7 +1124,7 @@
         boolean mapsModifiable = true;
         try
         {
-            runtimeDTOWithTwoSerlvets.servletContextDTOs[0].servletDTOs[0].initParams.clear();
+            defaultContextTwoServlets.servletDTOs[0].initParams.clear();
         } catch (UnsupportedOperationException e)
         {
             mapsModifiable = false;
@@ -1045,9 +1139,10 @@
 
     private ServletContextDTO assertDefaultContext(RuntimeDTO runtimeDTO)
     {
-        assertTrue(0 < runtimeDTO.servletContextDTOs.length);
-        assertEquals("default", runtimeDTO.servletContextDTOs[0].name);
-        return runtimeDTO.servletContextDTOs[0];
+        assertTrue(1 < runtimeDTO.servletContextDTOs.length);
+        assertEquals("Http service context", runtimeDTO.servletContextDTOs[0].name);
+        assertEquals("default", runtimeDTO.servletContextDTOs[1].name);
+        return runtimeDTO.servletContextDTOs[1];
     }
 
     private void awaitServiceRegistration() throws InterruptedException
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
index 8bc7f02..931d203 100644
--- 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
@@ -20,6 +20,12 @@
 package org.apache.felix.http.itest;
 
 import static org.junit.Assert.assertTrue;
+import static org.osgi.framework.Constants.SERVICE_RANKING;
+import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME;
+import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH;
+import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT;
+import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_INIT_PARAM_PREFIX;
+import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN;
 
 import java.io.IOException;
 import java.util.Dictionary;
@@ -36,135 +42,203 @@
 import org.ops4j.pax.exam.junit.JUnit4TestRunner;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
-
+import org.osgi.service.http.context.ServletContextHelper;
 
 @RunWith(JUnit4TestRunner.class)
-public class ServletPatternTest extends BaseIntegrationTest 
+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);
+    @Test
+    public void testHighRankReplaces() throws Exception
+    {
+        CountDownLatch initLatch = new CountDownLatch(2);
+        CountDownLatch destroyLatch = new CountDownLatch(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));
-	}
+        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(HTTP_WHITEBOARD_SERVLET_PATTERN, lowRankPattern);
+        lowRankProps.put(HTTP_WHITEBOARD_FILTER_INIT_PARAM_PREFIX + ".myname", "lowRankServlet");
+        lowRankProps.put(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(HTTP_WHITEBOARD_SERVLET_PATTERN, highRankPattern);
+        highRankProps.put(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 testHttpServiceReplaces() throws Exception
+    {
+        Dictionary<String, ?> properties = createDictionary(
+            HTTP_WHITEBOARD_CONTEXT_NAME, "test",
+            HTTP_WHITEBOARD_CONTEXT_PATH, "/test");
+
+        ServletContextHelper servletContextHelper = new ServletContextHelper(m_context.getBundle()) {};
+        m_context.registerService(ServletContextHelper.class.getName(), servletContextHelper, properties);
+
+        CountDownLatch initLatch = new CountDownLatch(2);
+        CountDownLatch destroyLatch = new CountDownLatch(2);
+
+        TestServlet whiteboardServlet = new TestServlet(initLatch, destroyLatch)
+        {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException
+            {
+                resp.getWriter().print("whiteboardServlet");
+                resp.flushBuffer();
+            }
+        };
+
+        TestServlet httpServiceServlet = new TestServlet(initLatch, destroyLatch)
+        {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException
+            {
+                resp.getWriter().print("httpServiceServlet");
+                resp.flushBuffer();
+            }
+        };
+
+        String whiteboardPattern[] = { "/foo", "/bar" };
+        Dictionary<String, Object> whiteboardProps = new Hashtable<String, Object>();
+        whiteboardProps.put(HTTP_WHITEBOARD_CONTEXT_SELECT, "(" + HTTP_WHITEBOARD_CONTEXT_NAME + "=test)");
+        whiteboardProps.put(HTTP_WHITEBOARD_SERVLET_PATTERN, whiteboardPattern);
+        whiteboardProps.put(SERVICE_RANKING, Integer.MAX_VALUE);
+        ServiceRegistration<?> serviceRegistration = m_context.registerService(Servlet.class.getName(), whiteboardServlet, whiteboardProps);
+
+        register("/test/foo", httpServiceServlet);
+
+        try
+        {
+            assertTrue(initLatch.await(5, TimeUnit.SECONDS));
+
+            assertContent("whiteboardServlet", createURL("/test/bar"));
+            assertContent("httpServiceServlet", createURL("/test/foo"));
+        }
+        finally
+        {
+            serviceRegistration.unregister();
+            unregister(httpServiceServlet);
+        }
+    }
+
+    @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(HTTP_WHITEBOARD_SERVLET_PATTERN, lowRankPattern);
+        props1.put(HTTP_WHITEBOARD_FILTER_INIT_PARAM_PREFIX + ".myname", "lowRankServlet");
+        props1.put(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(HTTP_WHITEBOARD_SERVLET_PATTERN, highRankPattern);
+        props2.put(HTTP_WHITEBOARD_FILTER_INIT_PARAM_PREFIX + ".myname", "highRankServlet");
+        props2.put(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));
+    }
 }
-
-
-