FELIX-4545 : Implement Servlet Context Helper. Merge new path handling from branch

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1656064 13f79535-47bb-0310-9956-ffa450edef68
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 f48edf8..17922fb 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
@@ -16,19 +16,374 @@
  */
 package org.apache.felix.http.base.internal.dispatch;
 
+import static javax.servlet.RequestDispatcher.FORWARD_CONTEXT_PATH;
+import static javax.servlet.RequestDispatcher.FORWARD_PATH_INFO;
+import static javax.servlet.RequestDispatcher.FORWARD_QUERY_STRING;
+import static javax.servlet.RequestDispatcher.FORWARD_REQUEST_URI;
+import static javax.servlet.RequestDispatcher.FORWARD_SERVLET_PATH;
+import static javax.servlet.RequestDispatcher.INCLUDE_CONTEXT_PATH;
+import static javax.servlet.RequestDispatcher.INCLUDE_PATH_INFO;
+import static javax.servlet.RequestDispatcher.INCLUDE_QUERY_STRING;
+import static javax.servlet.RequestDispatcher.INCLUDE_REQUEST_URI;
+import static javax.servlet.RequestDispatcher.INCLUDE_SERVLET_PATH;
+import static org.apache.felix.http.base.internal.util.UriUtils.concat;
+import static org.apache.felix.http.base.internal.util.UriUtils.decodePath;
+import static org.apache.felix.http.base.internal.util.UriUtils.removeDotSegments;
+
 import java.io.IOException;
 
+import javax.servlet.DispatcherType;
 import javax.servlet.FilterChain;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
 import javax.servlet.http.HttpServletResponse;
+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;
+import org.apache.felix.http.base.internal.handler.ServletHandler;
+import org.apache.felix.http.base.internal.util.UriUtils;
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.useradmin.Authorization;
 
-public final class Dispatcher
+public final class Dispatcher implements RequestDispatcherProvider
 {
+    /**
+     * Wrapper implementation for {@link RequestDispatcher}.
+     */
+    final class RequestDispatcherImpl implements RequestDispatcher
+    {
+        private final RequestInfo requestInfo;
+        private final ServletHandler handler;
+
+        public RequestDispatcherImpl(ServletHandler handler, RequestInfo requestInfo)
+        {
+            this.handler = handler;
+            this.requestInfo = requestInfo;
+        }
+
+        @Override
+        public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException
+        {
+            if (response.isCommitted())
+            {
+                throw new ServletException("Response has been committed");
+            }
+            else
+            {
+                // See section 9.4 of Servlet 3.0 spec
+                response.resetBuffer();
+            }
+
+            try
+            {
+                ServletRequestWrapper req = new ServletRequestWrapper((HttpServletRequest) request, this.handler.getContext(), this.requestInfo, DispatcherType.FORWARD);
+                Dispatcher.this.forward(this.handler, req, (HttpServletResponse) response);
+            }
+            finally
+            {
+                // After a forward has taken place, the results should be committed,
+                // see section 9.4 of Servlet 3.0 spec...
+                if (!request.isAsyncStarted())
+                {
+                    response.flushBuffer();
+                    response.getWriter().close();
+                }
+            }
+        }
+
+        @Override
+        public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException
+        {
+            ServletRequestWrapper req = new ServletRequestWrapper((HttpServletRequest) request, this.handler.getContext(), this.requestInfo, DispatcherType.INCLUDE);
+            Dispatcher.this.include(this.handler, req, (HttpServletResponse) response);
+        }
+    }
+
+    final class ServletRequestWrapper extends HttpServletRequestWrapper
+    {
+        private final DispatcherType type;
+        private final RequestInfo requestInfo;
+        private final ExtServletContext servletContext;
+
+        public ServletRequestWrapper(HttpServletRequest req, ExtServletContext servletContext, RequestInfo requestInfo)
+        {
+            this(req, servletContext, requestInfo, null /* type */);
+        }
+
+        public ServletRequestWrapper(HttpServletRequest req, ExtServletContext servletContext, RequestInfo requestInfo, DispatcherType type)
+        {
+            super(req);
+
+            this.servletContext = servletContext;
+            this.requestInfo = requestInfo;
+            this.type = type;
+        }
+
+        @Override
+        public Object getAttribute(String name)
+        {
+            HttpServletRequest request = (HttpServletRequest) getRequest();
+            if (isInclusionDispatcher())
+            {
+                // The javax.servlet.include.* attributes refer to the information of the *included* request,
+                // meaning that the request information comes from the *original* request...
+                if (INCLUDE_REQUEST_URI.equals(name))
+                {
+                    return concat(request.getContextPath(), this.requestInfo.requestURI);
+                }
+                else if (INCLUDE_CONTEXT_PATH.equals(name))
+                {
+                    return request.getContextPath();
+                }
+                else if (INCLUDE_SERVLET_PATH.equals(name))
+                {
+                    return this.requestInfo.servletPath;
+                }
+                else if (INCLUDE_PATH_INFO.equals(name))
+                {
+                    return this.requestInfo.pathInfo;
+                }
+                else if (INCLUDE_QUERY_STRING.equals(name))
+                {
+                    return this.requestInfo.queryString;
+                }
+            }
+            else if (isForwardingDispatcher())
+            {
+                // The javax.servlet.forward.* attributes refer to the information of the *original* request,
+                // meaning that the request information comes from the *forwarded* request...
+                if (FORWARD_REQUEST_URI.equals(name))
+                {
+                    return super.getRequestURI();
+                }
+                else if (FORWARD_CONTEXT_PATH.equals(name))
+                {
+                    return request.getContextPath();
+                }
+                else if (FORWARD_SERVLET_PATH.equals(name))
+                {
+                    return super.getServletPath();
+                }
+                else if (FORWARD_PATH_INFO.equals(name))
+                {
+                    return super.getPathInfo();
+                }
+                else if (FORWARD_QUERY_STRING.equals(name))
+                {
+                    return super.getQueryString();
+                }
+            }
+            return super.getAttribute(name);
+        }
+
+        @Override
+        public String getAuthType()
+        {
+            String authType = (String) getAttribute(HttpContext.AUTHENTICATION_TYPE);
+            if (authType == null)
+            {
+                authType = super.getAuthType();
+            }
+            return authType;
+        }
+
+        @Override
+        public String getContextPath()
+        {
+            /*
+             * FELIX-2030 Calculate the context path for the Http Service
+             * registered servlets from the container context and servlet paths
+             */
+            //            if (contextPath == null)
+            //            {
+            //                final String context = super.getContextPath();
+            //                final String servlet = super.getServletPath();
+            //                if (context == null || context.length() == 0)
+            //                {
+            //                    contextPath = servlet;
+            //                }
+            //                else if (servlet == null || servlet.length() == 0)
+            //                {
+            //                    contextPath = context;
+            //                }
+            //                else
+            //                {
+            //                    contextPath = context + servlet;
+            //                }
+            //            }
+
+            return super.getContextPath();
+        }
+
+        @Override
+        public DispatcherType getDispatcherType()
+        {
+            return (this.type == null) ? super.getDispatcherType() : this.type;
+        }
+
+        @Override
+        public String getPathInfo()
+        {
+            String pathInfo = super.getPathInfo();
+            if (isForwardingDispatcher() || !isWrapperFor(ServletRequestWrapper.class))
+            {
+                pathInfo = this.requestInfo.pathInfo;
+            }
+            return pathInfo;
+        }
+
+        @Override
+        @SuppressWarnings("deprecation")
+        public String getPathTranslated()
+        {
+            final String info = getPathInfo();
+            return (null == info) ? null : getRealPath(info);
+        }
+
+        @Override
+        public String getRemoteUser()
+        {
+            String remoteUser = (String) getAttribute(HttpContext.REMOTE_USER);
+            if (remoteUser != null)
+            {
+                return remoteUser;
+            }
+
+            return super.getRemoteUser();
+        }
+
+        @Override
+        public RequestDispatcher getRequestDispatcher(String path)
+        {
+            // See section 9.1 of Servlet 3.0 specification...
+            if (path == null)
+            {
+                return null;
+            }
+            // Handle relative paths, see Servlet 3.0 spec, section 9.1 last paragraph.
+            boolean relPath = !path.startsWith("/") && !"".equals(path);
+            if (relPath)
+            {
+                path = concat(getServletPath(), path);
+            }
+            return Dispatcher.this.getRequestDispatcher(path);
+        }
+
+        @Override
+        public String getRequestURI()
+        {
+            String requestURI = super.getRequestURI();
+            if (isForwardingDispatcher() || !isWrapperFor(ServletRequestWrapper.class))
+            {
+                requestURI = concat(getContextPath(), this.requestInfo.requestURI);
+            }
+            return requestURI;
+        }
+
+        @Override
+        public ServletContext getServletContext()
+        {
+            return new ServletContextWrapper(this.servletContext, Dispatcher.this);
+        }
+
+        @Override
+        public String getServletPath()
+        {
+            String servletPath = super.getServletPath();
+            if (isForwardingDispatcher() || !isWrapperFor(ServletRequestWrapper.class))
+            {
+                servletPath = this.requestInfo.servletPath;
+            }
+            if ("/".equals(servletPath))
+            {
+                return ""; // XXX still necessary?
+            }
+            return servletPath;
+        }
+
+        @Override
+        public HttpSession getSession(boolean create)
+        {
+            // FELIX-2797: wrap the original HttpSession to provide access to the correct ServletContext...
+            HttpSession session = super.getSession(create);
+            if (session == null)
+            {
+                return null;
+            }
+            return new HttpSessionWrapper(session, this.servletContext);
+        }
+
+        @Override
+        public boolean isUserInRole(String role)
+        {
+            Authorization authorization = (Authorization) getAttribute(HttpContext.AUTHORIZATION);
+            if (authorization != null)
+            {
+                return authorization.hasRole(role);
+            }
+
+            return super.isUserInRole(role);
+        }
+
+        @Override
+        public String toString()
+        {
+            return getClass().getSimpleName() + "->" + super.getRequest();
+        }
+
+        private boolean isForwardingDispatcher()
+        {
+            return (DispatcherType.FORWARD == this.type) && (this.requestInfo != null);
+        }
+
+        private boolean isInclusionDispatcher()
+        {
+            return (DispatcherType.INCLUDE == this.type) && (this.requestInfo != null);
+        }
+    }
+
+    private static class RequestInfo
+    {
+        final String servletPath;
+        final String pathInfo;
+        final String queryString;
+        final String requestURI;
+
+        public RequestInfo(String servletPath, String pathInfo, String queryString)
+        {
+            this.servletPath = servletPath;
+            this.pathInfo = pathInfo;
+            this.queryString = queryString;
+            this.requestURI = UriUtils.compactPath(concat(servletPath, pathInfo));
+        }
+
+        @Override
+        public String toString()
+        {
+            StringBuilder sb = new StringBuilder("RequestInfo[servletPath =");
+            sb.append(this.servletPath).append(", pathInfo = ").append(this.pathInfo);
+            sb.append(", queryString = ").append(this.queryString).append("]");
+            return sb.toString();
+        }
+    }
+
+    /**
+     * Request attribute that provides access to the request dispatcher provider.
+     */
     public static final String REQUEST_DISPATCHER_PROVIDER = "org.apache.felix.http.requestDispatcherProvider";
 
+    /**
+     * Catch-all filter chain that simple finishes all requests with a "404 Not Found" error.
+     */
     private static final FilterChain DEFAULT_CHAIN = new NotFoundFilterChain();
 
     private final HandlerRegistry handlerRegistry;
@@ -38,19 +393,145 @@
         this.handlerRegistry = handlerRegistry;
     }
 
