You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ja...@apache.org on 2014/07/04 15:25:21 UTC

svn commit: r1607850 [1/2] - in /felix/sandbox/http-rfc189: api/src/main/java/org/apache/felix/http/api/ base/src/main/java/org/apache/felix/http/base/internal/dispatch/ base/src/main/java/org/apache/felix/http/base/internal/handler/ base/src/main/java...

Author: jawi
Date: Fri Jul  4 13:25:21 2014
New Revision: 1607850

URL: http://svn.apache.org/r1607850
Log:
RFC-189 / FELIX-2137 / FELIX-1651 / FELIX-4541:

- add support for multiple patterns (and regex's in case of Filters), which also includes
  adding support for wildcards in patterns (FELIX-1651 & FELIX-4541);
- add support for named servlets/filters, and added tests for named request dispatchers;
- simplified the dispatching stage by determining the applicable servlet/filters as soon
  as possible (allows us to simplify the Servlet/FilterHandler);
- unify the various used ServletRequestWrappers into one, again to simplify the dispatch
  logic;
- added integration tests for filters & servlets with some of the new functionality we
  now have implemented.


Added:
    felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpSessionWrapper.java
      - copied, changed from r1607310, felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpSessionWrapper.java
    felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletContextWrapper.java
      - copied, changed from r1607310, felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletContextWrapper.java
    felix/sandbox/http-rfc189/itest/src/test/java/org/apache/felix/http/itest/FilterTest.java   (with props)
    felix/sandbox/http-rfc189/itest/src/test/java/org/apache/felix/http/itest/ServletTest.java   (with props)
Removed:
    felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/FilterPipeline.java
    felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletPipeline.java
    felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpSessionWrapper.java
    felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletContextWrapper.java
    felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandlerRequest.java
    felix/sandbox/http-rfc189/base/src/test/java/org/apache/felix/http/base/internal/handler/ServletHandlerRequestTest.java
Modified:
    felix/sandbox/http-rfc189/api/src/main/java/org/apache/felix/http/api/FilterInfo.java
    felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java
    felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpFilterChain.java
    felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/InvocationFilterChain.java
    felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/NotFoundFilterChain.java
    felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/AbstractHandler.java
    felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterHandler.java
    felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerMapping.java
    felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java
    felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpServicePlugin.java
    felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java
    felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java
    felix/sandbox/http-rfc189/base/src/test/java/org/apache/felix/http/base/internal/handler/FilterHandlerTest.java
    felix/sandbox/http-rfc189/base/src/test/java/org/apache/felix/http/base/internal/handler/HandlerMappingTest.java
    felix/sandbox/http-rfc189/base/src/test/java/org/apache/felix/http/base/internal/handler/ServletHandlerTest.java
    felix/sandbox/http-rfc189/itest/src/test/java/org/apache/felix/http/itest/AsyncTest.java
    felix/sandbox/http-rfc189/itest/src/test/java/org/apache/felix/http/itest/BaseIntegrationTest.java
    felix/sandbox/http-rfc189/itest/src/test/java/org/apache/felix/http/itest/HttpJettyTest.java
    felix/sandbox/http-rfc189/itest/src/test/java/org/apache/felix/http/itest/RequestDispatchTest.java
    felix/sandbox/http-rfc189/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManager.java

Modified: felix/sandbox/http-rfc189/api/src/main/java/org/apache/felix/http/api/FilterInfo.java
URL: http://svn.apache.org/viewvc/felix/sandbox/http-rfc189/api/src/main/java/org/apache/felix/http/api/FilterInfo.java?rev=1607850&r1=1607849&r2=1607850&view=diff
==============================================================================
--- felix/sandbox/http-rfc189/api/src/main/java/org/apache/felix/http/api/FilterInfo.java (original)
+++ felix/sandbox/http-rfc189/api/src/main/java/org/apache/felix/http/api/FilterInfo.java Fri Jul  4 13:25:21 2014
@@ -88,7 +88,7 @@ public final class FilterInfo
      * See {@link DispatcherType} and Servlet 3.0 specification, section 6.2.5.
      * </p>
      */
-    public String[] dispatcher = { "REQUEST" };
+    public DispatcherType[] dispatcher = { DispatcherType.REQUEST };
 
     /**
      * The filter initialization parameters as provided during registration of the filter.

Modified: felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java
URL: http://svn.apache.org/viewvc/felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java?rev=1607850&r1=1607849&r2=1607850&view=diff
==============================================================================
--- felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java (original)
+++ felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java Fri Jul  4 13:25:21 2014
@@ -16,19 +16,372 @@
  */
 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.compactPath;
+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.relativePath;
+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.FilterHandler;
 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.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;
+        }
+
+        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();
+                }
+            }
+        }
+
+        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 = 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;
@@ -40,18 +393,103 @@ public final class Dispatcher
 
     public void dispatch(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
     {
-        ServletPipeline servletPipeline = new ServletPipeline(this.handlerRegistry.getServletMapping());
+        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 = compactPath(relativePath(servletPath, requestURI));
+        String queryString = null; // XXX
+
+        ExtServletContext servletContext = (servletHandler != null) ? servletHandler.getContext() : null;
+        RequestInfo requestInfo = new RequestInfo(servletPath, pathInfo, queryString);
 
-        FilterPipeline filterPipeline = new FilterPipeline(this.handlerRegistry.getFilterMapping(), servletPipeline);
         try
         {
-            filterPipeline.dispatch(req, res, DEFAULT_CHAIN);
+            invokeChain(filterHandlers, servletHandler, new ServletRequestWrapper(req, servletContext, requestInfo), res);
         }
         finally
         {
             req.removeAttribute(REQUEST_DISPATCHER_PROVIDER);
         }
     }
+
+    public RequestDispatcher getNamedDispatcher(String name)
+    {
+        ServletHandler handler = this.handlerRegistry.getServletHandlerByName(name);
+        return handler != null ? new RequestDispatcherImpl(handler, null) : 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 requestURI = decodePath(removeDotSegments(path));
+
+        ServletHandler handler = this.handlerRegistry.getServletHander(requestURI);
+        if (handler == null)
+        {
+            return null;
+        }
+
+        String servletPath = handler.determineServletPath(requestURI);
+        String pathInfo = 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);
+    }
 }

