Revision: fee2d237bd4d
Author:   Sam Berlin <[email protected]>
Date:     Mon Oct 31 13:35:33 2011
Log: Ensure servlets & filters are processed in the correct order in all scenarios. Fix so that code like:

MainModule extends ServletModule {
   configureServlets() {
       filter("/").through(FirstFilter.class);
       install(new SecondaryModule());
   }
}

SecondaryModule extends ServletModule {
  configureServlets() {
     filter("/").through(SecondaryFilter.class);
  }
}

.. ends up with a request to "/" going to FirstFilter and _then_ SecondaryFilter.

Revision created by MOE tool push_codebase.
MOE_MIGRATION=3589

http://code.google.com/p/google-guice/source/detail?r=fee2d237bd4d

Modified:
/extensions/servlet/src/com/google/inject/servlet/FilterChainInvocation.java
 /extensions/servlet/src/com/google/inject/servlet/FiltersModuleBuilder.java
/extensions/servlet/src/com/google/inject/servlet/ManagedFilterPipeline.java
 /extensions/servlet/src/com/google/inject/servlet/ServletModule.java
/extensions/servlet/src/com/google/inject/servlet/ServletsModuleBuilder.java /extensions/servlet/test/com/google/inject/servlet/FilterDefinitionTest.java /extensions/servlet/test/com/google/inject/servlet/FilterDispatchIntegrationTest.java
 /extensions/servlet/test/com/google/inject/servlet/FilterPipelineTest.java
 /extensions/servlet/test/com/google/inject/servlet/ServletTestUtils.java

=======================================
--- /extensions/servlet/src/com/google/inject/servlet/FilterChainInvocation.java Mon Oct 31 13:34:04 2011 +++ /extensions/servlet/src/com/google/inject/servlet/FilterChainInvocation.java Mon Oct 31 13:35:33 2011
@@ -18,7 +18,6 @@
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;

 import java.io.IOException;
 import java.util.List;
=======================================
--- /extensions/servlet/src/com/google/inject/servlet/FiltersModuleBuilder.java Thu Jul 7 17:34:16 2011 +++ /extensions/servlet/src/com/google/inject/servlet/FiltersModuleBuilder.java Mon Oct 31 13:35:33 2011
@@ -15,8 +15,7 @@
  */
 package com.google.inject.servlet;

-import com.google.common.collect.Lists;
-import com.google.inject.AbstractModule;
+import com.google.inject.Binder;
 import com.google.inject.Key;
 import com.google.inject.internal.UniqueAnnotations;

@@ -33,23 +32,12 @@
  *
  * @author [email protected] (Dhanji R. Prasanna)
  */