+    /**
+     * Responsible for dispatching a given request to the actual applicable servlet and/or filters in the local registry.
+     *
+     * @param req the {@link ServletRequest} to dispatch;
+     * @param res the {@link ServletResponse} to dispatch.
+     * @throws ServletException in case of exceptions during the actual dispatching;
+     * @throws IOException in case of I/O problems.
+     */
     public void dispatch(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
     {
-        ServletPipeline servletPipeline = new ServletPipeline(this.handlerRegistry.getServlets());
+        String requestURI = getRequestURI(req);
+
+        // Determine which servlets we should forward the request to...
+        ServletHandler servletHandler = this.handlerRegistry.getServletHander(requestURI);
+        FilterHandler[] filterHandlers = this.handlerRegistry.getFilterHandlers(servletHandler, req.getDispatcherType(), requestURI);
+
         // Provides access to the correct request dispatcher...
-        req.setAttribute(REQUEST_DISPATCHER_PROVIDER, servletPipeline);
+        req.setAttribute(REQUEST_DISPATCHER_PROVIDER, this);
+
+        String servletPath = (servletHandler != null) ? servletHandler.determineServletPath(requestURI) : "";
+        String pathInfo = UriUtils.compactPath(UriUtils.relativePath(servletPath, requestURI));
+        String queryString = null; // XXX
+
+        ExtServletContext servletContext = (servletHandler != null) ? servletHandler.getContext() : null;
+        RequestInfo requestInfo = new RequestInfo(servletPath, pathInfo, queryString);
 
         try
         {
-            new FilterPipeline(this.handlerRegistry.getFilters(), servletPipeline).dispatch(req, res, DEFAULT_CHAIN);
+            invokeChain(filterHandlers, servletHandler, new ServletRequestWrapper(req, servletContext, requestInfo), res);
         }
         finally
         {
             req.removeAttribute(REQUEST_DISPATCHER_PROVIDER);
         }
     }
+
+    public boolean handleError(HttpServletRequest request, HttpServletResponse response, int errorCode, String exceptionType) throws IOException
+    {
+        ErrorsMapping errorsMapping = this.handlerRegistry.getErrorsMapping();
+        ServletHandler errorHandler = null;
+
+        if (exceptionType != null)
+        {
+            errorHandler = errorsMapping.get(exceptionType);
+        }
+        else
+        {
+            errorHandler = errorsMapping.get(errorCode);
+        }
+
+        if (errorHandler != null)
+        {
+            // TODO set error attributes! See Servlet 3.0 specification, section 10.9.1...
+            try
+            {
+                return errorHandler.handle(request, response);
+            }
+            catch (ServletException e)
+            {
+                return false; // XXX
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public RequestDispatcher getNamedDispatcher(String name)
+    {
+        ServletHandler handler = this.handlerRegistry.getServletHandlerByName(name);
+        return handler != null ? new RequestDispatcherImpl(handler, null) : null;
+    }
+
+    @Override
+    public RequestDispatcher getRequestDispatcher(String path)
+    {
+        // See section 9.1 of Servlet 3.x specification...
+        if (path == null || (!path.startsWith("/") && !"".equals(path)))
+        {
+            return null;
+        }
+
+        String query = null;
+        int q = 0;
+        if ((q = path.indexOf('?')) > 0)
+        {
+            query = path.substring(q + 1);
+            path = path.substring(0, q);
+        }
+        // TODO remove path parameters...
+        String requestURI = decodePath(removeDotSegments(path));
+
+        ServletHandler handler = this.handlerRegistry.getServletHander(requestURI);
+        if (handler == null)
+        {
+            return null;
+        }
+
+        String servletPath = handler.determineServletPath(requestURI);
+        String pathInfo = UriUtils.relativePath(servletPath, path);
+
+        RequestInfo requestInfo = new RequestInfo(servletPath, pathInfo, query);
+        return new RequestDispatcherImpl(handler, requestInfo);
+    }
+
+    /**
+     * @param servletHandler the servlet that should handle the forward request;
+     * @param request the {@link HttpServletRequest};
+     * @param response the {@link HttpServletResponse};
+     */
+    void forward(ServletHandler servletHandler, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        String requestURI = getRequestURI(request);
+        FilterHandler[] filterHandlers = this.handlerRegistry.getFilterHandlers(servletHandler, DispatcherType.FORWARD, requestURI);
+
+        invokeChain(filterHandlers, servletHandler, request, response);
+    }
+
+    /**
+     * @param servletHandler the servlet that should handle the include request;
+     * @param request the {@link HttpServletRequest};
+     * @param response the {@link HttpServletResponse};
+     */
+    void include(ServletHandler servletHandler, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        String requestURI = getRequestURI(request);
+        FilterHandler[] filterHandlers = this.handlerRegistry.getFilterHandlers(servletHandler, DispatcherType.INCLUDE, requestURI);
+
+        invokeChain(filterHandlers, servletHandler, request, response);
+    }
+
+    private String getRequestURI(HttpServletRequest req)
+    {
+        return UriUtils.relativePath(req.getContextPath(), req.getRequestURI());
+    }
+
+    private void invokeChain(FilterHandler[] filterHandlers, ServletHandler servletHandler, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+    {
+        FilterChain filterChain = new InvocationFilterChain(servletHandler, filterHandlers, DEFAULT_CHAIN);
+        filterChain.doFilter(request, response);
+    }
 }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/FilterPipeline.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/FilterPipeline.java
deleted file mode 100644
index c6a696a..0000000
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/FilterPipeline.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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.dispatch;
-
-import java.io.IOException;
-
-import javax.servlet.FilterChain;
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.felix.http.base.internal.handler.FilterHandler;
-
-public final class FilterPipeline
-{
-    private static class FilterRequestWrapper extends HttpServletRequestWrapper
-    {
-        public FilterRequestWrapper(HttpServletRequest req)
-        {
-            super(req);
-        }
-
-        @Override
-        public RequestDispatcher getRequestDispatcher(String path)
-        {
-            RequestDispatcherProvider provider = (RequestDispatcherProvider) getAttribute(Dispatcher.REQUEST_DISPATCHER_PROVIDER);
-            RequestDispatcher dispatcher = null;
-            if (provider != null)
-            {
-                dispatcher = provider.getRequestDispatcher(path);
-            }
-            return (dispatcher != null) ? dispatcher : super.getRequestDispatcher(path);
-        }
-
-        @Override
-        public String toString()
-        {
-            return getClass().getSimpleName() + "->" + super.getRequest();
-        }
-    }
-
-    private final FilterHandler[] handlers;
-    private final ServletPipeline servletPipeline;
-
-    public FilterPipeline(FilterHandler[] handlers, ServletPipeline servletPipeline)
-    {
-        this.handlers = handlers;
-        this.servletPipeline = servletPipeline;
-    }
-
-    public void dispatch(HttpServletRequest req, HttpServletResponse res, FilterChain proceedingChain) throws ServletException, IOException
-    {
-        FilterChain chain = new InvocationFilterChain(this.handlers, this.servletPipeline, proceedingChain);
-
-        if (this.servletPipeline.hasServletsMapped())
-        {
-            req = new FilterRequestWrapper(req);
-        }
-
-        chain.doFilter(req, res);
-    }
-}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpFilterChain.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpFilterChain.java
index e8cc480..574f572 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpFilterChain.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpFilterChain.java
@@ -16,20 +16,28 @@
  */
 package org.apache.felix.http.base.internal.dispatch;
 
-import javax.servlet.FilterChain;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Convenience adapter for {@link FilterChain}s that makes the obtrusive casting unnecessary.
+ */
 public abstract class HttpFilterChain implements FilterChain
 {
+    @Override
     public final void doFilter(ServletRequest req, ServletResponse res) throws IOException, ServletException
     {
         doFilter((HttpServletRequest) req, (HttpServletResponse) res);
     }
 
+    /**
+     * @see FilterChain#doFilter(ServletRequest, ServletResponse)
+     */
     protected abstract void doFilter(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException;
 }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/InvocationFilterChain.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/InvocationFilterChain.java
index 6bbf4f2..b806c39 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/InvocationFilterChain.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/InvocationFilterChain.java
@@ -16,41 +16,59 @@
  */
 package org.apache.felix.http.base.internal.dispatch;
 
+import static javax.servlet.http.HttpServletResponse.SC_OK;
+
+import java.io.IOException;
+
 import javax.servlet.FilterChain;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import org.apache.felix.http.base.internal.handler.FilterHandler;
 
-public final class InvocationFilterChain extends HttpFilterChain
+import org.apache.felix.http.base.internal.handler.FilterHandler;
+import org.apache.felix.http.base.internal.handler.ServletHandler;
+
+class InvocationFilterChain extends HttpFilterChain
 {
-    private final FilterHandler[] handlers;
-    private final ServletPipeline servletPipeline;
-    private final FilterChain proceedingChain;
+    private final ServletHandler servletHandler;
+    private final FilterHandler[] filterHandlers;
+    private final FilterChain defaultChain;
+
     private int index = -1;
 
-    public InvocationFilterChain(FilterHandler[] handlers, ServletPipeline servletPipeline, FilterChain proceedingChain)
+    public InvocationFilterChain(ServletHandler servletHandler, FilterHandler[] filterHandlers, FilterChain defaultChain)
     {
-        this.handlers = handlers;
-        this.servletPipeline = servletPipeline;
-        this.proceedingChain = proceedingChain;
+        this.filterHandlers = filterHandlers;
+        this.servletHandler = servletHandler;
+        this.defaultChain = defaultChain;
     }
 
+    @Override
     protected void doFilter(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException
     {
         this.index++;
 
-        if (this.index < this.handlers.length)
+        if (this.index < this.filterHandlers.length)
         {
-            this.handlers[this.index].handle(req, res, this);
-        }
-        else
-        {
-            if (!this.servletPipeline.handle(req, res))
+            if (this.filterHandlers[this.index].handle(req, res, this))
             {
-                this.proceedingChain.doFilter(req, res);
+                // We're done...
+                return;
             }
         }
+
+        // Last entry in the chain...
+        if (this.servletHandler != null && this.servletHandler.handle(req, res))
+        {
+            // We're done...
+            return;
+        }
+
+        // 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)
+        {
+            this.defaultChain.doFilter(req, res);
+        }
     }
 }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/NotFoundFilterChain.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/NotFoundFilterChain.java
index 4f0d012..b1398f2 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/NotFoundFilterChain.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/NotFoundFilterChain.java
@@ -16,13 +16,15 @@
  */
 package org.apache.felix.http.base.internal.dispatch;
 
+import java.io.IOException;
+
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
 
 public final class NotFoundFilterChain extends HttpFilterChain
 {
+    @Override
     protected void doFilter(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException
     {
         res.sendError(HttpServletResponse.SC_NOT_FOUND);
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletContextWrapper.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletContextWrapper.java
similarity index 94%
rename from http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletContextWrapper.java
rename to http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletContextWrapper.java
index 33822fc..5240b48 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletContextWrapper.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletContextWrapper.java
@@ -17,13 +17,12 @@
  * under the License.
  */
 
-package org.apache.felix.http.base.internal.handler;
+package org.apache.felix.http.base.internal.dispatch;
 
 import javax.servlet.RequestDispatcher;
 
 import org.apache.felix.http.base.internal.context.ExtServletContext;
 import org.apache.felix.http.base.internal.context.ServletContextImpl;
-import org.apache.felix.http.base.internal.dispatch.RequestDispatcherProvider;
 
 /**
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletPipeline.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletPipeline.java
deleted file mode 100644
index b2618f2..0000000
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletPipeline.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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.dispatch;
-
-import static org.apache.felix.http.base.internal.util.UriUtils.decodePath;
-import static org.apache.felix.http.base.internal.util.UriUtils.removeDotSegments;
-
-import java.io.IOException;
-
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.felix.http.base.internal.handler.ServletHandler;
-
-public final class ServletPipeline implements RequestDispatcherProvider
-{
-    private final ServletHandler[] handlers;
-
-    public ServletPipeline(ServletHandler[] handlers)
-    {
-        this.handlers = handlers;
-    }
-
-    public RequestDispatcher getNamedDispatcher(String name)
-    {
-        // See section 9.1 of Servlet 3.x specification...
-        if (name == null)
-        {
-            return null;
-        }
-
-        for (ServletHandler handler : this.handlers)
-        {
-            if (name.equals(handler.getName()))
-            {
-                return handler.createNamedRequestDispatcher();
-            }
-        }
-
-        return null;
-    }
-
-    public RequestDispatcher getRequestDispatcher(String path)
-    {
-        // See section 9.1 of Servlet 3.x specification...
-        if (path == null || (!path.startsWith("/") && !"".equals(path)))
-        {
-            return null;
-        }
-
-        String query = null;
-        int q = 0;
-        if ((q = path.indexOf('?')) > 0)
-        {
-            query = path.substring(q + 1);
-            path = path.substring(0, q);
-        }
-        // TODO remove path parameters...
-        String pathInContext = decodePath(removeDotSegments(path));
-
-        for (ServletHandler handler : this.handlers)
-        {
-            if (handler.matches(pathInContext))
-            {
-                return handler.createRequestDispatcher(path, pathInContext, query);
-            }
-        }
-
-        return null;
-    }
-
-    public boolean handle(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
-    {
-        // NOTE: this code assumes that HttpServletRequest#getRequestDispatcher() is properly mapped, see FilterPipeline.FilterRequestWrapper!
-        for (ServletHandler handler : this.handlers)
-        {
-            if (handler.handle(req, res))
-            {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    public boolean hasServletsMapped()
-    {
-        return this.handlers.length > 0;
-    }
-}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/AbstractHandler.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/AbstractHandler.java
index 883e44a..d45f84b 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/AbstractHandler.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/AbstractHandler.java
@@ -16,11 +16,10 @@
  */
 package org.apache.felix.http.base.internal.handler;
 
-import java.util.Dictionary;
-import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Pattern;
 
 import javax.servlet.Filter;
 import javax.servlet.Servlet;
@@ -28,7 +27,7 @@
 
 import org.apache.felix.http.base.internal.context.ExtServletContext;
 
-public abstract class AbstractHandler
+public abstract class AbstractHandler<T extends AbstractHandler> implements Comparable<T>
 {
     private final static AtomicInteger ID = new AtomicInteger();
 
@@ -76,32 +75,7 @@
 
     public abstract void init() throws ServletException;
 
-    /**
-     * TODO - We can remove this, once we switched to {@link #setInitParams(Map)}
-     * @param map
-     */
-    public final void setInitParams(Dictionary map)
-    {
-        this.initParams.clear();
-        if (map == null)
-        {
-            return;
-        }
-
-        Enumeration e = map.keys();
-        while (e.hasMoreElements())
-        {
-            Object key = e.nextElement();
-            Object value = map.get(key);
-
-            if ((key instanceof String) && (value instanceof String))
-            {
-                this.initParams.put((String) key, (String) value);
-            }
-        }
-    }
-
-    protected final ExtServletContext getContext()
+    public ExtServletContext getContext()
     {
         return this.context;
     }
@@ -118,4 +92,6 @@
      * @return the {@link Servlet} or {@link Filter} this handler handles.
      */
     protected abstract Object getSubject();
+
+    public abstract Pattern[] getPatterns();
 }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterHandler.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterHandler.java
index 7f028d8..e26535c 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterHandler.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterHandler.java
@@ -20,6 +20,8 @@
 import static javax.servlet.http.HttpServletResponse.SC_OK;
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.regex.Pattern;
 
 import javax.servlet.Filter;
@@ -30,32 +32,33 @@
 
 import org.apache.felix.http.base.internal.context.ExtServletContext;
 import org.apache.felix.http.base.internal.runtime.FilterInfo;
+import org.apache.felix.http.base.internal.util.PatternUtil;
 
-public final class FilterHandler extends AbstractHandler implements Comparable<FilterHandler>
+public final class FilterHandler extends AbstractHandler<FilterHandler>
 {
     private final Filter filter;
     private final FilterInfo filterInfo;
+    private final Pattern[] patterns;
 
     public FilterHandler(ExtServletContext context, Filter filter, FilterInfo filterInfo)
     {
-        super(context, filterInfo.initParams, filterInfo.name);
+        super(context, filterInfo.getInitParams(), filterInfo.getName());
         this.filter = filter;
         this.filterInfo = filterInfo;
+        // Compose a single array of all patterns & regexs the filter must represent...
+        String[] patterns = getFilterPatterns(filterInfo);
+
+        this.patterns = new Pattern[patterns.length];
+        for (int i = 0; i < patterns.length; i++)
+        {
+            this.patterns[i] = Pattern.compile(patterns[i]);
+        }
     }
 
     @Override
     public int compareTo(FilterHandler other)
     {
-        if (other.filterInfo.ranking == this.filterInfo.ranking)
-        {
-            if (other.filterInfo.serviceId == this.filterInfo.serviceId)
-            {
-                return 0;
-            }
-            return other.filterInfo.serviceId > this.filterInfo.serviceId ? -1 : 1;
-        }
-
-        return (other.filterInfo.ranking > this.filterInfo.ranking) ? 1 : -1;
+        return this.filterInfo.compareTo(other.filterInfo);
     }
 
     @Override
@@ -69,59 +72,33 @@
         return this.filter;
     }
 
-    public String getPattern()
+    public FilterInfo getFilterInfo()
     {
-        final StringBuilder sb = new StringBuilder();
-        boolean first = true;
-        if ( this.filterInfo.regexs != null )
-        {
-            for(final String p : this.filterInfo.regexs)
-            {
-                if ( first )
-                {
-                    first = false;
-                }
-                else
-                {
-                    sb.append(", ");
-                }
-                sb.append(p);
-            }
-        }
-        if ( this.filterInfo.patterns != null )
-        {
-            for(final String p : this.filterInfo.patterns)
-            {
-                if ( first )
-                {
-                    first = false;
-                }
-                else
-                {
-                    sb.append(", ");
-                }
-                sb.append(p);
-            }
-        }
-        return sb.toString();
+        return this.filterInfo;
     }
 
     public int getRanking()
     {
-        return filterInfo.ranking;
+        return filterInfo.getRanking();
     }
 
-    public void handle(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws ServletException, IOException
+    public boolean handle(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws ServletException, IOException
     {
-        final boolean matches = matches(req.getPathInfo());
-        if (matches)
+        if (getContext().handleSecurity(req, res))
         {
-            doHandle(req, res, chain);
+            this.filter.doFilter(req, res, chain);
+
+            return true;
         }
-        else
+
+        // 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))
         {
-            chain.doFilter(req, res);
+            res.sendError(SC_FORBIDDEN);
         }
+
+        return false;
     }
 
     @Override
@@ -130,60 +107,34 @@
         this.filter.init(new FilterConfigImpl(getName(), getContext(), getInitParams()));
     }
 
-    public boolean matches(String uri)
-    {
-        // assume root if uri is null
-        if (uri == null)
-        {
-            uri = "/";
-        }
-
-        if ( this.filterInfo.patterns != null )
-        {
-            for(final String p : this.filterInfo.patterns)
-            {
-                if ( Pattern.compile(p.replace(".", "\\.").replace("*", ".*")).matcher(uri).matches() )
-                {
-                    return true;
-                }
-            }
-        }
-        if ( this.filterInfo.regexs != null )
-        {
-            for(final String p : this.filterInfo.regexs)
-            {
-                if ( Pattern.compile(p).matcher(uri).matches() )
-                {
-                    return true;
-                }
-            }
-        }
-
-        // TODO implement servlet name matching
-
-        return false;
-    }
-
-    final void doHandle(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws ServletException, IOException
-    {
-        if (getContext().handleSecurity(req, res))
-        {
-            this.filter.doFilter(req, res, chain);
-        }
-        else
-        {
-            // 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);
-            }
-        }
-    }
-
     @Override
     protected Object getSubject()
     {
         return this.filter;
     }
+
+    private static String[] getFilterPatterns(FilterInfo filterInfo)
+    {
+        List<String> result = new ArrayList<String>();
+        if (filterInfo.getPatterns() != null)
+        {
+            for (int i = 0; i < filterInfo.getPatterns().length; i++)
+            {
+                result.add(PatternUtil.convertToRegEx(filterInfo.getPatterns()[i]));
+            }
+        }
+        if (filterInfo.getRegexs() != null)
+        {
+            for (int i = 0; i < filterInfo.getRegexs().length; i++)
+            {
+                result.add(filterInfo.getRegexs()[i]);
+            }
+        }
+        return result.toArray(new String[result.size()]);
+    }
+
+    @Override
+    public Pattern[] getPatterns() {
+        return this.patterns;
+    }
 }
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 dc61cd4..a95a7da 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
@@ -16,157 +16,136 @@
  */
 package org.apache.felix.http.base.internal.handler;
 
+import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashSet;
+import java.util.HashMap;
 import java.util.Iterator;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
+import java.util.List;
+import java.util.Map;
 
+import javax.servlet.DispatcherType;
 import javax.servlet.Filter;
 import javax.servlet.Servlet;
 import javax.servlet.ServletException;
 
+import org.apache.felix.http.base.internal.runtime.ContextInfo;
+import org.apache.felix.http.base.internal.runtime.FilterInfo;
 import org.apache.felix.http.base.internal.runtime.ServletInfo;
 import org.osgi.service.http.NamespaceException;
 
 public final class HandlerRegistry
 {
-    private final ConcurrentMap<Filter, FilterHandler> filterMap = new ConcurrentHashMap<Filter, FilterHandler>();
-    private final ConcurrentMap<String, ServletHandler> aliasMap = new ConcurrentHashMap<String, ServletHandler>();
-    private volatile ServletHandler[] servlets;
-    private volatile FilterHandler[] filters;
+    private final Map<Servlet, ServletHandler> servletMap = new HashMap<Servlet, ServletHandler>();
+    private final Map<Filter, FilterHandler> filterMap = new HashMap<Filter, FilterHandler>();
+    private final Map<String, Servlet> servletPatternMap= new HashMap<String, Servlet>();
+    private volatile HandlerMapping<ServletHandler> servletMapping = new HandlerMapping<ServletHandler>();
+    private volatile HandlerMapping<FilterHandler> filterMapping = new HandlerMapping<FilterHandler>();
+    private volatile ErrorsMapping errorsMapping = new ErrorsMapping();
 
-    public HandlerRegistry()
+    public synchronized void addErrorServlet(String errorPage, ServletHandler handler) throws ServletException
     {
-        servlets = new ServletHandler[0];
-        filters = new FilterHandler[0];
+        this.errorsMapping.addErrorServlet(errorPage, handler);
     }
 
-    public ServletHandler[] getServlets()
+    public synchronized void addFilter(FilterHandler handler) throws ServletException
     {
-        return servlets;
-    }
-
-    public FilterHandler[] getFilters()
-    {
-        return filters;
-    }
-
-    public void addServlet(final ServletHandler handler)
-    throws ServletException, NamespaceException
-    {
-        handler.init();
-
-        if (aliasMap.putIfAbsent(handler.getAlias(), handler) != null)
+        if (this.filterMap.containsKey(handler.getFilter()))
         {
-            handler.destroy();
-            throw new NamespaceException("Servlet with alias '" + handler.getAlias() + "' already registered");
-        }
-
-        updateServletArray();
-    }
-
-    public void addFilter(FilterHandler handler) throws ServletException
-    {
-        handler.init();
-
-        // there is a window of opportunity that the servlet/alias was registered between the
-        // previous check and this one, so we have to atomically add if absent here.
-        if (filterMap.putIfAbsent(handler.getFilter(), handler) != null)
-        {
-            // Do not destroy the filter as the same instance was already registered
             throw new ServletException("Filter instance already registered");
         }
 
-        updateFilterArray();
-    }
+        handler.init();
+        this.filterMap.put(handler.getFilter(), handler);
 
-    public Set<Servlet> removeServlet(final ServletInfo servletInfo, final boolean destroy)
-    {
-        final Set<Servlet> servletInstances = new HashSet<Servlet>();
-        boolean update = false;
-        for (Iterator<ServletHandler> it = aliasMap.values().iterator(); it.hasNext(); )
-        {
-            final ServletHandler handler = it.next();
-            if (handler.getServletInfo().compareTo(servletInfo) == 0 ) {
-                it.remove();
-                servletInstances.add(handler.getServlet());
-                if (destroy)
-                {
-                    handler.destroy();
-                }
-                update = true;
-            }
-        }
-        if ( update )
-        {
-            updateServletArray();
-        }
-
-        return servletInstances;
+        updateFilterMapping();
     }
 
     /**
-     * Support for old Http Service registrations
-     * @param servlet
-     * @param destroy
+     * Add a new servlet.
      */
-    public void removeServlet(final Servlet servlet, final boolean destroy)
+    public synchronized void addServlet(ContextInfo contextInfo, ServletHandler handler) throws ServletException, NamespaceException
     {
-        boolean update = false;
-        for (Iterator<ServletHandler> it = aliasMap.values().iterator(); it.hasNext(); )
+        if (this.servletMap.containsKey(handler.getServlet()))
         {
-            ServletHandler handler = it.next();
-            if (handler.getServlet() == servlet ) {
-                it.remove();
-                if (destroy)
-                {
-                    handler.destroy();
-                }
-                update = true;
-            }
+            // Do not destroy the servlet as the same instance was already registered
+            throw new ServletException("Servlet instance " + handler.getName() + " already registered");
         }
-        if ( update )
-        {
-            updateServletArray();
-        }
-    }
 
-    public void removeFilter(Filter filter, final boolean destroy)
-    {
-        FilterHandler handler = filterMap.remove(filter);
-        if (handler != null)
+        // Can be null in case of error-handling servlets...
+        String[] patterns = handler.getServletInfo().getPatterns();
+        int length = patterns == null ? 0 : patterns.length;
+
+        for (int i = 0; i < length; i++)
         {
-            updateFilterArray();
-            if (destroy)
+            String pattern = contextInfo != null ? contextInfo.getFullPath(patterns[i]) : patterns[i];
+            if (this.servletPatternMap.containsKey(pattern))
             {
-                handler.destroy();
+                throw new ServletException("Servlet instance " + handler.getName() + " already registered");
+            }
+            this.servletPatternMap.put(pattern, handler.getServlet());
+        }
+
+        handler.init();
+        this.servletMap.put(handler.getServlet(), handler);
+
+        updateServletMapping();
+    }
+
+    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();
             }
         }
-    }
 
-    public Servlet getServletByAlias(String alias)
-    {
-        final ServletHandler handler = aliasMap.get(alias);
-        if ( handler != null ) {
-            return handler.getServlet();
+        String servletName = (servletHandler != null) ? servletHandler.getName() : null;
+        // TODO this is not the most efficient/fastest way of doing this...
+        for (FilterHandler filterHandler : this.filterMapping.getAllElements())
+        {
+            if (referencesServletByName(filterHandler, servletName))
+            {
+                result.add(filterHandler);
+            }
         }
-        return null;
+
+        return result.toArray(new FilterHandler[result.size()]);
     }
 
-    public void addErrorServlet(String errorPage, ServletHandler handler) throws ServletException
+    public synchronized Servlet getServletByAlias(String alias)
     {
-        // TODO
+        return this.servletPatternMap.get(alias);
     }
 
-    public void removeAll()
+    public ServletHandler getServletHandlerByName(String name)
     {
-        for (Iterator<ServletHandler> it = aliasMap.values().iterator(); it.hasNext(); )
+        return this.servletMapping.getByName(name);
+    }
+
+    public ServletHandler getServletHander(String requestURI)
+    {
+        return this.servletMapping.getBestMatch(requestURI);
+    }
+
+    public synchronized void removeAll()
+    {
+        for (Iterator<ServletHandler> it = servletMap.values().iterator(); it.hasNext(); )
         {
             ServletHandler handler = it.next();
             it.remove();
-
             handler.destroy();
         }
 
@@ -177,21 +156,127 @@
             handler.destroy();
         }
 
-        updateServletArray();
-        updateFilterArray();
+        this.servletMap.clear();
+        this.filterMap.clear();
+        this.servletPatternMap.clear();
+        this.errorsMapping.clear();
+
+        updateServletMapping();
+        updateFilterMapping();
     }
 
-    private void updateServletArray()
+    public synchronized void removeFilter(Filter filter, final boolean destroy)
     {
-        final ServletHandler[] tmp = aliasMap.values().toArray(new ServletHandler[aliasMap.size()]);
-        Arrays.sort(tmp);
-        servlets = tmp;
+        FilterHandler handler = this.filterMap.remove(filter);
+        if (handler != null)
+        {
+            updateFilterMapping();
+            if (destroy)
+            {
+                handler.destroy();
+            }
+        }
     }
 
-    private void updateFilterArray()
+    public synchronized Filter removeFilter(final FilterInfo filterInfo, final boolean destroy)
     {
-        final FilterHandler[] tmp = filterMap.values().toArray(new FilterHandler[filterMap.size()]);
-        Arrays.sort(tmp);
-        filters = tmp;
+        for(final FilterHandler handler : this.filterMap.values())
+        {
+            if ( handler.getFilterInfo().compareTo(filterInfo) == 0)
+            {
+                this.filterMap.remove(handler.getFilter());
+                updateFilterMapping();
+                if (destroy)
+                {
+                    handler.destroy();
+                }
+                return handler.getFilter();
+            }
+        }
+        return null;
+    }
+
+    public synchronized Servlet removeServlet(final ContextInfo contextInfo, ServletInfo servletInfo, final boolean destroy)
+    {
+        for(final ServletHandler handler : this.servletMap.values())
+        {
+            if ( handler.getServletInfo().compareTo(servletInfo) == 0 )
+            {
+                this.servletMap.remove(handler.getServlet());
+                updateServletMapping();
+
+                // Can be null in case of error-handling servlets...
+                String[] patterns = handler.getServletInfo().getPatterns();
+                int length = patterns == null ? 0 : patterns.length;
+
+                for (int i = 0; i < length; i++)
+                {
+                    this.servletPatternMap.remove(contextInfo.getFullPath(patterns[i]));
+                }
+
+                this.errorsMapping.removeServlet(handler.getServlet());
+
+                if (destroy)
+                {
+                    handler.destroy();
+                }
+                return handler.getServlet();
+            }
+        }
+        return null;
+    }
+
+    public synchronized void removeServlet(Servlet servlet, final boolean destroy)
+    {
+        ServletHandler handler = this.servletMap.remove(servlet);
+        if (handler != null)
+        {
+            updateServletMapping();
+
+            // Can be null in case of error-handling servlets...
+            String[] patterns = handler.getServletInfo().getPatterns();
+            int length = patterns == null ? 0 : patterns.length;
+
+            for (int i = 0; i < length; i++)
+            {
+                this.servletPatternMap.remove(patterns[i]);
+            }
+
+            this.errorsMapping.removeServlet(servlet);
+
+            if (destroy)
+            {
+                handler.destroy();
+            }
+        }
+    }
+
+    private boolean referencesDispatcherType(FilterHandler handler, DispatcherType dispatcherType)
+    {
+        return Arrays.asList(handler.getFilterInfo().getDispatcher()).contains(dispatcherType);
+    }
+
+    private boolean referencesServletByName(FilterHandler handler, String servletName)
+    {
+        if (servletName == null)
+        {
+            return false;
+        }
+        String[] names = handler.getFilterInfo().getServletNames();
+        if (names != null && names.length > 0)
+        {
+            return Arrays.asList(names).contains(servletName);
+        }
+        return false;
+    }
+
+    private void updateFilterMapping()
+    {
+        this.filterMapping = new HandlerMapping<FilterHandler>(this.filterMap.values());
+    }
+
+    private void updateServletMapping()
+    {
+        this.servletMapping = new HandlerMapping<ServletHandler>(this.servletMap.values());
     }
 }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpServicePlugin.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpServicePlugin.java
index 127e539..fcf12db 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpServicePlugin.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpServicePlugin.java
@@ -53,7 +53,7 @@
 
     public void register()
     {
-        final Dictionary<String, Object> props = new Hashtable<String, Object>();
+        Dictionary<String, Object> props = new Hashtable<String, Object>();
         props.put(Constants.SERVICE_VENDOR, "Apache Software Foundation");
         props.put(Constants.SERVICE_DESCRIPTION, "HTTP Service Web Console Plugin");
         props.put("felix.webconsole.label", "httpservice");
@@ -88,14 +88,14 @@
         pw.println("<th class=\"header\">${Bundle}</th>");
         pw.println("</tr></thead>");
 
-        FilterHandler[] filters = registry.getFilters();
+        FilterHandler[] filters = new FilterHandler[0]; // XXX was: registry.getFilters();
         Arrays.sort(filters);
         String rowClass = "odd";
         for (FilterHandler filter : filters)
         {
             pw.println("<tr class=\"" + rowClass + " ui-state-default\">");
-            pw.println("<td>" + filter.getPattern() + "</td>");
-            pw.println("<td>" + filter.getFilter().getClass().getName() + "(" + filter.getRanking() + ")" + "</td>");
+//            pw.println("<td>" + Arrays.toString(filter.getPatternStrings()) + "</td>"); // XXX
+//            pw.println("<td>" + filter.getFilter().getClass().getName() + "(" + filter.getRanking() + ")" + "</td>");
 
             printBundleDetails(pw, filter.getFilter().getClass());
 
@@ -122,14 +122,14 @@
         pw.println("<th class=\"header\">${Bundle}</th>");
         pw.println("</tr></thead>");
 
-        ServletHandler[] servlets = registry.getServlets();
+        ServletHandler[] servlets = new ServletHandler[0]; // XXX was: registry.getServlets();
         String rowClass = "odd";
         for (ServletHandler servlet : servlets)
         {
 
             pw.println("<tr class=\"" + rowClass + " ui-state-default\">");
-            pw.println("<td>" + servlet.getAlias() + "</td>");
-            pw.println("<td>" + servlet.getServlet().getClass().getName() + "</td>");
+//            pw.println("<td>" + Arrays.toString(servlet.getPatternStrings()) + "</td>"); // XXX
+//            pw.println("<td>" + servlet.getServlet().getClass().getName() + "</td>");
 
             printBundleDetails(pw, servlet.getServlet().getClass());
 
@@ -154,33 +154,30 @@
         pw.println("HTTP Service Details:");
         pw.println();
         pw.println("Registered Servlet Services");
-        ServletHandler[] servlets = registry.getServlets();
+        ServletHandler[] servlets = new ServletHandler[0]; // XXX was: registry.getServlets();
         for (ServletHandler servlet : servlets)
         {
-            pw.println("Alias : " + servlet.getAlias());
-
+//            pw.println("Patterns : " + Arrays.toString(servlet.getPatternStrings())); // XXX
             addSpace(pw, 1);
-            pw.println("Class  :" + servlet.getServlet().getClass().getName());
+            pw.println("Class    : " + servlet.getServlet().getClass().getName());
             addSpace(pw, 1);
-            pw.println("Bundle :" + getBundleDetails(servlet.getServlet().getClass()));
-
+            pw.println("Bundle   : " + getBundleDetails(servlet.getServlet().getClass()));
         }
 
         pw.println();
 
         pw.println("Registered Filter Services");
-        FilterHandler[] filters = registry.getFilters();
+        FilterHandler[] filters = new FilterHandler[0]; // XXX was: registry.getFilters();
         Arrays.sort(filters);
         for (FilterHandler filter : filters)
         {
-            pw.println("Pattern : " + filter.getPattern());
-
+//            pw.println("Patterns : " + Arrays.toString(filter.getPatternStrings())); // XXX
             addSpace(pw, 1);
-            pw.println("Ranking :" + filter.getRanking());
+//            pw.println("Ranking  : " + filter.getRanking()); // XXX
             addSpace(pw, 1);
-            pw.println("Class   :" + filter.getFilter().getClass().getName());
+            pw.println("Class    : " + filter.getFilter().getClass().getName());
             addSpace(pw, 1);
-            pw.println("Bundle  :" + getBundleDetails(filter.getFilter().getClass()));
+            pw.println("Bundle   : " + getBundleDetails(filter.getFilter().getClass()));
         }
     }
 
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 65ad762..8564d3e 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
@@ -16,264 +16,85 @@
  */
 package org.apache.felix.http.base.internal.handler;
 
-import static javax.servlet.RequestDispatcher.FORWARD_CONTEXT_PATH;
-import static javax.servlet.RequestDispatcher.FORWARD_PATH_INFO;
-import static javax.servlet.RequestDispatcher.FORWARD_QUERY_STRING;
-import static javax.servlet.RequestDispatcher.FORWARD_REQUEST_URI;
-import static javax.servlet.RequestDispatcher.FORWARD_SERVLET_PATH;
-import static javax.servlet.RequestDispatcher.INCLUDE_CONTEXT_PATH;
-import static javax.servlet.RequestDispatcher.INCLUDE_PATH_INFO;
-import static javax.servlet.RequestDispatcher.INCLUDE_QUERY_STRING;
-import static javax.servlet.RequestDispatcher.INCLUDE_REQUEST_URI;
-import static javax.servlet.RequestDispatcher.INCLUDE_SERVLET_PATH;
 import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
 import static javax.servlet.http.HttpServletResponse.SC_OK;
-import static org.apache.felix.http.base.internal.util.UriUtils.concat;
 
 import java.io.IOException;
+import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import javax.servlet.DispatcherType;
-import javax.servlet.RequestDispatcher;
 import javax.servlet.Servlet;
 import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.felix.http.base.internal.context.ExtServletContext;
+import org.apache.felix.http.base.internal.runtime.ContextInfo;
 import org.apache.felix.http.base.internal.runtime.ServletInfo;
+import org.apache.felix.http.base.internal.util.PatternUtil;
 
 /**
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
-public final class ServletHandler extends AbstractHandler implements Comparable<ServletHandler>
+public final class ServletHandler extends AbstractHandler<ServletHandler>
 {
-    private class RequestDispatcherImpl implements RequestDispatcher
-    {
-        final String servletPath = "/".equals(getAlias()) ? "" : getAlias(); // XXX handle wildcard aliases!
-        final String requestURI;
-        final String pathInfo;
-        final String query;
-        final boolean named;
-
-        public RequestDispatcherImpl()
-        {
-            this.requestURI = null;
-            this.pathInfo = null;
-            this.query = null;
-            this.named = true;
-        }
-
-        public RequestDispatcherImpl(String uri, String pathInContext, String query)
-        {
-            this.requestURI = uri;
-            this.pathInfo = this.servletPath.equals(pathInContext) ? null : pathInContext;
-            this.query = query;
-            this.named = false;
-        }
-
-        @Override
-        public void forward(ServletRequest req, ServletResponse res) throws ServletException, IOException
-        {
-            if (res.isCommitted())
-            {
-                throw new ServletException("Response has been committed");
-            }
-            else
-            {
-                // See section 9.4 of Servlet 3.0 spec
-                res.resetBuffer();
-            }
-
-            // Since we're already created this RequestDispatcher for *this* servlet handler, we do not need to
-            // recheck whether its patch matches, but instead can directly handle the forward-request...
-            doHandle(new ServletRequestWrapper((HttpServletRequest) req, this, DispatcherType.FORWARD), (HttpServletResponse) res);
-
-            // After a forward has taken place, the results should be committed,
-            // see section 9.4 of Servlet 3.0 spec...
-            if (!req.isAsyncStarted())
-            {
-                res.flushBuffer();
-                res.getWriter().close();
-            }
-        }
-
-        @Override
-        public void include(ServletRequest req, ServletResponse res) throws ServletException, IOException
-        {
-            // Since we're already created this RequestDispatcher for *this* servlet handler, we do not need to
-            // recheck whether its patch matches, but instead can directly handle the include-request...
-            doHandle(new ServletRequestWrapper((HttpServletRequest) req, this, DispatcherType.INCLUDE), (HttpServletResponse) res);
-        }
-
-        boolean isNamedDispatcher()
-        {
-            return this.named;
-        }
-    }
-
-    private static class ServletRequestWrapper extends HttpServletRequestWrapper
-    {
-        private final RequestDispatcherImpl dispatcher;
-        private final DispatcherType type;
-
-        public ServletRequestWrapper(HttpServletRequest req, RequestDispatcherImpl dispatcher, DispatcherType type)
-        {
-            super(req);
-            this.dispatcher = dispatcher;
-            this.type = type;
-        }
-
-        @Override
-        public Object getAttribute(String name)
-        {
-            HttpServletRequest request = (HttpServletRequest) getRequest();
-            if (isInclusionDispatcher())
-            {
-                if (INCLUDE_REQUEST_URI.equals(name))
-                {
-                    return concat(request.getContextPath(), this.dispatcher.requestURI);
-                }
-                else if (INCLUDE_CONTEXT_PATH.equals(name))
-                {
-                    return request.getContextPath();
-                }
-                else if (INCLUDE_SERVLET_PATH.equals(name))
-                {
-                    return this.dispatcher.servletPath;
-                }
-                else if (INCLUDE_PATH_INFO.equals(name))
-                {
-                    return this.dispatcher.pathInfo;
-                }
-                else if (INCLUDE_QUERY_STRING.equals(name))
-                {
-                    return this.dispatcher.query;
-                }
-            }
-            else if (isForwardingDispatcher())
-            {
-                // NOTE: the forward.* attributes *always* yield the *original* values...
-                if (FORWARD_REQUEST_URI.equals(name))
-                {
-                    return super.getRequestURI();
-                }
-                else if (FORWARD_CONTEXT_PATH.equals(name))
-                {
-                    return request.getContextPath();
-                }
-                else if (FORWARD_SERVLET_PATH.equals(name))
-                {
-                    return super.getServletPath();
-                }
-                else if (FORWARD_PATH_INFO.equals(name))
-                {
-                    return super.getPathInfo();
-                }
-                else if (FORWARD_QUERY_STRING.equals(name))
-                {
-                    return super.getQueryString();
-                }
-            }
-
-            return super.getAttribute(name);
-        }
-
-        @Override
-        public DispatcherType getDispatcherType()
-        {
-            return this.type;
-        }
-
-        @Override
-        public String getPathInfo()
-        {
-            if (isForwardingDispatcher())
-            {
-                return this.dispatcher.pathInfo;
-            }
-            return super.getPathInfo();
-        }
-
-        @Override
-        public String getRequestURI()
-        {
-            if (isForwardingDispatcher())
-            {
-                return concat(getContextPath(), this.dispatcher.requestURI);
-            }
-            return super.getRequestURI();
-        }
-
-        @Override
-        public String getServletPath()
-        {
-            if (isForwardingDispatcher())
-            {
-                return this.dispatcher.servletPath;
-            }
-            return super.getServletPath();
-        }
-
-        @Override
-        public String toString()
-        {
-            return getClass().getSimpleName() + "->" + super.getRequest();
-        }
-
-        private boolean isForwardingDispatcher()
-        {
-            return DispatcherType.FORWARD == this.type && !this.dispatcher.isNamedDispatcher();
-        }
-
-        private boolean isInclusionDispatcher()
-        {
-            return DispatcherType.INCLUDE == this.type && !this.dispatcher.isNamedDispatcher();
-        }
-    }
-
     private final ServletInfo servletInfo;
 
     private final Servlet servlet;
 
-    private final Pattern pattern;
+    private final Pattern[] patterns;
 
-    private final String alias;
+    private final long contextServiceId;
 
-    public ServletHandler(final ExtServletContext context,
+    public ServletHandler(final ContextInfo contextInfo,
+                          final ExtServletContext context,
                           final ServletInfo servletInfo,
-                          final Servlet servlet,
-                          final String alias)
+                          final Servlet servlet)
     {
         super(context, servletInfo.getInitParams(), servletInfo.getName());
         this.servlet = servlet;
-        this.pattern = Pattern.compile(alias.replace(".", "\\.").replace("*", ".*"));
-        this.alias = alias;
         this.servletInfo = servletInfo;
+
+        // Can be null in case of error-handling servlets...
+        String[] patterns = this.servletInfo.getPatterns();
+        final int length = patterns == null ? 0 : patterns.length;
+
+        this.patterns = new Pattern[length];
+        for (int i = 0; i < length; i++)
+        {
+            String pattern = (contextInfo == null ? patterns[i] : contextInfo.getFullPath(patterns[i]));
+            this.patterns[i] = Pattern.compile(PatternUtil.convertToRegEx(pattern));
+        }
+        if ( contextInfo != null )
+        {
+            this.contextServiceId = contextInfo.getServiceId();
+        }
+        else
+        {
+            this.contextServiceId = -1;
+        }
     }
 
     @Override
     public int compareTo(ServletHandler other)
     {
-        int result = other.alias.length() - this.alias.length();
-        if ( result == 0 )
+        return getId() - other.getId();
+    }
+
+    public String determineServletPath(String uri)
+    {
+        if (uri == null)
         {
-            result = this.alias.compareTo(other.alias);
+            uri = "/";
         }
-        return result;
-    }
 
-    public RequestDispatcher createNamedRequestDispatcher()
-    {
-        return new RequestDispatcherImpl();
-    }
+        Matcher matcher = this.patterns[0].matcher(uri);
+        if (matcher.find(0))
+        {
+            return matcher.groupCount() > 0 ? matcher.group(1) : matcher.group();
+        }
 
-    public RequestDispatcher createRequestDispatcher(String path, String pathInContext, String query)
-    {
-        return new RequestDispatcherImpl(path, pathInContext, query);
+        return null;
     }
 
     @Override
@@ -282,43 +103,15 @@
         this.servlet.destroy();
     }
 
-    public String getAlias()
-    {
-        return this.alias;
-    }
-
     public Servlet getServlet()
     {
         return this.servlet;
     }
 
-    public boolean handle(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
+    @Override
+    public Pattern[] getPatterns()
     {
-        String path;
-        if (DispatcherType.INCLUDE == req.getDispatcherType())
-        {
-            path = (String) req.getAttribute(INCLUDE_SERVLET_PATH);
-        }
-        else if (DispatcherType.FORWARD == req.getDispatcherType())
-        {
-            path = (String) req.getAttribute(FORWARD_SERVLET_PATH);
-        }
-        else if (DispatcherType.ASYNC == req.getDispatcherType())
-        {
-            path = (String) req.getAttribute("javax.servlet.async.path_info");
-        }
-        else
-        {
-            path = req.getPathInfo();
-        }
-
-        final boolean matches = matches(path);
-        if (matches)
-        {
-            doHandle(req, res);
-        }
-
-        return matches;
+        return this.patterns;
     }
 
     @Override
@@ -327,22 +120,23 @@
         this.servlet.init(new ServletConfigImpl(getName(), getContext(), getInitParams()));
     }
 
-    public boolean matches(String uri)
+    public boolean handle(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
     {
-        // TODO handle wildcard aliases and extension specs...
-        if (uri == null)
+        if (getContext().handleSecurity(req, res))
         {
-            return this.alias.equals("/");
-        }
-        else if (this.alias.equals("/"))
-        {
-            return uri.startsWith(this.alias);
-        }
-        else if ( uri.equals(this.alias) || uri.startsWith(this.alias + "/") )
-        {
+            this.servlet.service(req, res);
+
             return true;
         }
-        return this.pattern.matcher(uri).matches();
+
+        // 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()
@@ -350,33 +144,14 @@
         return this.servletInfo;
     }
 
-    final void doHandle(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
-    {
-        // Only wrap the original ServletRequest in case we're handling plain requests,
-        // not inclusions or forwards from servlets. Should solve FELIX-2774 and FELIX-3054...
-        if (DispatcherType.REQUEST == req.getDispatcherType())
-        {
-            req = new ServletHandlerRequest(req, getContext(), this.alias);
-        }
-
-        if (getContext().handleSecurity(req, res))
-        {
-            this.servlet.service(req, res);
-        }
-        else
-        {
-            // 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.sendError(SC_FORBIDDEN);
-            }
-        }
-    }
-
     @Override
     protected Object getSubject()
     {
         return this.servlet;
     }
+
+    public long getContextServiceId()
+    {
+        return this.contextServiceId;
+    }
 }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandlerRequest.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandlerRequest.java
deleted file mode 100644
index 9e6a9cf..0000000
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandlerRequest.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * 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.RequestDispatcher;
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
-import javax.servlet.http.HttpSession;
-
-import org.apache.felix.http.base.internal.context.ExtServletContext;
-import org.apache.felix.http.base.internal.dispatch.Dispatcher;
-import org.apache.felix.http.base.internal.dispatch.RequestDispatcherProvider;
-import org.apache.felix.http.base.internal.util.UriUtils;
-import org.osgi.service.http.HttpContext;
-import org.osgi.service.useradmin.Authorization;
-
-/**
- * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
- */
-@SuppressWarnings("deprecation")
-class ServletHandlerRequest extends HttpServletRequestWrapper
-{
-    private final String alias;
-    private final ServletContextWrapper context;
-    private String contextPath;
-    private String pathInfo;
-    private boolean pathInfoCalculated = false;
-
-    public ServletHandlerRequest(HttpServletRequest req, ExtServletContext context, String alias)
-    {
-        super(req);
-        this.context = new ServletContextWrapper(context, (RequestDispatcherProvider) req.getAttribute(Dispatcher.REQUEST_DISPATCHER_PROVIDER));
-        this.alias = alias;
-    }
-
-    @Override
-    public String getAuthType()
-    {
-        String authType = (String) getAttribute(HttpContext.AUTHENTICATION_TYPE);
-        if (authType != null)
-        {
-            return authType;
-        }
-
-        return super.getAuthType();
-    }
-
-    @Override
-    public String getContextPath()
-    {
-        /*
-         * FELIX-2030 Calculate the context path for the Http Service
-         * registered servlets from the container context and servlet paths
-         */
-        if (contextPath == null)
-        {
-            final String context = super.getContextPath();
-            final String servlet = super.getServletPath();
-            if (context == null || context.length() == 0)
-            {
-                contextPath = servlet;
-            }
-            else if (servlet == null || servlet.length() == 0)
-            {
-                contextPath = context;
-            }
-            else
-            {
-                contextPath = context + servlet;
-            }
-        }
-
-        return contextPath;
-    }
-
-    @Override
-    public String getPathInfo()
-    {
-        if (!this.pathInfoCalculated)
-        {
-            this.pathInfo = calculatePathInfo();
-            this.pathInfoCalculated = true;
-        }
-
-        return this.pathInfo;
-    }
-
-    @Override
-    public String getPathTranslated()
-    {
-        final String info = getPathInfo();
-        return (null == info) ? null : getRealPath(info);
-    }
-
-    @Override
-    public String getRemoteUser()
-    {
-        String remoteUser = (String) getAttribute(HttpContext.REMOTE_USER);
-        if (remoteUser != null)
-        {
-            return remoteUser;
-        }
-
-        return super.getRemoteUser();
-    }
-
-    @Override
-    public RequestDispatcher getRequestDispatcher(String path)
-    {
-        // See section 9.1 of Servlet 3.0 specification...
-        if (path == null)
-        {
-            return null;
-        }
-        // Handle relative paths, see Servlet 3.0 spec, section 9.1 last paragraph.
-        boolean relPath = !path.startsWith("/") && !"".equals(path);
-        if (relPath)
-        {
-            path = UriUtils.concat(this.alias, path);
-        }
-        return super.getRequestDispatcher(path);
-    }
-
-    @Override
-    public ServletContext getServletContext()
-    {
-        return this.context;
-    }
-
-    @Override
-    public String getServletPath()
-    {
-        if ("/".equals(this.alias))
-        {
-            return "";
-        }
-        return this.alias;
-    }
-
-    @Override
-    public HttpSession getSession(boolean create)
-    {
-        // FELIX-2797: wrap the original HttpSession to provide access to the correct ServletContext...
-        HttpSession session = super.getSession(create);
-        if (session == null)
-        {
-            return null;
-        }
-        return new HttpSessionWrapper(session, this.context);
-    }
-
-    @Override
-    public boolean isUserInRole(String role)
-    {
-        Authorization authorization = (Authorization) getAttribute(HttpContext.AUTHORIZATION);
-        if (authorization != null)
-        {
-            return authorization.hasRole(role);
-        }
-
-        return super.isUserInRole(role);
-    }
-
-    @Override
-    public String toString()
-    {
-        return getClass().getSimpleName() + "->" + super.getRequest();
-    }
-
-    private String calculatePathInfo()
-    {
-        /*
-         * The pathInfo from the servlet container is
-         *       servletAlias + pathInfo
-         * where pathInfo is either an empty string (in which case the
-         * client directly requested the servlet) or starts with a slash
-         * (in which case the client requested a child of the servlet).
-         *
-         * Note, the servlet container pathInfo may also be null if the
-         * servlet is registered as the root servlet
-         */
-        String pathInfo = super.getPathInfo();
-        if (pathInfo != null)
-        {
-            // cut off alias of this servlet (if not the root servlet)
-            if (!"/".equals(this.alias) && pathInfo.startsWith(this.alias))
-            {
-                pathInfo = pathInfo.substring(alias.length());
-            }
-
-            // ensure empty string is coerced to null
-            if (pathInfo.length() == 0)
-            {
-                pathInfo = null;
-            }
-        }
-
-        return pathInfo;
-    }
-}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java
index ee74584..1c6c356 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java
@@ -119,6 +119,19 @@
             {
                 return 0;
             }
+            // service id might be negative, we have to change the behaviour in that case
+            if ( this.serviceId < 0 )
+            {
+                if ( other.serviceId > 0 )
+                {
+                    return -1;
+                }
+                return other.serviceId < this.serviceId ? -1 : 1;
+            }
+            if ( other.serviceId < 0 )
+            {
+                return -1;
+            }
             return other.serviceId > this.serviceId ? -1 : 1;
         }
 
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ContextInfo.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ContextInfo.java
index 5e046eb..450b11f 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ContextInfo.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ContextInfo.java
@@ -31,10 +31,21 @@
 
     private final String path;
 
+    private final String prefix;
+
     public ContextInfo(final ServiceReference<ServletContextHelper> ref) {
         super(ref);
         this.name = this.getStringProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME);
         this.path = this.getStringProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH);
+        String prefix = null;
+        if ( !isEmpty(this.path) )
+        {
+            if ( !this.path.equals("/") && this.path.length() > 1 )
+            {
+                prefix = this.path.substring(0, this.path.length() - 1);
+            }
+        }
+        this.prefix = prefix;
     }
 
     @Override
@@ -53,4 +64,18 @@
     {
         return this.path;
     }
+
+    public String getPrefix()
+    {
+        return this.prefix;
+    }
+
+    public String getFullPath(final String path)
+    {
+        if ( this.prefix == null )
+        {
+            return path;
+        }
+        return this.prefix.concat(path);
+    }
 }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/FilterInfo.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/FilterInfo.java
index 41bef17..7d67a1a 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/FilterInfo.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/FilterInfo.java
@@ -24,10 +24,10 @@
 import javax.servlet.Filter;
 
 import org.osgi.dto.DTO;
+import org.osgi.framework.ServiceReference;
 import org.osgi.service.http.HttpContext;
 import org.osgi.service.http.runtime.dto.FilterDTO;
-
-import aQute.bnd.annotation.ConsumerType;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
 
 /**
  * Provides registration information for a {@link Filter}, and is used to programmatically register {@link Filter}s.
@@ -37,13 +37,18 @@
  *
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
-@ConsumerType
-public final class FilterInfo
+public final class FilterInfo extends AbstractInfo<Filter>
 {
     /**
-     * The name of the servlet.
+     * Properties starting with this prefix are passed as filter init parameters to the
+     * {@code init()} method of the filter.
      */
-    public String name;
+    private static final String FILTER_INIT_PREFIX = "filter.init.";
+
+    /**
+     * The name of the filter.
+     */
+    private final String name;
 
     /**
      * The request mappings for the servlet.
@@ -52,7 +57,7 @@
      * Note that these patterns should conform to the Servlet specification.
      * </p>
      */
-    public String[] patterns;
+    private final String[] patterns;
 
     /**
      * The servlet names for the servlet filter.
@@ -60,7 +65,7 @@
      * The specified names are used to determine the servlets whose requests are mapped to the servlet filter.
      * </p>
      */
-    public String[] servletNames;
+    private final String[] servletNames;
 
     /**
      * The request mappings for the servlet filter.
@@ -69,18 +74,12 @@
      * These regular expressions are a convenience extension allowing one to specify filters that match paths that are difficult to match with plain Servlet patterns alone.
      * </p>
      */
-    public String[] regexs;
+    private final String[] regexs;
 
     /**
      * Specifies whether the servlet filter supports asynchronous processing.
      */
-    public boolean asyncSupported = false;
-
-    /**
-     * Specifies the ranking order in which this filter should be called. Higher rankings are called first.
-     */
-    public int ranking = 0;
-    public long serviceId;
+    private final boolean asyncSupported;
 
     /**
      * The dispatcher associations for the servlet filter.
@@ -89,16 +88,108 @@
      * See {@link DispatcherType} and Servlet 3.0 specification, section 6.2.5.
      * </p>
      */
-    public DispatcherType[] dispatcher = { DispatcherType.REQUEST };
+    private final DispatcherType[] dispatcher;
 
     /**
      * The filter initialization parameters as provided during registration of the filter.
      */
-    public Map<String, String> initParams;
+    private final Map<String, String> initParams;
 
     /**
-     * The {@link HttpContext} for the servlet.
+     * The {@link HttpContext} for the filter.
      */
-    public HttpContext context;
+    private final HttpContext context;
 
+    private final Filter filter;
+
+    public FilterInfo(final ServiceReference<Filter> ref)
+    {
+        super(ref);
+        this.name = getStringProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_NAME);
+        this.asyncSupported = getBooleanProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_ASYNC_SUPPORTED);
+        this.servletNames = getStringArrayProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_SERVLET);
+        this.patterns = getStringArrayProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN);
+        this.regexs = getStringArrayProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_REGEX);
+        this.initParams = getInitParams(ref, FILTER_INIT_PREFIX);
+        String[] dispatcherNames = getStringArrayProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_DISPATCHER);
+        if (dispatcherNames != null && dispatcherNames.length > 0)
+        {
+            DispatcherType[] dispatchers = new DispatcherType[dispatcherNames.length];
+            for (int i = 0; i < dispatchers.length; i++)
+            {
+                dispatchers[i] = DispatcherType.valueOf(dispatcherNames[i].toUpperCase());
+            }
+            this.dispatcher = dispatchers;
+        }
+        else
+        {
+            this.dispatcher = new DispatcherType[] {DispatcherType.REQUEST};
+        }
+        this.context = null;
+        this.filter = null;
+    }
+
+    /**
+     * Constructor for Http Service
+     */
+    public FilterInfo(final String name,
+            final String regex,
+            final int serviceRanking,
+            final Map<String, String> initParams,
+            final Filter filter,
+            final HttpContext context)
+    {
+        super(serviceRanking);
+        this.name = name;
+        this.patterns = null;
+        this.servletNames = null;
+        this.regexs = new String[] {regex};
+        this.initParams = initParams;
+        this.asyncSupported = false;
+        this.dispatcher = new DispatcherType[] {DispatcherType.REQUEST};
+        this.filter = filter;
+        this.context = context;
+    }
+
+    @Override
+    public boolean isValid()
+    {
+        return super.isValid() && (!isEmpty(this.patterns) || !isEmpty(this.regexs) || !isEmpty(this.servletNames));
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String[] getPatterns() {
+        return patterns;
+    }
+
+    public String[] getServletNames() {
+        return servletNames;
+    }
+
+    public String[] getRegexs() {
+        return regexs;
+    }
+
+    public boolean isAsyncSupported() {
+        return asyncSupported;
+    }
+
+    public DispatcherType[] getDispatcher() {
+        return dispatcher;
+    }
+
+    public Map<String, String> getInitParams() {
+        return initParams;
+    }
+
+    public HttpContext getContext() {
+        return context;
+    }
+
+    public Filter getFilter() {
+        return filter;
+    }
 }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java b/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java