Modified: felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpFilterChain.java
URL: http://svn.apache.org/viewvc/felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpFilterChain.java?rev=1607850&r1=1607849&r2=1607850&view=diff
==============================================================================
--- felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpFilterChain.java (original)
+++ felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpFilterChain.java Fri Jul  4 13:25:21 2014
@@ -24,6 +24,9 @@ import javax.servlet.http.HttpServletReq
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 
+/**
+ * Convenience adapter for {@link FilterChain}s that makes the obtrusive casting unnecessary. 
+ */
 public abstract class HttpFilterChain implements FilterChain
 {
     public final void doFilter(ServletRequest req, ServletResponse res) throws IOException, ServletException
@@ -31,5 +34,8 @@ public abstract class HttpFilterChain im
         doFilter((HttpServletRequest) req, (HttpServletResponse) res);
     }
 
+    /**
+     * @see FilterChain#doFilter(ServletRequest, ServletResponse)
+     */
     protected abstract void doFilter(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException;
 }

Copied: felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpSessionWrapper.java (from r1607310, felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpSessionWrapper.java)
URL: http://svn.apache.org/viewvc/felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpSessionWrapper.java?p2=felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpSessionWrapper.java&p1=felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpSessionWrapper.java&r1=1607310&r2=1607850&rev=1607850&view=diff
==============================================================================
--- felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpSessionWrapper.java (original)
+++ felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpSessionWrapper.java Fri Jul  4 13:25:21 2014
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.felix.http.base.internal.handler;
+package org.apache.felix.http.base.internal.dispatch;
 
 import java.util.Enumeration;
 

Modified: felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/InvocationFilterChain.java
URL: http://svn.apache.org/viewvc/felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/InvocationFilterChain.java?rev=1607850&r1=1607849&r2=1607850&view=diff
==============================================================================
--- felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/InvocationFilterChain.java (original)
+++ felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/InvocationFilterChain.java Fri Jul  4 13:25:21 2014
@@ -16,8 +16,9 @@
  */
 package org.apache.felix.http.base.internal.dispatch;
 
+import static javax.servlet.http.HttpServletResponse.SC_OK;
+
 import java.io.IOException;
-import java.util.List;
 
 import javax.servlet.FilterChain;
 import javax.servlet.ServletException;
@@ -25,35 +26,48 @@ import javax.servlet.http.HttpServletReq
 import javax.servlet.http.HttpServletResponse;
 
 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(List<FilterHandler> handlers, ServletPipeline servletPipeline, FilterChain proceedingChain)
+    public InvocationFilterChain(ServletHandler servletHandler, FilterHandler[] filterHandlers, FilterChain defaultChain)
     {
-        this.handlers = handlers.toArray(new FilterHandler[handlers.size()]);
-        this.servletPipeline = servletPipeline;
-        this.proceedingChain = proceedingChain;
+        this.filterHandlers = filterHandlers;
+        this.servletHandler = servletHandler;
+        this.defaultChain = defaultChain;
     }
 
     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);
+        }
     }
 }

