Enable asyncSupported flag in Spring Test MVC

Also remove Servlet 3 bridge classes no longer needed since Spring
Framework 4 compiles with the Servlet 3 API. That means applications
may need to run tests with the Servlet 3 API jar although
technically they don't have to run with that in production.
This commit is contained in:
Rossen Stoyanchev
2013-07-18 17:06:50 -04:00
parent 9af1984b39
commit 2a15b5a895
5 changed files with 18 additions and 368 deletions

View File

@@ -1,145 +0,0 @@
/*
* 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.test.web.servlet.request;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.BeanUtils;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.util.WebUtils;
/**
* Mock implementation of the {@link AsyncContext} interface.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
class MockAsyncContext implements AsyncContext {
private final HttpServletRequest request;
private final HttpServletResponse response;
private final List<AsyncListener> listeners = new ArrayList<AsyncListener>();
private String dispatchedPath;
private long timeout = 10 * 1000L; // 10 seconds is Tomcat's default
public MockAsyncContext(ServletRequest request, ServletResponse response) {
this.request = (HttpServletRequest) request;
this.response = (HttpServletResponse) response;
}
@Override
public ServletRequest getRequest() {
return this.request;
}
@Override
public ServletResponse getResponse() {
return this.response;
}
@Override
public boolean hasOriginalRequestAndResponse() {
return (this.request instanceof MockHttpServletRequest) && (this.response instanceof MockHttpServletResponse);
}
public String getDispatchedPath() {
return this.dispatchedPath;
}
@Override
public void dispatch() {
dispatch(this.request.getRequestURI());
}
@Override
public void dispatch(String path) {
dispatch(null, path);
}
@Override
public void dispatch(ServletContext context, String path) {
this.dispatchedPath = path;
}
@Override
public void complete() {
Servlet3MockHttpServletRequest mockRequest = WebUtils.getNativeRequest(request, Servlet3MockHttpServletRequest.class);
if (mockRequest != null) {
mockRequest.setAsyncStarted(false);
}
for (AsyncListener listener : this.listeners) {
try {
listener.onComplete(new AsyncEvent(this, this.request, this.response));
}
catch (IOException e) {
throw new IllegalStateException("AsyncListener failure", e);
}
}
}
@Override
public void start(Runnable runnable) {
runnable.run();
}
public List<AsyncListener> getListeners() {
return this.listeners;
}
@Override
public void addListener(AsyncListener listener) {
this.listeners.add(listener);
}
@Override
public void addListener(AsyncListener listener, ServletRequest request, ServletResponse response) {
this.listeners.add(listener);
}
@Override
public <T extends AsyncListener> T createListener(Class<T> clazz) throws ServletException {
return BeanUtils.instantiateClass(clazz);
}
@Override
public long getTimeout() {
return this.timeout;
}
@Override
public void setTimeout(long timeout) {
this.timeout = timeout;
}
}

View File

@@ -653,6 +653,8 @@ public class MockHttpServletRequestBuilder implements RequestBuilder, Mergeable
Assert.notNull(request, "Post-processor [" + postProcessor.getClass().getName() + "] returned null");
}
request.setAsyncSupported(true);
return request;
}

View File

@@ -1,114 +0,0 @@
/*
* 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.test.web.servlet.request;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import org.springframework.mock.web.MockHttpServletRequest;
/**
* A Servlet 3 sub-class of MockHttpServletRequest.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
class Servlet3MockHttpServletRequest extends MockHttpServletRequest {
private boolean asyncStarted;
private MockAsyncContext asyncContext;
private Map<String, Part> parts = new HashMap<String, Part>();
public Servlet3MockHttpServletRequest(ServletContext servletContext) {
super(servletContext);
}
@Override
public boolean isAsyncSupported() {
return true;
}
@Override
public AsyncContext startAsync() {
return startAsync(this, null);
}
@Override
public AsyncContext startAsync(ServletRequest request, ServletResponse response) {
this.asyncStarted = true;
this.asyncContext = new MockAsyncContext(request, response);
return this.asyncContext;
}
@Override
public AsyncContext getAsyncContext() {
return this.asyncContext;
}
public void setAsyncContext(MockAsyncContext asyncContext) {
this.asyncContext = asyncContext;
}
@Override
public DispatcherType getDispatcherType() {
return DispatcherType.REQUEST;
}
@Override
public boolean isAsyncStarted() {
return this.asyncStarted;
}
@Override
public void setAsyncStarted(boolean asyncStarted) {
this.asyncStarted = asyncStarted;
}
@Override
public void addPart(Part part) {
this.parts.put(part.getName(), part);
}
@Override
public Part getPart(String key) throws IOException, IllegalStateException, ServletException {
return this.parts.get(key);
}
@Override
public Collection<Part> getParts() throws IOException, IllegalStateException, ServletException {
return this.parts.values();
}
@Override
public boolean authenticate(HttpServletResponse response) throws IOException, ServletException {
throw new UnsupportedOperationException();
}
}

View File

@@ -1,109 +0,0 @@
/*
* 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.test.web.servlet.request;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import org.springframework.mock.web.MockMultipartHttpServletRequest;
/**
* A Servlet 3 sub-class of MockMultipartHttpServletRequest.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
class Servlet3MockMultipartHttpServletRequest extends MockMultipartHttpServletRequest {
private boolean asyncStarted;
private MockAsyncContext asyncContext;
private Map<String, Part> parts = new HashMap<String, Part>();
@Override
public boolean isAsyncSupported() {
return true;
}
@Override
public AsyncContext startAsync() {
return startAsync(this, null);
}
@Override
public AsyncContext startAsync(ServletRequest request, ServletResponse response) {
this.asyncStarted = true;
this.asyncContext = new MockAsyncContext(request, response);
return this.asyncContext;
}
@Override
public AsyncContext getAsyncContext() {
return this.asyncContext;
}
public void setAsyncContext(MockAsyncContext asyncContext) {
this.asyncContext = asyncContext;
}
@Override
public DispatcherType getDispatcherType() {
return DispatcherType.REQUEST;
}
@Override
public boolean isAsyncStarted() {
return this.asyncStarted;
}
@Override
public void setAsyncStarted(boolean asyncStarted) {
this.asyncStarted = asyncStarted;
}
@Override
public void addPart(Part part) {
this.parts.put(part.getName(), part);
}
@Override
public Part getPart(String key) throws IOException, IllegalStateException, ServletException {
return this.parts.get(key);
}
@Override
public Collection<Part> getParts() throws IOException, IllegalStateException, ServletException {
return this.parts.values();
}
@Override
public boolean authenticate(HttpServletResponse response) throws IOException, ServletException {
throw new UnsupportedOperationException();
}
}

View File

@@ -47,6 +47,7 @@ import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.servlet.handler.MappedInterceptor;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
@@ -107,6 +108,8 @@ public class StandaloneMockMvcBuilder extends DefaultMockMvcBuilder<StandaloneMo
private boolean useTrailingSlashPatternMatch = true;
private Boolean removeSemicolonContent;
/**
* Protected constructor. Not intended for direct instantiation.
@@ -274,6 +277,15 @@ public class StandaloneMockMvcBuilder extends DefaultMockMvcBuilder<StandaloneMo
return this;
}
/**
* Set if ";" (semicolon) content should be stripped from the request URI. The value,
* if provided, is in turn set on
* {@link AbstractHandlerMapping#setRemoveSemicolonContent(boolean)}.
*/
public void setRemoveSemicolonContent(boolean removeSemicolonContent) {
this.removeSemicolonContent = removeSemicolonContent;
}
@Override
protected void initWebAppContext(WebApplicationContext cxt) {
StubWebApplicationContext mockCxt = (StubWebApplicationContext) cxt;
@@ -336,6 +348,10 @@ public class StandaloneMockMvcBuilder extends DefaultMockMvcBuilder<StandaloneMo
handlerMapping.setOrder(0);
handlerMapping.setInterceptors(getInterceptors());
if (removeSemicolonContent != null) {
handlerMapping.setRemoveSemicolonContent(removeSemicolonContent);
}
return handlerMapping;
}