index d19b424..9b34453 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java
@@ -21,8 +21,6 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicLong;
 
 import javax.servlet.Filter;
 import javax.servlet.Servlet;
@@ -37,6 +35,7 @@
 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.logger.SystemLogger;
+import org.apache.felix.http.base.internal.runtime.ContextInfo;
 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.HttpContextBridge;
@@ -53,8 +52,6 @@
     private final HashSet<Filter> localFilters;
     private final ServletContextManager contextManager;
 
-    private final AtomicLong serviceIdCounter = new AtomicLong(-1);
-
     public HttpServiceImpl(Bundle bundle, ServletContext context, HandlerRegistry handlerRegistry, ServletContextAttributeListener servletAttributeListener, boolean sharedContextAttributes)
     {
         this.bundle = bundle;
@@ -97,36 +94,29 @@
     }
 
     /**
-     * TODO As the filter can be registered with multiple patterns
-     *      we shouldn't pass the filter object around in order to
-     *      be able to get different instances (prototype scope).
-     *      Or we do the splitting into single pattern registrations
-     *      already before calling registerFilter()
-     * @param servlet
-     * @param servletInfo
-     * @throws ServletException
-     * @throws NamespaceException
+     * Register a filter
      */
-    public void registerFilter(final Filter filter, final FilterInfo filterInfo)
+    public void registerFilter(final ServletContextHelper context,
+            final ContextInfo contextInfo,
+            final FilterInfo filterInfo)
     {
-        if (filter == null)
+        final ExtServletContext httpContext;
+        if ( context != null )
         {
-            throw new IllegalArgumentException("Filter cannot be null!");
+            httpContext = getServletContext(new HttpContextBridge(context));
         }
-        if (filterInfo == null)
+        else
         {
-            throw new IllegalArgumentException("FilterInfo cannot be null!");
+            httpContext = getServletContext(filterInfo.getContext());
         }
-        if (isEmpty(filterInfo.patterns) && isEmpty(filterInfo.regexs) && isEmpty(filterInfo.servletNames))
+        Filter filter = filterInfo.getFilter();
+        if ( filter == null )
         {
-            throw new IllegalArgumentException("FilterInfo must have at least one pattern or regex, or provide at least one servlet name!");
-        }
-        if (isEmpty(filterInfo.name))
-        {
-            filterInfo.name = filter.getClass().getName();
+            filter = this.bundle.getBundleContext().getServiceObjects(filterInfo.getServiceReference()).getService();
+            // TODO create failure DTO if null
         }
 
-        FilterHandler handler = new FilterHandler(getServletContext(filterInfo.context), filter, filterInfo);
+        FilterHandler handler = new FilterHandler(httpContext, filter, filterInfo);
         try {
             this.handlerRegistry.addFilter(handler);
         } catch (ServletException e) {
@@ -145,10 +135,10 @@
         {
             throw new IllegalArgumentException("Filter must not be null");
         }
-        final FilterInfo info = new FilterInfo();
+
+        final Map<String, String> paramMap = new HashMap<String, String>();
         if ( initParams != null && initParams.size() > 0 )
         {
-            info.initParams = new HashMap<String, String>();
             Enumeration e = initParams.keys();
             while (e.hasMoreElements())
             {
@@ -157,16 +147,17 @@
 
                 if ((key instanceof String) && (value instanceof String))
                 {
-                    info.initParams.put((String) key, (String) value);
+                    paramMap.put((String) key, (String) value);
                 }
             }
         }
-        info.patterns = new String[] {pattern};
-        info.context = context;
-        info.ranking = ranking;
-        info.serviceId = serviceIdCounter.getAndDecrement();
 
-        this.registerFilter(filter, info);
+        final FilterInfo info = new FilterInfo(null, pattern, ranking, paramMap, filter, context);
+        if ( !info.isValid() )
+        {
+            throw new ServletException("Invalid registration information for filter.");
+        }
+        this.registerFilter(null, null, info);
     }
 
     @Override
@@ -189,9 +180,11 @@
     }
 
     /**
+     * Register a servlet with a {@link ServletContextHelper}.
+     * The prefix is the path where the servlet context helper is mounted.
      */
     public void registerServlet(final ServletContextHelper context,
-    		final String prefix,
+    		final ContextInfo contextInfo,
     		final ServletInfo servletInfo)
     {
         if (servletInfo == null)
@@ -212,55 +205,52 @@
         {
         	httpContext = getServletContext(servletInfo.getContext());
         }
-        for(final String alias : servletInfo.getPatterns())
+        Servlet servlet = servletInfo.getServlet();
+        if ( servlet == null )
         {
-            // create a handler for each alias
-            Servlet servlet = servletInfo.getServlet();
-            if ( servlet == null )
-            {
-                servlet = this.bundle.getBundleContext().getServiceObjects(servletInfo.getServiceReference()).getService();
-                // TODO - handle null
-            }
-            final String pattern = (prefix == null ? alias : prefix + alias);
-            final ServletHandler handler = new ServletHandler(httpContext,
-                    servletInfo,
-                    servlet,
-                    pattern);
-            try {
-                this.handlerRegistry.addServlet(handler);
-            } catch (ServletException e) {
-                // TODO create failure DTO
-            } catch (NamespaceException e) {
-                // TODO create failure DTO
-            }
-            this.localServlets.add(servlet);
+            servlet = this.bundle.getBundleContext().getServiceObjects(servletInfo.getServiceReference()).getService();
+            // TODO create failure DTO if null
         }
+
+        final ServletHandler handler = new ServletHandler(contextInfo,
+                httpContext,
+                servletInfo,
+                servlet);
+        try {
+            this.handlerRegistry.addServlet(contextInfo, handler);
+        } catch (ServletException e) {
+            // TODO create failure DTO
+        } catch (NamespaceException e) {
+            // TODO create failure DTO
+        }
+
+        this.localServlets.add(servlet);
     }
 
