Blame SOURCES/GUICE_618_extensible_filter_pipeline.patch

249b5c
Description: Let sub-classes of GuiceFilter customize the servlet filter pipeline
249b5c
Author: Stuart McCulloch <mcculls@gmail.com>
249b5c
Bug-Google: http://code.google.com/p/google-guice/issues/detail?id=618
249b5c
Last-Update: 2015-01-20
249b5c
249b5c
diff --git a/extensions/servlet/src/com/google/inject/servlet/AbstractFilterPipeline.java b/extensions/servlet/src/com/google/inject/servlet/AbstractFilterPipeline.java
249b5c
new file mode 100644
249b5c
index 0000000..ab8f746
249b5c
--- /dev/null
249b5c
+++ b/extensions/servlet/src/com/google/inject/servlet/AbstractFilterPipeline.java
249b5c
@@ -0,0 +1,150 @@
249b5c
+/**
249b5c
+ * Copyright (C) 2008 Google Inc.
249b5c
+ *
249b5c
+ * Licensed under the Apache License, Version 2.0 (the "License");
249b5c
+ * you may not use this file except in compliance with the License.
249b5c
+ * You may obtain a copy of the License at
249b5c
+ *
249b5c
+ * http://www.apache.org/licenses/LICENSE-2.0
249b5c
+ *
249b5c
+ * Unless required by applicable law or agreed to in writing, software
249b5c
+ * distributed under the License is distributed on an "AS IS" BASIS,
249b5c
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
249b5c
+ * See the License for the specific language governing permissions and
249b5c
+ * limitations under the License.
249b5c
+ */
249b5c
+package com.google.inject.servlet;
249b5c
+
249b5c
+import com.google.common.collect.Sets;
249b5c
+import com.google.inject.Injector;
249b5c
+import com.google.inject.Provider;
249b5c
+
249b5c
+import java.io.IOException;
249b5c
+import java.util.Set;
249b5c
+
249b5c
+import javax.servlet.Filter;
249b5c
+import javax.servlet.FilterChain;
249b5c
+import javax.servlet.RequestDispatcher;
249b5c
+import javax.servlet.ServletContext;
249b5c
+import javax.servlet.ServletException;
249b5c
+import javax.servlet.ServletRequest;
249b5c
+import javax.servlet.ServletResponse;
249b5c
+import javax.servlet.http.HttpServletRequest;
249b5c
+import javax.servlet.http.HttpServletRequestWrapper;
249b5c
+
249b5c
+/**
249b5c
+ * Skeleton implementation of a central routing/dispatch class which uses a sequence of
249b5c
+ * {@link FilterDefinition}s to filter requests before delegating to the servlet pipeline.
249b5c
+ *
249b5c
+ * @author dhanji@gmail.com (Dhanji R. Prasanna)
249b5c
+ */
249b5c
+public abstract class AbstractFilterPipeline implements FilterPipeline {
249b5c
+
249b5c
+  private final AbstractServletPipeline servletPipeline;
249b5c
+  private final Provider<ServletContext> servletContext;
249b5c
+
249b5c
+  //Unfortunately, we need the injector itself in order to create filters + servlets
249b5c
+  private final Injector injector;
249b5c
+
249b5c
+  //Guards a DCL, so needs to be volatile
249b5c
+  private volatile boolean initialized = false;
249b5c
+
249b5c
+  protected AbstractFilterPipeline(Injector injector, AbstractServletPipeline servletPipeline,
249b5c
+      Provider<ServletContext> servletContext) {
249b5c
+    this.injector = injector;
249b5c
+    this.servletPipeline = servletPipeline;
249b5c
+    this.servletContext = servletContext;
249b5c
+  }
249b5c
+
249b5c
+  /**
249b5c
+   * @return {@code true} if any filter mappings exist; otherwise {@code false}
249b5c
+   */
249b5c
+  protected abstract boolean hasFiltersMapped();
249b5c
+
249b5c
+  /**
249b5c
+   * @return snapshot of the filter mappings currently defined for this pipeline
249b5c
+   */
249b5c
+  protected abstract FilterDefinition[] filterDefinitions();
249b5c
+
249b5c
+  public synchronized void initPipeline(ServletContext servletContext)
249b5c
+      throws ServletException {
249b5c
+
249b5c
+    //double-checked lock, prevents duplicate initialization
249b5c
+    if (initialized)
249b5c
+      return;
249b5c
+
249b5c
+    // Used to prevent duplicate initialization.
249b5c
+    Set<Filter> initializedSoFar = Sets.newIdentityHashSet();
249b5c
+
249b5c
+    for (FilterDefinition filterDefinition : filterDefinitions()) {
249b5c
+      filterDefinition.init(servletContext, injector, initializedSoFar);
249b5c
+    }
249b5c
+
249b5c
+    //next, initialize servlets...
249b5c
+    servletPipeline.init(servletContext, injector);
249b5c
+
249b5c
+    //everything was ok...
249b5c
+    initialized = true;
249b5c
+  }
249b5c
+
249b5c
+  public void dispatch(ServletRequest request, ServletResponse response,
249b5c
+      FilterChain proceedingFilterChain) throws IOException, ServletException {
249b5c
+
249b5c
+    //lazy init of filter pipeline (OK by the servlet specification). This is needed
249b5c
+    //in order for us not to force users to create a GuiceServletContextListener subclass.
249b5c
+    if (!initialized) {
249b5c
+      initPipeline(servletContext.get());
249b5c
+    }
249b5c
+
249b5c
+    //obtain the servlet pipeline to dispatch against
249b5c
+    new FilterChainInvocation(filterDefinitions(), servletPipeline, proceedingFilterChain)
249b5c
+        .doFilter(withDispatcher(request, servletPipeline), response);
249b5c
+
249b5c
+  }
249b5c
+
249b5c
+  /**
249b5c
+   * Used to create an proxy that dispatches either to the guice-servlet pipeline or the regular
249b5c
+   * pipeline based on uri-path match. This proxy also provides minimal forwarding support.
249b5c
+   *
249b5c
+   * We cannot forward from a web.xml Servlet/JSP to a guice-servlet (because the filter pipeline
249b5c
+   * is not called again). However, we can wrap requests with our own dispatcher to forward the
249b5c
+   * *other* way. web.xml Servlets/JSPs can forward to themselves as per normal.
249b5c
+   *
249b5c
+   * This is not a problem cuz we intend for people to migrate from web.xml to guice-servlet,
249b5c
+   * incrementally, but not the other way around (which, we should actively discourage).
249b5c
+   */
249b5c
+  @SuppressWarnings({ "JavaDoc", "deprecation" })
249b5c
+  private ServletRequest withDispatcher(ServletRequest servletRequest,
249b5c
+      final AbstractServletPipeline servletPipeline) {
249b5c
+
249b5c
+    // don't wrap the request if there are no servlets mapped. This prevents us from inserting our
249b5c
+    // wrapper unless it's actually going to be used. This is necessary for compatibility for apps
249b5c
+    // that downcast their HttpServletRequests to a concrete implementation.
249b5c
+    if (!servletPipeline.hasServletsMapped()) {
249b5c
+      return servletRequest;
249b5c
+    }
249b5c
+
249b5c
+    HttpServletRequest request = (HttpServletRequest) servletRequest;
249b5c
+    //noinspection OverlyComplexAnonymousInnerClass
249b5c
+    return new HttpServletRequestWrapper(request) {
249b5c
+
249b5c
+      @Override
249b5c
+      public RequestDispatcher getRequestDispatcher(String path) {
249b5c
+        final RequestDispatcher dispatcher = servletPipeline.getRequestDispatcher(path);
249b5c
+
249b5c
+        return (null != dispatcher) ? dispatcher : super.getRequestDispatcher(path);
249b5c
+      }
249b5c
+    };
249b5c
+  }
249b5c
+
249b5c
+  public void destroyPipeline() {
249b5c
+    //destroy servlets first
249b5c
+    servletPipeline.destroy();
249b5c
+
249b5c
+    //go down chain and destroy all our filters
249b5c
+    Set<Filter> destroyedSoFar = Sets.newIdentityHashSet();
249b5c
+    for (FilterDefinition filterDefinition : filterDefinitions()) {
249b5c
+      filterDefinition.destroy(destroyedSoFar);
249b5c
+    }
249b5c
+  }
249b5c
+}
249b5c
diff --git a/extensions/servlet/src/com/google/inject/servlet/AbstractServletPipeline.java b/extensions/servlet/src/com/google/inject/servlet/AbstractServletPipeline.java
249b5c
new file mode 100644
249b5c
index 0000000..811820a
249b5c
--- /dev/null
249b5c
+++ b/extensions/servlet/src/com/google/inject/servlet/AbstractServletPipeline.java
249b5c
@@ -0,0 +1,187 @@
249b5c
+/**
249b5c
+ * Copyright (C) 2008 Google Inc.
249b5c
+ *
249b5c
+ * Licensed under the Apache License, Version 2.0 (the "License");
249b5c
+ * you may not use this file except in compliance with the License.
249b5c
+ * You may obtain a copy of the License at
249b5c
+ *
249b5c
+ * http://www.apache.org/licenses/LICENSE-2.0
249b5c
+ *
249b5c
+ * Unless required by applicable law or agreed to in writing, software
249b5c
+ * distributed under the License is distributed on an "AS IS" BASIS,
249b5c
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
249b5c
+ * See the License for the specific language governing permissions and
249b5c
+ * limitations under the License.
249b5c
+ */
249b5c
+package com.google.inject.servlet;
249b5c
+
249b5c
+import com.google.common.base.Preconditions;
249b5c
+import com.google.common.collect.Sets;
249b5c
+import com.google.inject.Injector;
249b5c
+
249b5c
+import java.io.IOException;
249b5c
+import java.util.Set;
249b5c
+
249b5c
+import javax.servlet.RequestDispatcher;
249b5c
+import javax.servlet.ServletContext;
249b5c
+import javax.servlet.ServletException;
249b5c
+import javax.servlet.ServletRequest;
249b5c
+import javax.servlet.ServletResponse;
249b5c
+import javax.servlet.http.HttpServlet;
249b5c
+import javax.servlet.http.HttpServletRequest;
249b5c
+import javax.servlet.http.HttpServletRequestWrapper;
249b5c
+
249b5c
+/**
249b5c
+ * Skeleton implementation of a wrapping dispatcher for servlets based on a sequence of
249b5c
+ * {@link ServletDefinition}s, much like {@link AbstractFilterPipeline} is for filters.
249b5c
+ *
249b5c
+ * @author dhanji@gmail.com (Dhanji R. Prasanna)
249b5c
+ */
249b5c
+public abstract class AbstractServletPipeline {
249b5c
+
249b5c
+  /**
249b5c
+   * @return {@code true} if any servlet mappings exist; otherwise {@code false}
249b5c
+   */
249b5c
+  protected abstract boolean hasServletsMapped();
249b5c
+
249b5c
+  /**
249b5c
+   * @return snapshot of the servlet mappings currently defined for this pipeline
249b5c
+   */
249b5c
+  protected abstract ServletDefinition[] servletDefinitions();
249b5c
+
249b5c
+  void init(ServletContext servletContext, Injector injector) throws ServletException {
249b5c
+    Set<HttpServlet> initializedSoFar = Sets.newIdentityHashSet();
249b5c
+
249b5c
+    for (ServletDefinition servletDefinition : servletDefinitions()) {
249b5c
+      servletDefinition.init(servletContext, injector, initializedSoFar);
249b5c
+    }
249b5c
+  }
249b5c
+
249b5c
+  boolean service(ServletRequest request, ServletResponse response)
249b5c
+      throws IOException, ServletException {
249b5c
+
249b5c
+    //stop at the first matching servlet and service
249b5c
+    for (ServletDefinition servletDefinition : servletDefinitions()) {
249b5c
+      if (servletDefinition.service(request, response)) {
249b5c
+        return true;
249b5c
+      }
249b5c
+    }
249b5c
+
249b5c
+    //there was no match...
249b5c
+    return false;
249b5c
+  }
249b5c
+
249b5c
+  void destroy() {
249b5c
+    Set<HttpServlet> destroyedSoFar = Sets.newIdentityHashSet();
249b5c
+    for (ServletDefinition servletDefinition : servletDefinitions()) {
249b5c
+      servletDefinition.destroy(destroyedSoFar);
249b5c
+    }
249b5c
+  }
249b5c
+
249b5c
+  /**
249b5c
+   * @return Returns a request dispatcher wrapped with a servlet mapped to
249b5c
+   * the given path or null if no mapping was found.
249b5c
+   */
249b5c
+  RequestDispatcher getRequestDispatcher(String path) {
249b5c
+    final String newRequestUri = path;
249b5c
+
249b5c
+    // TODO(dhanji): check servlet spec to see if the following is legal or not.
249b5c
+    // Need to strip query string if requested...
249b5c
+
249b5c
+    for (final ServletDefinition servletDefinition : servletDefinitions()) {
249b5c
+      if (servletDefinition.shouldServe(path)) {
249b5c
+        return new RequestDispatcher() {
249b5c
+          public void forward(ServletRequest servletRequest, ServletResponse servletResponse)
249b5c
+              throws ServletException, IOException {
249b5c
+            Preconditions.checkState(!servletResponse.isCommitted(),
249b5c
+                "Response has been committed--you can only call forward before"
249b5c
+                + " committing the response (hint: don't flush buffers)");
249b5c
+
249b5c
+            // clear buffer before forwarding
249b5c
+            servletResponse.resetBuffer();
249b5c
+
249b5c
+            ServletRequest requestToProcess;
249b5c
+            if (servletRequest instanceof HttpServletRequest) {
249b5c
+               requestToProcess = wrapRequest((HttpServletRequest)servletRequest, newRequestUri);
249b5c
+            } else {
249b5c
+              // This should never happen, but instead of throwing an exception
249b5c
+              // we will allow a happy case pass thru for maximum tolerance to
249b5c
+              // legacy (and internal) code.
249b5c
+              requestToProcess = servletRequest;
249b5c
+            }
249b5c
+
249b5c
+            // now dispatch to the servlet
249b5c
+            doServiceImpl(servletDefinition, requestToProcess, servletResponse);
249b5c
+          }
249b5c
+
249b5c
+          public void include(ServletRequest servletRequest, ServletResponse servletResponse)
249b5c
+              throws ServletException, IOException {
249b5c
+            // route to the target servlet
249b5c
+            doServiceImpl(servletDefinition, servletRequest, servletResponse);
249b5c
+          }
249b5c
+
249b5c
+          private void doServiceImpl(ServletDefinition servletDefinition, ServletRequest servletRequest,
249b5c
+              ServletResponse servletResponse) throws ServletException, IOException {
249b5c
+            servletRequest.setAttribute(REQUEST_DISPATCHER_REQUEST, Boolean.TRUE);
249b5c
+
249b5c
+            try {
249b5c
+              servletDefinition.doService(servletRequest, servletResponse);
249b5c
+            } finally {
249b5c
+              servletRequest.removeAttribute(REQUEST_DISPATCHER_REQUEST);
249b5c
+            }
249b5c
+          }
249b5c
+        };
249b5c
+      }
249b5c
+    }
249b5c
+
249b5c
+    //otherwise, can't process
249b5c
+    return null;
249b5c
+  }
249b5c
+
249b5c
+  // visible for testing
249b5c
+  static HttpServletRequest wrapRequest(HttpServletRequest request, String newUri) {
249b5c
+    return new RequestDispatcherRequestWrapper(request, newUri);
249b5c
+  }
249b5c
+
249b5c
+  /**
249b5c
+   * A Marker constant attribute that when present in the request indicates to Guice servlet that
249b5c
+   * this request has been generated by a request dispatcher rather than the servlet pipeline.
249b5c
+   * In accordance with section 8.4.2 of the Servlet 2.4 specification.
249b5c
+   */
249b5c
+  static final String REQUEST_DISPATCHER_REQUEST = "javax.servlet.forward.servlet_path";
249b5c
+
249b5c
+  private static class RequestDispatcherRequestWrapper extends HttpServletRequestWrapper {
249b5c
+    private final String newRequestUri;
249b5c
+
249b5c
+    RequestDispatcherRequestWrapper(HttpServletRequest servletRequest, String newRequestUri) {
249b5c
+      super(servletRequest);
249b5c
+      this.newRequestUri = newRequestUri;
249b5c
+    }
249b5c
+
249b5c
+    @Override
249b5c
+    public String getRequestURI() {
249b5c
+      return newRequestUri;
249b5c
+    }
249b5c
+
249b5c
+    @Override
249b5c
+    public StringBuffer getRequestURL() {
249b5c
+      StringBuffer url = new StringBuffer();
249b5c
+      String scheme = getScheme();
249b5c
+      int port = getServerPort();
249b5c
+
249b5c
+      url.append(scheme);
249b5c
+      url.append("://");
249b5c
+      url.append(getServerName());
249b5c
+      // port might be -1 in some cases (see java.net.URL.getPort)
249b5c
+      if (port > 0 &&
249b5c
+          (("http".equals(scheme) && (port != 80)) ||
249b5c
+           ("https".equals(scheme) && (port != 443)))) {
249b5c
+        url.append(':');
249b5c
+        url.append(port);
249b5c
+      }
249b5c
+      url.append(getRequestURI());
249b5c
+
249b5c
+      return (url);
249b5c
+    }
249b5c
+  }
249b5c
+}
249b5c
diff --git a/extensions/servlet/src/com/google/inject/servlet/FilterChainInvocation.java b/extensions/servlet/src/com/google/inject/servlet/FilterChainInvocation.java
249b5c
index b4112cf..bfe5a83 100644
249b5c
--- a/extensions/servlet/src/com/google/inject/servlet/FilterChainInvocation.java
249b5c
+++ b/extensions/servlet/src/com/google/inject/servlet/FilterChainInvocation.java
249b5c
@@ -50,7 +50,7 @@ class FilterChainInvocation implements FilterChain {
249b5c
   
249b5c
   private final FilterDefinition[] filterDefinitions;
249b5c
   private final FilterChain proceedingChain;
249b5c
-  private final ManagedServletPipeline servletPipeline;
249b5c
+  private final AbstractServletPipeline servletPipeline;
249b5c
 
249b5c
   //state variable tracks current link in filterchain
249b5c
   private int index = -1;
249b5c
@@ -58,7 +58,7 @@ class FilterChainInvocation implements FilterChain {
249b5c
   private boolean cleanedStacks = false;
249b5c
 
249b5c
   public FilterChainInvocation(FilterDefinition[] filterDefinitions,
249b5c
-      ManagedServletPipeline servletPipeline, FilterChain proceedingChain) {
249b5c
+      AbstractServletPipeline servletPipeline, FilterChain proceedingChain) {
249b5c
 
249b5c
     this.filterDefinitions = filterDefinitions;
249b5c
     this.servletPipeline = servletPipeline;
249b5c
diff --git a/extensions/servlet/src/com/google/inject/servlet/FilterDefinition.java b/extensions/servlet/src/com/google/inject/servlet/FilterDefinition.java
249b5c
index ff1e5b6..76ece31 100644
249b5c
--- a/extensions/servlet/src/com/google/inject/servlet/FilterDefinition.java
249b5c
+++ b/extensions/servlet/src/com/google/inject/servlet/FilterDefinition.java
249b5c
@@ -37,11 +37,11 @@ import javax.servlet.ServletException;
249b5c
 import javax.servlet.http.HttpServletRequest;
249b5c
 
249b5c
 /**
249b5c
- * An internal representation of a filter definition against a particular URI pattern.
249b5c
+ * Defines a filter mapped to a URI pattern and performs the request filtering for that filter.
249b5c
  *
249b5c
  * @author dhanji@gmail.com (Dhanji R. Prasanna)
249b5c
  */
249b5c
-class FilterDefinition implements ProviderWithExtensionVisitor<FilterDefinition> {
249b5c
+public class FilterDefinition implements ProviderWithExtensionVisitor<FilterDefinition> {
249b5c
   private final String pattern;
249b5c
   private final Key filterKey;
249b5c
   private final UriPatternMatcher patternMatcher;
249b5c
@@ -90,7 +90,7 @@ class FilterDefinition implements ProviderWithExtensionVisitor<FilterDefinition>
249b5c
     return uri != null && patternMatcher.matches(uri);
249b5c
   }
249b5c
 
249b5c
-  public void init(final ServletContext servletContext, Injector injector,
249b5c
+  void init(final ServletContext servletContext, Injector injector,
249b5c
       Set<Filter> initializedSoFar) throws ServletException {
249b5c
 
249b5c
     // This absolutely must be a singleton, and so is only initialized once.
249b5c
@@ -130,7 +130,7 @@ class FilterDefinition implements ProviderWithExtensionVisitor<FilterDefinition>
249b5c
     initializedSoFar.add(filter);
249b5c
   }
249b5c
 
249b5c
-  public void destroy(Set<Filter> destroyedSoFar) {
249b5c
+  void destroy(Set<Filter> destroyedSoFar) {
249b5c
     // filters are always singletons
249b5c
     Filter reference = filter.get();
249b5c
 
249b5c
@@ -150,7 +150,7 @@ class FilterDefinition implements ProviderWithExtensionVisitor<FilterDefinition>
249b5c
     }
249b5c
   }
249b5c
 
249b5c
-  public Filter getFilterIfMatching(HttpServletRequest request) {
249b5c
+  Filter getFilterIfMatching(HttpServletRequest request) {
249b5c
 
249b5c
     final String path = ServletUtils.getContextRelativePath(request);
249b5c
     if (shouldFilter(path)) {
249b5c
diff --git a/extensions/servlet/src/com/google/inject/servlet/FilterPipeline.java b/extensions/servlet/src/com/google/inject/servlet/FilterPipeline.java
249b5c
index 985064b..c745d20 100644
249b5c
--- a/extensions/servlet/src/com/google/inject/servlet/FilterPipeline.java
249b5c
+++ b/extensions/servlet/src/com/google/inject/servlet/FilterPipeline.java
249b5c
@@ -26,7 +26,7 @@ import javax.servlet.ServletRequest;
249b5c
 import javax.servlet.ServletResponse;
249b5c
 
249b5c
 /**
249b5c
- * An internal dispatcher for guice-servlet registered servlets and filters.
249b5c
+ * A dispatcher abstraction for guice-servlet registered servlets and filters.
249b5c
  * By default, we assume a Guice 1.0 style servlet module is in play. In other
249b5c
  * words, we dispatch directly to the web.xml pipeline after setting up scopes.
249b5c
  *
249b5c
@@ -39,10 +39,27 @@ import javax.servlet.ServletResponse;
249b5c
  * @author dhanji@gmail.com (Dhanji R. Prasanna)
249b5c
  */
249b5c
 @ImplementedBy(DefaultFilterPipeline.class)
249b5c
-interface FilterPipeline {
249b5c
+public interface FilterPipeline {
249b5c
+
249b5c
+  /**
249b5c
+   * Initializes the pipeline, putting it into service.
249b5c
+   * 
249b5c
+   * @param context of the web application
249b5c
+   */
249b5c
   void initPipeline(ServletContext context) throws ServletException;
249b5c
+
249b5c
+  /**
249b5c
+   * Destroys the pipeline, taking it out of service.
249b5c
+   */
249b5c
   void destroyPipeline();
249b5c
 
249b5c
+  /**
249b5c
+   * Dispatches a request against the pipeline.
249b5c
+   * 
249b5c
+   * @param request to dispatch
249b5c
+   * @param response to populate
249b5c
+   * @param defaultFilterChain for last resort filtering
249b5c
+   */
249b5c
   void dispatch(ServletRequest request, ServletResponse response,
249b5c
       FilterChain defaultFilterChain) throws IOException, ServletException;
249b5c
 }
249b5c
diff --git a/extensions/servlet/src/com/google/inject/servlet/GuiceFilter.java b/extensions/servlet/src/com/google/inject/servlet/GuiceFilter.java
249b5c
index ba7a5af..0737b38 100644
249b5c
--- a/extensions/servlet/src/com/google/inject/servlet/GuiceFilter.java
249b5c
+++ b/extensions/servlet/src/com/google/inject/servlet/GuiceFilter.java
249b5c
@@ -89,7 +89,7 @@ public class GuiceFilter implements Filter {
249b5c
     this(null);
249b5c
   }
249b5c
 
249b5c
-  @Inject GuiceFilter(FilterPipeline filterPipeline) {
249b5c
+  @Inject protected GuiceFilter(FilterPipeline filterPipeline) {
249b5c
     injectedPipeline = filterPipeline;
249b5c
   }
249b5c
 
249b5c
diff --git a/extensions/servlet/src/com/google/inject/servlet/ManagedFilterPipeline.java b/extensions/servlet/src/com/google/inject/servlet/ManagedFilterPipeline.java
249b5c
index 538e10a..76bc269 100644
249b5c
--- a/extensions/servlet/src/com/google/inject/servlet/ManagedFilterPipeline.java
249b5c
+++ b/extensions/servlet/src/com/google/inject/servlet/ManagedFilterPipeline.java
249b5c
@@ -16,7 +16,6 @@
249b5c
 package com.google.inject.servlet;
249b5c
 
249b5c
 import com.google.common.collect.Lists;
249b5c
-import com.google.common.collect.Sets;
249b5c
 import com.google.inject.Binding;
249b5c
 import com.google.inject.Inject;
249b5c
 import com.google.inject.Injector;
249b5c
@@ -24,50 +23,41 @@ import com.google.inject.Provider;
249b5c
 import com.google.inject.Singleton;
249b5c
 import com.google.inject.TypeLiteral;
249b5c
 
249b5c
-import java.io.IOException;
249b5c
 import java.util.List;
249b5c
-import java.util.Set;
249b5c
 
249b5c
-import javax.servlet.Filter;
249b5c
-import javax.servlet.FilterChain;
249b5c
-import javax.servlet.RequestDispatcher;
249b5c
 import javax.servlet.ServletContext;
249b5c
-import javax.servlet.ServletException;
249b5c
-import javax.servlet.ServletRequest;
249b5c
-import javax.servlet.ServletResponse;
249b5c
-import javax.servlet.http.HttpServletRequest;
249b5c
-import javax.servlet.http.HttpServletRequestWrapper;
249b5c
 
249b5c
 /**
249b5c
- * Central routing/dispatch class handles lifecycle of managed filters, and delegates to the servlet
249b5c
- * pipeline.
249b5c
+ * Managed implementation of a central routing/dispatch class which handles lifecycle of managed
249b5c
+ * filters, and delegates to a managed servlet pipeline.
249b5c
  *
249b5c
  * @author dhanji@gmail.com (Dhanji R. Prasanna)
249b5c
  */
249b5c
 @Singleton
249b5c
-class ManagedFilterPipeline implements FilterPipeline{
249b5c
+class ManagedFilterPipeline extends AbstractFilterPipeline {
249b5c
   private final FilterDefinition[] filterDefinitions;
249b5c
-  private final ManagedServletPipeline servletPipeline;
249b5c
-  private final Provider<ServletContext> servletContext;
249b5c
 
249b5c
-  //Unfortunately, we need the injector itself in order to create filters + servlets
249b5c
-  private final Injector injector;
249b5c
-
249b5c
-  //Guards a DCL, so needs to be volatile
249b5c
-  private volatile boolean initialized = false;
249b5c
   private static final TypeLiteral<FilterDefinition> FILTER_DEFS =
249b5c
       TypeLiteral.get(FilterDefinition.class);
249b5c
 
249b5c
   @Inject
249b5c
   public ManagedFilterPipeline(Injector injector, ManagedServletPipeline servletPipeline,
249b5c
       Provider<ServletContext> servletContext) {
249b5c
-    this.injector = injector;
249b5c
-    this.servletPipeline = servletPipeline;
249b5c
-    this.servletContext = servletContext;
249b5c
+    super(injector, servletPipeline, servletContext);
249b5c
 
249b5c
     this.filterDefinitions = collectFilterDefinitions(injector);
249b5c
   }
249b5c
 
249b5c
+  @Override
249b5c
+  protected boolean hasFiltersMapped() {
249b5c
+    return filterDefinitions.length > 0;
249b5c
+  }
249b5c
+
249b5c
+  @Override
249b5c
+  protected FilterDefinition[] filterDefinitions() {
249b5c
+    return filterDefinitions;
249b5c
+  }
249b5c
+
249b5c
   /**
249b5c
    * Introspects the injector and collects all instances of bound {@code List<FilterDefinition>}
249b5c
    * into a master list.
249b5c
@@ -84,86 +74,4 @@ class ManagedFilterPipeline implements FilterPipeline{
249b5c
     // Copy to a fixed-size array for speed of iteration.
249b5c
     return filterDefinitions.toArray(new FilterDefinition[filterDefinitions.size()]);
249b5c
   }
249b5c
-
249b5c
-  public synchronized void initPipeline(ServletContext servletContext)
249b5c
-      throws ServletException {
249b5c
-
249b5c
-    //double-checked lock, prevents duplicate initialization
249b5c
-    if (initialized)
249b5c
-      return;
249b5c
-
249b5c
-    // Used to prevent duplicate initialization.
249b5c
-    Set<Filter> initializedSoFar = Sets.newIdentityHashSet();
249b5c
-
249b5c
-    for (FilterDefinition filterDefinition : filterDefinitions) {
249b5c
-      filterDefinition.init(servletContext, injector, initializedSoFar);
249b5c
-    }
249b5c
-
249b5c
-    //next, initialize servlets...
249b5c
-    servletPipeline.init(servletContext, injector);
249b5c
-
249b5c
-    //everything was ok...
249b5c
-    initialized = true;
249b5c
-  }
249b5c
-
249b5c
-  public void dispatch(ServletRequest request, ServletResponse response,
249b5c
-      FilterChain proceedingFilterChain) throws IOException, ServletException {
249b5c
-
249b5c
-    //lazy init of filter pipeline (OK by the servlet specification). This is needed
249b5c
-    //in order for us not to force users to create a GuiceServletContextListener subclass.
249b5c
-    if (!initialized) {
249b5c
-      initPipeline(servletContext.get());
249b5c
-    }
249b5c
-
249b5c
-    //obtain the servlet pipeline to dispatch against
249b5c
-    new FilterChainInvocation(filterDefinitions, servletPipeline, proceedingFilterChain)
249b5c
-        .doFilter(withDispatcher(request, servletPipeline), response);
249b5c
-
249b5c
-  }
249b5c
-
249b5c
-  /**
249b5c
-   * Used to create an proxy that dispatches either to the guice-servlet pipeline or the regular
249b5c
-   * pipeline based on uri-path match. This proxy also provides minimal forwarding support.
249b5c
-   *
249b5c
-   * We cannot forward from a web.xml Servlet/JSP to a guice-servlet (because the filter pipeline
249b5c
-   * is not called again). However, we can wrap requests with our own dispatcher to forward the
249b5c
-   * *other* way. web.xml Servlets/JSPs can forward to themselves as per normal.
249b5c
-   *
249b5c
-   * This is not a problem cuz we intend for people to migrate from web.xml to guice-servlet,
249b5c
-   * incrementally, but not the other way around (which, we should actively discourage).
249b5c
-   */
249b5c
-  @SuppressWarnings({ "JavaDoc", "deprecation" })
249b5c
-  private ServletRequest withDispatcher(ServletRequest servletRequest,
249b5c
-      final ManagedServletPipeline servletPipeline) {
249b5c
-
249b5c
-    // don't wrap the request if there are no servlets mapped. This prevents us from inserting our
249b5c
-    // wrapper unless it's actually going to be used. This is necessary for compatibility for apps
249b5c
-    // that downcast their HttpServletRequests to a concrete implementation.
249b5c
-    if (!servletPipeline.hasServletsMapped()) {
249b5c
-      return servletRequest;
249b5c
-    }
249b5c
-
249b5c
-    HttpServletRequest request = (HttpServletRequest) servletRequest;
249b5c
-    //noinspection OverlyComplexAnonymousInnerClass
249b5c
-    return new HttpServletRequestWrapper(request) {
249b5c
-
249b5c
-      @Override
249b5c
-      public RequestDispatcher getRequestDispatcher(String path) {
249b5c
-        final RequestDispatcher dispatcher = servletPipeline.getRequestDispatcher(path);
249b5c
-
249b5c
-        return (null != dispatcher) ? dispatcher : super.getRequestDispatcher(path);
249b5c
-      }
249b5c
-    };
249b5c
-  }
249b5c
-
249b5c
-  public void destroyPipeline() {
249b5c
-    //destroy servlets first
249b5c
-    servletPipeline.destroy();
249b5c
-
249b5c
-    //go down chain and destroy all our filters
249b5c
-    Set<Filter> destroyedSoFar = Sets.newIdentityHashSet();
249b5c
-    for (FilterDefinition filterDefinition : filterDefinitions) {
249b5c
-      filterDefinition.destroy(destroyedSoFar);
249b5c
-    }
249b5c
-  }
249b5c
 }
249b5c
diff --git a/extensions/servlet/src/com/google/inject/servlet/ManagedServletPipeline.java b/extensions/servlet/src/com/google/inject/servlet/ManagedServletPipeline.java
249b5c
index 455551a..ab58a8e 100644
249b5c
--- a/extensions/servlet/src/com/google/inject/servlet/ManagedServletPipeline.java
249b5c
+++ b/extensions/servlet/src/com/google/inject/servlet/ManagedServletPipeline.java
249b5c
@@ -15,27 +15,14 @@
249b5c
  */
249b5c
 package com.google.inject.servlet;
249b5c
 
249b5c
-import com.google.common.base.Preconditions;
249b5c
 import com.google.common.collect.Lists;
249b5c
-import com.google.common.collect.Sets;
249b5c
 import com.google.inject.Binding;
249b5c
 import com.google.inject.Inject;
249b5c
 import com.google.inject.Injector;
249b5c
 import com.google.inject.Singleton;
249b5c
 import com.google.inject.TypeLiteral;
249b5c
 
249b5c
-import java.io.IOException;
249b5c
 import java.util.List;
249b5c
-import java.util.Set;
249b5c
-
249b5c
-import javax.servlet.RequestDispatcher;
249b5c
-import javax.servlet.ServletContext;
249b5c
-import javax.servlet.ServletException;
249b5c
-import javax.servlet.ServletRequest;
249b5c
-import javax.servlet.ServletResponse;
249b5c
-import javax.servlet.http.HttpServlet;
249b5c
-import javax.servlet.http.HttpServletRequest;
249b5c
-import javax.servlet.http.HttpServletRequestWrapper;
249b5c
 
249b5c
 /**
249b5c
  * A wrapping dispatcher for servlets, in much the same way as {@link ManagedFilterPipeline} is for
249b5c
@@ -44,7 +31,7 @@ import javax.servlet.http.HttpServletRequestWrapper;
249b5c
  * @author dhanji@gmail.com (Dhanji R. Prasanna)
249b5c
  */
249b5c
 @Singleton
249b5c
-class ManagedServletPipeline {
249b5c
+class ManagedServletPipeline extends AbstractServletPipeline {
249b5c
   private final ServletDefinition[] servletDefinitions;
249b5c
   private static final TypeLiteral<ServletDefinition> SERVLET_DEFS =
249b5c
       TypeLiteral.get(ServletDefinition.class);
249b5c
@@ -54,10 +41,16 @@ class ManagedServletPipeline {
249b5c
     this.servletDefinitions = collectServletDefinitions(injector);
249b5c
   }
249b5c
 
249b5c
-  boolean hasServletsMapped() {
249b5c
+  @Override
249b5c
+  protected boolean hasServletsMapped() {
249b5c
     return servletDefinitions.length > 0;
249b5c
   }
249b5c
 
249b5c
+  @Override
249b5c
+  protected ServletDefinition[] servletDefinitions() {
249b5c
+    return servletDefinitions;
249b5c
+  }
249b5c
+
249b5c
   /**
249b5c
    * Introspects the injector and collects all instances of bound {@code List<ServletDefinition>}
249b5c
    * into a master list.
249b5c
@@ -74,140 +67,4 @@ class ManagedServletPipeline {
249b5c
     // Copy to a fixed size array for speed.
249b5c
     return servletDefinitions.toArray(new ServletDefinition[servletDefinitions.size()]);
249b5c
   }
249b5c
-
249b5c
-  public void init(ServletContext servletContext, Injector injector) throws ServletException {
249b5c
-    Set<HttpServlet> initializedSoFar = Sets.newIdentityHashSet();
249b5c
-
249b5c
-    for (ServletDefinition servletDefinition : servletDefinitions) {
249b5c
-      servletDefinition.init(servletContext, injector, initializedSoFar);
249b5c
-    }
249b5c
-  }
249b5c
-
249b5c
-  public boolean service(ServletRequest request, ServletResponse response)
249b5c
-      throws IOException, ServletException {
249b5c
-
249b5c
-    //stop at the first matching servlet and service
249b5c
-    for (ServletDefinition servletDefinition : servletDefinitions) {
249b5c
-      if (servletDefinition.service(request, response)) {
249b5c
-        return true;
249b5c
-      }
249b5c
-    }
249b5c
-
249b5c
-    //there was no match...
249b5c
-    return false;
249b5c
-  }
249b5c
-
249b5c
-  public void destroy() {
249b5c
-    Set<HttpServlet> destroyedSoFar = Sets.newIdentityHashSet();
249b5c
-    for (ServletDefinition servletDefinition : servletDefinitions) {
249b5c
-      servletDefinition.destroy(destroyedSoFar);
249b5c
-    }
249b5c
-  }
249b5c
-
249b5c
-  /**
249b5c
-   * @return Returns a request dispatcher wrapped with a servlet mapped to
249b5c
-   * the given path or null if no mapping was found.
249b5c
-   */
249b5c
-  RequestDispatcher getRequestDispatcher(String path) {
249b5c
-    final String newRequestUri = path;
249b5c
-
249b5c
-    // TODO(dhanji): check servlet spec to see if the following is legal or not.
249b5c
-    // Need to strip query string if requested...
249b5c
-
249b5c
-    for (final ServletDefinition servletDefinition : servletDefinitions) {
249b5c
-      if (servletDefinition.shouldServe(path)) {
249b5c
-        return new RequestDispatcher() {
249b5c
-          public void forward(ServletRequest servletRequest, ServletResponse servletResponse)
249b5c
-              throws ServletException, IOException {
249b5c
-            Preconditions.checkState(!servletResponse.isCommitted(),
249b5c
-                "Response has been committed--you can only call forward before"
249b5c
-                + " committing the response (hint: don't flush buffers)");
249b5c
-
249b5c
-            // clear buffer before forwarding
249b5c
-            servletResponse.resetBuffer();
249b5c
-
249b5c
-            ServletRequest requestToProcess;
249b5c
-            if (servletRequest instanceof HttpServletRequest) {
249b5c
-               requestToProcess = wrapRequest((HttpServletRequest)servletRequest, newRequestUri);
249b5c
-            } else {
249b5c
-              // This should never happen, but instead of throwing an exception
249b5c
-              // we will allow a happy case pass thru for maximum tolerance to
249b5c
-              // legacy (and internal) code.
249b5c
-              requestToProcess = servletRequest;
249b5c
-            }
249b5c
-
249b5c
-            // now dispatch to the servlet
249b5c
-            doServiceImpl(servletDefinition, requestToProcess, servletResponse);
249b5c
-          }
249b5c
-
249b5c
-          public void include(ServletRequest servletRequest, ServletResponse servletResponse)
249b5c
-              throws ServletException, IOException {
249b5c
-            // route to the target servlet
249b5c
-            doServiceImpl(servletDefinition, servletRequest, servletResponse);
249b5c
-          }
249b5c
-
249b5c
-          private void doServiceImpl(ServletDefinition servletDefinition, ServletRequest servletRequest,
249b5c
-              ServletResponse servletResponse) throws ServletException, IOException {
249b5c
-            servletRequest.setAttribute(REQUEST_DISPATCHER_REQUEST, Boolean.TRUE);
249b5c
-
249b5c
-            try {
249b5c
-              servletDefinition.doService(servletRequest, servletResponse);
249b5c
-            } finally {
249b5c
-              servletRequest.removeAttribute(REQUEST_DISPATCHER_REQUEST);
249b5c
-            }
249b5c
-          }
249b5c
-        };
249b5c
-      }
249b5c
-    }
249b5c
-
249b5c
-    //otherwise, can't process
249b5c
-    return null;
249b5c
-  }
249b5c
-
249b5c
-  // visible for testing
249b5c
-  static HttpServletRequest wrapRequest(HttpServletRequest request, String newUri) {
249b5c
-    return new RequestDispatcherRequestWrapper(request, newUri);
249b5c
-  }
249b5c
-
249b5c
-  /**
249b5c
-   * A Marker constant attribute that when present in the request indicates to Guice servlet that
249b5c
-   * this request has been generated by a request dispatcher rather than the servlet pipeline.
249b5c
-   * In accordance with section 8.4.2 of the Servlet 2.4 specification.
249b5c
-   */
249b5c
-  public static final String REQUEST_DISPATCHER_REQUEST = "javax.servlet.forward.servlet_path";
249b5c
-
249b5c
-  private static class RequestDispatcherRequestWrapper extends HttpServletRequestWrapper {
249b5c
-    private final String newRequestUri;
249b5c
-
249b5c
-    public RequestDispatcherRequestWrapper(HttpServletRequest servletRequest, String newRequestUri) {
249b5c
-      super(servletRequest);
249b5c
-      this.newRequestUri = newRequestUri;
249b5c
-    }
249b5c
-
249b5c
-    @Override
249b5c
-    public String getRequestURI() {
249b5c
-      return newRequestUri;
249b5c
-    }
249b5c
-
249b5c
-    @Override
249b5c
-    public StringBuffer getRequestURL() {
249b5c
-      StringBuffer url = new StringBuffer();
249b5c
-      String scheme = getScheme();
249b5c
-      int port = getServerPort();
249b5c
-
249b5c
-      url.append(scheme);
249b5c
-      url.append("://");
249b5c
-      url.append(getServerName());
249b5c
-      // port might be -1 in some cases (see java.net.URL.getPort)
249b5c
-      if (port > 0 &&
249b5c
-          (("http".equals(scheme) && (port != 80)) ||
249b5c
-           ("https".equals(scheme) && (port != 443)))) {
249b5c
-        url.append(':');
249b5c
-        url.append(port);
249b5c
-      }
249b5c
-      url.append(getRequestURI());
249b5c
-
249b5c
-      return (url);
249b5c
-    }
249b5c
-  }
249b5c
 }
249b5c
diff --git a/extensions/servlet/src/com/google/inject/servlet/ServletDefinition.java b/extensions/servlet/src/com/google/inject/servlet/ServletDefinition.java
249b5c
index 11328ed..285ff31 100644
249b5c
--- a/extensions/servlet/src/com/google/inject/servlet/ServletDefinition.java
249b5c
+++ b/extensions/servlet/src/com/google/inject/servlet/ServletDefinition.java
249b5c
@@ -46,12 +46,11 @@ import javax.servlet.http.HttpServletRequestWrapper;
249b5c
 import javax.servlet.http.HttpServletResponse;
249b5c
 
249b5c
 /**
249b5c
- * An internal representation of a servlet definition mapped to a particular URI pattern. Also
249b5c
- * performs the request dispatch to that servlet. How nice and OO =)
249b5c
+ * Defines a servlet mapped to a URI pattern and performs the request dispatch to that servlet.
249b5c
  *
249b5c
  * @author dhanji@gmail.com (Dhanji R. Prasanna)
249b5c
  */
249b5c
-class ServletDefinition implements ProviderWithExtensionVisitor<ServletDefinition> {
249b5c
+public class ServletDefinition implements ProviderWithExtensionVisitor<ServletDefinition> {
249b5c
   private final String pattern;
249b5c
   private final Key servletKey;
249b5c
   private final UriPatternMatcher patternMatcher;
249b5c
@@ -100,7 +99,7 @@ class ServletDefinition implements ProviderWithExtensionVisitor
249b5c
     return uri != null && patternMatcher.matches(uri);
249b5c
   }
249b5c
 
249b5c
-  public void init(final ServletContext servletContext, Injector injector,
249b5c
+  void init(final ServletContext servletContext, Injector injector,
249b5c
       Set<HttpServlet> initializedSoFar) throws ServletException {
249b5c
 
249b5c
     // This absolutely must be a singleton, and so is only initialized once.
249b5c
@@ -140,7 +139,7 @@ class ServletDefinition implements ProviderWithExtensionVisitor
249b5c
     initializedSoFar.add(httpServlet);
249b5c
   }
249b5c
 
249b5c
-  public void destroy(Set<HttpServlet> destroyedSoFar) {
249b5c
+  void destroy(Set<HttpServlet> destroyedSoFar) {
249b5c
     HttpServlet reference = httpServlet.get();
249b5c
 
249b5c
     // Do nothing if this Servlet was invalid (usually due to not being scoped
249b5c
@@ -169,7 +168,7 @@ class ServletDefinition implements ProviderWithExtensionVisitor
249b5c
    * @throws IOException If thrown by underlying servlet
249b5c
    * @throws ServletException If thrown by underlying servlet
249b5c
    */
249b5c
-  public boolean service(ServletRequest servletRequest,
249b5c
+  boolean service(ServletRequest servletRequest,
249b5c
       ServletResponse servletResponse) throws IOException, ServletException {
249b5c
 
249b5c
     final HttpServletRequest request = (HttpServletRequest) servletRequest;
249b5c
diff --git a/extensions/servlet/src/com/google/inject/servlet/UriPatternMatcher.java b/extensions/servlet/src/com/google/inject/servlet/UriPatternMatcher.java
249b5c
index d8bac74..169dd89 100644
249b5c
--- a/extensions/servlet/src/com/google/inject/servlet/UriPatternMatcher.java
249b5c
+++ b/extensions/servlet/src/com/google/inject/servlet/UriPatternMatcher.java
249b5c
@@ -22,7 +22,7 @@ package com.google.inject.servlet;
249b5c
  *
249b5c
  * @author dhanji@gmail.com (Dhanji R. Prasanna)
249b5c
  */
249b5c
-interface UriPatternMatcher {
249b5c
+public interface UriPatternMatcher {
249b5c
   /**
249b5c
    * @param uri A "contextual" (i.e. relative) Request URI, *not* a complete one.
249b5c
    * @return Returns true if the uri matches the pattern.
249b5c
diff --git a/extensions/servlet/src/com/google/inject/servlet/UriPatternType.java b/extensions/servlet/src/com/google/inject/servlet/UriPatternType.java
249b5c
index 80d6aea..c76199d 100644
249b5c
--- a/extensions/servlet/src/com/google/inject/servlet/UriPatternType.java
249b5c
+++ b/extensions/servlet/src/com/google/inject/servlet/UriPatternType.java
249b5c
@@ -26,7 +26,14 @@ import java.util.regex.Pattern;
249b5c
 public enum UriPatternType {
249b5c
   SERVLET, REGEX;
249b5c
 
249b5c
-  static UriPatternMatcher get(UriPatternType type, String pattern) {
249b5c
+  /**
249b5c
+   * Returns the appropriate {@link UriPatternMatcher} for {@code pattern}.
249b5c
+   * 
249b5c
+   * @param type of pattern matching
249b5c
+   * @param pattern for matching URI
249b5c
+   * @return {@link UriPatternMatcher} that matches the given pattern
249b5c
+   */
249b5c
+  public static UriPatternMatcher get(UriPatternType type, String pattern) {
249b5c
     switch (type) {
249b5c
       case SERVLET:
249b5c
         return new ServletStyleUriPatternMatcher(pattern);
249b5c
commit c2a5f1b0d3dc946bf4c9c240bce600ccf65230fa
249b5c
Author: Stuart McCulloch <mcculls@gmail.com>
249b5c
Date:   Tue Jan 20 18:51:01 2015 +0000
249b5c
249b5c
    Enhance logging in filter/servlet definitions
249b5c
249b5c
diff --git a/extensions/servlet/src/com/google/inject/servlet/FilterDefinition.java b/extensions/servlet/src/com/google/inject/servlet/FilterDefinition.java
249b5c
index 76ece31..6819665 100644
249b5c
--- a/extensions/servlet/src/com/google/inject/servlet/FilterDefinition.java
249b5c
+++ b/extensions/servlet/src/com/google/inject/servlet/FilterDefinition.java
249b5c
@@ -15,6 +15,7 @@
249b5c
  */
249b5c
 package com.google.inject.servlet;
249b5c
 
249b5c
+import com.google.common.base.Strings;
249b5c
 import com.google.common.collect.Iterators;
249b5c
 import com.google.inject.Injector;
249b5c
 import com.google.inject.Key;
249b5c
@@ -29,6 +30,8 @@ import java.util.HashMap;
249b5c
 import java.util.Map;
249b5c
 import java.util.Set;
249b5c
 import java.util.concurrent.atomic.AtomicReference;
249b5c
+import java.util.logging.Level;
249b5c
+import java.util.logging.Logger;
249b5c
 
249b5c
 import javax.servlet.Filter;
249b5c
 import javax.servlet.FilterConfig;
249b5c
@@ -42,6 +45,8 @@ import javax.servlet.http.HttpServletRequest;
249b5c
  * @author dhanji@gmail.com (Dhanji R. Prasanna)
249b5c
  */
249b5c
 public class FilterDefinition implements ProviderWithExtensionVisitor<FilterDefinition> {
249b5c
+  private static final Logger logger = Logger.getLogger(FilterDefinition.class.getName());
249b5c
+
249b5c
   private final String pattern;
249b5c
   private final Key filterKey;
249b5c
   private final UriPatternMatcher patternMatcher;
249b5c
@@ -154,7 +159,11 @@ public class FilterDefinition implements ProviderWithExtensionVisitor
249b5c
 
249b5c
     final String path = ServletUtils.getContextRelativePath(request);
249b5c
     if (shouldFilter(path)) {
249b5c
-      return filter.get();
249b5c
+      Filter reference = filter.get();
249b5c
+      if (logger.isLoggable(Level.FINEST)) {
249b5c
+        logger.finest("Filtering " + path + " with " + reference);
249b5c
+      }
249b5c
+      return reference;
249b5c
     } else {
249b5c
       return null;
249b5c
     }
249b5c
@@ -164,4 +173,10 @@ public class FilterDefinition implements ProviderWithExtensionVisitor
249b5c
   Filter getFilter() {
249b5c
     return filter.get();
249b5c
   }
249b5c
+
249b5c
+  public String toPaddedString(int padding) {
249b5c
+    Filter reference = filter.get();
249b5c
+    return Strings.padEnd(pattern, padding, ' ') + ' '
249b5c
+        + (reference != null ? reference : filterKey);
249b5c
+  }
249b5c
 }
249b5c
diff --git a/extensions/servlet/src/com/google/inject/servlet/GuiceFilter.java b/extensions/servlet/src/com/google/inject/servlet/GuiceFilter.java
249b5c
index 0737b38..1a98bf4 100644
249b5c
--- a/extensions/servlet/src/com/google/inject/servlet/GuiceFilter.java
249b5c
+++ b/extensions/servlet/src/com/google/inject/servlet/GuiceFilter.java
249b5c
@@ -99,9 +99,9 @@ public class GuiceFilter implements Filter {
249b5c
 
249b5c
     // This can happen if you create many injectors and they all have their own
249b5c
     // servlet module. This is legal, caveat a small warning.
249b5c
-    if (GuiceFilter.pipeline instanceof ManagedFilterPipeline) {
249b5c
-      LOGGER.warning(MULTIPLE_INJECTORS_WARNING);
249b5c
-    }
249b5c
+    //if (GuiceFilter.pipeline instanceof ManagedFilterPipeline) {
249b5c
+    //  LOGGER.warning(MULTIPLE_INJECTORS_WARNING);
249b5c
+    //}
249b5c
 
249b5c
     // We overwrite the default pipeline
249b5c
     GuiceFilter.pipeline = pipeline;
249b5c
diff --git a/extensions/servlet/src/com/google/inject/servlet/ServletDefinition.java b/extensions/servlet/src/com/google/inject/servlet/ServletDefinition.java
249b5c
index 285ff31..9e49ce5 100644
249b5c
--- a/extensions/servlet/src/com/google/inject/servlet/ServletDefinition.java
249b5c
+++ b/extensions/servlet/src/com/google/inject/servlet/ServletDefinition.java
249b5c
@@ -17,6 +17,7 @@ package com.google.inject.servlet;
249b5c
 
249b5c
 import static com.google.inject.servlet.ManagedServletPipeline.REQUEST_DISPATCHER_REQUEST;
249b5c
 
249b5c
+import com.google.common.base.Strings;
249b5c
 import com.google.common.collect.Iterators;
249b5c
 import com.google.inject.Injector;
249b5c
 import com.google.inject.Key;
249b5c
@@ -34,6 +35,8 @@ import java.util.HashMap;
249b5c
 import java.util.Map;
249b5c
 import java.util.Set;
249b5c
 import java.util.concurrent.atomic.AtomicReference;
249b5c
+import java.util.logging.Level;
249b5c
+import java.util.logging.Logger;
249b5c
 
249b5c
 import javax.servlet.ServletConfig;
249b5c
 import javax.servlet.ServletContext;
249b5c
@@ -51,6 +54,8 @@ import javax.servlet.http.HttpServletResponse;
249b5c
  * @author dhanji@gmail.com (Dhanji R. Prasanna)
249b5c
  */
249b5c
 public class ServletDefinition implements ProviderWithExtensionVisitor<ServletDefinition> {
249b5c
+  private static final Logger logger = Logger.getLogger(ServletDefinition.class.getName());
249b5c
+
249b5c
   private final String pattern;
249b5c
   private final Key servletKey;
249b5c
   private final UriPatternMatcher patternMatcher;
249b5c
@@ -283,7 +288,14 @@ public class ServletDefinition implements ProviderWithExtensionVisitor
249b5c
         = (previous != null) ? previous.getOriginalRequest() : request;
249b5c
     GuiceFilter.localContext.set(new GuiceFilter.Context(originalRequest, request, response));
249b5c
     try {
249b5c
-      httpServlet.get().service(request, response);
249b5c
+      HttpServlet reference = httpServlet.get();
249b5c
+      if (logger.isLoggable(Level.FINEST)) {
249b5c
+        String path = ServletUtils.getContextRelativePath(request);
249b5c
+        logger.finest("Serving " + path + " with " + reference);
249b5c
+      }
249b5c
+      if (reference != null) {
249b5c
+        reference.service(request, response);
249b5c
+      }
249b5c
     } finally {
249b5c
       GuiceFilter.localContext.set(previous);
249b5c
     }
249b5c
@@ -296,4 +308,10 @@ public class ServletDefinition implements ProviderWithExtensionVisitor
249b5c
   String getPattern() {
249b5c
     return pattern;
249b5c
   }
249b5c
+
249b5c
+  public String toPaddedString(int padding) {
249b5c
+    HttpServlet reference = httpServlet.get();
249b5c
+    return Strings.padEnd(pattern, padding, ' ') + ' '
249b5c
+        + (reference != null ? reference : servletKey);
249b5c
+  }
249b5c
 }