Modified: felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/NotFoundFilterChain.java
URL: http://svn.apache.org/viewvc/felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/NotFoundFilterChain.java?rev=1607850&r1=1607849&r2=1607850&view=diff
==============================================================================
--- felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/NotFoundFilterChain.java (original)
+++ felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/NotFoundFilterChain.java Fri Jul  4 13:25:21 2014
@@ -16,10 +16,11 @@
  */
 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
 {

Copied: felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletContextWrapper.java (from r1607310, felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletContextWrapper.java)
URL: http://svn.apache.org/viewvc/felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletContextWrapper.java?p2=felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletContextWrapper.java&p1=felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletContextWrapper.java&r1=1607310&r2=1607850&rev=1607850&view=diff
==============================================================================
--- felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletContextWrapper.java (original)
+++ felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletContextWrapper.java Fri Jul  4 13:25:21 2014
@@ -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>

Modified: felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/AbstractHandler.java
URL: http://svn.apache.org/viewvc/felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/AbstractHandler.java?rev=1607850&r1=1607849&r2=1607850&view=diff
==============================================================================
--- felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/AbstractHandler.java (original)
+++ felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/AbstractHandler.java Fri Jul  4 13:25:21 2014
@@ -29,8 +29,10 @@ import javax.servlet.ServletException;
 
 import org.apache.felix.http.base.internal.context.ExtServletContext;
 
-public abstract class AbstractHandler
+public abstract class AbstractHandler<T extends AbstractHandler> implements Comparable<T>
 {
+    public static final String ASYNC_SERVLET_PATH = "javax.servlet.async.path_info";
+    
     private final static AtomicInteger ID = new AtomicInteger();
 
     private final int id;
@@ -48,6 +50,11 @@ public abstract class AbstractHandler
 
     public abstract void destroy();
 
+    public final ExtServletContext getContext()
+    {
+        return this.context;
+    }
+
     public final Map<String, String> getInitParams()
     {
         return this.initParams;
@@ -62,20 +69,11 @@ public abstract class AbstractHandler
         }
         return name;
     }
-
+    
     public abstract Pattern[] getPatterns();
 
     public abstract void init() throws ServletException;
 
-    public final void setInitParams(Map<String, String> map)
-    {
-        this.initParams.clear();
-        if (map != null)
-        {
-            this.initParams.putAll(map);
-        }
-    }
-
     public final void setInitParams(Dictionary map)
     {
         this.initParams.clear();
@@ -97,9 +95,13 @@ public abstract class AbstractHandler
         }
     }
 
-    protected final ExtServletContext getContext()
+    public final void setInitParams(Map<String, String> map)
     {
-        return this.context;
+        this.initParams.clear();
+        if (map != null)
+        {
+            this.initParams.putAll(map);
+        }
     }
 
     /**

Modified: felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterHandler.java
URL: http://svn.apache.org/viewvc/felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterHandler.java?rev=1607850&r1=1607849&r2=1607850&view=diff
==============================================================================
--- felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterHandler.java (original)
+++ felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterHandler.java Fri Jul  4 13:25:21 2014
@@ -18,8 +18,11 @@ package org.apache.felix.http.base.inter
 
 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.PatternUtil.convertToRegEx;
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.regex.Pattern;
 
 import javax.servlet.Filter;
@@ -28,36 +31,44 @@ import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.felix.http.api.FilterInfo;
 import org.apache.felix.http.base.internal.context.ExtServletContext;
 
-public final class FilterHandler extends AbstractHandler implements Comparable<FilterHandler>
+public final class FilterHandler extends AbstractHandler<FilterHandler>
 {
     private final Filter filter;
-    private final String[] patternStrings;
+    private final FilterInfo filterInfo;
     private final Pattern[] patterns;
-    private final int ranking;
 
-    public FilterHandler(ExtServletContext context, Filter filter, String[] patterns, int ranking, String name)
+    public FilterHandler(ExtServletContext context, Filter filter, FilterInfo filterInfo)
     {
-        super(context, name);
+        super(context, filterInfo.name);
         this.filter = filter;
-        this.ranking = ranking;
-        this.patternStrings = patterns;
+        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]);
         }
+
+        setInitParams(filterInfo.initParams);
     }
 
     public int compareTo(FilterHandler other)
     {
-        if (other.ranking == this.ranking)
+        int thisRanking = this.filterInfo.ranking;
+        int thatRanking = other.filterInfo.ranking;
+
+        int r = (thatRanking - thisRanking);
+        if (r == 0)
         {
-            return 0;
+            r = getId() - other.getId();
         }
-
-        return (other.ranking > this.ranking) ? 1 : -1;
+        return r;
     }
 
     @Override
@@ -71,33 +82,34 @@ public final class FilterHandler extends
         return this.filter;
     }
 
-    @Override
-    public Pattern[] getPatterns()
-    {
-        return patterns;
-    }
-
-    public String[] getPatternStrings()
+    public FilterInfo getFilterInfo()
     {
-        return patternStrings;
+        return this.filterInfo;
     }
 
-    public int getRanking()
+    @Override
+    public Pattern[] getPatterns()
     {
-        return ranking;
+        return patterns;
     }
 
-    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)
         {
-            chain.doFilter(req, res);
+            res.sendError(SC_FORBIDDEN);
         }
+
+        return false;
     }
 
     @Override
@@ -106,43 +118,29 @@ public final class FilterHandler extends
         this.filter.init(new FilterConfigImpl(getName(), getContext(), getInitParams()));
     }
 
-    public boolean matches(String uri)
+    @Override
+    protected Object getSubject()
     {
-        // assume root if uri is null
-        if (uri == null)
-        {
-            uri = "/";
-        }
-        for (Pattern p : this.patterns)
-        {
-            if (p.matcher(uri).matches())
-            {
-                return true;
-            }
-        }
-        return false;
+        return this.filter;
     }
 
-    final void doHandle(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws ServletException, IOException
+    private static String[] getFilterPatterns(FilterInfo filterInfo)
     {
-        if (getContext().handleSecurity(req, res))
+        List<String> result = new ArrayList<String>();
+        if (filterInfo.patterns != null)
         {
-            this.filter.doFilter(req, res, chain);
+            for (int i = 0; i < filterInfo.patterns.length; i++)
+            {
+                result.add(convertToRegEx(filterInfo.patterns[i]));
+            }
         }
-        else
+        if (filterInfo.regexs != null)
         {
-            // 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)
+            for (int i = 0; i < filterInfo.regexs.length; i++)
             {
-                res.sendError(SC_FORBIDDEN);
+                result.add(filterInfo.regexs[i]);
             }
         }
-    }
-
-    @Override
-    protected Object getSubject()
-    {
-        return this.filter;
+        return result.toArray(new String[result.size()]);
     }
 }

Modified: felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerMapping.java
URL: http://svn.apache.org/viewvc/felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerMapping.java?rev=1607850&r1=1607849&r2=1607850&view=diff
==============================================================================
--- felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerMapping.java (original)
+++ felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerMapping.java Fri Jul  4 13:25:21 2014
@@ -121,8 +121,8 @@ public class HandlerMapping<V extends Ab
         }
     }
 
-    private final SortedMap<Pattern, V> exactMap;
-    private final SortedMap<Pattern, V> wildcardMap;
+    private final SortedMap<Pattern, List<V>> exactMap;
+    private final SortedMap<Pattern, List<V>> wildcardMap;
     private final Set<V> all;
 
     /**
@@ -140,8 +140,8 @@ public class HandlerMapping<V extends Ab
      */
     public HandlerMapping(Collection<V> elements)
     {
-        this.exactMap = new TreeMap<Pattern, V>(new PatternComparator());
-        this.wildcardMap = new TreeMap<Pattern, V>(new PatternComparator());
+        this.exactMap = new TreeMap<Pattern, List<V>>(new PatternComparator());
+        this.wildcardMap = new TreeMap<Pattern, List<V>>(new PatternComparator());
         this.all = new HashSet<V>(elements);
 
         for (V element : elements)
@@ -150,11 +150,29 @@ public class HandlerMapping<V extends Ab
             {
                 if (isWildcardPattern(pattern))
                 {
-                    this.wildcardMap.put(pattern, element);
+                    List<V> vs = this.wildcardMap.get(pattern);
+                    if (vs == null)
+                    {
+                        vs = new ArrayList<V>();
+                        this.wildcardMap.put(pattern, vs);
+                    }
+                    if (!vs.contains(element))
+                    {
+                        vs.add(element);
+                    }
                 }
                 else
                 {
-                    this.exactMap.put(pattern, element);
+                    List<V> vs = this.exactMap.get(pattern);
+                    if (vs == null)
+                    {
+                        vs = new ArrayList<V>();
+                        this.exactMap.put(pattern, vs);
+                    }
+                    if (!vs.contains(element))
+                    {
+                        vs.add(element);
+                    }
                 }
             }
         }