-    public void unregisterServlet(final ServletInfo servletInfo)
+    public void unregisterServlet(final ContextInfo contextInfo, final ServletInfo servletInfo)
     {
         if (servletInfo == null)
         {
             throw new IllegalArgumentException("ServletInfo cannot be null!");
         }
-        if ( servletInfo.getPatterns() != null )
+        final Servlet servlet = this.handlerRegistry.removeServlet(contextInfo, servletInfo, true);
+        if ( servlet != null )
         {
-            final Set<Servlet> instances = this.handlerRegistry.removeServlet(servletInfo, true);
-            for(final Servlet servlet : instances)
-            {
-                this.localServlets.remove(servlet);
-            }
+            this.localServlets.remove(servlet);
         }
     }
 
-    public void unregisterFilter(final Filter filter, final FilterInfo filterInfo)
+    public void unregisterFilter(final ContextInfo contextInfo, final FilterInfo filterInfo)
     {
         if (filterInfo == null)
         {
             throw new IllegalArgumentException("FilterInfo cannot be null!");
         }
-        this.handlerRegistry.removeFilter(filter, true);
-        this.localFilters.remove(filter);
+        final Filter instance = this.handlerRegistry.removeFilter(filterInfo, true);
+        if ( instance != null )
+        {
+            this.localFilters.remove(instance);
+        }
     }
 
     /**
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/util/UriUtils.java b/http/base/src/main/java/org/apache/felix/http/base/internal/util/UriUtils.java
index 759ccd2..f8e4a49 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/util/UriUtils.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/util/UriUtils.java
@@ -37,6 +37,66 @@
     private static final char DOT = '.';
     private static final char SLASH = '/';
 
+    public static String compactPath(String path)
+    {
+        if (path == null)
+        {
+            return null;
+        }
+
+        StringBuilder sb = new StringBuilder();
+
+        int len = path.length();
+        boolean inQuery = false;
+        for (int i = 0; i < len; i++)
+        {
+            char ch = path.charAt(i);
+            if (!inQuery && ch == SLASH && la(path, i + 1) == SLASH)
+            {
+                continue;
+            }
+            if (ch == '?')
+            {
+                inQuery = true;
+            }
+            sb.append(ch);
+        }
+
+        return sb.toString();
+    }
+
+    public static String relativePath(String base, String path)
+    {
+        if (path == null)
+        {
+            return null;
+        }
+        if (base == null)
+        {
+            return path;
+        }
+
+        int len = base.length();
+        if (base.endsWith("/"))
+        {
+            len--;
+        }
+        else if (!base.equals(path))
+        {
+            base = base.concat("/");
+        }
+
+        if (path.startsWith(base))
+        {
+            path = path.substring(len);
+        }
+        if ("".equals(path) || "/".equals(path))
+        {
+            return null;
+        }
+        return path;
+    }
+
     /**
      * Concatenates two paths keeping their respective path-parts into consideration.
      * 
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/DefaultHttpContext.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/DefaultHttpContext.java
deleted file mode 100644
index 6853a16..0000000
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/DefaultHttpContext.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.whiteboard;
-
-import org.osgi.service.http.HttpContext;
-import org.osgi.framework.Bundle;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.net.URL;
-
-public final class DefaultHttpContext
-    implements HttpContext
-{
-    private Bundle bundle;
-
-    public DefaultHttpContext(Bundle bundle)
-    {
-        this.bundle = bundle;
-    }
-
-    public String getMimeType(String name)
-    {
-        return null;
-    }
-
-    public URL getResource(String name)
-    {
-        if (name.startsWith("/")) {
-            name = name.substring(1);
-        }
-
-        return this.bundle.getResource(name);
-    }
-
-    public boolean handleSecurity(HttpServletRequest req, HttpServletResponse res)
-    {
-        return true;
-    }
-
-    @Override
-    public String toString()
-    {
-        return getClass().getSimpleName() + " (" + Integer.toHexString(System.identityHashCode(this)) + ")";
-    }
-}
\ No newline at end of file
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ExtenderManager.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ExtenderManager.java
index 308685c..b87050e 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ExtenderManager.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ExtenderManager.java
@@ -21,11 +21,7 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import javax.servlet.DispatcherType;
-import javax.servlet.Filter;
-
 import org.apache.felix.http.base.internal.logger.SystemLogger;
-import org.apache.felix.http.base.internal.runtime.FilterInfo;
 import org.apache.felix.http.base.internal.service.HttpServiceImpl;
 import org.apache.felix.http.base.internal.whiteboard.tracker.FilterTracker;
 import org.apache.felix.http.base.internal.whiteboard.tracker.ServletContextHelperTracker;
@@ -42,18 +38,6 @@
 {
     static final String TYPE_RESOURCE = "r";
 
-    /**
-     * Properties starting with this prefix are passed as servlet init parameters to the
-     * {@code init()} method of the servlet.
-     */
-    public static final String SERVLET_INIT_PREFIX = "servlet.init.";
-
-    /**
-     * Properties starting with this prefix are passed as filter
-     * init parameters to the {@code init()} method of the filter.
-     */
-    public static final String FILTER_INIT_PREFIX = "filter.init.";
-
     private final Map<String, AbstractMapping> mapping;
 
     private final ServletContextHelperManager contextManager;
