/* * 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 org.easymock.EasyMock.createControl; import static org.easymock.EasyMock.expect; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Scopes; import com.google.inject.name.Named; import com.google.inject.name.Names; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import junit.framework.TestCase; import org.easymock.IMocksControl; /** Tests to make sure that servlets with a context path are handled right. */ public class ContextPathTest extends TestCase { @Inject @Named("foo") private TestServlet fooServlet; @Inject @Named("bar") private TestServlet barServlet; private IMocksControl globalControl; private Injector injector; private ServletContext servletContext; private FilterConfig filterConfig; private GuiceFilter guiceFilter; @Override public final void setUp() throws Exception { injector = Guice.createInjector( new ServletModule() { @Override protected void configureServlets() { bind(TestServlet.class) .annotatedWith(Names.named("foo")) .to(TestServlet.class) .in(Scopes.SINGLETON); bind(TestServlet.class) .annotatedWith(Names.named("bar")) .to(TestServlet.class) .in(Scopes.SINGLETON); serve("/foo/*").with(Key.get(TestServlet.class, Names.named("foo"))); serve("/bar/*").with(Key.get(TestServlet.class, Names.named("bar"))); // TODO: add a filter(..) call and validate it is correct } }); injector.injectMembers(this); assertNotNull(fooServlet); assertNotNull(barServlet); assertNotSame(fooServlet, barServlet); globalControl = createControl(); servletContext = globalControl.createMock(ServletContext.class); filterConfig = globalControl.createMock(FilterConfig.class); expect(servletContext.getAttribute(GuiceServletContextListener.INJECTOR_NAME)) .andReturn(injector) .anyTimes(); expect(filterConfig.getServletContext()).andReturn(servletContext).anyTimes(); globalControl.replay(); guiceFilter = new GuiceFilter(); guiceFilter.init(filterConfig); } @Override public final void tearDown() { assertNotNull(fooServlet); assertNotNull(barServlet); fooServlet = null; barServlet = null; guiceFilter.destroy(); guiceFilter = null; injector = null; filterConfig = null; servletContext = null; globalControl.verify(); } public void testSimple() throws Exception { IMocksControl testControl = createControl(); TestFilterChain testFilterChain = new TestFilterChain(); HttpServletRequest req = testControl.createMock(HttpServletRequest.class); HttpServletResponse res = testControl.createMock(HttpServletResponse.class); expect(req.getMethod()).andReturn("GET").anyTimes(); expect(req.getRequestURI()).andReturn("/bar/foo").anyTimes(); expect(req.getServletPath()).andReturn("/bar/foo").anyTimes(); expect(req.getContextPath()).andReturn("").anyTimes(); testControl.replay(); guiceFilter.doFilter(req, res, testFilterChain); assertFalse(testFilterChain.isTriggered()); assertFalse(fooServlet.isTriggered()); assertTrue(barServlet.isTriggered()); testControl.verify(); } // // each of the following "runTest" calls takes three path parameters: // // The value of "getRequestURI()" // The value of "getServletPath()" // The value of "getContextPath()" // // these values have been captured using a filter in Apache Tomcat 6.0.32 // and are used for real-world values that a servlet container would send into // the GuiceFilter. // // the remaining three booleans are: // // True if the request gets passed down the filter chain // True if the request hits the "foo" servlet // True if the request hits the "bar" sevlet // // After adjusting the request URI for the web app deployment location, all // calls // should always produce the same result. // // ROOT Web app, using Tomcat default servlet public void testRootDefault() throws Exception { // fetching /. Should go up the filter chain (only mappings on /foo/* and /bar/*). runTest("/", "/", "", true, false, false); // fetching /bar/. Should hit the bar servlet runTest("/bar/", "/bar/", "", false, false, true); // fetching /foo/xxx. Should hit the foo servlet runTest("/foo/xxx", "/foo/xxx", "", false, true, false); // fetching /xxx. Should go up the chain runTest("/xxx", "/xxx", "", true, false, false); } // ROOT Web app, using explicit backing servlet mounted at /* public void testRootExplicit() throws Exception { // fetching /. Should go up the filter chain (only mappings on /foo/* and /bar/*). runTest("/", "", "", true, false, false); // fetching /bar/. Should hit the bar servlet runTest("/bar/", "", "", false, false, true); // fetching /foo/xxx. Should hit the foo servlet runTest("/foo/xxx", "", "", false, true, false); // fetching /xxx. Should go up the chain runTest("/xxx", "", "", true, false, false); } // ROOT Web app, using two backing servlets, mounted at /bar/* and /foo/* public void testRootSpecific() throws Exception { // fetching /. Should go up the filter chain (only mappings on /foo/* and /bar/*). runTest("/", "/", "", true, false, false); // fetching /bar/. Should hit the bar servlet runTest("/bar/", "/bar", "", false, false, true); // fetching /foo/xxx. Should hit the foo servlet runTest("/foo/xxx", "/foo", "", false, true, false); // fetching /xxx. Should go up the chain runTest("/xxx", "/xxx", "", true, false, false); } // Web app located at /webtest, using Tomcat default servlet public void testWebtestDefault() throws Exception { // fetching /. Should go up the filter chain (only mappings on /foo/* and /bar/*). runTest("/webtest/", "/", "/webtest", true, false, false); // fetching /bar/. Should hit the bar servlet runTest("/webtest/bar/", "/bar/", "/webtest", false, false, true); // fetching /foo/xxx. Should hit the foo servlet runTest("/webtest/foo/xxx", "/foo/xxx", "/webtest", false, true, false); // fetching /xxx. Should go up the chain runTest("/webtest/xxx", "/xxx", "/webtest", true, false, false); } // Web app located at /webtest, using explicit backing servlet mounted at /* public void testWebtestExplicit() throws Exception { // fetching /. Should go up the filter chain (only mappings on /foo/* and /bar/*). runTest("/webtest/", "", "/webtest", true, false, false); // fetching /bar/. Should hit the bar servlet runTest("/webtest/bar/", "", "/webtest", false, false, true); // fetching /foo/xxx. Should hit the foo servlet runTest("/webtest/foo/xxx", "", "/webtest", false, true, false); // fetching /xxx. Should go up the chain runTest("/webtest/xxx", "", "/webtest", true, false, false); } // Web app located at /webtest, using two backing servlets, mounted at /bar/* // and /foo/* public void testWebtestSpecific() throws Exception { // fetching /. Should go up the filter chain (only mappings on /foo/* and // /bar/*). runTest("/webtest/", "/", "/webtest", true, false, false); // fetching /bar/. Should hit the bar servlet runTest("/webtest/bar/", "/bar", "/webtest", false, false, true); // fetching /foo/xxx. Should hit the foo servlet runTest("/webtest/foo/xxx", "/foo", "/webtest", false, true, false); // fetching /xxx. Should go up the chain runTest("/webtest/xxx", "/xxx", "/webtest", true, false, false); } private void runTest( final String requestURI, final String servletPath, final String contextPath, final boolean filterResult, final boolean fooResult, final boolean barResult) throws Exception { IMocksControl testControl = createControl(); barServlet.clear(); fooServlet.clear(); TestFilterChain testFilterChain = new TestFilterChain(); HttpServletRequest req = testControl.createMock(HttpServletRequest.class); HttpServletResponse res = testControl.createMock(HttpServletResponse.class); expect(req.getMethod()).andReturn("GET").anyTimes(); expect(req.getRequestURI()).andReturn(requestURI).anyTimes(); expect(req.getServletPath()).andReturn(servletPath).anyTimes(); expect(req.getContextPath()).andReturn(contextPath).anyTimes(); testControl.replay(); guiceFilter.doFilter(req, res, testFilterChain); assertEquals(filterResult, testFilterChain.isTriggered()); assertEquals(fooResult, fooServlet.isTriggered()); assertEquals(barResult, barServlet.isTriggered()); testControl.verify(); } public static class TestServlet extends HttpServlet { private boolean triggered = false; @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) { triggered = true; } public boolean isTriggered() { return triggered; } public void clear() { triggered = false; } } public static class TestFilterChain implements FilterChain { private boolean triggered = false; @Override public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { triggered = true; } public boolean isTriggered() { return triggered; } public void clear() { triggered = false; } } }