-class FiltersModuleBuilder extends AbstractModule {
- private final List<FilterDefinition> filterDefinitions = Lists.newArrayList(); - private final List<FilterInstanceBindingEntry> filterInstanceEntries = Lists.newArrayList();
-
-  //invoked on injector config
-  @Override
-  protected void configure() {
-    // Create bindings for filter instances
-    for (FilterInstanceBindingEntry entry : filterInstanceEntries) {
-      bind(entry.key).toInstance(entry.filter);
-    }
-
- // Bind these filter definitions to a unique random key. Doesn't matter what it is,
-    // coz it's never used.
-    for(FilterDefinition fd : filterDefinitions) {
- bind(FilterDefinition.class).annotatedWith(UniqueAnnotations.create()).toProvider(fd);
-    }
+class FiltersModuleBuilder {
+
+  private final Binder binder;
+
+  public FiltersModuleBuilder(Binder binder) {
+    this.binder = binder;
   }

public ServletModule.FilterKeyBindingBuilder filter(List<String> patterns) {
@@ -59,16 +47,6 @@
public ServletModule.FilterKeyBindingBuilder filterRegex(List<String> regexes) {
     return new FilterKeyBindingBuilderImpl(regexes, UriPatternType.REGEX);
   }
-
-  private static class FilterInstanceBindingEntry {
-    final Key<Filter> key;
-    final Filter filter;
-
-    FilterInstanceBindingEntry(Key<Filter> key, Filter filter) {
-      this.key = key;
-      this.filter = filter;
-    }
-  }

   //non-static inner class so it can access state of enclosing module class
class FilterKeyBindingBuilderImpl implements ServletModule.FilterKeyBindingBuilder {
@@ -108,7 +86,7 @@
         Map<String, String> initParams,
         Filter filterInstance) {
       for (String pattern : uriPatterns) {
-        filterDefinitions.add(
+ binder.bind(FilterDefinition.class).annotatedWith(UniqueAnnotations.create()).toProvider( new FilterDefinition(pattern, filterKey, UriPatternType.get(uriPatternType, pattern),
                 initParams, filterInstance));
       }
@@ -117,7 +95,7 @@
     public void through(Filter filter,
         Map<String, String> initParams) {
Key<Filter> filterKey = Key.get(Filter.class, UniqueAnnotations.create()); - filterInstanceEntries.add(new FilterInstanceBindingEntry(filterKey, filter));
+      binder.bind(filterKey).toInstance(filter);
       through(filterKey, initParams, filter);
     }
   }
=======================================
--- /extensions/servlet/src/com/google/inject/servlet/ManagedFilterPipeline.java Tue Sep 27 08:36:19 2011 +++ /extensions/servlet/src/com/google/inject/servlet/ManagedFilterPipeline.java Mon Oct 31 13:35:33 2011
@@ -81,8 +81,8 @@
for (Binding<FilterDefinition> entry : injector.findBindingsByType(FILTER_DEFS)) {
       filterDefinitions.add(entry.getProvider().get());
     }
-
-    // Convert to a fixed size array for speed.
+
+    // Copy to a fixed-size array for speed of iteration.
return filterDefinitions.toArray(new FilterDefinition[filterDefinitions.size()]);
   }

=======================================
--- /extensions/servlet/src/com/google/inject/servlet/ServletModule.java Tue Oct 18 13:43:18 2011 +++ /extensions/servlet/src/com/google/inject/servlet/ServletModule.java Mon Oct 31 13:35:33 2011
@@ -45,16 +45,14 @@
   protected final void configure() {
     checkState(filtersModuleBuilder == null, "Re-entry is not allowed.");
     checkState(servletsModuleBuilder == null, "Re-entry is not allowed.");
-    filtersModuleBuilder = new FiltersModuleBuilder();
-    servletsModuleBuilder = new ServletsModuleBuilder();
+    filtersModuleBuilder = new FiltersModuleBuilder(binder());
+    servletsModuleBuilder = new ServletsModuleBuilder(binder());
     try {
       // Install common bindings (skipped if already installed).
       install(new InternalServletModule());

       // Install local filter and servlet bindings.
       configureServlets();
-      install(filtersModuleBuilder);
-      install(servletsModuleBuilder);
     } finally {
       filtersModuleBuilder = null;
       servletsModuleBuilder = null;
=======================================
--- /extensions/servlet/src/com/google/inject/servlet/ServletsModuleBuilder.java Thu Jul 7 17:34:16 2011 +++ /extensions/servlet/src/com/google/inject/servlet/ServletsModuleBuilder.java Mon Oct 31 13:35:33 2011
@@ -15,9 +15,8 @@
  */
 package com.google.inject.servlet;

-import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
-import com.google.inject.AbstractModule;
+import com.google.inject.Binder;
 import com.google.inject.Key;
 import com.google.inject.internal.UniqueAnnotations;

@@ -35,30 +34,13 @@
  *
  * @author Dhanji R. Prasanna ([email protected])
  */
-class ServletsModuleBuilder extends AbstractModule {
- private final List<ServletDefinition> servletDefinitions = Lists.newArrayList(); - private final List<ServletInstanceBindingEntry> servletInstanceEntries = Lists.newArrayList();
-
-  //invoked on injector config
-  @Override
-  protected void configure() {
-    // Create bindings for servlet instances
-    for (ServletInstanceBindingEntry entry : servletInstanceEntries) {
-      bind(entry.key).toInstance(entry.servlet);
-    }
-
-    // Ensure that servlets are not bound twice to the same pattern.
-    Set<String> servletUris = Sets.newHashSet();
-    for (ServletDefinition servletDefinition : servletDefinitions) {
-      if (servletUris.contains(servletDefinition.getPattern())) {
- addError("More than one servlet was mapped to the same URI pattern: "
-            + servletDefinition.getPattern());
-      }
-      else {
- bind(Key.get(ServletDefinition.class, UniqueAnnotations.create())).toProvider(servletDefinition);
-        servletUris.add(servletDefinition.getPattern());
-      }
-    }
+class ServletsModuleBuilder {
+
+  private final Set<String> servletUris = Sets.newHashSet();
+  private final Binder binder;
+
+  public ServletsModuleBuilder(Binder binder) {
+    this.binder = binder;
   }

   //the first level of the EDSL--
@@ -69,16 +51,6 @@
public ServletModule.ServletKeyBindingBuilder serveRegex(List<String> regexes) {
     return new ServletKeyBindingBuilderImpl(regexes, UriPatternType.REGEX);
   }
-
-  private static class ServletInstanceBindingEntry {
-    final Key<HttpServlet> key;
-    final HttpServlet servlet;
-
- ServletInstanceBindingEntry(Key<HttpServlet> key, HttpServlet servlet) {
-      this.key = key;
-      this.servlet = servlet;
-    }
-  }

   //non-static inner class so it can access state of enclosing module class
class ServletKeyBindingBuilderImpl implements ServletModule.ServletKeyBindingBuilder {
@@ -111,21 +83,25 @@
         Map<String, String> initParams) {
       with(servletKey, initParams, null);
     }
-
-    private void with(Key<? extends HttpServlet> servletKey,
-        Map<String, String> initParams,
+
+ private void with(Key<? extends HttpServlet> servletKey, Map<String, String> initParams,
         HttpServlet servletInstance) {
       for (String pattern : uriPatterns) {
-        servletDefinitions.add(
- new ServletDefinition(pattern, servletKey, UriPatternType.get(uriPatternType, pattern),
-                initParams, servletInstance));
+        // Ensure two servlets aren't bound to the same pattern.
+        if (!servletUris.add(pattern)) {
+ binder.addError("More than one servlet was mapped to the same URI pattern: " + pattern);
+        } else {
+ binder.bind(Key.get(ServletDefinition.class, UniqueAnnotations.create())).toProvider(
+              new ServletDefinition(pattern, servletKey, UriPatternType
+ .get(uriPatternType, pattern), initParams, servletInstance));
+        }
       }
     }

     public void with(HttpServlet servlet,
         Map<String, String> initParams) {
Key<HttpServlet> servletKey = Key.get(HttpServlet.class, UniqueAnnotations.create()); - servletInstanceEntries.add(new ServletInstanceBindingEntry(servletKey, servlet));
+      binder.bind(servletKey).toInstance(servlet);
       with(servletKey, initParams, servlet);
     }
   }
=======================================
--- /extensions/servlet/test/com/google/inject/servlet/FilterDefinitionTest.java Thu Jul 7 17:34:16 2011 +++ /extensions/servlet/test/com/google/inject/servlet/FilterDefinitionTest.java Mon Oct 31 13:35:33 2011
@@ -119,7 +119,8 @@

     String pattern = "/*";
final FilterDefinition filterDef = new FilterDefinition(pattern, Key.get(Filter.class), - UriPatternType.get(UriPatternType.SERVLET, pattern), new HashMap<String, String>(), null);
+        UriPatternType.get(UriPatternType.SERVLET, pattern),
+        new HashMap<String, String>(), null);
     //should fire on mockfilter now
     filterDef.init(createMock(ServletContext.class), injector,
         Sets.newSetFromMap(Maps.<Filter, Boolean>newIdentityHashMap()));
@@ -129,9 +130,8 @@

     final boolean proceed[] = new boolean[1];
filterDef.doFilter(request, null, new FilterChainInvocation(null, null, null) { - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse)
-          throws IOException, ServletException {
-
+      @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse) {
         proceed[0] = true;
       }
     });
@@ -153,10 +153,9 @@
     HttpServletRequest request = createMock(HttpServletRequest.class);

     final MockFilter mockFilter = new MockFilter() {
+      @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
-          FilterChain filterChain) throws IOException, ServletException {
-        setRun(true);
-
+          FilterChain filterChain) {
         //suppress rest of chain...
       }
     };
@@ -179,7 +178,8 @@

     String pattern = "/*";
final FilterDefinition filterDef = new FilterDefinition(pattern, Key.get(Filter.class), - UriPatternType.get(UriPatternType.SERVLET, pattern), new HashMap<String, String>(), null);
+        UriPatternType.get(UriPatternType.SERVLET, pattern),
+        new HashMap<String, String>(), null);
     //should fire on mockfilter now
     filterDef.init(createMock(ServletContext.class), injector,
         Sets.newSetFromMap(Maps.<Filter, Boolean>newIdentityHashMap()));
@@ -190,8 +190,8 @@

     final boolean proceed[] = new boolean[1];
filterDef.doFilter(request, null, new FilterChainInvocation(null, null, null) { - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse)
-          throws IOException, ServletException {
+      @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse) {
         proceed[0] = true;
       }
     });
@@ -208,10 +208,9 @@
   private static class MockFilter implements Filter {
     private boolean init;
     private boolean destroy;
-    private boolean run;
     private FilterConfig config;

-    public void init(FilterConfig filterConfig) throws ServletException {
+    public void init(FilterConfig filterConfig) {
       init = true;

       this.config = filterConfig;
@@ -219,15 +218,9 @@

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
         FilterChain filterChain) throws IOException, ServletException {
-      run = true;
-
       //proceed
       filterChain.doFilter(servletRequest, servletResponse);
     }
-
-    protected void setRun(boolean run) {
-      this.run = run;
-    }

     public void destroy() {
       destroy = true;
@@ -240,10 +233,6 @@
     public boolean isDestroy() {
       return destroy;
     }
-
-    public boolean isRun() {
-      return run;
-    }

     public FilterConfig getConfig() {
       return config;
=======================================
--- /extensions/servlet/test/com/google/inject/servlet/FilterDispatchIntegrationTest.java Mon Oct 31 13:34:04 2011 +++ /extensions/servlet/test/com/google/inject/servlet/FilterDispatchIntegrationTest.java Mon Oct 31 13:35:33 2011
@@ -1,7 +1,24 @@
+/**
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.google.inject.servlet;

import static com.google.inject.servlet.ManagedServletPipeline.REQUEST_DISPATCHER_REQUEST; import static com.google.inject.servlet.ServletTestUtils.newFakeHttpServletRequest; +import static com.google.inject.servlet.ServletTestUtils.newNoOpFilterChain;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.expectLastCall;

@@ -18,12 +35,11 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;

 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
-import javax.servlet.Servlet;
-import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
@@ -199,7 +215,7 @@

   @Singleton
   public static class TestFilter implements Filter {
-    public void init(FilterConfig filterConfig) throws ServletException {
+    public void init(FilterConfig filterConfig) {
       inits++;
     }

@@ -220,6 +236,7 @@
     public static final String FORWARD_TO = "/forwarded.html";
     public List<String> processedUris = new ArrayList<String>();

+    @Override
protected void service(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
         throws ServletException, IOException {
       String requestUri = httpServletRequest.getRequestURI();
@@ -232,11 +249,62 @@
       }
     }

+    @Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse)
         throws ServletException, IOException {
service((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
     }
   }
+
+  public void testFilterOrder() throws Exception {
+    AtomicInteger counter = new AtomicInteger();
+    final CountFilter f1 = new CountFilter(counter);
+    final CountFilter f2 = new CountFilter(counter);
+
+    Injector injector = Guice.createInjector(new ServletModule() {
+      @Override
+      protected void configureServlets() {
+        filter("/").through(f1);
+        install(new ServletModule() {
+          @Override
+          protected void configureServlets() {
+            filter("/").through(f2);
+          }
+        });
+      }
+    });
+
+    HttpServletRequest request = newFakeHttpServletRequest();
+ final FilterPipeline pipeline = injector.getInstance(FilterPipeline.class);
+    pipeline.initPipeline(null);
+    pipeline.dispatch(request, null, newNoOpFilterChain());
+    assertEquals(0, f1.calledAt);
+    assertEquals(1, f2.calledAt);
+  }
+
+ /** A filter that keeps count of when it was called by increment a counter. */
+  private static class CountFilter implements Filter {
+    private final AtomicInteger counter;
+    private int calledAt = -1;
+
+    public CountFilter(AtomicInteger counter) {
+      this.counter = counter;
+    }
+
+    public void destroy() {
+    }
+
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+        throws ServletException, IOException {
+      if (calledAt != -1) {
+        fail("not expecting to be called twice");
+      }
+      calledAt = counter.getAndIncrement();
+      chain.doFilter(request, response);
+    }
+
+    public void init(FilterConfig filterConfig) {}
+  }

   public final void testFilterExceptionPrunesStack() throws Exception {
     Injector injector = Guice.createInjector(new ServletModule() {
@@ -295,7 +363,7 @@

     @Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
-        throws ServletException, IOException {
+        throws ServletException {
       throw new ServletException("failure!");
     }

=======================================
--- /extensions/servlet/test/com/google/inject/servlet/FilterPipelineTest.java Thu Jul 7 17:34:16 2011 +++ /extensions/servlet/test/com/google/inject/servlet/FilterPipelineTest.java Mon Oct 31 13:35:33 2011
@@ -98,7 +98,7 @@

   @Singleton
   public static class TestFilter implements Filter {
-    public void init(FilterConfig filterConfig) throws ServletException {
+    public void init(FilterConfig filterConfig) {
     }

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
@@ -112,11 +112,11 @@

   @Singleton
   public static class NeverFilter implements Filter {
-    public void init(FilterConfig filterConfig) throws ServletException {
+    public void init(FilterConfig filterConfig) {
     }

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
-        FilterChain filterChain) throws IOException, ServletException {
+        FilterChain filterChain) {
       fail("This filter should never have fired");
     }

=======================================
--- /extensions/servlet/test/com/google/inject/servlet/ServletTestUtils.java Mon Oct 31 13:34:04 2011 +++ /extensions/servlet/test/com/google/inject/servlet/ServletTestUtils.java Mon Oct 31 13:35:33 2011
@@ -11,6 +11,9 @@
 import java.lang.reflect.Proxy;
 import java.util.Map;

+import javax.servlet.FilterChain;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequestWrapper;
 import javax.servlet.http.HttpServletResponse;
@@ -26,10 +29,20 @@
   private ServletTestUtils() {}

private static class ThrowingInvocationHandler implements InvocationHandler { - @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { throw new UnsupportedOperationException("No methods are supported on this object");
     }
   }
+
+  /**
+   * Returns a FilterChain that does nothing.
+   */
+  public static FilterChain newNoOpFilterChain() {
+    return new FilterChain() {
+ public void doFilter(ServletRequest request, ServletResponse response) {
+      }
+    };
+  }

   /**
* Returns a fake, HttpServletRequest which stores attributes in a HashMap.

--
You received this message because you are subscribed to the Google Groups 
"google-guice-dev" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/google-guice-dev?hl=en.

Reply via email to