@@ -67,7 +51,7 @@
         this.mapping = new HashMap<String, AbstractMapping>();
         this.contextManager = new ServletContextHelperManager(bundleContext, httpService);
         this.httpService = httpService;
-        addTracker(new FilterTracker(bundleContext, this));
+        addTracker(new FilterTracker(bundleContext, contextManager));
         addTracker(new ServletTracker(bundleContext, this.contextManager));
         addTracker(new ServletContextHelperTracker(bundleContext, this.contextManager));
     }
@@ -134,64 +118,6 @@
         return null;
     }
 
-    private boolean getBooleanProperty(ServiceReference ref, String key)
-    {
-        Object value = ref.getProperty(key);
-        if (value instanceof String)
-        {
-            return Boolean.valueOf((String) value);
-        }
-        else if (value instanceof Boolean)
-        {
-            return ((Boolean) value).booleanValue();
-        }
-        return false;
-    }
-
-    private int getIntProperty(ServiceReference ref, String key, int defValue)
-    {
-        Object value = ref.getProperty(key);
-        if (value == null)
-        {
-            return defValue;
-        }
-
-        try
-        {
-            return Integer.parseInt(value.toString());
-        }
-        catch (Exception e)
-        {
-            return defValue;
-        }
-    }
-
-    /**
-     * Get the init parameters.
-     */
-    private Map<String, String> getInitParams(final ServiceReference<?> ref, final String prefix)
-    {
-        Map<String, String> result = null;
-        for (final String key : ref.getPropertyKeys())
-        {
-            if ( key.startsWith(prefix))
-            {
-                final String paramKey = key.substring(prefix.length());
-                final String paramValue = getStringProperty(ref, key);
-
-                if (paramValue != null)
-                {
-                    if ( result == null )
-                    {
-                        result = new HashMap<String, String>();
-                    }
-                    result.put(paramKey, paramValue);
-                }
-            }
-        }
-        return result;
-    }
-
     public void addResource(final ServiceReference ref)
     {
         final String[] pattern = getStringArrayProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PATTERN);