@@ -201,6 +219,29 @@ public class HandlerMapping<V extends Ab
     }
 
     /**
+     * Returns the (first) handler identified by the given name.
+     * @param name the name of the handler to return, can be <code>null</code> in which case this method will return <code>null</code>.
+     * @return the element with the given name, or <code>null</code> if not found, or the given argument was <code>null</code>.
+     */
+    public V getByName(String name)
+    {
+        if (name == null)
+        {
+            return null;
+        }
+
+        for (V element : this.all)
+        {
+            if (name.equals(element.getName()))
+            {
+                return element;
+            }
+        }
+
+        return null;
+    }
+
+    /**
      * Provides information on whether there are elements mapped or not.
      * 
      * @return <code>true</code> if there is at least one element mapped, <code>false</code> otherwise.
@@ -225,16 +266,19 @@ public class HandlerMapping<V extends Ab
 
         List<V> result = new ArrayList<V>();
         // Look for exact matches only, that is, those patterns without wildcards...
-        for (Entry<Pattern, V> entry : this.exactMap.entrySet())
+        for (Entry<Pattern, List<V>> entry : this.exactMap.entrySet())
         {
             Matcher matcher = entry.getKey().matcher(path);
             // !!! we should always match the *entire* pattern, instead of the longest prefix...
             if (matcher.matches())
             {
-                V v = entry.getValue();
-                if (!result.contains(v))
+                List<V> vs = entry.getValue();
+                for (V v : vs)
                 {
-                    result.add(v);
+                    if (!result.contains(v))
+                    {
+                        result.add(v);
+                    }
 
                     if (firstOnly)
                     {
@@ -245,15 +289,18 @@ public class HandlerMapping<V extends Ab
         }
 
         // Try to apply the wildcard patterns...
-        for (Entry<Pattern, V> entry : this.wildcardMap.entrySet())
+        for (Entry<Pattern, List<V>> entry : this.wildcardMap.entrySet())
         {
             Matcher matcher = entry.getKey().matcher(path);
             if (matcher.find(0))
             {
-                V v = entry.getValue();
-                if (!result.contains(v))
+                List<V> vs = entry.getValue();
+                for (V v : vs)
                 {
-                    result.add(v);
+                    if (!result.contains(v))
+                    {
+                        result.add(v);
+                    }
 
                     if (firstOnly)
                     {
@@ -262,6 +309,9 @@ public class HandlerMapping<V extends Ab
                 }
             }
         }
+        
+        // Make sure the results are properly sorted...
+        Collections.sort(result);
 
         return result;
     }

Modified: felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java
URL: http://svn.apache.org/viewvc/felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java?rev=1607850&r1=1607849&r2=1607850&view=diff
==============================================================================
--- felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java (original)
+++ felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java Fri Jul  4 13:25:21 2014
@@ -16,14 +16,18 @@
  */
 package org.apache.felix.http.base.internal.handler;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.Iterator;
