diff --git a/spring-orm/src/test/java/org/springframework/mock/web/MockFilterChain.java b/spring-orm/src/test/java/org/springframework/mock/web/MockFilterChain.java index 48ba862b70..4657e97682 100644 --- a/spring-orm/src/test/java/org/springframework/mock/web/MockFilterChain.java +++ b/spring-orm/src/test/java/org/springframework/mock/web/MockFilterChain.java @@ -16,11 +16,20 @@ package org.springframework.mock.web; +import java.io.IOException; +import java.util.Arrays; +import java.util.Iterator; + +import javax.servlet.Filter; import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.Servlet; +import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * Mock implementation of the {@link javax.servlet.FilterChain} interface. @@ -29,6 +38,8 @@ import org.springframework.util.Assert; * custom {@link javax.servlet.Filter} implementations. * * @author Juergen Hoeller + * @author Rob Winch + * * @since 2.0.3 * @see MockFilterConfig * @see PassThroughFilterChain @@ -39,16 +50,69 @@ public class MockFilterChain implements FilterChain { private ServletResponse response; + private final Iterator iterator; + /** - * Records the request and response. + * Register a single do-nothing {@link Filter} implementation. The first + * invocation saves the request and response. Subsequent invocations raise + * an {@link IllegalStateException}. */ - public void doFilter(ServletRequest request, ServletResponse response) { + public MockFilterChain() { + this.iterator = null; + } + + /** + * Create a FilterChain with a {@link Servlet} but without filters. + * + * @param servlet the {@link Servlet} to use in this {@link FilterChain} + * @since 3.2 + */ + public MockFilterChain(Servlet servlet) { + this(new ServletFilterProxy(servlet)); + } + + /** + * Create a FilterChain with one or more {@link Filter} instances and a {@link Servlet}. + * + * @param servlet the {@link Servlet} to use in this {@link FilterChain} + * @param filters the {@link Filter}'s to use in this {@link FilterChain} + * @since 3.2 + */ + public MockFilterChain(Servlet servlet, Filter... filters) { + this(ObjectUtils.addObjectToArray(filters, new ServletFilterProxy(servlet))); + } + + /** + * Create a {@link FilterChain} with one or more {@link Filter} instances. + * + * @param filters the {@link Filter}'s to use in this {@link FilterChain} + * @since 3.2 + */ + private MockFilterChain(Filter... filters) { + Assert.notNull(filters, "filters cannot be null"); + Assert.notEmpty(filters, "filters cannot be empty"); + Assert.noNullElements(filters, "filters cannot contain null values"); + this.iterator = Arrays.asList(filters).iterator(); + } + + /** + * Invoke registered {@link Filter}s and/or {@link Servlet} also saving the + * request and response. + */ + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { Assert.notNull(request, "Request must not be null"); Assert.notNull(response, "Response must not be null"); + if (this.request != null) { - throw new IllegalStateException("This FilterChain has already been called!"); + throw new IllegalStateException("This FilterChain has already been called!"); } + + if ((this.iterator != null) && (this.iterator.hasNext())) { + Filter nextFilter = this.iterator.next(); + nextFilter.doFilter(request, response, this); + } + this.request = request; this.response = response; } @@ -67,4 +131,35 @@ public class MockFilterChain implements FilterChain { return this.response; } + + /** + * A filter that simply delegates to a Servlet. + */ + private static class ServletFilterProxy implements Filter { + + private final Servlet delegateServlet; + + private ServletFilterProxy(Servlet servlet) { + Assert.notNull(servlet, "servlet cannot be null"); + this.delegateServlet = servlet; + } + + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + + this.delegateServlet.service(request, response); + } + + public void init(FilterConfig filterConfig) throws ServletException { + } + + public void destroy() { + } + + @Override + public String toString() { + return this.delegateServlet.toString(); + } + } + } diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockFilterChain.java b/spring-test/src/main/java/org/springframework/mock/web/MockFilterChain.java index 48ba862b70..4657e97682 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/MockFilterChain.java +++ b/spring-test/src/main/java/org/springframework/mock/web/MockFilterChain.java @@ -16,11 +16,20 @@ package org.springframework.mock.web; +import java.io.IOException; +import java.util.Arrays; +import java.util.Iterator; + +import javax.servlet.Filter; import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.Servlet; +import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * Mock implementation of the {@link javax.servlet.FilterChain} interface. @@ -29,6 +38,8 @@ import org.springframework.util.Assert; * custom {@link javax.servlet.Filter} implementations. * * @author Juergen Hoeller + * @author Rob Winch + * * @since 2.0.3 * @see MockFilterConfig * @see PassThroughFilterChain @@ -39,16 +50,69 @@ public class MockFilterChain implements FilterChain { private ServletResponse response; + private final Iterator iterator; + /** - * Records the request and response. + * Register a single do-nothing {@link Filter} implementation. The first + * invocation saves the request and response. Subsequent invocations raise + * an {@link IllegalStateException}. */ - public void doFilter(ServletRequest request, ServletResponse response) { + public MockFilterChain() { + this.iterator = null; + } + + /** + * Create a FilterChain with a {@link Servlet} but without filters. + * + * @param servlet the {@link Servlet} to use in this {@link FilterChain} + * @since 3.2 + */ + public MockFilterChain(Servlet servlet) { + this(new ServletFilterProxy(servlet)); + } + + /** + * Create a FilterChain with one or more {@link Filter} instances and a {@link Servlet}. + * + * @param servlet the {@link Servlet} to use in this {@link FilterChain} + * @param filters the {@link Filter}'s to use in this {@link FilterChain} + * @since 3.2 + */ + public MockFilterChain(Servlet servlet, Filter... filters) { + this(ObjectUtils.addObjectToArray(filters, new ServletFilterProxy(servlet))); + } + + /** + * Create a {@link FilterChain} with one or more {@link Filter} instances. + * + * @param filters the {@link Filter}'s to use in this {@link FilterChain} + * @since 3.2 + */ + private MockFilterChain(Filter... filters) { + Assert.notNull(filters, "filters cannot be null"); + Assert.notEmpty(filters, "filters cannot be empty"); + Assert.noNullElements(filters, "filters cannot contain null values"); + this.iterator = Arrays.asList(filters).iterator(); + } + + /** + * Invoke registered {@link Filter}s and/or {@link Servlet} also saving the + * request and response. + */ + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { Assert.notNull(request, "Request must not be null"); Assert.notNull(response, "Response must not be null"); + if (this.request != null) { - throw new IllegalStateException("This FilterChain has already been called!"); + throw new IllegalStateException("This FilterChain has already been called!"); } + + if ((this.iterator != null) && (this.iterator.hasNext())) { + Filter nextFilter = this.iterator.next(); + nextFilter.doFilter(request, response, this); + } + this.request = request; this.response = response; } @@ -67,4 +131,35 @@ public class MockFilterChain implements FilterChain { return this.response; } + + /** + * A filter that simply delegates to a Servlet. + */ + private static class ServletFilterProxy implements Filter { + + private final Servlet delegateServlet; + + private ServletFilterProxy(Servlet servlet) { + Assert.notNull(servlet, "servlet cannot be null"); + this.delegateServlet = servlet; + } + + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + + this.delegateServlet.service(request, response); + } + + public void init(FilterConfig filterConfig) throws ServletException { + } + + public void destroy() { + } + + @Override + public String toString() { + return this.delegateServlet.toString(); + } + } + } diff --git a/spring-test/src/test/java/org/springframework/mock/web/MockFilterChainTests.java b/spring-test/src/test/java/org/springframework/mock/web/MockFilterChainTests.java new file mode 100644 index 0000000000..e627a333be --- /dev/null +++ b/spring-test/src/test/java/org/springframework/mock/web/MockFilterChainTests.java @@ -0,0 +1,166 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.mock.web; + +import static org.easymock.EasyMock.*; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.*; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.Servlet; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +import org.junit.Before; +import org.junit.Test; + +/** + * Test fixture for {@link MockFilterChain}. + * + * @author Rob Winch + */ +public class MockFilterChainTests { + + private ServletRequest request; + + private ServletResponse response; + + @Before + public void setup() { + this.request = new MockHttpServletRequest(); + this.response = new MockHttpServletResponse(); + } + + @Test(expected=IllegalArgumentException.class) + public void constructorNullServlet() { + new MockFilterChain((Servlet) null); + } + + @Test(expected=IllegalArgumentException.class) + public void constructorNullFilter() { + new MockFilterChain(createMock(Servlet.class), (Filter) null); + } + + @Test(expected = IllegalArgumentException.class) + public void doFilterNullRequest() throws Exception { + MockFilterChain chain = new MockFilterChain(); + chain.doFilter(null, this.response); + } + + @Test(expected = IllegalArgumentException.class) + public void doFilterNullResponse() throws Exception { + MockFilterChain chain = new MockFilterChain(); + chain.doFilter(this.request, null); + } + + @Test + public void doFilterEmptyChain() throws Exception { + MockFilterChain chain = new MockFilterChain(); + chain.doFilter(this.request, this.response); + + assertThat(chain.getRequest(), is(request)); + assertThat(chain.getResponse(), is(response)); + + try { + chain.doFilter(this.request, this.response); + fail("Expected Exception"); + } + catch(IllegalStateException ex) { + assertEquals("This FilterChain has already been called!", ex.getMessage()); + } + } + + @Test + public void doFilterWithServlet() throws Exception { + Servlet servlet = createMock(Servlet.class); + + MockFilterChain chain = new MockFilterChain(servlet); + servlet.service(this.request, this.response); + replay(servlet); + + chain.doFilter(this.request, this.response); + + verify(servlet); + + try { + chain.doFilter(this.request, this.response); + fail("Expected Exception"); + } + catch(IllegalStateException ex) { + assertEquals("This FilterChain has already been called!", ex.getMessage()); + } + } + + @Test + public void doFilterWithServletAndFilters() throws Exception { + Servlet servlet = createMock(Servlet.class); + servlet.service(this.request, this.response); + replay(servlet); + + MockFilter filter2 = new MockFilter(servlet); + MockFilter filter1 = new MockFilter(null); + MockFilterChain chain = new MockFilterChain(servlet, filter1, filter2); + + chain.doFilter(this.request, this.response); + + assertTrue(filter1.invoked); + assertTrue(filter2.invoked); + + verify(servlet); + + try { + chain.doFilter(this.request, this.response); + fail("Expected Exception"); + } + catch(IllegalStateException ex) { + assertEquals("This FilterChain has already been called!", ex.getMessage()); + } + } + + + private static class MockFilter implements Filter { + + private final Servlet servlet; + + private boolean invoked; + + public MockFilter(Servlet servlet) { + this.servlet = servlet; + } + + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + + this.invoked = true; + + if (this.servlet != null) { + this.servlet.service(request, response); + } + else { + chain.doFilter(request, response); + } + } + + public void init(FilterConfig filterConfig) throws ServletException { + } + + public void destroy() { + } + } + +} diff --git a/spring-web/src/test/java/org/springframework/mock/web/MockFilterChain.java b/spring-web/src/test/java/org/springframework/mock/web/MockFilterChain.java index 48ba862b70..4657e97682 100644 --- a/spring-web/src/test/java/org/springframework/mock/web/MockFilterChain.java +++ b/spring-web/src/test/java/org/springframework/mock/web/MockFilterChain.java @@ -16,11 +16,20 @@ package org.springframework.mock.web; +import java.io.IOException; +import java.util.Arrays; +import java.util.Iterator; + +import javax.servlet.Filter; import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.Servlet; +import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * Mock implementation of the {@link javax.servlet.FilterChain} interface. @@ -29,6 +38,8 @@ import org.springframework.util.Assert; * custom {@link javax.servlet.Filter} implementations. * * @author Juergen Hoeller + * @author Rob Winch + * * @since 2.0.3 * @see MockFilterConfig * @see PassThroughFilterChain @@ -39,16 +50,69 @@ public class MockFilterChain implements FilterChain { private ServletResponse response; + private final Iterator iterator; + /** - * Records the request and response. + * Register a single do-nothing {@link Filter} implementation. The first + * invocation saves the request and response. Subsequent invocations raise + * an {@link IllegalStateException}. */ - public void doFilter(ServletRequest request, ServletResponse response) { + public MockFilterChain() { + this.iterator = null; + } + + /** + * Create a FilterChain with a {@link Servlet} but without filters. + * + * @param servlet the {@link Servlet} to use in this {@link FilterChain} + * @since 3.2 + */ + public MockFilterChain(Servlet servlet) { + this(new ServletFilterProxy(servlet)); + } + + /** + * Create a FilterChain with one or more {@link Filter} instances and a {@link Servlet}. + * + * @param servlet the {@link Servlet} to use in this {@link FilterChain} + * @param filters the {@link Filter}'s to use in this {@link FilterChain} + * @since 3.2 + */ + public MockFilterChain(Servlet servlet, Filter... filters) { + this(ObjectUtils.addObjectToArray(filters, new ServletFilterProxy(servlet))); + } + + /** + * Create a {@link FilterChain} with one or more {@link Filter} instances. + * + * @param filters the {@link Filter}'s to use in this {@link FilterChain} + * @since 3.2 + */ + private MockFilterChain(Filter... filters) { + Assert.notNull(filters, "filters cannot be null"); + Assert.notEmpty(filters, "filters cannot be empty"); + Assert.noNullElements(filters, "filters cannot contain null values"); + this.iterator = Arrays.asList(filters).iterator(); + } + + /** + * Invoke registered {@link Filter}s and/or {@link Servlet} also saving the + * request and response. + */ + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { Assert.notNull(request, "Request must not be null"); Assert.notNull(response, "Response must not be null"); + if (this.request != null) { - throw new IllegalStateException("This FilterChain has already been called!"); + throw new IllegalStateException("This FilterChain has already been called!"); } + + if ((this.iterator != null) && (this.iterator.hasNext())) { + Filter nextFilter = this.iterator.next(); + nextFilter.doFilter(request, response, this); + } + this.request = request; this.response = response; } @@ -67,4 +131,35 @@ public class MockFilterChain implements FilterChain { return this.response; } + + /** + * A filter that simply delegates to a Servlet. + */ + private static class ServletFilterProxy implements Filter { + + private final Servlet delegateServlet; + + private ServletFilterProxy(Servlet servlet) { + Assert.notNull(servlet, "servlet cannot be null"); + this.delegateServlet = servlet; + } + + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + + this.delegateServlet.service(request, response); + } + + public void init(FilterConfig filterConfig) throws ServletException { + } + + public void destroy() { + } + + @Override + public String toString() { + return this.delegateServlet.toString(); + } + } + } diff --git a/spring-webmvc/src/test/java/org/springframework/mock/web/MockFilterChain.java b/spring-webmvc/src/test/java/org/springframework/mock/web/MockFilterChain.java index 48ba862b70..4657e97682 100644 --- a/spring-webmvc/src/test/java/org/springframework/mock/web/MockFilterChain.java +++ b/spring-webmvc/src/test/java/org/springframework/mock/web/MockFilterChain.java @@ -16,11 +16,20 @@ package org.springframework.mock.web; +import java.io.IOException; +import java.util.Arrays; +import java.util.Iterator; + +import javax.servlet.Filter; import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.Servlet; +import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * Mock implementation of the {@link javax.servlet.FilterChain} interface. @@ -29,6 +38,8 @@ import org.springframework.util.Assert; * custom {@link javax.servlet.Filter} implementations. * * @author Juergen Hoeller + * @author Rob Winch + * * @since 2.0.3 * @see MockFilterConfig * @see PassThroughFilterChain @@ -39,16 +50,69 @@ public class MockFilterChain implements FilterChain { private ServletResponse response; + private final Iterator iterator; + /** - * Records the request and response. + * Register a single do-nothing {@link Filter} implementation. The first + * invocation saves the request and response. Subsequent invocations raise + * an {@link IllegalStateException}. */ - public void doFilter(ServletRequest request, ServletResponse response) { + public MockFilterChain() { + this.iterator = null; + } + + /** + * Create a FilterChain with a {@link Servlet} but without filters. + * + * @param servlet the {@link Servlet} to use in this {@link FilterChain} + * @since 3.2 + */ + public MockFilterChain(Servlet servlet) { + this(new ServletFilterProxy(servlet)); + } + + /** + * Create a FilterChain with one or more {@link Filter} instances and a {@link Servlet}. + * + * @param servlet the {@link Servlet} to use in this {@link FilterChain} + * @param filters the {@link Filter}'s to use in this {@link FilterChain} + * @since 3.2 + */ + public MockFilterChain(Servlet servlet, Filter... filters) { + this(ObjectUtils.addObjectToArray(filters, new ServletFilterProxy(servlet))); + } + + /** + * Create a {@link FilterChain} with one or more {@link Filter} instances. + * + * @param filters the {@link Filter}'s to use in this {@link FilterChain} + * @since 3.2 + */ + private MockFilterChain(Filter... filters) { + Assert.notNull(filters, "filters cannot be null"); + Assert.notEmpty(filters, "filters cannot be empty"); + Assert.noNullElements(filters, "filters cannot contain null values"); + this.iterator = Arrays.asList(filters).iterator(); + } + + /** + * Invoke registered {@link Filter}s and/or {@link Servlet} also saving the + * request and response. + */ + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { Assert.notNull(request, "Request must not be null"); Assert.notNull(response, "Response must not be null"); + if (this.request != null) { - throw new IllegalStateException("This FilterChain has already been called!"); + throw new IllegalStateException("This FilterChain has already been called!"); } + + if ((this.iterator != null) && (this.iterator.hasNext())) { + Filter nextFilter = this.iterator.next(); + nextFilter.doFilter(request, response, this); + } + this.request = request; this.response = response; } @@ -67,4 +131,35 @@ public class MockFilterChain implements FilterChain { return this.response; } + + /** + * A filter that simply delegates to a Servlet. + */ + private static class ServletFilterProxy implements Filter { + + private final Servlet delegateServlet; + + private ServletFilterProxy(Servlet servlet) { + Assert.notNull(servlet, "servlet cannot be null"); + this.delegateServlet = servlet; + } + + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + + this.delegateServlet.service(request, response); + } + + public void init(FilterConfig filterConfig) throws ServletException { + } + + public void destroy() { + } + + @Override + public String toString() { + return this.delegateServlet.toString(); + } + } + }