@@ -227,58 +153,6 @@
         this.removeMapping(TYPE_RESOURCE, ref);
     }
 
-    public void add(final Filter service, final ServiceReference ref)
-    {
-        final FilterInfo filterInfo = createFilterInfo(ref, true);
-        if ( filterInfo != null )
-        {
-            ((HttpServiceImpl)this.httpService).registerFilter(service, filterInfo);
-        }
-    }
-
-    private FilterInfo createFilterInfo(final ServiceReference<?> filterRef, final boolean log)
-    {
-        final FilterInfo filterInfo = new FilterInfo();
-        filterInfo.name = getStringProperty(filterRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_NAME);
-        filterInfo.asyncSupported = getBooleanProperty(filterRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_ASYNC_SUPPORTED);
-        filterInfo.servletNames = getStringArrayProperty(filterRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_SERVLET);
-        filterInfo.patterns = getStringArrayProperty(filterRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN);
-        filterInfo.ranking = getIntProperty(filterRef, Constants.SERVICE_RANKING, 0);
-        filterInfo.serviceId = (Long)filterRef.getProperty(Constants.SERVICE_ID);
-        filterInfo.initParams = getInitParams(filterRef, FILTER_INIT_PREFIX);
-        String[] dispatcherNames = getStringArrayProperty(filterRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_DISPATCHER);
-        if (dispatcherNames != null && dispatcherNames.length > 0)
-        {
-            DispatcherType[] dispatchers = new DispatcherType[dispatcherNames.length];
-            for (int i = 0; i < dispatchers.length; i++)
-            {
-                dispatchers[i] = DispatcherType.valueOf(dispatcherNames[i].toUpperCase());
-            }
-            filterInfo.dispatcher = dispatchers;
-        }
-
-        if (isEmpty(filterInfo.patterns))
-        {
-            if ( log )
-            {
-                SystemLogger.debug("Ignoring Filter Service " + filterRef + ", " + HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN +
-                        " is missing or empty");
-            }
-            return null;
-        }
-
-        return filterInfo;
-    }
-
-    public void removeFilter(final Filter service, ServiceReference<Filter> ref)
-    {
-        final FilterInfo filterInfo = createFilterInfo(ref, false);
-        if ( filterInfo != null )
-        {
-            ((HttpServiceImpl)this.httpService).unregisterFilter(service, filterInfo);
-        }
-    }
-
     private synchronized void unregisterAll()
     {
         AbstractMapping[] mappings = null;
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletContextHelperManager.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletContextHelperManager.java
index 93d9498..28edf08 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletContextHelperManager.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletContextHelperManager.java
@@ -20,16 +20,14 @@
 import java.util.Collections;
 import java.util.Dictionary;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Hashtable;
-import java.util.IdentityHashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import org.apache.felix.http.base.internal.runtime.AbstractInfo;
 import org.apache.felix.http.base.internal.runtime.ContextInfo;
+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.service.HttpServiceImpl;
 import org.osgi.framework.Bundle;
@@ -44,13 +42,13 @@
     /** A map containing all servlet context registrations. Mapped by context name */
     private final Map<String, List<ContextHolder>> contextMap = new HashMap<String, List<ContextHolder>>();
 
-    /** A map with all servlet registrations, mapped by servlet info. */
-    private final Map<ServletInfo, List<ContextHolder>> servletList = new HashMap<ServletInfo, List<ContextHolder>>();
+    /** A map with all servlet/filter registrations, mapped by abstract info. */
+    private final Map<AbstractInfo<?>, List<ContextHolder>> servicesMap = new HashMap<AbstractInfo<?>, List<ContextHolder>>();
 
     private final HttpServiceImpl httpService;
 
     private final ServiceRegistration<ServletContextHelper> defaultContextRegistration;
-    
+
     /**
      * Create a new servlet context helper manager
      * and the default context
@@ -94,25 +92,39 @@
 
     private void activate(final ContextHolder holder)
     {
-        for(final Map.Entry<ServletInfo, List<ContextHolder>> entry : this.servletList.entrySet())
+        for(final Map.Entry<AbstractInfo<?>, List<ContextHolder>> entry : this.servicesMap.entrySet())
         {
             if ( entry.getKey().getContextSelectionFilter().match(holder.getInfo().getServiceReference()) )
             {
                 entry.getValue().add(holder);
-            	this.registerServlet(entry.getKey(), holder);
+                if ( entry.getKey() instanceof ServletInfo )
+                {
+                    this.registerServlet((ServletInfo)entry.getKey(), holder);
+                }
+                else if ( entry.getKey() instanceof FilterInfo )
+                {
+                    this.registerFilter((FilterInfo)entry.getKey(), holder);
+                }
             }
         }
     }
 
     private void deactivate(final ContextHolder holder)
     {
-        final Iterator<Map.Entry<ServletInfo, List<ContextHolder>>> i = this.servletList.entrySet().iterator();
+        final Iterator<Map.Entry<AbstractInfo<?>, List<ContextHolder>>> i = this.servicesMap.entrySet().iterator();
         while ( i.hasNext() )
         {
-            final Map.Entry<ServletInfo, List<ContextHolder>> entry = i.next();
+            final Map.Entry<AbstractInfo<?>, List<ContextHolder>> entry = i.next();
             if ( entry.getValue().remove(holder) )
             {
-                this.unregisterServlet(entry.getKey(), holder);
+                if ( entry.getKey() instanceof ServletInfo )
+                {
+                    this.unregisterServlet((ServletInfo)entry.getKey(), holder);
+                }
+                else if ( entry.getKey() instanceof FilterInfo )
+                {
+                    this.unregisterFilter((FilterInfo)entry.getKey(), holder);
+                }
                 if ( entry.getValue().isEmpty() ) {
                     i.remove();
                 }
@@ -206,13 +218,23 @@
     private void registerServlet(final ServletInfo servletInfo, final ContextHolder holder)
     {
     	final ServletContextHelper helper = holder.getContext(servletInfo.getServiceReference().getBundle());
-    	String prefix = holder.getPrefix();
-        this.httpService.registerServlet(helper, prefix, servletInfo);    	
+        this.httpService.registerServlet(helper, holder.getInfo(), servletInfo);
     }
 
     private void unregisterServlet(final ServletInfo servletInfo, final ContextHolder holder)
     {
-        this.httpService.unregisterServlet(servletInfo);    	
+        this.httpService.unregisterServlet(holder.getInfo(), servletInfo);
+    }
+
+    private void registerFilter(final FilterInfo filterInfo, final ContextHolder holder)
+    {
+        final ServletContextHelper helper = holder.getContext(filterInfo.getServiceReference().getBundle());
+        this.httpService.registerFilter(helper, holder.getInfo(), filterInfo);
+    }
+
+    private void unregisterFilter(final FilterInfo filterInfo, final ContextHolder holder)
+    {
+        this.httpService.unregisterFilter(holder.getInfo(), filterInfo);
     }
 
     /**
@@ -224,7 +246,7 @@
         synchronized ( this.contextMap )
         {
             final List<ContextHolder> holderList = this.getMatchingContexts(servletInfo);
-            this.servletList.put(servletInfo, holderList);
+            this.servicesMap.put(servletInfo, holderList);
             for(final ContextHolder h : holderList)
             {
             	this.registerServlet(servletInfo, h);
@@ -240,7 +262,7 @@
     {
         synchronized ( this.contextMap )
         {
-            final List<ContextHolder> holderList = this.servletList.remove(servletInfo);
+            final List<ContextHolder> holderList = this.servicesMap.remove(servletInfo);
             if ( holderList != null )
             {
                 for(final ContextHolder h : holderList)
@@ -258,19 +280,9 @@
     {
         private final ContextInfo info;
 
-        private final String prefix;
-        
         public ContextHolder(final ContextInfo info)
         {
             this.info = info;
-            if ( info.getPath().equals("/") )
-            {
-            	prefix = null;
-            }
-            else
-            {
-            	prefix = info.getPath().substring(0, info.getPath().length() - 1);
-            }
         }
 
         public ContextInfo getInfo()
@@ -278,22 +290,42 @@
             return this.info;
         }
 
-        public String getPrefix() 
-        {
-        	return this.prefix;
-        }
-        
         @Override
         public int compareTo(final ContextHolder o)
         {
             return this.info.compareTo(o.info);
         }
-        
-        public ServletContextHelper getContext(final Bundle b) 
+
+        public ServletContextHelper getContext(final Bundle b)
         {
         	// TODO - we should somehow keep track of these objects to later on dispose them
         	return b.getBundleContext().getServiceObjects(this.info.getServiceReference()).getService();
         }
     }
 
+    public void addFilter(final FilterInfo info) {
+        synchronized ( this.contextMap )
+        {
+            final List<ContextHolder> holderList = this.getMatchingContexts(info);
+            this.servicesMap.put(info, holderList);
+            for(final ContextHolder h : holderList)
+            {
+                this.registerFilter(info, h);
+            }
+        }
+    }
+
+    public void removeFilter(final FilterInfo info) {
+        synchronized ( this.contextMap )
+        {
+            final List<ContextHolder> holderList = this.servicesMap.remove(info);
+            if ( holderList != null )
+            {
+                for(final ContextHolder h : holderList)
+                {
+                    this.unregisterFilter(info, h);
+                }
+            }
+        }
+    }
 }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/FilterTracker.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/FilterTracker.java
index 5cec516..e2a7c78 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/FilterTracker.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/FilterTracker.java
@@ -18,15 +18,17 @@
 
 import javax.servlet.Filter;
 
-import org.apache.felix.http.base.internal.whiteboard.ExtenderManager;
+import org.apache.felix.http.base.internal.logger.SystemLogger;
+import org.apache.felix.http.base.internal.runtime.FilterInfo;
+import org.apache.felix.http.base.internal.whiteboard.ServletContextHelperManager;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
 import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
 
-public final class FilterTracker extends AbstractTracker<Filter>
+public final class FilterTracker extends AbstractReferenceTracker<Filter>
 {
-    private final ExtenderManager manager;
+    private final ServletContextHelperManager contextManager;
 
     private static org.osgi.framework.Filter createFilter(final BundleContext btx)
     {
@@ -45,28 +47,35 @@
         return null; // we never get here - and if we get an NPE which is fine
     }
 
-    public FilterTracker(BundleContext context, ExtenderManager manager)
+    public FilterTracker(final BundleContext context, final ServletContextHelperManager manager)
     {
         super(context, createFilter(context));
-        this.manager = manager;
+        this.contextManager = manager;
     }
 
     @Override
-    protected void added(Filter service, ServiceReference ref)
+    protected void added(final ServiceReference<Filter> ref)
     {
-        this.manager.add(service, ref);
+        final FilterInfo info = new FilterInfo(ref);
+
+        if ( info.isValid() )
+        {
+            this.contextManager.addFilter(info);
+        }
+        else
+        {
+            SystemLogger.debug("Ignoring Filter service " + ref);
+        }
     }
 
     @Override
-    protected void modified(Filter service, ServiceReference ref)
+    protected void removed(final ServiceReference<Filter> ref)
     {
-        removed(service, ref);
-        added(service, ref);
-    }
+        final FilterInfo info = new FilterInfo(ref);
 
-    @Override
-    protected void removed(Filter service, ServiceReference ref)
-    {
-        this.manager.removeFilter(service, ref);
+        if ( info.isValid() )
+        {
+            this.contextManager.removeFilter(info);
+        }
     }
 }
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/AbstractHandlerTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/AbstractHandlerTest.java
index 30df83e..e2fc82c 100644
--- a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/AbstractHandlerTest.java
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/AbstractHandlerTest.java
@@ -16,8 +16,8 @@
  */
 package org.apache.felix.http.base.internal.handler;
 
-import java.util.Dictionary;
 import java.util.Hashtable;
+import java.util.Map;
 
 import org.apache.felix.http.base.internal.context.ExtServletContext;
 import org.junit.Assert;
@@ -30,6 +30,8 @@
 
     protected abstract AbstractHandler createHandler();
 
+    protected abstract AbstractHandler createHandler(Map<String, String> map);
+
     public void setUp()
     {
         this.context = Mockito.mock(ExtServletContext.class);
@@ -52,10 +54,10 @@
         AbstractHandler handler = createHandler();
         Assert.assertEquals(0, handler.getInitParams().size());
 
-        Dictionary<String, String> map = new Hashtable<String, String>();
+        Map<String, String> map = new Hashtable<String, String>();
         map.put("key1", "value1");
 
-        handler.setInitParams(map);
+        handler = createHandler(map);
         Assert.assertEquals(1, handler.getInitParams().size());
         Assert.assertEquals("value1", handler.getInitParams().get("key1"));
     }
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/FilterHandlerTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/FilterHandlerTest.java
index 600aac3..445a5c0 100644
--- a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/FilterHandlerTest.java
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/FilterHandlerTest.java
@@ -20,14 +20,15 @@
 import static javax.servlet.http.HttpServletResponse.SC_OK;
 import static javax.servlet.http.HttpServletResponse.SC_PAYMENT_REQUIRED;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.util.Map;
+
+import javax.servlet.DispatcherType;
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
@@ -53,17 +54,24 @@
     @Test
     public void testCompare()
     {
-        FilterHandler h1 = createHandler("a", 0);
-        FilterHandler h2 = createHandler("b", 10);
+        FilterHandler h1 = createHandler(0, "a");
+        FilterHandler h2 = createHandler(10, "b");
+        FilterHandler h3 = createHandler(10, "c");
+
+        assertEquals(0, h1.compareTo(h1));
 
         assertEquals(1, h1.compareTo(h2));
         assertEquals(-1, h2.compareTo(h1));
+
+        // h2 is actually registered first, so should be called first...
+        assertEquals(-1, h2.compareTo(h3));
+        assertEquals(1, h3.compareTo(h2));
     }
 
     @Test
     public void testDestroy()
     {
-        FilterHandler h1 = createHandler("/a", 0);
+        FilterHandler h1 = createHandler(0, "/a");
         h1.destroy();
         verify(this.filter).destroy();
     }
@@ -71,13 +79,13 @@
     @Test
     public void testHandleFound() throws Exception
     {
-        FilterHandler h1 = createHandler("/a", 0);
-        HttpServletRequest req = mock(HttpServletRequest.class);
-        HttpServletResponse res = mock(HttpServletResponse.class);
+        FilterHandler h1 = createHandler(0, "/a");
+        HttpServletRequest req = createServletRequest();
+        HttpServletResponse res = createServletResponse();
         FilterChain chain = mock(FilterChain.class);
         when(this.context.handleSecurity(req, res)).thenReturn(true);
 
-        when(req.getPathInfo()).thenReturn("/a");
+        when(req.getRequestURI()).thenReturn("/a");
         h1.handle(req, res, chain);
 
         verify(this.filter).doFilter(req, res, chain);
@@ -87,13 +95,13 @@
     @Test
     public void testHandleFoundContextRoot() throws Exception
     {
-        FilterHandler h1 = createHandler("/", 0);
-        HttpServletRequest req = mock(HttpServletRequest.class);
-        HttpServletResponse res = mock(HttpServletResponse.class);
+        FilterHandler h1 = createHandler(0, "/");
+        HttpServletRequest req = createServletRequest();
+        HttpServletResponse res = createServletResponse();
         FilterChain chain = mock(FilterChain.class);
         when(this.context.handleSecurity(req, res)).thenReturn(true);
 
-        when(req.getPathInfo()).thenReturn(null);
+        when(req.getRequestURI()).thenReturn(null);
         h1.handle(req, res, chain);
 
         verify(this.filter).doFilter(req, res, chain);
@@ -106,12 +114,12 @@
     @Test
     public void testHandleFoundForbidden() throws Exception
     {
-        FilterHandler h1 = createHandler("/a", 0);
-        HttpServletRequest req = mock(HttpServletRequest.class);
-        HttpServletResponse res = mock(HttpServletResponse.class);
+        FilterHandler h1 = createHandler(0, "/a");
+        HttpServletRequest req = createServletRequest();
+        HttpServletResponse res = createServletResponse();
         FilterChain chain = mock(FilterChain.class);
 
-        when(req.getPathInfo()).thenReturn("/a");
+        when(req.getRequestURI()).thenReturn("/a");
         // Default behaviour: uncomitted response and default status code...
         when(res.isCommitted()).thenReturn(false);
         when(res.getStatus()).thenReturn(SC_OK);
@@ -131,12 +139,12 @@
     @Test
     public void testHandleFoundForbiddenCommittedOwnResponse() throws Exception
     {
-        FilterHandler h1 = createHandler("/a", 0);
-        HttpServletRequest req = mock(HttpServletRequest.class);
-        HttpServletResponse res = mock(HttpServletResponse.class);
+        FilterHandler h1 = createHandler(0, "/a");
+        HttpServletRequest req = createServletRequest();
+        HttpServletResponse res = createServletResponse();
         FilterChain chain = mock(FilterChain.class);
 
-        when(req.getPathInfo()).thenReturn("/a");
+        when(req.getRequestURI()).thenReturn("/a");
         // Simulate an already committed response...
         when(res.isCommitted()).thenReturn(true);
         when(res.getStatus()).thenReturn(SC_OK);
@@ -157,12 +165,12 @@
     @Test
     public void testHandleFoundForbiddenCustomStatusCode() throws Exception
     {
-        FilterHandler h1 = createHandler("/a", 0);
-        HttpServletRequest req = mock(HttpServletRequest.class);
-        HttpServletResponse res = mock(HttpServletResponse.class);
+        FilterHandler h1 = createHandler(0, "/a");
+        HttpServletRequest req = createServletRequest();
+        HttpServletResponse res = createServletResponse();
         FilterChain chain = mock(FilterChain.class);
 
-        when(req.getPathInfo()).thenReturn("/a");
+        when(req.getRequestURI()).thenReturn("/a");
         // Simulate an uncommitted response with a non-default status code...
         when(res.isCommitted()).thenReturn(false);
         when(res.getStatus()).thenReturn(SC_PAYMENT_REQUIRED);
@@ -180,76 +188,78 @@
     @Test
     public void testHandleNotFound() throws Exception
     {
-        FilterHandler h1 = createHandler("/a", 0);
-        HttpServletRequest req = mock(HttpServletRequest.class);
-        HttpServletResponse res = mock(HttpServletResponse.class);
+        FilterHandler h1 = createHandler(0, "/a");
+        HttpServletRequest req = createServletRequest();
+        HttpServletResponse res = createServletResponse();
         FilterChain chain = mock(FilterChain.class);
 
-        when(req.getPathInfo()).thenReturn("/");
+        when(req.getRequestURI()).thenReturn("/");
         h1.handle(req, res, chain);
 
         verify(this.filter, never()).doFilter(req, res, chain);
-        verify(chain).doFilter(req, res);
+        verify(chain, never()).doFilter(req, res);
     }
 
     @Test
     public void testHandleNotFoundContextRoot() throws Exception
     {
-        FilterHandler h1 = createHandler("/a", 0);
-        HttpServletRequest req = mock(HttpServletRequest.class);
-        HttpServletResponse res = mock(HttpServletResponse.class);
+        FilterHandler h1 = createHandler(0, "/a");
+        HttpServletRequest req = createServletRequest();
+        HttpServletResponse res = createServletResponse();
         FilterChain chain = mock(FilterChain.class);
 
-        when(req.getPathInfo()).thenReturn(null);
+        when(req.getRequestURI()).thenReturn(null);
         h1.handle(req, res, chain);
 
         verify(this.filter, never()).doFilter(req, res, chain);
-        verify(chain).doFilter(req, res);
+        verify(chain, never()).doFilter(req, res);
     }
 
     @Test
     public void testInit() throws Exception
     {
-        FilterHandler h1 = createHandler("/a", 0);
+        FilterHandler h1 = createHandler(0, "/a");
         h1.init();
         verify(this.filter).init(any(FilterConfig.class));
     }
 
-    @Test
-    public void testMatches()
+   @Override
+    protected AbstractHandler createHandler()
     {
-        FilterHandler h1 = createHandler("/a/b", 0);
-        FilterHandler h2 = createHandler("/a/b/.+", 0);
-        FilterHandler h3 = createHandler("/", 0);
-        FilterHandler h4 = createHandler("/.*", 0);
-
-        assertFalse(h1.matches(null));
-        assertFalse(h1.matches("/a"));
-        assertTrue(h1.matches("/a/b"));
-        assertFalse(h1.matches("/a/b/c"));
-        assertFalse(h2.matches(null));
-        assertFalse(h1.matches("/a"));
-        assertTrue(h2.matches("/a/b/c"));
-        assertFalse(h2.matches("/a/b/"));
-        assertTrue(h3.matches(null));
-        assertTrue(h3.matches("/"));
-        assertFalse(h3.matches("/a/b/"));
-        assertTrue(h4.matches(null));
-        assertTrue(h4.matches("/"));
-        assertTrue(h4.matches("/a/b/"));
+        return createHandler(0, "dummy");
     }
 
     @Override
-    protected AbstractHandler createHandler()
+    protected AbstractHandler createHandler(final Map<String, String> initParams)
     {
-        return createHandler("dummy", 0);
+        return createHandler("dummy", 0, initParams);
     }
 
-    private FilterHandler createHandler(String pattern, int ranking)
+    private FilterHandler createHandler(int ranking, String pattern)
     {
-        final FilterInfo info = new FilterInfo();
-        info.regexs = new String[] {pattern};
-        info.ranking = ranking;
+        return createHandler(pattern, ranking, null);
+    }
+
+    private FilterHandler createHandler(String pattern, int ranking, final Map<String, String> initParams)
+    {
+        final FilterInfo info = new FilterInfo(null, pattern, ranking, initParams, this.filter, null);
         return new FilterHandler(this.context, this.filter, info);
     }
+
+    private HttpServletRequest createServletRequest()
+    {
+        return createServletRequest(DispatcherType.REQUEST);
+    }
+
+    private HttpServletRequest createServletRequest(DispatcherType type)
+    {
+        HttpServletRequest result = mock(HttpServletRequest.class);
+        when(result.getDispatcherType()).thenReturn(type);
+        return result;
+    }
+
+    private HttpServletResponse createServletResponse()
+    {
+        return mock(HttpServletResponse.class);
+    }
 }
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/HandlerRegistryTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/HandlerRegistryTest.java
index 93bb41f..52a999a 100644
--- a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/HandlerRegistryTest.java
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/HandlerRegistryTest.java
@@ -16,27 +16,10 @@
  */
 package org.apache.felix.http.base.internal.handler;
 
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.fail;
-
-import javax.servlet.Filter;
-import javax.servlet.FilterConfig;
-import javax.servlet.Servlet;
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletException;
-
-import org.apache.felix.http.base.internal.runtime.FilterInfo;
-import org.apache.felix.http.base.internal.runtime.ServletInfo;
-import org.junit.Test;
-import org.mockito.Mockito;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-import org.osgi.service.http.NamespaceException;
 
 public class HandlerRegistryTest
 {
+    /*
     @Test
     public void testAddRemoveServlet() throws Exception
     {
@@ -44,18 +27,18 @@
 
         Servlet servlet = Mockito.mock(Servlet.class);
         final ServletInfo info = new ServletInfo("foo", "/foo", 0, null, servlet, null);
-        ServletHandler handler = new ServletHandler(null, info, info.getServlet(), info.getPatterns()[0]);
+        ServletHandler handler = new ServletHandler(null, null, info, info.getServlet());
         assertEquals("Precondition", 0, hr.getServlets().length);
-        hr.addServlet(handler);
+        hr.addServlet(null, handler);
         Mockito.verify(servlet, Mockito.times(1)).init(Mockito.any(ServletConfig.class));
         assertEquals(1, hr.getServlets().length);
         assertSame(handler, hr.getServlets()[0]);
 
         final ServletInfo info2 = new ServletInfo("bar", "/bar", 0, null, servlet, null);
-        ServletHandler handler2 = new ServletHandler(null, info2, info2.getServlet(), info2.getPatterns()[0]);
+        ServletHandler handler2 = new ServletHandler(null, null, info2, info2.getServlet());
         try
         {
-            hr.addServlet(handler2);
+            hr.addServlet(null, handler2);
             // TODO
 //            fail("Should not have allowed to add the same servlet twice");
         }
@@ -66,11 +49,11 @@
         assertArrayEquals(new ServletHandler[] {handler2, handler}, hr.getServlets());
 
         final ServletInfo info3 = new ServletInfo("zar", "/foo", 0, null, Mockito.mock(Servlet.class), null);
-        ServletHandler handler3 = new ServletHandler(null,info3, info3.getServlet(), info3.getPatterns()[0]);
+        ServletHandler handler3 = new ServletHandler(null, null,info3, info3.getServlet());
 
         try
         {
-            hr.addServlet(handler3);
+            hr.addServlet(null, handler3);
             fail("Should not have allowed to add the same alias twice");
         }
         catch (NamespaceException ne) {
@@ -93,7 +76,7 @@
 
         Servlet servlet = Mockito.mock(Servlet.class);
         final ServletInfo info = new ServletInfo("bar", "/bar", 0, null, servlet, null);
-        final ServletHandler otherHandler = new ServletHandler(null, info, info.getServlet(), info.getPatterns()[0]);
+        final ServletHandler otherHandler = new ServletHandler(null, null, info, info.getServlet());
 
         Mockito.doAnswer(new Answer<Void>()
         {
@@ -106,17 +89,17 @@
                     registered = true;
                     // sneakily register another handler with this servlet before this
                     // one has finished calling init()
-                    hr.addServlet(otherHandler);
+                    hr.addServlet(null, otherHandler);
                 }
                 return null;
             }
         }).when(servlet).init(Mockito.any(ServletConfig.class));
 
         final ServletInfo info2 = new ServletInfo("foo", "/foo", 0, null, servlet, null);
-        ServletHandler handler = new ServletHandler(null, info2, info2.getServlet(), info2.getPatterns()[0]);
+        ServletHandler handler = new ServletHandler(null, null, info2, info2.getServlet());
         try
         {
-            hr.addServlet(handler);
+            hr.addServlet(null, handler);
 
             // TODO
 //            fail("Should not have allowed the servlet to be added as it was already "
@@ -137,7 +120,7 @@
 
         Servlet otherServlet = Mockito.mock(Servlet.class);
         final ServletInfo info = new ServletInfo("bar", "/foo", 0, null, otherServlet, null);
-        final ServletHandler otherHandler = new ServletHandler(null, info, info.getServlet(), info.getPatterns()[0]);
+        final ServletHandler otherHandler = new ServletHandler(null, null, info, info.getServlet());
 
         Servlet servlet = Mockito.mock(Servlet.class);
         Mockito.doAnswer(new Answer<Void>()
@@ -146,17 +129,17 @@
             public Void answer(InvocationOnMock invocation) throws Throwable
             {
                 // sneakily register another servlet before this one has finished calling init()
-                hr.addServlet(otherHandler);
+                hr.addServlet(null, otherHandler);
                 return null;
             }
         }).when(servlet).init(Mockito.any(ServletConfig.class));
 
         final ServletInfo info2 = new ServletInfo("foo", "/foo", 0, null, servlet, null);
-        ServletHandler handler = new ServletHandler(null, info2, info2.getServlet(), info2.getPatterns()[0]);
+        ServletHandler handler = new ServletHandler(null, null, info2, info2.getServlet());
 
         try
         {
-            hr.addServlet(handler);
+            hr.addServlet(null, handler);
             fail("Should not have allowed the servlet to be added as another one got in there with the same alias");
         }
         catch (NamespaceException ne)
@@ -175,10 +158,7 @@
         HandlerRegistry hr = new HandlerRegistry();
 
         Filter filter = Mockito.mock(Filter.class);
-        final FilterInfo info = new FilterInfo();
-        info.name = "oho";
-        info.patterns = new String[] {"/aha"};
-        info.ranking = 1;
+        final FilterInfo info = new FilterInfo("oho", "/aha", 1, null, filter, null);
 
         FilterHandler handler = new FilterHandler(null, filter, info);
         assertEquals("Precondition", 0, hr.getFilters().length);
@@ -187,10 +167,7 @@
         assertEquals(1, hr.getFilters().length);
         assertSame(handler, hr.getFilters()[0]);
 
-        final FilterInfo info2 = new FilterInfo();
-        info2.name = "haha";
-        info2.patterns = new String[] {"/hihi"};
-        info2.ranking = 2;
+        final FilterInfo info2 = new FilterInfo("haha", "/hihi", 2, null, filter, null);
         FilterHandler handler2 = new FilterHandler(null, filter, info2);
         try
         {
@@ -215,10 +192,7 @@
         final HandlerRegistry hr = new HandlerRegistry();
 
         Filter filter = Mockito.mock(Filter.class);
-        final FilterInfo info = new FilterInfo();
-        info.name = "two";
-        info.patterns = new String[] {"/two"};
-        info.ranking = 99;
+        final FilterInfo info = new FilterInfo("two", "/two", 99, null, filter, null);
         final FilterHandler otherHandler = new FilterHandler(null, filter, info);
 
         Mockito.doAnswer(new Answer<Void>()
@@ -238,10 +212,7 @@
             }
         }).when(filter).init(Mockito.any(FilterConfig.class));
 
-        final FilterInfo info2 = new FilterInfo();
-        info2.name = "one";
-        info2.patterns = new String[] {"/one"};
-        info2.ranking = 1;
+        final FilterInfo info2 = new FilterInfo("one", "/one", 1, null, filter, null);
         FilterHandler handler = new FilterHandler(null, filter, info2);
 
         try
@@ -264,17 +235,14 @@
 
         Servlet servlet = Mockito.mock(Servlet.class);
         final ServletInfo info = new ServletInfo("f", "/f", 0, null, servlet, null);
-        ServletHandler servletHandler = new ServletHandler(null, info, info.getServlet(), info.getPatterns()[0]);
-        hr.addServlet(servletHandler);
+        ServletHandler servletHandler = new ServletHandler(null, null, info, info.getServlet());
+        hr.addServlet(null, servletHandler);
         Servlet servlet2 = Mockito.mock(Servlet.class);
         final ServletInfo info2 = new ServletInfo("ff", "/ff", 0, null, servlet2, null);
-        ServletHandler servletHandler2 = new ServletHandler(null, info2, info2.getServlet(), info2.getPatterns()[0]);
-        hr.addServlet(servletHandler2);
+        ServletHandler servletHandler2 = new ServletHandler(null, null, info2, info2.getServlet());
+        hr.addServlet(null, servletHandler2);
         Filter filter = Mockito.mock(Filter.class);
-        final FilterInfo fi = new FilterInfo();
-        fi.name = "f";
-        fi.patterns = new String[] {"/f"};
-        fi.ranking = 0;
+        final FilterInfo fi = new FilterInfo("f", "/f", 0, null, filter, null);
         FilterHandler filterHandler = new FilterHandler(null, filter, fi);
         hr.addFilter(filterHandler);
 
@@ -296,4 +264,5 @@
         assertEquals(0, hr.getServlets().length);
         assertEquals(0, hr.getFilters().length);
     }
+    */
 }
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/ServletHandlerRequestTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/ServletHandlerRequestTest.java
deleted file mode 100644
index 877114d..0000000
--- a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/ServletHandlerRequestTest.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * 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 org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.apache.felix.http.base.internal.context.ServletContextImpl;
-import org.apache.felix.http.base.internal.dispatch.Dispatcher;
-import org.junit.Before;
-import org.junit.Test;
-import org.osgi.service.http.HttpContext;
-
-public class ServletHandlerRequestTest
-{
-    private HttpServletRequest superReq1;
-    private HttpServletRequest superReq2;
-    private HttpServletRequest superReq3;
-    private HttpServletRequest superReq4;
-
-    private HttpServletRequest req1;
-    private HttpServletRequest req2;
-    private HttpServletRequest req3;
-    private HttpServletRequest req4;
-
-    @Before
-    public void setUp()
-    {
-        ServletContextImpl context = mock(ServletContextImpl.class);
-
-        this.superReq1 = mock(HttpServletRequest.class);
-        when(this.superReq1.getContextPath()).thenReturn("/mycontext");
-        when(this.superReq1.getServletPath()).thenReturn("");
-        when(this.superReq1.getRequestURI()).thenReturn("/mycontext/request/to/resource");
-        when(this.superReq1.getPathInfo()).thenReturn("/request/to/resource");
-        when(this.superReq1.getAttribute(HttpContext.AUTHENTICATION_TYPE)).thenReturn(HttpServletRequest.BASIC_AUTH);
-        when(this.superReq1.getAttribute(HttpContext.REMOTE_USER)).thenReturn("felix");
-        this.req1 = new ServletHandlerRequest(this.superReq1, context, "/");
-
-        this.superReq2 = mock(HttpServletRequest.class);
-        when(this.superReq2.getContextPath()).thenReturn("/mycontext");
-        when(this.superReq2.getServletPath()).thenReturn("");
-        when(this.superReq2.getRequestURI()).thenReturn("/mycontext/myservlet/request/to/resource;jsession=123");
-        when(this.superReq2.getPathInfo()).thenReturn("/myservlet/request/to/resource");
-        when(this.superReq2.getAttribute(HttpContext.AUTHENTICATION_TYPE)).thenReturn(null);
-        when(this.superReq2.getAuthType()).thenReturn(HttpServletRequest.DIGEST_AUTH);
-        when(this.superReq2.getAttribute(HttpContext.REMOTE_USER)).thenReturn(null);
-        when(this.superReq2.getRemoteUser()).thenReturn("sling");
-        this.req2 = new ServletHandlerRequest(this.superReq2, context, "/myservlet");
-
-        this.superReq3 = mock(HttpServletRequest.class);
-        when(this.superReq3.getContextPath()).thenReturn("/mycontext");
-        when(this.superReq3.getServletPath()).thenReturn("/proxyservlet");
-        when(this.superReq3.getRequestURI()).thenReturn("/mycontext/proxyservlet/request/to/resource");
-        when(this.superReq3.getPathInfo()).thenReturn("/request/to/resource");
-        when(this.superReq3.getAttribute(HttpContext.AUTHENTICATION_TYPE)).thenReturn(HttpServletRequest.BASIC_AUTH);
-        when(this.superReq3.getAttribute(HttpContext.REMOTE_USER)).thenReturn("felix");
-        this.req3 = new ServletHandlerRequest(this.superReq3, context, "/");
-
-        this.superReq4 = mock(HttpServletRequest.class);
-        when(this.superReq4.getContextPath()).thenReturn("/mycontext");
-        when(this.superReq4.getServletPath()).thenReturn("/proxyservlet");
-        when(this.superReq4.getRequestURI()).thenReturn("/mycontext/proxyservlet/myservlet/request/to/resource;jsession=123");
-        when(this.superReq4.getPathInfo()).thenReturn("/myservlet/request/to/resource");
-        when(this.superReq4.getAttribute(HttpContext.AUTHENTICATION_TYPE)).thenReturn(null);
-        when(this.superReq4.getAuthType()).thenReturn(HttpServletRequest.DIGEST_AUTH);
-        when(this.superReq4.getAttribute(HttpContext.REMOTE_USER)).thenReturn(null);
-        when(this.superReq4.getRemoteUser()).thenReturn("sling");
-        this.req4 = new ServletHandlerRequest(this.superReq4, context, "/myservlet");
-    }
-
-    @Test
-    public void testPathInfo()
-    {
-        assertEquals("/request/to/resource", this.req1.getPathInfo());
-        assertEquals("/request/to/resource", this.req2.getPathInfo());
-        assertEquals("/request/to/resource", this.req3.getPathInfo());
-        assertEquals("/request/to/resource", this.req4.getPathInfo());
-    }
-
-    @Test
-    public void testSuperGetServletPath()
-    {
-        assertEquals("", this.superReq1.getServletPath());
-        assertEquals("", this.superReq2.getServletPath());
-        assertEquals("/proxyservlet", this.superReq3.getServletPath());
-        assertEquals("/proxyservlet", this.superReq4.getServletPath());
-    }
-
-    @Test
-    public void testServletPath()
-    {
-        assertEquals("", this.req1.getServletPath());
-        assertEquals("/myservlet", this.req2.getServletPath());
-        assertEquals("", this.req3.getServletPath());
-        assertEquals("/myservlet", this.req4.getServletPath());
-    }
-
-    @Test
-    public void testContextPath()
-    {
-        assertEquals("/mycontext", this.req1.getContextPath());
-        assertEquals("/mycontext", this.req2.getContextPath());
-        assertEquals("/mycontext/proxyservlet", this.req3.getContextPath());
-        assertEquals("/mycontext/proxyservlet", this.req4.getContextPath());
-    }
-
-    @Test
-    public void testGetAuthType()
-    {
-        assertEquals(HttpServletRequest.BASIC_AUTH, this.req1.getAuthType());
-        verify(this.superReq1).getAttribute(Dispatcher.REQUEST_DISPATCHER_PROVIDER);
-        verify(this.superReq1).getAttribute(HttpContext.AUTHENTICATION_TYPE);
-        verifyNoMoreInteractions(this.superReq1);
-
-        assertEquals(HttpServletRequest.DIGEST_AUTH, this.req2.getAuthType());
-        verify(this.superReq2).getAttribute(Dispatcher.REQUEST_DISPATCHER_PROVIDER);
-        verify(this.superReq2).getAttribute(HttpContext.AUTHENTICATION_TYPE);
-        verify(this.superReq2).getAuthType();
-        verifyNoMoreInteractions(this.superReq2);
-    }
-
-    @Test
-    public void testGetRemoteUser()
-    {
-        assertEquals("felix", this.req1.getRemoteUser());
-        verify(this.superReq1).getAttribute(Dispatcher.REQUEST_DISPATCHER_PROVIDER);
-        verify(this.superReq1).getAttribute(HttpContext.REMOTE_USER);
-        verifyNoMoreInteractions(this.superReq1);
-
-        assertEquals("sling", this.req2.getRemoteUser());
-        verify(this.superReq2).getAttribute(Dispatcher.REQUEST_DISPATCHER_PROVIDER);
-        verify(this.superReq2).getAttribute(HttpContext.REMOTE_USER);
-        verify(this.superReq2).getRemoteUser();
-        verifyNoMoreInteractions(this.superReq2);
-    }
-}
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/ServletHandlerTest.java
index 0eeb909..f2d0304 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/ServletHandlerTest.java
@@ -19,7 +19,6 @@
 import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
 import static javax.servlet.http.HttpServletResponse.SC_OK;
 import static javax.servlet.http.HttpServletResponse.SC_PAYMENT_REQUIRED;
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
@@ -28,6 +27,8 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.util.Map;
+
 import javax.servlet.Servlet;
 import javax.servlet.ServletConfig;
 import javax.servlet.http.HttpServletRequest;
@@ -50,16 +51,6 @@
     }
 
     @Test
-    public void testCompare()
-    {
-        ServletHandler h1 = createHandler("/a");
-        ServletHandler h2 = createHandler("/a/b");
-
-        assertEquals(2, h1.compareTo(h2));
-        assertEquals(-2, h2.compareTo(h1));
-    }
-
-    @Test
     public void testDestroy()
     {
         ServletHandler h1 = createHandler("/a");
@@ -117,7 +108,7 @@
         when(req.getPathInfo()).thenReturn("/a/b");
         boolean result = h1.handle(req, res);
 
-        assertTrue(result);
+        assertFalse(result);
         verify(this.servlet, never()).service(req, res);
         verify(res).sendError(SC_FORBIDDEN);
     }
@@ -142,7 +133,7 @@
         when(req.getPathInfo()).thenReturn("/a/b");
         boolean result = h1.handle(req, res);
 
-        assertTrue(result);
+        assertFalse(result);
         verify(this.servlet, never()).service(req, res);
         verify(res, never()).sendError(SC_FORBIDDEN);
     }
@@ -167,7 +158,7 @@
         when(req.getPathInfo()).thenReturn("/a/b");
         boolean result = h1.handle(req, res);
 
-        assertTrue(result);
+        assertFalse(result);
         verify(this.servlet, never()).service(req, res);
         verify(res, never()).sendError(SC_FORBIDDEN);
     }
@@ -194,11 +185,11 @@
         HttpServletResponse res = mock(HttpServletResponse.class);
         when(this.context.handleSecurity(req, res)).thenReturn(true);
 
-        when(req.getPathInfo()).thenReturn(null);
+        when(req.getRequestURI()).thenReturn(null);
         boolean result = h1.handle(req, res);
 
-        assertFalse(result);
-        verify(this.servlet, never()).service(req, res);
+        assertTrue(result);
+        verify(this.servlet).service(req, res);
     }
 
     @Test
@@ -209,28 +200,26 @@
         verify(this.servlet).init(any(ServletConfig.class));
     }
 
