Added varargs mapping for filters and servlets. And added a bunch of tests that verify the dispatch pipeline, servlet spec compliance, and so forth.
git-svn-id: https://google-guice.googlecode.com/svn/trunk@738 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/servlet/src/com/google/inject/servlet/FiltersModuleBuilder.java b/servlet/src/com/google/inject/servlet/FiltersModuleBuilder.java
index 2ae75e7..66ca704 100755
--- a/servlet/src/com/google/inject/servlet/FiltersModuleBuilder.java
+++ b/servlet/src/com/google/inject/servlet/FiltersModuleBuilder.java
@@ -41,21 +41,21 @@
bind(FilterPipeline.class).toInstance(new ManagedFilterPipeline(filterDefinitions));
}
- public ServletModule.FilterKeyBindingBuilder filter(String urlPattern) {
- return new FilterKeyBindingBuilderImpl(urlPattern, UriPatternType.SERVLET);
+ public ServletModule.FilterKeyBindingBuilder filter(List<String> patterns) {
+ return new FilterKeyBindingBuilderImpl(patterns, UriPatternType.SERVLET);
}
- public ServletModule.FilterKeyBindingBuilder filterRegex(String regex) {
- return new FilterKeyBindingBuilderImpl(regex, UriPatternType.REGEX);
+ public ServletModule.FilterKeyBindingBuilder filterRegex(List<String> regexes) {
+ return new FilterKeyBindingBuilderImpl(regexes, UriPatternType.REGEX);
}
//non-static inner class so it can access state of enclosing module class
class FilterKeyBindingBuilderImpl implements ServletModule.FilterKeyBindingBuilder {
- private final String uriPattern;
+ private final List<String> uriPatterns;
private final UriPatternType uriPatternType;
- private FilterKeyBindingBuilderImpl(String uriPattern, UriPatternType uriPatternType) {
- this.uriPattern = uriPattern;
+ private FilterKeyBindingBuilderImpl(List<String> uriPatterns, UriPatternType uriPatternType) {
+ this.uriPatterns = uriPatterns;
this.uriPatternType = uriPatternType;
}
@@ -76,9 +76,11 @@
public void through(Key<? extends Filter> filterKey,
Map<String, String> contextParams) {
- filterDefinitions.add(
- new FilterDefinition(uriPattern, filterKey, UriPatternType.get(uriPatternType),
- contextParams));
+ for (String pattern : uriPatterns) {
+ filterDefinitions.add(
+ new FilterDefinition(pattern, filterKey, UriPatternType.get(uriPatternType),
+ contextParams));
+ }
}
}
}
diff --git a/servlet/src/com/google/inject/servlet/GuiceFilter.java b/servlet/src/com/google/inject/servlet/GuiceFilter.java
index 52b07fc..0973fc1 100644
--- a/servlet/src/com/google/inject/servlet/GuiceFilter.java
+++ b/servlet/src/com/google/inject/servlet/GuiceFilter.java
@@ -56,8 +56,7 @@
*/
public class GuiceFilter implements Filter {
static final ThreadLocal<Context> localContext = new ThreadLocal<Context>();
- static volatile WeakReference<FilterPipeline> pipeline =
- new WeakReference<FilterPipeline>(new DefaultFilterPipeline());
+ static volatile FilterPipeline pipeline = new DefaultFilterPipeline();
/** Used to inject the servlets configured via {@link ServletModule} */
static volatile WeakReference<ServletContext> servletContext =
@@ -68,7 +67,7 @@
static void setPipeline(FilterPipeline pipeline) {
// Multiple injectors with ServletModules?
- if (GuiceFilter.pipeline.get() instanceof ManagedFilterPipeline) {
+ if (GuiceFilter.pipeline instanceof ManagedFilterPipeline) {
throw new RuntimeException("Multiple injectors detected. Please install only one"
+ " ServletModule in your web application. While you may "
+ "have more than one injector, you should only configure"
@@ -77,12 +76,12 @@
}
// We will only overwrite the default pipeline
- GuiceFilter.pipeline = new WeakReference<FilterPipeline>(pipeline);
+ GuiceFilter.pipeline = pipeline;
}
//VisibleForTesting
static void clearPipeline() {
- pipeline = new WeakReference<FilterPipeline>(null);
+ pipeline = null;
}
public void doFilter(ServletRequest servletRequest,
@@ -90,7 +89,7 @@
throws IOException, ServletException {
Context previous = localContext.get();
- FilterPipeline filterPipeline = pipeline.get();
+ FilterPipeline filterPipeline = pipeline;
try {
localContext.set(new Context((HttpServletRequest) servletRequest,
@@ -155,17 +154,17 @@
// In the default pipeline, this is a noop. However, if replaced
// by a managed pipeline, a lazy init will be triggered the first time
// dispatch occurs.
- GuiceFilter.pipeline.get().initPipeline(servletContext);
+ GuiceFilter.pipeline.initPipeline(servletContext);
}
public void destroy() {
try {
// Destroy all registered filters & servlets in that order
- pipeline.get().destroyPipeline();
+ pipeline.destroyPipeline();
} finally {
- pipeline.clear();
+ clearPipeline();
servletContext.clear();
}
}
diff --git a/servlet/src/com/google/inject/servlet/ManagedServletPipeline.java b/servlet/src/com/google/inject/servlet/ManagedServletPipeline.java
index b03a390..a354a3c 100755
--- a/servlet/src/com/google/inject/servlet/ManagedServletPipeline.java
+++ b/servlet/src/com/google/inject/servlet/ManagedServletPipeline.java
@@ -77,17 +77,17 @@
+ "flush buffers)");
}
- //clear buffer before forwarding
+ // clear buffer before forwarding
servletResponse.resetBuffer();
- //now dispatch to the servlet
+ // now dispatch to the servlet
servletDefinition.doService(servletRequest, servletResponse);
}
public void include(ServletRequest servletRequest, ServletResponse servletResponse)
throws ServletException, IOException {
- //route to the target servlet
+ // route to the target servlet
servletDefinition.doService(servletRequest, servletResponse);
}
};
diff --git a/servlet/src/com/google/inject/servlet/ServletDefinition.java b/servlet/src/com/google/inject/servlet/ServletDefinition.java
index 008d8aa..43b0df3 100755
--- a/servlet/src/com/google/inject/servlet/ServletDefinition.java
+++ b/servlet/src/com/google/inject/servlet/ServletDefinition.java
@@ -153,8 +153,8 @@
pathInfo = getRequestURI().substring(getContextPath().length()).replaceAll("[/]{2,}", "/")
.substring(servletPathLength);
- //corner case: when servlet path and request path match exactly (without trailing '/'),
- //pathinfo is null
+ // Corner case: when servlet path and request path match exactly (without trailing '/'),
+ // then pathinfo is null
if ("".equals(pathInfo) && servletPathLength != 0) {
pathInfo = null;
}
@@ -177,7 +177,7 @@
return (null == info) ? null : getRealPath(info);
}
- //memoizer pattern
+ // Memoizer pattern.
private String computePath() {
if (!pathComputed) {
path = patternMatcher.extractPath(pattern);
diff --git a/servlet/src/com/google/inject/servlet/ServletModule.java b/servlet/src/com/google/inject/servlet/ServletModule.java
index c0d45b5..b62e694 100644
--- a/servlet/src/com/google/inject/servlet/ServletModule.java
+++ b/servlet/src/com/google/inject/servlet/ServletModule.java
@@ -16,14 +16,13 @@
package com.google.inject.servlet;
-import static com.google.inject.servlet.ServletScopes.REQUEST;
-import static com.google.inject.servlet.ServletScopes.SESSION;
-
+import com.google.common.collect.Lists;
import com.google.inject.AbstractModule;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
-
+import static com.google.inject.servlet.ServletScopes.REQUEST;
+import static com.google.inject.servlet.ServletScopes.SESSION;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.ServletContext;
@@ -154,7 +153,13 @@
* <b>serve("/my/*").with(MyServlet.class)</b>
* </pre>
*
- * You are free to register as many servlets and filters as you like this way:
+ * Every servlet is required to be a singleton and will implicitly be bound as one if it isn't
+ * already. Mapping a servlet that is bound under any other scope is an error.
+ *
+ * <p>
+ * <h4>Dispatch Order</h4>
+ * You are free to register as many servlets and filters as you like this way. They will
+ * be compared and dispatched in the order in which the filter methods are called:
*
* <pre>
*
@@ -162,17 +167,41 @@
*
* {@literal @}Override
* protected void configureServlets() {
- * filter("/*").through(MyFilter.class)
- * filter("*.css").through(MyCssFilter.class)
+ * filter("/*").through(MyFilter.class);
+ * filter("*.css").through(MyCssFilter.class);
* // etc..
*
- * serve("*.html").with(MyServlet.class)
- * serve("/my/*").with(MyServlet.class)
+ * serve("*.html").with(MyServlet.class);
+ * serve("/my/*").with(MyServlet.class);
* // etc..
* }
* }
* </pre>
+ * This will traverse down the list of rules in lexical order. For example, a url
+ * "{@code /my/file.js}" (after it runs through the matching filters) will first
+ * be compared against the servlet mapping:
+ *
+ * <pre>
+ * serve("*.html").with(MyServlet.class);
+ * </pre>
+ * And failing that, it will descend to the next servlet mapping:
*
+ * <pre>
+ * serve("/my/*").with(MyServlet.class);
+ * </pre>
+ *
+ * Since this rule matches, Guice Servlet will dispatch to {@code MyServlet}. These
+ * two mapping rules can also be written in more compact form using varargs syntax:
+ *
+ * <pre>
+ * serve(<b>"*.html", "/my/*"</b>).with(MyServlet.class);
+ * </pre>
+ *
+ * This way you can map several URI patterns to the same servlet. A similar syntax is
+ * also available for filter mappings.
+ *
+ * <p>
+ * <h4>Regular Expressions</h4>
* You can also map servlets (or filters) to URIs using regular expressions:
* <pre>
* <b>serveRegex("(.)*ajax(.)*").with(MyAjaxServlet.class)</b>
@@ -203,15 +232,14 @@
* serve("/*").with(MyServlet.class, <b>params</b>)
* </pre>
*
- *
+ * <p>
* <h3>Binding Keys</h3>
*
- * <p> You can also bind keys rather than classes. This lets you hide
+ * You can also bind keys rather than classes. This lets you hide
* implementations with package-local visbility and expose them using
* only a Guice module and an annotation:
*
* <pre>
- *
* ...
* filter("/*").through(<b>Key.get(Filter.class, Fave.class)</b>);
* </pre>
@@ -224,7 +252,7 @@
* bind(Filter.class)<b>.annotatedWith(Fave.class)</b>.to(MyFilterImpl.class);
* </pre>
*
- * See Guice documentation for more information on binding annotations.
+ * See {@link com.google.inject.Binder} for more information on binding syntax.
*/
protected void configureServlets() {
}
@@ -236,29 +264,29 @@
/**
* @param urlPattern Any Servlet-style pattern. examples: /*, /html/*, *.html, etc.
*/
- protected final FilterKeyBindingBuilder filter(String urlPattern) {
- return filtersModuleBuilder.filter(urlPattern);
+ protected final FilterKeyBindingBuilder filter(String urlPattern, String... morePatterns) {
+ return filtersModuleBuilder.filter(Lists.asList(urlPattern, morePatterns));
}
/**
* @param regex Any Java-style regular expression.
*/
- protected final FilterKeyBindingBuilder filterRegex(String regex) {
- return filtersModuleBuilder.filterRegex(regex);
+ protected final FilterKeyBindingBuilder filterRegex(String regex, String... regexes) {
+ return filtersModuleBuilder.filterRegex(Lists.asList(regex, regexes));
}
/**
* @param urlPattern Any Servlet-style pattern. examples: /*, /html/*, *.html, etc.
*/
- protected final ServletKeyBindingBuilder serve(String urlPattern) {
- return servletsModuleBuilder.serve(urlPattern);
+ protected final ServletKeyBindingBuilder serve(String urlPattern, String... morePatterns) {
+ return servletsModuleBuilder.serve(Lists.asList(urlPattern, morePatterns));
}
/**
* @param regex Any Java-style regular expression.
*/
- protected final ServletKeyBindingBuilder serveRegex(String regex) {
- return servletsModuleBuilder.serveRegex(regex);
+ protected final ServletKeyBindingBuilder serveRegex(String regex, String... regexes) {
+ return servletsModuleBuilder.serveRegex(Lists.asList(regex, regexes));
}
public static interface FilterKeyBindingBuilder {
diff --git a/servlet/src/com/google/inject/servlet/ServletsModuleBuilder.java b/servlet/src/com/google/inject/servlet/ServletsModuleBuilder.java
index bb79139..6f22d3f 100755
--- a/servlet/src/com/google/inject/servlet/ServletsModuleBuilder.java
+++ b/servlet/src/com/google/inject/servlet/ServletsModuleBuilder.java
@@ -41,21 +41,21 @@
}
//the first level of the EDSL--
- public ServletModule.ServletKeyBindingBuilder serve(String urlPattern) {
- return new ServletKeyBindingBuilderImpl(urlPattern, UriPatternType.SERVLET);
+ public ServletModule.ServletKeyBindingBuilder serve(List<String> urlPatterns) {
+ return new ServletKeyBindingBuilderImpl(urlPatterns, UriPatternType.SERVLET);
}
- public ServletModule.ServletKeyBindingBuilder serveRegex(String regex) {
- return new ServletKeyBindingBuilderImpl(regex, UriPatternType.REGEX);
+ public ServletModule.ServletKeyBindingBuilder serveRegex(List<String> regexes) {
+ return new ServletKeyBindingBuilderImpl(regexes, UriPatternType.REGEX);
}
//non-static inner class so it can access state of enclosing module class
class ServletKeyBindingBuilderImpl implements ServletModule.ServletKeyBindingBuilder {
- private final String uriPattern;
+ private final List<String> uriPatterns;
private final UriPatternType uriPatternType;
- private ServletKeyBindingBuilderImpl(String uriPattern, UriPatternType uriPatternType) {
- this.uriPattern = uriPattern;
+ private ServletKeyBindingBuilderImpl(List<String> uriPatterns, UriPatternType uriPatternType) {
+ this.uriPatterns = uriPatterns;
this.uriPatternType = uriPatternType;
}
@@ -74,9 +74,12 @@
public void with(Key<? extends HttpServlet> servletKey,
Map<String, String> contextParams) {
- servletDefinitions.add(
- new ServletDefinition(uriPattern, servletKey, UriPatternType.get(uriPatternType),
- contextParams));
+
+ for (String pattern : uriPatterns) {
+ servletDefinitions.add(
+ new ServletDefinition(pattern, servletKey, UriPatternType.get(uriPatternType),
+ contextParams));
+ }
}
}
}
\ No newline at end of file
diff --git a/servlet/test/com/google/inject/servlet/AllTests.java b/servlet/test/com/google/inject/servlet/AllTests.java
index fe2475c..d868bf1 100644
--- a/servlet/test/com/google/inject/servlet/AllTests.java
+++ b/servlet/test/com/google/inject/servlet/AllTests.java
@@ -27,12 +27,22 @@
public static Test suite() {
TestSuite suite = new TestSuite();
- // Servlet tests.
+ // Filter tests.
suite.addTestSuite(EdslTest.class);
suite.addTestSuite(FilterDefinitionTest.class);
suite.addTestSuite(FilterDispatchIntegrationTest.class);
suite.addTestSuite(FilterPipelineTest.class);
+
+ // Servlet + integration tests.
suite.addTestSuite(ServletTest.class);
+ suite.addTestSuite(ServletDefinitionTest.class);
+ suite.addTestSuite(ServletDefinitionPathsTest.class);
+ suite.addTestSuite(ServletPipelineRequestDispatcherTest.class);
+ suite.addTestSuite(ServletDispatchIntegrationTest.class);
+
+ // Varargs URL mapping tests.
+ suite.addTestSuite(VarargsFilterDispatchIntegrationTest.class);
+ suite.addTestSuite(VarargsServletDispatchIntegrationTest.class);
return suite;
}
diff --git a/servlet/test/com/google/inject/servlet/ServletDefinitionPathsTest.java b/servlet/test/com/google/inject/servlet/ServletDefinitionPathsTest.java
new file mode 100644
index 0000000..a8e3a36
--- /dev/null
+++ b/servlet/test/com/google/inject/servlet/ServletDefinitionPathsTest.java
@@ -0,0 +1,260 @@
+/**
+ * Copyright (C) 2008 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 org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+
+import com.google.inject.Injector;
+import com.google.inject.Key;
+
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.util.HashMap;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Ensures servlet spec compliance for CGI-style variables and general
+ * path/pattern matching.
+ *
+ * @author Dhanji R. Prasanna (dhanji@gmail com)
+ */
+public class ServletDefinitionPathsTest extends TestCase {
+
+ // Data-driven test.
+ public final void testServletPathMatching() throws IOException, ServletException {
+ servletPath("/index.html", "*.html", "/index.html");
+ servletPath("/somewhere/index.html", "*.html", "/somewhere/index.html");
+ servletPath("/somewhere/index.html", "/*", "");
+ servletPath("/index.html", "/*", "");
+ servletPath("/", "/*", "");
+ servletPath("//", "/*", "");
+ servletPath("/////", "/*", "");
+ servletPath("", "/*", "");
+ servletPath("/thing/index.html", "/thing/*", "/thing");
+ servletPath("/thing/wing/index.html", "/thing/*", "/thing");
+ }
+
+ private void servletPath(final String requestPath, String mapping,
+ final String expectedServletPath) throws IOException, ServletException {
+
+ Injector injector = createMock(Injector.class);
+ HttpServletRequest request = createMock(HttpServletRequest.class);
+ HttpServletResponse response = createMock(HttpServletResponse.class);
+
+ final boolean[] run = new boolean[1];
+
+ //get an instance of this servlet
+ expect(injector.getInstance(Key.get(HttpServlet.class)))
+ .andReturn(new HttpServlet() {
+
+ @Override
+ protected void service(HttpServletRequest servletRequest,
+ HttpServletResponse httpServletResponse) throws ServletException, IOException {
+
+ final String path = servletRequest.getServletPath();
+ assertEquals(String.format("expected [%s] but was [%s]", expectedServletPath, path),
+ expectedServletPath, path);
+ run[0] = true;
+ }
+ });
+
+ expect(request.getServletPath())
+ .andReturn(requestPath);
+
+ replay(injector, request);
+
+ ServletDefinition servletDefinition = new ServletDefinition(mapping, Key.get(HttpServlet.class),
+ UriPatternType.get(UriPatternType.SERVLET), new HashMap<String, String>());
+
+ servletDefinition.init(null, injector);
+ servletDefinition.doService(request, response);
+
+ assertTrue("Servlet did not run!", run[0]);
+
+ }
+
+ // Data-driven test.
+ public final void testPathInfoWithServletStyleMatching() throws IOException, ServletException {
+ pathInfoWithServletStyleMatching("/path/index.html", "/path", "/*", "/index.html", "");
+ pathInfoWithServletStyleMatching("/path//hulaboo///index.html", "/path", "/*",
+ "/hulaboo/index.html", "");
+ pathInfoWithServletStyleMatching("/path/", "/path", "/*", "/", "");
+ pathInfoWithServletStyleMatching("/path////////", "/path", "/*", "/", "");
+
+ // a servlet mapping of /thing/*
+ pathInfoWithServletStyleMatching("/path/thing////////", "/path", "/thing/*", "/", "/thing");
+ pathInfoWithServletStyleMatching("/path/thing/stuff", "/path", "/thing/*", "/stuff", "/thing");
+ pathInfoWithServletStyleMatching("/path/thing/stuff.html", "/path", "/thing/*", "/stuff.html",
+ "/thing");
+ pathInfoWithServletStyleMatching("/path/thing", "/path", "/thing/*", null, "/thing");
+
+ // *.xx style mapping
+ pathInfoWithServletStyleMatching("/path/thing.thing", "/path", "*.thing", null, "/thing.thing");
+ pathInfoWithServletStyleMatching("/path///h.thing", "/path", "*.thing", null, "/h.thing");
+ pathInfoWithServletStyleMatching("/path///...//h.thing", "/path", "*.thing", null,
+ "/.../h.thing");
+ pathInfoWithServletStyleMatching("/path/my/h.thing", "/path", "*.thing", null, "/my/h.thing");
+
+ }
+
+ private void pathInfoWithServletStyleMatching(final String requestUri, final String contextPath,
+ String mapping, final String expectedPathInfo, final String servletPath)
+ throws IOException, ServletException {
+
+ Injector injector = createMock(Injector.class);
+ HttpServletRequest request = createMock(HttpServletRequest.class);
+ HttpServletResponse response = createMock(HttpServletResponse.class);
+
+ final boolean[] run = new boolean[1];
+
+ //get an instance of this servlet
+ expect(injector.getInstance(Key.get(HttpServlet.class)))
+ .andReturn(new HttpServlet() {
+
+ @Override
+ protected void service(HttpServletRequest servletRequest,
+ HttpServletResponse httpServletResponse) throws ServletException, IOException {
+
+ final String path = servletRequest.getPathInfo();
+
+ if (null == expectedPathInfo) {
+ assertNull(String.format("expected [%s] but was [%s]", expectedPathInfo, path),
+ path);
+ }
+ else {
+ assertEquals(String.format("expected [%s] but was [%s]", expectedPathInfo, path),
+ expectedPathInfo, path);
+ }
+
+ //assert memoizer
+ //noinspection StringEquality
+ assertSame("memo field did not work", path, servletRequest.getPathInfo());
+
+ run[0] = true;
+ }
+ });
+
+ expect(request.getRequestURI())
+ .andReturn(requestUri);
+
+ expect(request.getServletPath())
+ .andReturn(servletPath)
+ .anyTimes();
+
+ expect(request.getContextPath())
+ .andReturn(contextPath);
+
+ replay(injector, request);
+
+ ServletDefinition servletDefinition = new ServletDefinition(mapping, Key.get(HttpServlet.class),
+ UriPatternType.get(UriPatternType.SERVLET), new HashMap<String, String>());
+
+ servletDefinition.init(null, injector);
+ servletDefinition.doService(request, response);
+
+ assertTrue("Servlet did not run!", run[0]);
+
+ }
+
+ // Data-driven test.
+ public final void testPathInfoWithRegexMatching() throws IOException, ServletException {
+ // first a mapping of /*
+ pathInfoWithRegexMatching("/path/index.html", "/path", "/(.)*", "/index.html", "");
+ pathInfoWithRegexMatching("/path//hulaboo///index.html", "/path", "/(.)*",
+ "/hulaboo/index.html", "");
+ pathInfoWithRegexMatching("/path/", "/path", "/(.)*", "/", "");
+ pathInfoWithRegexMatching("/path////////", "/path", "/(.)*", "/", "");
+
+ // a servlet mapping of /thing/*
+ pathInfoWithRegexMatching("/path/thing////////", "/path", "/thing/(.)*", "/", "/thing");
+ pathInfoWithRegexMatching("/path/thing/stuff", "/path", "/thing/(.)*", "/stuff", "/thing");
+ pathInfoWithRegexMatching("/path/thing/stuff.html", "/path", "/thing/(.)*", "/stuff.html",
+ "/thing");
+ pathInfoWithRegexMatching("/path/thing", "/path", "/thing/(.)*", null, "/thing");
+
+ // *.xx style mapping
+ pathInfoWithRegexMatching("/path/thing.thing", "/path", "(.)*\\.thing", null, "/thing.thing");
+ pathInfoWithRegexMatching("/path///h.thing", "/path", "(.)*\\.thing", null, "/h.thing");
+ pathInfoWithRegexMatching("/path///...//h.thing", "/path", "(.)*\\.thing", null,
+ "/.../h.thing");
+ pathInfoWithRegexMatching("/path/my/h.thing", "/path", "(.)*\\.thing", null, "/my/h.thing");
+ }
+
+ public final void pathInfoWithRegexMatching(final String requestUri, final String contextPath,
+ String mapping, final String expectedPathInfo, final String servletPath)
+ throws IOException, ServletException {
+
+ Injector injector = createMock(Injector.class);
+ HttpServletRequest request = createMock(HttpServletRequest.class);
+ HttpServletResponse response = createMock(HttpServletResponse.class);
+
+ final boolean[] run = new boolean[1];
+
+ //get an instance of this servlet
+ expect(injector.getInstance(Key.get(HttpServlet.class)))
+ .andReturn(new HttpServlet() {
+
+ @Override
+ protected void service(HttpServletRequest servletRequest,
+ HttpServletResponse httpServletResponse) throws ServletException, IOException {
+
+ final String path = servletRequest.getPathInfo();
+
+ if (null == expectedPathInfo) {
+ assertNull(String.format("expected [%s] but was [%s]", expectedPathInfo, path),
+ path);
+ }
+ else {
+ assertEquals(String.format("expected [%s] but was [%s]", expectedPathInfo, path),
+ expectedPathInfo, path);
+ }
+
+ //assert memoizer
+ //noinspection StringEquality
+ assertSame("memo field did not work", path, servletRequest.getPathInfo());
+
+ run[0] = true;
+ }
+ });
+
+ expect(request.getRequestURI())
+ .andReturn(requestUri);
+
+ expect(request.getServletPath())
+ .andReturn(servletPath)
+ .anyTimes();
+
+ expect(request.getContextPath())
+ .andReturn(contextPath);
+
+ replay(injector, request);
+
+ ServletDefinition servletDefinition = new ServletDefinition(mapping, Key.get(HttpServlet.class),
+ UriPatternType.get(UriPatternType.REGEX), new HashMap<String, String>());
+
+ servletDefinition.init(null, injector);
+ servletDefinition.doService(request, response);
+
+ assertTrue("Servlet did not run!", run[0]);
+ }
+}
diff --git a/servlet/test/com/google/inject/servlet/ServletDefinitionTest.java b/servlet/test/com/google/inject/servlet/ServletDefinitionTest.java
new file mode 100644
index 0000000..8197914
--- /dev/null
+++ b/servlet/test/com/google/inject/servlet/ServletDefinitionTest.java
@@ -0,0 +1,90 @@
+/**
+ * Copyright (C) 2008 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 org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import junit.framework.TestCase;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+
+/**
+ * Basic unit test for lifecycle of a ServletDefinition (wrapper).
+ *
+ * @author Dhanji R. Prasanna (dhanji@gmail com)
+ */
+public class ServletDefinitionTest extends TestCase {
+
+ public final void testServletInitAndConfig() throws ServletException {
+ Injector injector = createMock(Injector.class);
+
+ final HttpServlet mockServlet = new HttpServlet() {
+ };
+ expect(injector.getInstance(Key.get(HttpServlet.class)))
+ .andReturn(mockServlet)
+ .anyTimes();
+
+ replay(injector);
+
+ //some init params
+ //noinspection SSBasedInspection
+ final Map<String, String> initParams = new HashMap<String, String>() {
+ {
+ put("ahsd", "asdas24dok");
+ put("ahssd", "asdasd124ok");
+ put("ahfsasd", "asda124sdok");
+ put("ahsasgd", "a124sdasdok");
+ put("ahsd124124", "as124124124dasdok");
+ }
+ };
+
+ final ServletDefinition servletDefinition = new ServletDefinition("/*",
+ Key.get(HttpServlet.class), UriPatternType.get(UriPatternType.SERVLET), initParams);
+
+ ServletContext servletContext = createMock(ServletContext.class);
+ final String contextName = "thing__!@@44__SRV" + getClass();
+ expect(servletContext.getServletContextName())
+ .andReturn(contextName);
+
+ replay(servletContext);
+
+ servletDefinition.init(servletContext, injector);
+
+ assertNotNull(mockServlet.getServletContext());
+ assertEquals(contextName, mockServlet.getServletContext().getServletContextName());
+ assertEquals(Key.get(HttpServlet.class).toString(), mockServlet.getServletName());
+
+ final ServletConfig servletConfig = mockServlet.getServletConfig();
+ final Enumeration names = servletConfig.getInitParameterNames();
+ while (names.hasMoreElements()) {
+ String name = (String) names.nextElement();
+
+ assertTrue(initParams.containsKey(name));
+ assertEquals(initParams.get(name), servletConfig.getInitParameter(name));
+ }
+ }
+}
diff --git a/servlet/test/com/google/inject/servlet/ServletDispatchIntegrationTest.java b/servlet/test/com/google/inject/servlet/ServletDispatchIntegrationTest.java
new file mode 100644
index 0000000..1264623
--- /dev/null
+++ b/servlet/test/com/google/inject/servlet/ServletDispatchIntegrationTest.java
@@ -0,0 +1,191 @@
+/**
+ * Copyright (C) 2008 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 com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Singleton;
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import junit.framework.TestCase;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+
+/**
+ * Tests the FilterPipeline that dispatches to guice-managed servlets,
+ * is a full integration test, with a real injector.
+ *
+ * @author Dhanji R. Prasanna (dhanji gmail com)
+ */
+public class ServletDispatchIntegrationTest extends TestCase {
+ private static int inits, services, destroys, doFilters;
+
+ @Override
+ public void setUp() {
+ inits = 0;
+ services = 0;
+ destroys = 0;
+ doFilters = 0;
+
+ GuiceFilter.clearPipeline();
+ }
+
+ public final void testDispatchRequestToManagedPipelineServlets()
+ throws ServletException, IOException {
+ final Injector injector = Guice.createInjector(new ServletModule() {
+
+ @Override
+ protected void configureServlets() {
+ serve("/*").with(TestServlet.class);
+
+ // These servets should never fire... (ordering test)
+ serve("*.html").with(NeverServlet.class);
+ serve("/*").with(Key.get(NeverServlet.class));
+ serve("/index/*").with(Key.get(NeverServlet.class));
+ serve("*.jsp").with(Key.get(NeverServlet.class));
+ }
+ });
+
+ final FilterPipeline pipeline = injector.getInstance(FilterPipeline.class);
+
+ pipeline.initPipeline(null);
+
+ //create ourselves a mock request with test URI
+ HttpServletRequest requestMock = createMock(HttpServletRequest.class);
+
+ expect(requestMock.getServletPath())
+ .andReturn("/index.html")
+ .times(1);
+
+ //dispatch request
+ replay(requestMock);
+
+ pipeline.dispatch(requestMock, null, createMock(FilterChain.class));
+
+ pipeline.destroyPipeline();
+
+ verify(requestMock);
+
+ assertTrue("lifecycle states did not fire correct number of times-- inits: " + inits + "; dos: "
+ + services + "; destroys: " + destroys,
+ inits == 5 && services == 1 && destroys == 5);
+ }
+
+ public final void testDispatchRequestToManagedPipelineWithFilter()
+ throws ServletException, IOException {
+ final Injector injector = Guice.createInjector(new ServletModule() {
+
+ @Override
+ protected void configureServlets() {
+ filter("/*").through(TestFilter.class);
+
+ serve("/*").with(TestServlet.class);
+
+ // These servets should never fire...
+ serve("*.html").with(NeverServlet.class);
+ serve("/*").with(Key.get(NeverServlet.class));
+ serve("/index/*").with(Key.get(NeverServlet.class));
+ serve("*.jsp").with(Key.get(NeverServlet.class));
+
+ }
+ });
+
+ final FilterPipeline pipeline = injector.getInstance(FilterPipeline.class);
+
+ pipeline.initPipeline(null);
+
+ //create ourselves a mock request with test URI
+ HttpServletRequest requestMock = createMock(HttpServletRequest.class);
+
+ expect(requestMock.getServletPath())
+ .andReturn("/index.html")
+ .times(2);
+
+ //dispatch request
+ replay(requestMock);
+
+ pipeline.dispatch(requestMock, null, createMock(FilterChain.class));
+
+ pipeline.destroyPipeline();
+
+ verify(requestMock);
+
+ assertTrue("lifecycle states did not fire correct number of times-- inits: " + inits + "; dos: "
+ + services + "; destroys: " + destroys,
+ inits == 6 && services == 1 && destroys == 6 && doFilters == 1);
+ }
+
+ @Singleton
+ public static class TestServlet extends HttpServlet {
+ public void init(ServletConfig filterConfig) throws ServletException {
+ inits++;
+ }
+
+ public void service(ServletRequest servletRequest, ServletResponse servletResponse)
+ throws IOException, ServletException {
+ services++;
+ }
+
+ public void destroy() {
+ destroys++;
+ }
+ }
+
+ @Singleton
+ public static class NeverServlet extends HttpServlet {
+ public void init(ServletConfig filterConfig) throws ServletException {
+ inits++;
+ }
+
+ public void service(ServletRequest servletRequest, ServletResponse servletResponse)
+ throws IOException, ServletException {
+ assertTrue("NeverServlet was fired, when it should not have been.", false);
+ }
+
+ public void destroy() {
+ destroys++;
+ }
+ }
+
+ @Singleton
+ public static class TestFilter implements Filter {
+ public void init(FilterConfig filterConfig) throws ServletException {
+ inits++;
+ }
+
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
+ FilterChain filterChain) throws IOException, ServletException {
+ doFilters++;
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+
+ public void destroy() {
+ destroys++;
+ }
+ }
+}
\ No newline at end of file
diff --git a/servlet/test/com/google/inject/servlet/ServletPipelineRequestDispatcherTest.java b/servlet/test/com/google/inject/servlet/ServletPipelineRequestDispatcherTest.java
new file mode 100644
index 0000000..b0ded86
--- /dev/null
+++ b/servlet/test/com/google/inject/servlet/ServletPipelineRequestDispatcherTest.java
@@ -0,0 +1,198 @@
+/**
+ * Copyright (C) 2008 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 com.google.inject.Injector;
+import com.google.inject.Key;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.UUID;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import junit.framework.TestCase;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+
+/**
+ * Tests forwarding and inclusion (RequestDispatcher actions from the
+ * servlet spec).
+ *
+ * @author Dhanji R. Prasanna (dhanji@gmail com)
+ */
+public class ServletPipelineRequestDispatcherTest extends TestCase {
+ private static final Key<HttpServlet> HTTP_SERLVET_KEY = Key.get(HttpServlet.class);
+ private static final String A_KEY = "thinglyDEgintly" + new Date() + UUID.randomUUID();
+ private static final String A_VALUE = ServletPipelineRequestDispatcherTest.class.toString()
+ + new Date() + UUID.randomUUID();
+
+ public final void testIncludeManagedServlet() throws IOException, ServletException {
+ final ServletDefinition servletDefinition = new ServletDefinition("blah.html",
+ Key.get(HttpServlet.class), UriPatternType.get(UriPatternType.SERVLET),
+ new HashMap<String, String>());
+
+ final Injector injector = createMock(Injector.class);
+ final HttpServletRequest mockRequest = createMock(HttpServletRequest.class);
+
+ expect(mockRequest.getAttribute(A_KEY))
+ .andReturn(A_VALUE);
+
+ final boolean[] run = new boolean[1];
+ final HttpServlet mockServlet = new HttpServlet() {
+ protected void service(HttpServletRequest request, HttpServletResponse httpServletResponse)
+ throws ServletException, IOException {
+ run[0] = true;
+
+ final Object o = request.getAttribute(A_KEY);
+ assertEquals("Wrong attrib returned - " + o, A_VALUE, o);
+ }
+ };
+
+ expect(injector.getInstance(HTTP_SERLVET_KEY))
+ .andReturn(mockServlet);
+
+ replay(injector, mockRequest);
+
+ // Have to init the Servlet before we can dispatch to it.
+ servletDefinition.init(null, injector);
+
+ final RequestDispatcher dispatcher = new ManagedServletPipeline(
+ Arrays.asList(servletDefinition))
+ .getRequestDispatcher("blah.html");
+
+ assertNotNull(dispatcher);
+ dispatcher.include(mockRequest, createMock(HttpServletResponse.class));
+
+ assertTrue("Include did not dispatch to our servlet!", run[0]);
+
+ verify(injector, mockRequest);
+ }
+
+ public final void testForwardToManagedServlet() throws IOException, ServletException {
+ final ServletDefinition servletDefinition = new ServletDefinition("blah.html",
+ Key.get(HttpServlet.class), UriPatternType.get(UriPatternType.SERVLET),
+ new HashMap<String, String>());
+
+ final Injector injector = createMock(Injector.class);
+ final HttpServletRequest mockRequest = createMock(HttpServletRequest.class);
+ final HttpServletResponse mockResponse = createMock(HttpServletResponse.class);
+
+ expect(mockRequest.getAttribute(A_KEY))
+ .andReturn(A_VALUE);
+
+ expect(mockResponse.isCommitted())
+ .andReturn(false);
+
+ mockResponse.resetBuffer();
+ expectLastCall().once();
+
+ final boolean[] run = new boolean[1];
+ final HttpServlet mockServlet = new HttpServlet() {
+ protected void service(HttpServletRequest request, HttpServletResponse httpServletResponse)
+ throws ServletException, IOException {
+ run[0] = true;
+
+ final Object o = request.getAttribute(A_KEY);
+ assertEquals("Wrong attrib returned - " + o, A_VALUE, o);
+ }
+ };
+
+ expect(injector.getInstance(HTTP_SERLVET_KEY))
+ .andReturn(mockServlet);
+
+ replay(injector, mockRequest, mockResponse);
+
+ // Have to init the Servlet before we can dispatch to it.
+ servletDefinition.init(null, injector);
+
+ final RequestDispatcher dispatcher = new ManagedServletPipeline(
+ Arrays.asList(servletDefinition))
+ .getRequestDispatcher("blah.html");
+
+ assertNotNull(dispatcher);
+ dispatcher.forward(mockRequest, mockResponse);
+
+ assertTrue("Include did not dispatch to our servlet!", run[0]);
+
+ verify(injector, mockRequest, mockResponse);
+ }
+
+ public final void testForwardToManagedServletFailureOnCommittedBuffer()
+ throws IOException, ServletException {
+ IllegalStateException expected = null;
+ try {
+ forwardToManagedServletFailureOnCommittedBuffer();
+ }
+ catch (IllegalStateException ise) {
+ expected = ise;
+ } finally {
+ assertNotNull("Expected IllegalStateException was not thrown", expected);
+ }
+ }
+
+ public final void forwardToManagedServletFailureOnCommittedBuffer()
+ throws IOException, ServletException {
+ final ServletDefinition servletDefinition = new ServletDefinition("blah.html",
+ Key.get(HttpServlet.class), UriPatternType.get(UriPatternType.SERVLET),
+ new HashMap<String, String>());
+
+ final Injector injector = createMock(Injector.class);
+ final HttpServletRequest mockRequest = createMock(HttpServletRequest.class);
+ final HttpServletResponse mockResponse = createMock(HttpServletResponse.class);
+
+ expect(mockResponse.isCommitted())
+ .andReturn(true);
+
+ final HttpServlet mockServlet = new HttpServlet() {
+ protected void service(HttpServletRequest request, HttpServletResponse httpServletResponse)
+ throws ServletException, IOException {
+
+ final Object o = request.getAttribute(A_KEY);
+ assertEquals("Wrong attrib returned - " + o, A_VALUE, o);
+ }
+ };
+
+ expect(injector.getInstance(Key.get(HttpServlet.class)))
+ .andReturn(mockServlet);
+
+ replay(injector, mockRequest, mockResponse);
+
+ // Have to init the Servlet before we can dispatch to it.
+ servletDefinition.init(null, injector);
+
+ final RequestDispatcher dispatcher = new ManagedServletPipeline(
+ Arrays.asList(servletDefinition))
+ .getRequestDispatcher("blah.html");
+
+ assertNotNull(dispatcher);
+
+ try {
+ dispatcher.forward(mockRequest, mockResponse);
+ }
+ finally {
+ verify(injector, mockRequest, mockResponse);
+ }
+
+ }
+}
diff --git a/servlet/test/com/google/inject/servlet/VarargsFilterDispatchIntegrationTest.java b/servlet/test/com/google/inject/servlet/VarargsFilterDispatchIntegrationTest.java
new file mode 100644
index 0000000..e4ec35c
--- /dev/null
+++ b/servlet/test/com/google/inject/servlet/VarargsFilterDispatchIntegrationTest.java
@@ -0,0 +1,168 @@
+package com.google.inject.servlet;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Singleton;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import junit.framework.TestCase;
+
+/**
+ *
+ * This tests that filter stage of the pipeline dispatches
+ * correctly to guice-managed filters.
+ *
+ * WARNING(dhanji): Non-parallelizable test =(
+ *
+ * @author dhanji@gmail.com (Dhanji R. Prasanna)
+ */
+public class VarargsFilterDispatchIntegrationTest extends TestCase {
+ private static int inits, doFilters, destroys;
+
+ @Override
+ public final void setUp() {
+ inits = 0;
+ doFilters = 0;
+ destroys = 0;
+
+ GuiceFilter.clearPipeline();
+ }
+
+
+ public final void testDispatchRequestToManagedPipeline() throws ServletException, IOException {
+ final Injector injector = Guice.createInjector(new ServletModule() {
+
+ @Override
+ protected void configureServlets() {
+ // This is actually a double match for "/*"
+ filter("/*", "*.html", "/*").through(Key.get(TestFilter.class));
+
+ // These filters should never fire
+ filter("/index/*").through(Key.get(TestFilter.class));
+ filter("*.jsp").through(Key.get(TestFilter.class));
+ }
+ });
+
+ final FilterPipeline pipeline = injector.getInstance(FilterPipeline.class);
+ pipeline.initPipeline(null);
+
+ //create ourselves a mock request with test URI
+ HttpServletRequest requestMock = createMock(HttpServletRequest.class);
+
+ expect(requestMock.getServletPath())
+ .andReturn("/index.html")
+ .anyTimes();
+
+ //dispatch request
+ replay(requestMock);
+ pipeline.dispatch(requestMock, null, createMock(FilterChain.class));
+ pipeline.destroyPipeline();
+
+ verify(requestMock);
+
+ assert inits == 5 && doFilters == 3 && destroys == 5 : "lifecycle states did not"
+ + " fire correct number of times-- inits: " + inits + "; dos: " + doFilters
+ + "; destroys: " + destroys;
+ }
+
+ public final void testDispatchThatNoFiltersFire() throws ServletException, IOException {
+ final Injector injector = Guice.createInjector(new ServletModule() {
+
+ @Override
+ protected void configureServlets() {
+ filter("/public/*", "*.html", "*.xml").through(Key.get(TestFilter.class));
+
+ // These filters should never fire
+ filter("/index/*").through(Key.get(TestFilter.class));
+ filter("*.jsp").through(Key.get(TestFilter.class));
+ }
+ });
+
+ final FilterPipeline pipeline = injector.getInstance(FilterPipeline.class);
+ pipeline.initPipeline(null);
+
+ //create ourselves a mock request with test URI
+ HttpServletRequest requestMock = createMock(HttpServletRequest.class);
+
+ expect(requestMock.getServletPath())
+ .andReturn("/index.xhtml")
+ .anyTimes();
+
+ //dispatch request
+ replay(requestMock);
+ pipeline.dispatch(requestMock, null, createMock(FilterChain.class));
+ pipeline.destroyPipeline();
+
+ verify(requestMock);
+
+ assert inits == 5 && doFilters == 0 && destroys == 5 : "lifecycle states did not "
+ + "fire correct number of times-- inits: " + inits + "; dos: " + doFilters
+ + "; destroys: " + destroys;
+ }
+
+ public final void testDispatchFilterPipelineWithRegexMatching() throws ServletException,
+ IOException {
+
+ final Injector injector = Guice.createInjector(new ServletModule() {
+
+ @Override
+ protected void configureServlets() {
+ filterRegex("/[A-Za-z]*", "/index").through(TestFilter.class);
+
+ //these filters should never fire
+ filterRegex("\\w").through(Key.get(TestFilter.class));
+ }
+ });
+
+ final FilterPipeline pipeline = injector.getInstance(FilterPipeline.class);
+ pipeline.initPipeline(null);
+
+ //create ourselves a mock request with test URI
+ HttpServletRequest requestMock = createMock(HttpServletRequest.class);
+
+ expect(requestMock.getServletPath())
+ .andReturn("/index")
+ .anyTimes();
+
+ //dispatch request
+ replay(requestMock);
+ pipeline.dispatch(requestMock, null, createMock(FilterChain.class));
+ pipeline.destroyPipeline();
+
+ verify(requestMock);
+
+ assert inits == 3 && doFilters == 2 && destroys == 3 : "lifecycle states did not fire "
+ + "correct number of times-- inits: " + inits + "; dos: " + doFilters
+ + "; destroys: " + destroys;
+ }
+
+ @Singleton
+ public static class TestFilter implements Filter {
+ public void init(FilterConfig filterConfig) throws ServletException {
+ inits++;
+ }
+
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
+ FilterChain filterChain) throws IOException, ServletException {
+ doFilters++;
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+
+ public void destroy() {
+ destroys++;
+ }
+ }
+}
\ No newline at end of file
diff --git a/servlet/test/com/google/inject/servlet/VarargsServletDispatchIntegrationTest.java b/servlet/test/com/google/inject/servlet/VarargsServletDispatchIntegrationTest.java
new file mode 100644
index 0000000..28d89d1
--- /dev/null
+++ b/servlet/test/com/google/inject/servlet/VarargsServletDispatchIntegrationTest.java
@@ -0,0 +1,221 @@
+/**
+ * Copyright (C) 2008 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 com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Singleton;
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import junit.framework.TestCase;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+
+/**
+ * Tests the FilterPipeline that dispatches to guice-managed servlets,
+ * is a full integration test, with a real injector.
+ *
+ * @author Dhanji R. Prasanna (dhanji gmail com)
+ */
+public class VarargsServletDispatchIntegrationTest extends TestCase {
+ private static int inits, services, destroys, doFilters;
+
+ @Override
+ public void setUp() {
+ inits = 0;
+ services = 0;
+ destroys = 0;
+ doFilters = 0;
+
+ GuiceFilter.clearPipeline();
+ }
+
+ public final void testDispatchRequestToManagedPipelineServlets()
+ throws ServletException, IOException {
+ final Injector injector = Guice.createInjector(new ServletModule() {
+
+ @Override
+ protected void configureServlets() {
+ serve("/*", "/index.html").with(TestServlet.class);
+
+ // These servets should never fire... (ordering test)
+ serve("*.html", "/*", "/index/*", "*.jsp").with(Key.get(NeverServlet.class));
+ }
+ });
+
+ final FilterPipeline pipeline = injector.getInstance(FilterPipeline.class);
+
+ pipeline.initPipeline(null);
+
+ //create ourselves a mock request with test URI
+ HttpServletRequest requestMock = createMock(HttpServletRequest.class);
+
+ expect(requestMock.getServletPath())
+ .andReturn("/index.html")
+ .times(1);
+
+ //dispatch request
+ replay(requestMock);
+
+ pipeline.dispatch(requestMock, null, createMock(FilterChain.class));
+ pipeline.destroyPipeline();
+
+ verify(requestMock);
+
+ assertTrue("lifecycle states did not fire correct number of times-- inits: " + inits + "; dos: "
+ + services + "; destroys: " + destroys,
+ inits == 6 && services == 1 && destroys == 6);
+ }
+
+ public final void testVarargsSkipDispatchRequestToManagedPipelineServlets()
+ throws ServletException, IOException {
+ final Injector injector = Guice.createInjector(new ServletModule() {
+
+ @Override
+ protected void configureServlets() {
+ serve("/notindex", "/&*", "/index.html").with(TestServlet.class);
+
+ // These servets should never fire... (ordering test)
+ serve("*.html", "/*", "/index/*", "*.jsp").with(Key.get(NeverServlet.class));
+ }
+ });
+
+ final FilterPipeline pipeline = injector.getInstance(FilterPipeline.class);
+
+ pipeline.initPipeline(null);
+
+ //create ourselves a mock request with test URI
+ HttpServletRequest requestMock = createMock(HttpServletRequest.class);
+
+ expect(requestMock.getServletPath())
+ .andReturn("/index.html")
+ .times(3);
+
+ //dispatch request
+ replay(requestMock);
+
+ pipeline.dispatch(requestMock, null, createMock(FilterChain.class));
+ pipeline.destroyPipeline();
+
+ verify(requestMock);
+
+ assertTrue("lifecycle states did not fire correct number of times-- inits: " + inits + "; dos: "
+ + services + "; destroys: " + destroys,
+ inits == 7 && services == 1 && destroys == 7);
+ }
+
+ public final void testDispatchRequestToManagedPipelineWithFilter()
+ throws ServletException, IOException {
+ final Injector injector = Guice.createInjector(new ServletModule() {
+
+ @Override
+ protected void configureServlets() {
+ filter("/*").through(TestFilter.class);
+
+ serve("/*").with(TestServlet.class);
+
+ // These servets should never fire...
+ serve("*.html", "/*", "/index/*", "*.jsp").with(Key.get(NeverServlet.class));
+
+ }
+ });
+
+ final FilterPipeline pipeline = injector.getInstance(FilterPipeline.class);
+
+ pipeline.initPipeline(null);
+
+ //create ourselves a mock request with test URI
+ HttpServletRequest requestMock = createMock(HttpServletRequest.class);
+
+ expect(requestMock.getServletPath())
+ .andReturn("/index.html")
+ .times(2);
+
+ //dispatch request
+ replay(requestMock);
+
+ pipeline.dispatch(requestMock, null, createMock(FilterChain.class));
+
+ pipeline.destroyPipeline();
+
+ verify(requestMock);
+
+ assertTrue("lifecycle states did not fire correct number of times-- inits: " + inits + "; dos: "
+ + services + "; destroys: " + destroys,
+ inits == 6 && services == 1 && destroys == 6 && doFilters == 1);
+ }
+
+ @Singleton
+ public static class TestServlet extends HttpServlet {
+ public void init(ServletConfig filterConfig) throws ServletException {
+ inits++;
+ }
+
+ public void service(ServletRequest servletRequest, ServletResponse servletResponse)
+ throws IOException, ServletException {
+ services++;
+ }
+
+ public void destroy() {
+ destroys++;
+ }
+ }
+
+ @Singleton
+ public static class NeverServlet extends HttpServlet {
+ public void init(ServletConfig filterConfig) throws ServletException {
+ inits++;
+ }
+
+ public void service(ServletRequest servletRequest, ServletResponse servletResponse)
+ throws IOException, ServletException {
+ assertTrue("NeverServlet was fired, when it should not have been.", false);
+ }
+
+ public void destroy() {
+ destroys++;
+ }
+ }
+
+ @Singleton
+ public static class TestFilter implements Filter {
+ public void init(FilterConfig filterConfig) throws ServletException {
+ inits++;
+ }
+
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
+ FilterChain filterChain) throws IOException, ServletException {
+ doFilters++;
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+
+ public void destroy() {
+ destroys++;
+ }
+ }
+}
\ No newline at end of file