+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.util.PatternUtil;
 import org.osgi.service.http.NamespaceException;
 
 public final class HandlerRegistry
@@ -62,7 +66,7 @@ public final class HandlerRegistry
         {
             throw new ServletException("Servlet instance " + handler.getName() + " already registered");
         }
-        for (String pattern : handler.getPatternStrings())
+        for (String pattern : handler.getServletInfo().patterns)
         {
             if (this.servletPatternMap.containsKey(pattern))
             {
@@ -77,19 +81,48 @@ public final class HandlerRegistry
         updateServletMapping();
     }
 
-    public HandlerMapping<FilterHandler> getFilterMapping()
+    public FilterHandler[] getFilterHandlers(ServletHandler servletHandler, DispatcherType dispatcherType, String requestURI)
     {
-        return this.filterMapping;
+        // See Servlet 3.0 specification, section 6.2.4...
+        List<FilterHandler> result = new ArrayList<FilterHandler>();
+        result.addAll(this.filterMapping.getAllMatches(requestURI));
+
+        // TODO this is not the most efficient/fastest way of doing this...
+        Iterator<FilterHandler> iter = result.iterator();
+        while (iter.hasNext())
+        {
+            if (!referencesDispatcherType(iter.next(), dispatcherType))
+            {
+                iter.remove();
+            }
+        }
+
+        String servletName = (servletHandler != null) ? servletHandler.getName() : null;
+        // TODO this is not the most efficient/fastest way of doing this...
+        for (FilterHandler filterHandler : this.filterMapping.getAllElements())
+        {
+            if (referencesServletByName(filterHandler, servletName))
+            {
+                result.add(filterHandler);
+            }
+        }
+
+        return result.toArray(new FilterHandler[result.size()]);
     }
 
     public synchronized Servlet getServletByAlias(String alias)
     {
-        return this.servletPatternMap.get(PatternUtil.convertToRegEx(alias));
+        return this.servletPatternMap.get(alias);
+    }
+
+    public ServletHandler getServletHandlerByName(String name)
+    {
+        return this.servletMapping.getByName(name);
     }
 
-    public HandlerMapping<ServletHandler> getServletMapping()
+    public ServletHandler getServletHander(String requestURI)
     {
-        return this.servletMapping;
+        return this.servletMapping.getBestMatch(requestURI);
     }
 
     public synchronized void removeAll()
@@ -132,7 +165,7 @@ public final class HandlerRegistry
         {
             updateServletMapping();
 
-            for (String pattern : handler.getPatternStrings())
+            for (String pattern : handler.getServletInfo().patterns)
             {
                 this.servletPatternMap.remove(pattern);
             }
@@ -144,6 +177,25 @@ public final class HandlerRegistry
         }
     }
 
+    private boolean referencesDispatcherType(FilterHandler handler, DispatcherType dispatcherType)
+    {
+        return Arrays.asList(handler.getFilterInfo().dispatcher).contains(dispatcherType);
+    }
+
+    private boolean referencesServletByName(FilterHandler handler, String servletName)
+    {
+        if (servletName == null)
+        {
+            return false;
+        }
+        String[] names = handler.getFilterInfo().servletNames;
+        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());