-    @Test
-    public void testMatches()
-    {
-        ServletHandler h1 = createHandler("/a/b");
-
-        assertFalse(h1.matches(null));
-        assertFalse(h1.matches("/"));
-        assertFalse(h1.matches("/a/"));
-        assertTrue(h1.matches("/a/b"));
-        assertTrue(h1.matches("/a/b/"));
-        assertTrue(h1.matches("/a/b/c"));
-    }
-
     @Override
     protected AbstractHandler createHandler()
     {
-        return createHandler("/dummy");
+        return createHandler("/dummy", null);
+    }
+
+    @Override
+    protected AbstractHandler createHandler(Map<String, String> map)
+    {
+        return createHandler("/dummy", map);
     }
 
     private ServletHandler createHandler(String alias)
     {
-        final ServletInfo info = new ServletInfo(null, alias, 0, null, this.servlet, null);
-        return new ServletHandler(this.context, info, info.getServlet(), info.getPatterns()[0]);
+        return createHandler(alias, null);
+    }
+
+    private ServletHandler createHandler(String alias, Map<String, String> map)
+    {
+        final ServletInfo info = new ServletInfo(null, alias, 0, map, this.servlet, null);
+        return new ServletHandler(null, this.context, info, info.getServlet());
     }
 }
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/util/UriUtilsTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/util/UriUtilsTest.java
index 6527430..2acc7a8 100644
--- a/http/base/src/test/java/org/apache/felix/http/base/internal/util/UriUtilsTest.java
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/util/UriUtilsTest.java
@@ -31,6 +31,37 @@
 public class UriUtilsTest
 {
     @Test
+    public void testCompactPath()
+    {
+        assertEquals(null, compactPath(null));
+        assertEquals("", compactPath(""));
+        assertEquals("/", compactPath("/"));
+        assertEquals("/", compactPath("//"));
+        assertEquals("/foo/", compactPath("/foo//"));
+        assertEquals("/foo/", compactPath("//foo/"));
+        assertEquals("/foo/bar", compactPath("/foo/bar"));
+        assertEquals("/foo/bar", compactPath("//foo//bar"));
+        assertEquals("/foo/bar", compactPath("/foo///bar"));
+        assertEquals("/foo/bar?qux=quu//baz", compactPath("/foo/bar?qux=quu//baz"));
+    }
+
+    @Test
+    public void testRelativePath()
+    {
+        assertEquals(null, relativePath("/foo", null));
+        assertEquals(null, relativePath("/foo", ""));
+        assertEquals(null, relativePath("/foo", "/foo"));
+        assertEquals(null, relativePath("/foo", "/foo/")); // XXX or "/"?   
+        assertEquals("/foo", relativePath("/", "/foo"));
+        assertEquals("/foo/", relativePath("/", "/foo/"));
+        assertEquals("/foo/", relativePath(null, "/foo/"));
+        assertEquals("/bar", relativePath("/foo", "/foo/bar"));
+        assertEquals("/bar/foo", relativePath("/foo", "/bar/foo"));
+        assertEquals("/bar", relativePath("/foo/", "/foo/bar"));
+        assertEquals("/foobar", relativePath("/foo", "/foobar"));
+    }
+
+    @Test
     public void testConcatOk()
     {
         assertEquals(null, concat(null, null));