Modified: felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpServicePlugin.java
URL: http://svn.apache.org/viewvc/felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpServicePlugin.java?rev=1607850&r1=1607849&r2=1607850&view=diff
==============================================================================
--- felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpServicePlugin.java (original)
+++ felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpServicePlugin.java Fri Jul  4 13:25:21 2014
@@ -92,8 +92,8 @@ public class HttpServicePlugin extends H
         for (FilterHandler filter : filters)
         {
             pw.println("<tr class=\"" + rowClass + " ui-state-default\">");
-            pw.println("<td>" + Arrays.toString(filter.getPatternStrings()) + "</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());
 
@@ -126,8 +126,8 @@ public class HttpServicePlugin extends H
         {
 
             pw.println("<tr class=\"" + rowClass + " ui-state-default\">");
-            pw.println("<td>" + Arrays.toString(servlet.getPatternStrings()) + "</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());
 
@@ -155,7 +155,7 @@ public class HttpServicePlugin extends H
         ServletHandler[] servlets = new ServletHandler[0]; // XXX was: registry.getServlets();
         for (ServletHandler servlet : servlets)
         {
-            pw.println("Patterns : " + Arrays.toString(servlet.getPatternStrings()));
+//            pw.println("Patterns : " + Arrays.toString(servlet.getPatternStrings())); // XXX
             addSpace(pw, 1);
             pw.println("Class    : " + servlet.getServlet().getClass().getName());
             addSpace(pw, 1);
@@ -169,9 +169,9 @@ public class HttpServicePlugin extends H
         Arrays.sort(filters);
         for (FilterHandler filter : filters)
         {
-            pw.println("Patterns : " + Arrays.toString(filter.getPatternStrings()));
+//            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());
             addSpace(pw, 1);

Modified: felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java
URL: http://svn.apache.org/viewvc/felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java?rev=1607850&r1=1607849&r2=1607850&view=diff
==============================================================================
--- felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java (original)
+++ felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java Fri Jul  4 13:25:21 2014
@@ -16,275 +16,76 @@
  */
 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 static org.apache.felix.http.base.internal.util.UriUtils.relativePath;
+import static org.apache.felix.http.base.internal.util.PatternUtil.convertToRegEx;
 
 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.api.ServletInfo;
 import org.apache.felix.http.base.internal.context.ExtServletContext;
 
 /**
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
-public class ServletHandler extends AbstractHandler implements Comparable<ServletHandler>
+public class ServletHandler extends AbstractHandler<ServletHandler>
 {
-    private class RequestDispatcherImpl implements RequestDispatcher
-    {
-        final String servletPath;
-        final String requestURI;
-        final String pathInfo;
-        final String query;
-        final boolean named;
-
-        public RequestDispatcherImpl()
-        {
-            this.servletPath = null;
-            this.requestURI = null;
-            this.pathInfo = null;
-            this.query = null;
-            this.named = true;
-        }
-
-        public RequestDispatcherImpl(String uri, String servletPath, String pathInContext, String query)
-        {
-            this.requestURI = uri;
-            this.servletPath = servletPath;
-            this.pathInfo = relativePath(servletPath, pathInContext);
-            this.query = query;
-            this.named = false;
-        }
-
-        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(this.pathInfo, 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();
-            }
-        }
-
-        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(this.pathInfo, 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 String[] patternStrings;
-    private final Pattern[] patterns;
     private final Servlet servlet;
+    private final ServletInfo servletInfo;
+    private final Pattern[] patterns;
 
-    public ServletHandler(ExtServletContext context, Servlet servlet, String[] patterns, String name)
+    public ServletHandler(ExtServletContext context, Servlet servlet, ServletInfo servletInfo)
     {
-        super(context, name);
+        super(context, servletInfo.name);
         this.servlet = servlet;
-        this.patternStrings = patterns;
+        this.servletInfo = servletInfo;
+
+        String[] patterns = this.servletInfo.patterns;
+
         this.patterns = new Pattern[patterns.length];
         for (int i = 0; i < patterns.length; i++)
         {
-            this.patterns[i] = Pattern.compile(patterns[i]);
+            this.patterns[i] = Pattern.compile(convertToRegEx(patterns[i]));
         }
+
+        setInitParams(servletInfo.initParams);
     }
 
     public int compareTo(ServletHandler other)
     {
-        return other.getId() - this.getId(); // XXX
+        return getId() - other.getId();
     }
 
-    public RequestDispatcher createNamedRequestDispatcher()
+    @Override
+    public void destroy()
     {
-        return new RequestDispatcherImpl();
+        this.servlet.destroy();
     }
 
-    /**
-     * Creates a request dispatcher based on this handler if, and only if, the given pathInContext attribute matches any of the patterns in this handler.
-     *  
-     * @param path
-     * @param pathInContext the path in context, that should be matched by this handler, cannot be <code>null</code>;
-     * @param query the optional query-part of the request URL, may be <code>null</code>.
-     * @return a new {@link RequestDispatcher} instance, or <code>null</code> if this handler cannot handle the given request.
-     */
-    public RequestDispatcher createRequestDispatcher(String path, String pathInContext, String query)
+    public String determineServletPath(String uri)
     {
-        // Determine which servletPath we should use...
-        String servletPath = determineServletPath(pathInContext);
-        if (servletPath != null)
+        if (uri == null)
         {
-            return new RequestDispatcherImpl(path, servletPath, pathInContext, query);
+            uri = "/";
         }
-        return null;
-    }
 
-    @Override
-    public void destroy()
-    {
-        this.servlet.destroy();
+        // Patterns are sorted on length in descending order, so we should get the longest match first...
+        for (int i = 0; i < this.patterns.length; i++)
+        {
+            Matcher matcher = this.patterns[i].matcher(uri);
+            if (matcher.find(0))
+            {
+                return matcher.groupCount() > 0 ? matcher.group(1) : matcher.group();
+            }
+        }
+        return null;
     }
 
     @Override
@@ -293,43 +94,33 @@ public class ServletHandler extends Abst
         return patterns;
     }
 
-    public String[] getPatternStrings()
+    public Servlet getServlet()
     {
-        return patternStrings;
+        return this.servlet;
     }
 
-    public Servlet getServlet()
+    public ServletInfo getServletInfo()
     {
-        return this.servlet;
+        return this.servletInfo;
     }
 
     public boolean handle(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
     {
-        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
+        if (getContext().handleSecurity(req, res))
         {
-            path = req.getPathInfo();
+            this.servlet.service(req, res);
+
+            return true;
         }
 
-        final boolean matches = matches(path);
-        if (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)
         {
-            doHandle(path, req, res);
+            res.sendError(SC_FORBIDDEN);
         }
 
-        return matches;
+        return false;
     }
 
     @Override
@@ -338,57 +129,9 @@ public class ServletHandler extends Abst
         this.servlet.init(new ServletConfigImpl(getName(), getContext(), getInitParams()));
     }
 
-    public boolean matches(String uri)
-    {
-        return determineServletPath(uri) != null;
-    }
-
-    final void doHandle(String path, 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())
-        {
-            String alias = determineServletPath(path);
-            req = new ServletHandlerRequest(req, getContext(), 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;
     }
-
-    private String determineServletPath(String uri)
-    {
-        if (uri == null)
-        {
-            uri = "/";
-        }
-
-        for (int i = 0; i < this.patterns.length; i++)
-        {
-            Matcher matcher = this.patterns[i].matcher(uri);
-            if (matcher.find(0))
-            {
-                return matcher.groupCount() > 0 ? matcher.group(1) : matcher.group();
-            }
-        }
-        return null;
-    }
 }

Modified: felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java?rev=1607850&r1=1607849&r2=1607850&view=diff
==============================================================================
--- felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java (original)
+++ felix/sandbox/http-rfc189/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java Fri Jul  4 13:25:21 2014
@@ -16,14 +16,10 @@
  */
 package org.apache.felix.http.base.internal.service;
 
-import static org.apache.felix.http.base.internal.util.PatternUtil.convertToRegEx;
-
-import java.util.ArrayList;
 import java.util.Dictionary;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 
 import javax.servlet.Filter;
@@ -78,6 +74,11 @@ public final class HttpServiceImpl imple
         return result;
     }
 
+    static <T> boolean isEmpty(T[] array)
+    {
+        return array == null || array.length < 1;
+    }
+
     static boolean isEmpty(String str)
     {
         return str == null || "".equals(str.trim());
@@ -98,28 +99,20 @@ public final class HttpServiceImpl imple
         {
             throw new IllegalArgumentException("FilterInfo cannot be null!");
         }
-        if ((filterInfo.patterns == null || filterInfo.patterns.length < 1) && (filterInfo.regexs == null || filterInfo.regexs.length < 1))
+        if (isEmpty(filterInfo.patterns) && isEmpty(filterInfo.regexs) && isEmpty(filterInfo.servletNames))
         {
-            throw new IllegalArgumentException("FilterInfo must have at least one pattern or regex!");
+            throw new IllegalArgumentException("FilterInfo must have at least one pattern or regex, or provide at least one servlet name!");
         }
-
-        String filterName = filterInfo.name;
-        if (isEmpty(filterName))
+        if (isEmpty(filterInfo.name))
         {
-            filterName = filter.getClass().getName();
+            filterInfo.name = filter.getClass().getName();
         }
 
-        Map<String, String> initParams = filterInfo.initParams;
         ExtServletContext servletContext = getServletContext(filterInfo.context);
-        int ranking = Math.max(0, filterInfo.ranking);
-
-        // Compose a single array of all patterns & regexs the filter must represent...
-        String[] patterns = getFilterPatterns(filterInfo);
 
         try
         {
-            FilterHandler handler = new FilterHandler(servletContext, filter, patterns, ranking, filterName);
-            handler.setInitParams(initParams);
+            FilterHandler handler = new FilterHandler(servletContext, filter, filterInfo);
 
             synchronized (this)
             {
@@ -137,7 +130,7 @@ public final class HttpServiceImpl imple
             }
             else
             {
-                throw new ServletException("Failed to register filter " + filterName, e);
+                throw new ServletException("Failed to register filter " + filterInfo.name, e);
             }
         }
     }
@@ -185,27 +178,16 @@ public final class HttpServiceImpl imple
         {
             throw new IllegalArgumentException("ServletInfo must at least have one pattern!");
         }
-
-        String servletName = servletInfo.name;
-        if (isEmpty(servletName))
+        if (isEmpty(servletInfo.name))
         {
-            servletName = servlet.getClass().getName();
+            servletInfo.name = servlet.getClass().getName();
         }
 
-        Map<String, String> initParams = servletInfo.initParams;
         ExtServletContext servletContext = getServletContext(servletInfo.context);
 
-        // Compose a single array of all patterns/aliases the servlet must represent...
-        String[] patterns = new String[servletInfo.patterns.length];
-        for (int i = 0; i < servletInfo.patterns.length; i++)
-        {
-            patterns[i] = convertToRegEx(servletInfo.patterns[i]);
-        }
-
         try
         {
-            ServletHandler handler = new ServletHandler(servletContext, servlet, patterns, servletName);
-            handler.setInitParams(initParams);
+            ServletHandler handler = new ServletHandler(servletContext, servlet, servletInfo);
 
             synchronized (this)
             {
@@ -227,7 +209,7 @@ public final class HttpServiceImpl imple
             }
             else
             {
-                throw new ServletException("Failed to register servlet " + servletName, e);
+                throw new ServletException("Failed to register servlet " + servletInfo.name, e);
             }
         }
     }
@@ -279,26 +261,6 @@ public final class HttpServiceImpl imple
         unregisterServlet(servlet, true);
     }
 
-    private String[] getFilterPatterns(FilterInfo filterInfo)
-    {
-        List<String> result = new ArrayList<String>();
-        if (filterInfo.patterns != null)
-        {
-            for (int i = 0; i < filterInfo.patterns.length; i++)
-            {
-                result.add(convertToRegEx(filterInfo.patterns[i]));
-            }
-        }
-        if (filterInfo.regexs != null)
-        {
-            for (int i = 0; i < filterInfo.regexs.length; i++)
-            {
-                result.add(filterInfo.regexs[i]);
-            }
-        }
-        return result.toArray(new String[result.size()]);
-    }
-
     private ExtServletContext getServletContext(HttpContext context)
     {
         if (context == null)

Modified: felix/sandbox/http-rfc189/base/src/test/java/org/apache/felix/http/base/internal/handler/FilterHandlerTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/http-rfc189/base/src/test/java/org/apache/felix/http/base/internal/handler/FilterHandlerTest.java?rev=1607850&r1=1607849&r2=1607850&view=diff
==============================================================================
--- felix/sandbox/http-rfc189/base/src/test/java/org/apache/felix/http/base/internal/handler/FilterHandlerTest.java (original)
+++ felix/sandbox/http-rfc189/base/src/test/java/org/apache/felix/http/base/internal/handler/FilterHandlerTest.java Fri Jul  4 13:25:21 2014
@@ -20,20 +20,20 @@ import static javax.servlet.http.HttpSer
 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 javax.servlet.DispatcherType;
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.felix.http.api.FilterInfo;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -53,9 +53,16 @@ public class FilterHandlerTest extends A
     {
         FilterHandler h1 = createHandler(0, "a");
         FilterHandler h2 = createHandler(10, "b");
+        FilterHandler h3 = createHandler(10, "c");
 
-        assertEquals(1, h1.compareTo(h2));
-        assertEquals(-1, h2.compareTo(h1));
+        assertEquals(0, h1.compareTo(h1));
+
+        assertEquals(10, h1.compareTo(h2));
+        assertEquals(-10, h2.compareTo(h1));
+
+        // h2 is actually registered first, so should be called first...
+        assertEquals(-1, h2.compareTo(h3));
+        assertEquals(1, h3.compareTo(h2));
     }
 
     @Test
@@ -70,12 +77,12 @@ public class FilterHandlerTest extends A
     public void testHandleFound() throws Exception
     {
         FilterHandler h1 = createHandler(0, "/a");
-        HttpServletRequest req = mock(HttpServletRequest.class);
-        HttpServletResponse res = mock(HttpServletResponse.class);
+        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);
@@ -86,12 +93,12 @@ public class FilterHandlerTest extends A
     public void testHandleFoundContextRoot() throws Exception
     {
         FilterHandler h1 = createHandler(0, "/");
-        HttpServletRequest req = mock(HttpServletRequest.class);
-        HttpServletResponse res = mock(HttpServletResponse.class);
+        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);
@@ -105,11 +112,11 @@ public class FilterHandlerTest extends A
     public void testHandleFoundForbidden() throws Exception
     {
         FilterHandler h1 = createHandler(0, "/a");
-        HttpServletRequest req = mock(HttpServletRequest.class);
-        HttpServletResponse res = mock(HttpServletResponse.class);
+        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);
@@ -130,11 +137,11 @@ public class FilterHandlerTest extends A
     public void testHandleFoundForbiddenCommittedOwnResponse() throws Exception
     {
         FilterHandler h1 = createHandler(0, "/a");
-        HttpServletRequest req = mock(HttpServletRequest.class);
-        HttpServletResponse res = mock(HttpServletResponse.class);
+        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);
@@ -156,11 +163,11 @@ public class FilterHandlerTest extends A
     public void testHandleFoundForbiddenCustomStatusCode() throws Exception
     {
         FilterHandler h1 = createHandler(0, "/a");
-        HttpServletRequest req = mock(HttpServletRequest.class);
-        HttpServletResponse res = mock(HttpServletResponse.class);
+        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);
@@ -179,30 +186,30 @@ public class FilterHandlerTest extends A
     public void testHandleNotFound() throws Exception
     {
         FilterHandler h1 = createHandler(0, "/a");
-        HttpServletRequest req = mock(HttpServletRequest.class);
-        HttpServletResponse res = mock(HttpServletResponse.class);
+        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(0, "/a");
-        HttpServletRequest req = mock(HttpServletRequest.class);
-        HttpServletResponse res = mock(HttpServletResponse.class);
+        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
@@ -213,30 +220,6 @@ public class FilterHandlerTest extends A
         verify(this.filter).init(any(FilterConfig.class));
     }
 
-    @Test
-    public void testMatches()
-    {
-        FilterHandler h1 = createHandler(0, "/a/b");
-        FilterHandler h2 = createHandler(0, "/a/b/.+");
-        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/"));
-    }
-
     protected AbstractHandler createHandler()
     {
         return createHandler(0, "dummy");
@@ -244,6 +227,27 @@ public class FilterHandlerTest extends A
 
     private FilterHandler createHandler(int ranking, String... pattern)
     {
-        return new FilterHandler(this.context, this.filter, pattern, ranking, null /* name */);
+        FilterInfo info = new FilterInfo();
+        info.ranking = ranking;
+        info.patterns = pattern;
+
+        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);
     }
 }