Add WebServerExchange

This change adds a WebServerExchange and updates all contracts at the
the same level (i.e. org.springframework.web.server) as well as the
org.springframework.web.reactive level to use it so that all
framework-related code will have access to server-side processing
features such as request attributes (and others to come).
This commit is contained in:
Rossen Stoyanchev
2016-01-08 12:24:34 -05:00
parent 54e4e012b2
commit 4f614fa0fd
47 changed files with 831 additions and 614 deletions
@@ -38,8 +38,8 @@ import reactor.Flux;
import org.springframework.core.ResolvableType;
import org.springframework.core.codec.CodecException;
import org.springframework.util.ByteBufferPublisherInputStream;
import org.springframework.util.Assert;
import org.springframework.util.ByteBufferPublisherInputStream;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
@@ -31,8 +31,8 @@ import reactor.io.buffer.Buffer;
import org.springframework.core.ResolvableType;
import org.springframework.core.codec.CodecException;
import org.springframework.util.BufferOutputStream;
import org.springframework.util.Assert;
import org.springframework.util.BufferOutputStream;
import org.springframework.util.ClassUtils;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
@@ -22,7 +22,6 @@ import java.nio.ByteBuffer;
import io.netty.buffer.ByteBuf;
import io.reactivex.netty.protocol.http.server.HttpServerRequest;
import org.reactivestreams.Publisher;
import reactor.Flux;
import reactor.core.publisher.convert.RxJava1Converter;
import rx.Observable;
@@ -22,9 +22,9 @@ import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.http.server.reactive.ServletHttpHandlerAdapter;
import org.springframework.util.Assert;
import org.springframework.util.SocketUtils;
import org.springframework.http.server.reactive.ServletHttpHandlerAdapter;
/**
* @author Rossen Stoyanchev
@@ -21,8 +21,8 @@ import reactor.io.buffer.Buffer;
import reactor.io.net.ReactiveNet;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
import org.springframework.util.Assert;
/**
* @author Stephane Maldini
@@ -19,8 +19,8 @@ package org.springframework.http.server.reactive.boot;
import io.netty.buffer.ByteBuf;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.http.server.reactive.RxNettyHttpHandlerAdapter;
import org.springframework.util.Assert;
/**
@@ -23,9 +23,9 @@ import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.http.server.reactive.ServletHttpHandlerAdapter;
import org.springframework.util.Assert;
import org.springframework.util.SocketUtils;
import org.springframework.http.server.reactive.ServletHttpHandlerAdapter;
/**
@@ -16,13 +16,13 @@
package org.springframework.http.server.reactive.boot;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.http.server.reactive.UndertowHttpHandlerAdapter;
import io.undertow.Undertow;
import io.undertow.server.HttpHandler;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.http.server.reactive.UndertowHttpHandlerAdapter;
import org.springframework.util.Assert;
/**
* @author Marek Hawrylczak
*/
@@ -31,9 +31,9 @@ import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.WebHandler;
import org.springframework.web.server.WebServerExchange;
/**
* Central dispatcher for HTTP request handlers/controllers. Dispatches to registered
@@ -53,7 +53,7 @@ import org.springframework.http.server.reactive.ServerHttpResponse;
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
*/
public class DispatcherHandler implements HttpHandler, ApplicationContextAware {
public class DispatcherHandler implements WebHandler, ApplicationContextAware {
private static final Log logger = LogFactory.getLog(DispatcherHandler.class);
@@ -112,31 +112,32 @@ public class DispatcherHandler implements HttpHandler, ApplicationContextAware {
@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
public Mono<Void> handle(WebServerExchange exchange) {
if (logger.isDebugEnabled()) {
ServerHttpRequest request = exchange.getRequest();
logger.debug("Processing " + request.getMethod() + " request for [" + request.getURI() + "]");
}
return Flux.fromIterable(this.handlerMappings)
.concatMap(mapping -> mapping.getHandler(request))
.concatMap(mapping -> mapping.getHandler(exchange))
.next()
.then(handler -> invokeHandler(request, response, handler))
.then(result -> handleResult(request, response, result))
.then(handler -> invokeHandler(exchange, handler))
.then(result -> handleResult(exchange, result))
.otherwise(ex -> Mono.error(this.errorMapper.apply(ex)));
}
private Mono<HandlerResult> invokeHandler(ServerHttpRequest request, ServerHttpResponse response, Object handler) {
private Mono<HandlerResult> invokeHandler(WebServerExchange exchange, Object handler) {
for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
if (handlerAdapter.supports(handler)) {
return handlerAdapter.handle(request, response, handler);
return handlerAdapter.handle(exchange, handler);
}
}
return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
}
private Mono<Void> handleResult(ServerHttpRequest request, ServerHttpResponse response, HandlerResult result) {
return getResultHandler(result).handleResult(request, response, result)
private Mono<Void> handleResult(WebServerExchange exchange, HandlerResult result) {
return getResultHandler(result).handleResult(exchange, result)
.otherwise(ex -> result.applyExceptionHandler(ex).then(exceptionResult ->
getResultHandler(result).handleResult(request, response, exceptionResult)));
getResultHandler(result).handleResult(exchange, exceptionResult)));
}
private HandlerResultHandler getResultHandler(HandlerResult handlerResult) {
@@ -156,7 +157,7 @@ public class DispatcherHandler implements HttpHandler, ApplicationContextAware {
@Override
public Mono<Object> getHandler(ServerHttpRequest request) {
public Mono<Object> getHandler(WebServerExchange exchange) {
return Mono.error(HANDLER_NOT_FOUND_EXCEPTION);
}
}
@@ -19,8 +19,8 @@ import java.util.function.Function;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.HttpStatus;
import org.springframework.web.ResponseStatusException;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.ResponseStatusException;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
@@ -20,8 +20,7 @@ import java.util.function.Function;
import reactor.Mono;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.WebServerExchange;
/**
* Contract that decouples the {@link DispatcherHandler} from the details of
@@ -53,14 +52,12 @@ public interface HandlerAdapter {
* handler} on the {@code HandlerResult} so that may also be applied later
* after result handling.
*
* @param request current request
* @param response current response
* @param exchange current server exchange
* @param handler the selected handler which must have been previously
* checked via {@link #supports(Object)}
* @return {@link Mono} that emits a single {@code HandlerResult} or none if
* the request has been fully handled and doesn't require further handling.
*/
Mono<HandlerResult> handle(ServerHttpRequest request, ServerHttpResponse response,
Object handler);
Mono<HandlerResult> handle(WebServerExchange exchange, Object handler);
}
@@ -18,7 +18,7 @@ package org.springframework.web.reactive;
import reactor.Mono;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.WebServerExchange;
/**
* Interface to be implemented by objects that define a mapping between
@@ -31,10 +31,10 @@ public interface HandlerMapping {
/**
* Return a handler for this request.
* @param request current HTTP request
* @param exchange current server exchange
* @return A {@link Mono} that emits one value or none in case the request
* cannot be resolved to a handler.
* cannot be resolved to a handler
*/
Mono<Object> getHandler(ServerHttpRequest request);
Mono<Object> getHandler(WebServerExchange exchange);
}
@@ -18,8 +18,7 @@ package org.springframework.web.reactive;
import reactor.Mono;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.WebServerExchange;
/**
* Process the {@link HandlerResult}, usually returned by an {@link HandlerAdapter}.
@@ -41,9 +40,10 @@ public interface HandlerResultHandler {
* Process the given result modifying response headers and/or writing data
* to the response.
*
* @param exchange current server exchange
* @param result the result from the handling
* @return {@code Mono<Void>} to indicate when request handling is complete.
*/
Mono<Void> handleResult(ServerHttpRequest request, ServerHttpResponse response,
HandlerResult result);
Mono<Void> handleResult(WebServerExchange exchange, HandlerResult result);
}
@@ -17,23 +17,22 @@ package org.springframework.web.reactive;
import reactor.Mono;
import org.springframework.web.server.HttpExceptionHandler;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.ResponseStatusException;
import org.springframework.web.server.WebExceptionHandler;
import org.springframework.web.server.WebServerExchange;
/**
* Handle {@link ResponseStatusException} by setting the response status.
*
* @author Rossen Stoyanchev
*/
public class ResponseStatusExceptionHandler implements HttpExceptionHandler {
public class ResponseStatusExceptionHandler implements WebExceptionHandler {
@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response, Throwable ex) {
public Mono<Void> handle(WebServerExchange exchange, Throwable ex) {
if (ex instanceof ResponseStatusException) {
response.setStatusCode(((ResponseStatusException) ex).getHttpStatus());
exchange.getResponse().setStatusCode(((ResponseStatusException) ex).getHttpStatus());
return Mono.empty();
}
return Mono.error(ex);
@@ -20,25 +20,20 @@ import org.reactivestreams.Publisher;
import reactor.Mono;
import org.springframework.core.ResolvableType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.reactive.DispatcherHandler;
import org.springframework.web.reactive.HandlerAdapter;
import org.springframework.web.reactive.HandlerResult;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.web.reactive.DispatcherHandler;
import org.springframework.web.server.WebHandler;
import org.springframework.web.server.WebServerExchange;
/**
* Support use of {@link HttpHandler} with
* {@link DispatcherHandler
* DispatcherHandler} (which implements the same contract).
* The use of {@code DispatcherHandler} this way enables routing requests to
* one of many {@code HttpHandler} instances depending on the configured
* handler mappings.
* Support use of {@link org.springframework.web.server.WebHandler} through the
* {@link DispatcherHandler}.
*
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
*/
public class HttpHandlerAdapter implements HandlerAdapter {
public class HttpHandlerHandlerAdapter implements HandlerAdapter {
private static final ResolvableType PUBLISHER_VOID = ResolvableType.forClassWithGenerics(
Publisher.class, Void.class);
@@ -46,14 +41,14 @@ public class HttpHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return HttpHandler.class.isAssignableFrom(handler.getClass());
return WebHandler.class.isAssignableFrom(handler.getClass());
}
@Override
public Mono<HandlerResult> handle(ServerHttpRequest request, ServerHttpResponse response, Object handler) {
HttpHandler httpHandler = (HttpHandler)handler;
Mono<Void> completion = httpHandler.handle(request, response);
return Mono.just(new HandlerResult(httpHandler, completion, PUBLISHER_VOID));
public Mono<HandlerResult> handle(WebServerExchange exchange, Object handler) {
WebHandler webHandler = (WebHandler) handler;
Mono<Void> completion = webHandler.handle(exchange);
return Mono.just(new HandlerResult(webHandler, completion, PUBLISHER_VOID));
}
}
@@ -22,11 +22,10 @@ import reactor.Mono;
import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.ConversionService;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.Assert;
import org.springframework.web.reactive.HandlerResult;
import org.springframework.web.reactive.HandlerResultHandler;
import org.springframework.web.server.WebServerExchange;
/**
* Supports {@link HandlerResult} with a {@code void} or {@code Publisher<Void>} value.
@@ -75,15 +74,11 @@ public class SimpleHandlerResultHandler implements Ordered, HandlerResultHandler
@SuppressWarnings("unchecked")
@Override
public Mono<Void> handleResult(ServerHttpRequest request,
ServerHttpResponse response, HandlerResult result) {
public Mono<Void> handleResult(WebServerExchange exchange, HandlerResult result) {
Object value = result.getResult();
if (Void.TYPE.equals(result.getResultType().getRawClass())) {
return Mono.empty();
}
return (value instanceof Mono ? (Mono<Void>)value :
Mono.from(this.conversionService.convert(value, Publisher.class)));
}
@@ -22,8 +22,8 @@ import java.util.Map;
import reactor.Flux;
import reactor.Mono;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.server.WebServerExchange;
/**
* @author Rossen Stoyanchev
@@ -42,9 +42,9 @@ public class SimpleUrlHandlerMapping implements HandlerMapping {
@Override
public Mono<Object> getHandler(ServerHttpRequest request) {
public Mono<Object> getHandler(WebServerExchange exchange) {
return Flux.create(subscriber -> {
String path = request.getURI().getPath();
String path = exchange.getRequest().getURI().getPath();
Object handler = this.handlerMap.get(path);
if (handler != null) {
subscriber.onNext(handler);
@@ -19,7 +19,7 @@ package org.springframework.web.reactive.method;
import reactor.Mono;
import org.springframework.core.MethodParameter;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.WebServerExchange;
/**
@@ -35,6 +35,6 @@ public interface HandlerMethodArgumentResolver {
* does not resolve to any value, which will result in {@code null} passed
* as the argument value.
*/
Mono<Object> resolveArgument(MethodParameter parameter, ServerHttpRequest request);
Mono<Object> resolveArgument(MethodParameter parameter, WebServerExchange exchange);
}
@@ -32,11 +32,11 @@ import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.ResolvableType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.reactive.HandlerResult;
import org.springframework.web.server.WebServerExchange;
/**
@@ -76,14 +76,14 @@ public class InvocableHandlerMethod extends HandlerMethod {
/**
* Invoke the method and return a Publisher for the return value.
* @param request the current request
* @param exchange the current exchange
* @param providedArgs optional list of argument values to check by type
* (via {@code instanceof}) for resolving method arguments.
* @return Publisher that produces a single HandlerResult or an error signal;
* never throws an exception.
* never throws an exception
*/
public Mono<HandlerResult> invokeForRequest(ServerHttpRequest request, Object... providedArgs) {
return resolveArguments(request, providedArgs).then(args -> {
public Mono<HandlerResult> invokeForRequest(WebServerExchange exchange, Object... providedArgs) {
return resolveArguments(exchange, providedArgs).then(args -> {
try {
Object value = doInvoke(args);
ResolvableType type = ResolvableType.forMethodParameter(getReturnType());
@@ -100,7 +100,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
});
}
private Mono<Object[]> resolveArguments(ServerHttpRequest request, Object... providedArgs) {
private Mono<Object[]> resolveArguments(WebServerExchange exchange, Object... providedArgs) {
if (ObjectUtils.isEmpty(getMethodParameters())) {
return NO_ARGS;
}
@@ -121,7 +121,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
.findFirst()
.orElseThrow(() -> getArgError("No resolver for ", param, null));
try {
return resolver.resolveArgument(param, request)
return resolver.resolveArgument(param, exchange)
.defaultIfEmpty(NO_VALUE)
.otherwise(ex -> Mono.error(getArgError("Error resolving ", param, ex)));
}
@@ -25,13 +25,13 @@ import reactor.Mono;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.core.codec.Decoder;
import org.springframework.core.convert.ConversionService;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.core.codec.Decoder;
import org.springframework.web.reactive.method.HandlerMethodArgumentResolver;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.reactive.method.HandlerMethodArgumentResolver;
import org.springframework.web.server.WebServerExchange;
/**
* @author Sebastien Deleuze
@@ -58,13 +58,13 @@ public class RequestBodyArgumentResolver implements HandlerMethodArgumentResolve
}
@Override
public Mono<Object> resolveArgument(MethodParameter parameter, ServerHttpRequest request) {
MediaType mediaType = request.getHeaders().getContentType();
public Mono<Object> resolveArgument(MethodParameter parameter, WebServerExchange exchange) {
MediaType mediaType = exchange.getRequest().getHeaders().getContentType();
if (mediaType == null) {
mediaType = MediaType.APPLICATION_OCTET_STREAM;
}
ResolvableType type = ResolvableType.forMethodParameter(parameter);
Flux<ByteBuffer> body = request.getBody();
Flux<ByteBuffer> body = exchange.getRequest().getBody();
Flux<?> elementFlux = body;
ResolvableType elementType = type.hasGenerics() ? type.getGeneric(0) : type;
@@ -35,8 +35,6 @@ import org.springframework.core.codec.support.JsonObjectDecoder;
import org.springframework.core.codec.support.StringDecoder;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.ObjectUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.annotation.ExceptionHandlerMethodResolver;
@@ -44,6 +42,7 @@ import org.springframework.web.reactive.HandlerAdapter;
import org.springframework.web.reactive.HandlerResult;
import org.springframework.web.reactive.method.HandlerMethodArgumentResolver;
import org.springframework.web.reactive.method.InvocableHandlerMethod;
import org.springframework.web.server.WebServerExchange;
/**
@@ -105,20 +104,18 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Initializin
}
@Override
public Mono<HandlerResult> handle(ServerHttpRequest request, ServerHttpResponse response,
Object handler) {
public Mono<HandlerResult> handle(WebServerExchange exchange, Object handler) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
InvocableHandlerMethod invocable = new InvocableHandlerMethod(handlerMethod);
invocable.setHandlerMethodArgumentResolvers(this.argumentResolvers);
return invocable.invokeForRequest(request)
.map(result -> result.setExceptionHandler(ex -> handleException(ex, handlerMethod, request, response)))
.otherwise(ex -> handleException(ex, handlerMethod, request, response));
return invocable.invokeForRequest(exchange)
.map(result -> result.setExceptionHandler(ex -> handleException(ex, handlerMethod, exchange)))
.otherwise(ex -> handleException(ex, handlerMethod, exchange));
}
private Mono<HandlerResult> handleException(Throwable ex, HandlerMethod handlerMethod,
ServerHttpRequest request, ServerHttpResponse response) {
WebServerExchange exchange) {
if (ex instanceof Exception) {
InvocableHandlerMethod invocable = findExceptionHandler(handlerMethod, (Exception) ex);
@@ -128,7 +125,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Initializin
logger.debug("Invoking @ExceptionHandler method: " + invocable);
}
invocable.setHandlerMethodArgumentResolvers(getArgumentResolvers());
return invocable.invokeForRequest(request, response, ex);
return invocable.invokeForRequest(exchange, ex);
}
catch (Exception invocationEx) {
if (logger.isErrorEnabled()) {
@@ -41,6 +41,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.HandlerMethodSelector;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.server.WebServerExchange;
/**
@@ -65,9 +66,7 @@ public class RequestMappingHandlerMapping implements HandlerMapping,
@Override
public void afterPropertiesSet() throws Exception {
for (Object bean : this.applicationContext.getBeansOfType(Object.class).values()) {
detectHandlerMethods(bean);
}
this.applicationContext.getBeansOfType(Object.class).values().forEach(this::detectHandlerMethods);
}
protected void detectHandlerMethods(final Object bean) {
@@ -94,15 +93,15 @@ public class RequestMappingHandlerMapping implements HandlerMapping,
}
@Override
public Mono<Object> getHandler(ServerHttpRequest request) {
public Mono<Object> getHandler(WebServerExchange exchange) {
return Flux.create(subscriber -> {
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : this.methodMap.entrySet()) {
RequestMappingInfo info = entry.getKey();
if (info.matchesRequest(request)) {
if (info.matchesRequest(exchange.getRequest())) {
HandlerMethod handlerMethod = entry.getValue();
if (logger.isDebugEnabled()) {
logger.debug("Mapped " + request.getMethod() + " " +
request.getURI().getPath() + " to [" + handlerMethod + "]");
logger.debug("Mapped " + exchange.getRequest().getMethod() + " " +
exchange.getRequest().getURI().getPath() + " to [" + handlerMethod + "]");
}
subscriber.onNext(handlerMethod);
break;
@@ -19,9 +19,9 @@ package org.springframework.web.reactive.method.annotation;
import reactor.Mono;
import org.springframework.core.MethodParameter;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.reactive.method.HandlerMethodArgumentResolver;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.reactive.method.HandlerMethodArgumentResolver;
import org.springframework.web.server.WebServerExchange;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
@@ -41,10 +41,10 @@ public class RequestParamArgumentResolver implements HandlerMethodArgumentResolv
@Override
public Mono<Object> resolveArgument(MethodParameter param, ServerHttpRequest request) {
public Mono<Object> resolveArgument(MethodParameter param, WebServerExchange exchange) {
RequestParam annotation = param.getParameterAnnotation(RequestParam.class);
String name = (annotation.value().length() != 0 ? annotation.value() : param.getParameterName());
UriComponents uriComponents = UriComponentsBuilder.fromUri(request.getURI()).build();
UriComponents uriComponents = UriComponentsBuilder.fromUri(exchange.getRequest().getURI()).build();
String value = uriComponents.getQueryParams().getFirst(name);
return (value != null ? Mono.just(value) : Mono.empty());
}
@@ -33,11 +33,11 @@ import reactor.Mono;
import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.codec.Encoder;
import org.springframework.core.convert.ConversionService;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.core.codec.Encoder;
import org.springframework.util.Assert;
import org.springframework.util.MimeType;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
@@ -45,6 +45,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.reactive.HandlerResult;
import org.springframework.web.reactive.HandlerResultHandler;
import org.springframework.web.server.WebServerExchange;
/**
@@ -127,8 +128,7 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered
@Override
@SuppressWarnings("unchecked")
public Mono<Void> handleResult(ServerHttpRequest request,
ServerHttpResponse response, HandlerResult result) {
public Mono<Void> handleResult(WebServerExchange exchange, HandlerResult result) {
Object value = result.getResult();
if (value == null) {
@@ -147,7 +147,7 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered
elementType = returnType;
}
List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(exchange.getRequest());
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(elementType);
if (producibleMediaTypes.isEmpty()) {
@@ -184,6 +184,7 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered
if (selectedMediaType != null) {
Encoder<?> encoder = resolveEncoder(elementType, selectedMediaType);
if (encoder != null) {
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().setContentType(selectedMediaType);
return response.setBody(encoder.encode((Publisher) publisher, elementType, selectedMediaType));
}
@@ -0,0 +1,62 @@
/*
* Copyright 2002-2015 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.web.server;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.Assert;
/**
* Default implementation of {@link WebServerExchange}.
*
* @author Rossen Stoyanchev
*/
public class DefaultWebServerExchange implements WebServerExchange {
private final ServerHttpRequest request;
private final ServerHttpResponse response;
private final Map<String, Object> attributes = new ConcurrentHashMap<>();
public DefaultWebServerExchange(ServerHttpRequest request, ServerHttpResponse response) {
Assert.notNull(request, "'request' is required.");
Assert.notNull(response, "'response' is required.");
this.request = request;
this.response = response;
}
@Override
public ServerHttpRequest getRequest() {
return this.request;
}
@Override
public ServerHttpResponse getResponse() {
return this.response;
}
@Override
public Map<String, Object> getAttributes() {
return this.attributes;
}
}
@@ -1,69 +0,0 @@
/*
* Copyright 2002-2015 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.web.server;
import java.util.Arrays;
import java.util.List;
import reactor.Mono;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.Assert;
/**
* {@link HttpHandler} that delegates to a target {@link HttpHandler} and handles
* any errors from it by invoking one or more {@link HttpExceptionHandler}s
* sequentially until one of them completes successfully.
*
* @author Rossen Stoyanchev
* @author Stephane Maldini
*/
public class ErrorHandlingHttpHandler extends HttpHandlerDecorator {
private final List<HttpExceptionHandler> exceptionHandlers;
public ErrorHandlingHttpHandler(HttpHandler targetHandler, HttpExceptionHandler... exceptionHandlers) {
super(targetHandler);
Assert.notEmpty(exceptionHandlers, "At least one exception handler is required");
this.exceptionHandlers = Arrays.asList(exceptionHandlers);
}
@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
Mono<Void> mono;
try {
mono = getDelegate().handle(request, response);
}
catch (Throwable ex) {
mono = Mono.error(ex);
}
for (HttpExceptionHandler handler : this.exceptionHandlers) {
mono = applyExceptionHandler(mono, handler, request, response);
}
return mono;
}
private static Mono<Void> applyExceptionHandler(Mono<Void> mono, HttpExceptionHandler handler,
ServerHttpRequest request, ServerHttpResponse response) {
return mono.otherwise(ex -> handler.handle(request, response, ex)).after();
}
}
@@ -0,0 +1,74 @@
/*
* Copyright 2002-2015 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.web.server;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import reactor.Mono;
import org.springframework.http.HttpStatus;
/**
* {@code WebHandler} that decorates another with exception handling using one
* or more instances of {@link WebExceptionHandler}.
*
* @author Rossen Stoyanchev
*/
public class ExceptionHandlingWebHandler extends WebHandlerDecorator {
private final List<WebExceptionHandler> exceptionHandlers;
public ExceptionHandlingWebHandler(WebHandler delegate, WebExceptionHandler... exceptionHandlers) {
super(delegate);
this.exceptionHandlers = initList(exceptionHandlers);
}
private static List<WebExceptionHandler> initList(WebExceptionHandler[] list) {
return (list != null ? Collections.unmodifiableList(Arrays.asList(list)): Collections.emptyList());
}
/**
* @return a read-only list of the configured exception handlers.
*/
public List<WebExceptionHandler> getExceptionHandlers() {
return this.exceptionHandlers;
}
@Override
public Mono<Void> handle(WebServerExchange exchange) {
Mono<Void> mono;
try {
mono = getDelegate().handle(exchange);
}
catch (Throwable ex) {
mono = Mono.error(ex);
}
for (WebExceptionHandler exceptionHandler : this.exceptionHandlers) {
mono = mono.otherwise(ex -> exceptionHandler.handle(exchange, ex));
}
return mono.otherwise(ex -> handleUnresolvedException(exchange));
}
private Mono<? extends Void> handleUnresolvedException(WebServerExchange exchange) {
exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
return Mono.empty();
}
}
@@ -1,67 +0,0 @@
/*
* Copyright 2002-2015 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.web.server;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import reactor.Mono;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
/**
* {@link HttpHandler} that delegates to a chain of {@link HttpFilter}s followed
* by a target {@link HttpHandler}.
*
* @author Rossen Stoyanchev
*/
public class FilterChainHttpHandler extends HttpHandlerDecorator {
private final List<HttpFilter> filters;
public FilterChainHttpHandler(HttpHandler targetHandler, HttpFilter... filters) {
super(targetHandler);
this.filters = (filters != null ? Arrays.asList(filters) : Collections.emptyList());
}
@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
return new DefaultHttpFilterChain().filter(request, response);
}
private class DefaultHttpFilterChain implements HttpFilterChain {
private int index;
@Override
public Mono<Void> filter(ServerHttpRequest request, ServerHttpResponse response) {
if (this.index < filters.size()) {
HttpFilter filter = filters.get(this.index++);
return filter.filter(request, response, this);
}
else {
return getDelegate().handle(request, response);
}
}
}
}
@@ -0,0 +1,74 @@
/*
* Copyright 2002-2015 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.web.server;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import reactor.Mono;
/**
* {@code WebHandler} that decorates another with a chain of {@link WebFilter}s.
*
* @author Rossen Stoyanchev
*/
public class FilteringWebHandler extends WebHandlerDecorator {
private final List<WebFilter> filters;
public FilteringWebHandler(WebHandler targetHandler, WebFilter... filters) {
super(targetHandler);
this.filters = initList(filters);
}
private static List<WebFilter> initList(WebFilter[] list) {
return (list != null ? Collections.unmodifiableList(Arrays.asList(list)): Collections.emptyList());
}
/**
* @return a read-only list of the configured filters.
*/
public List<WebFilter> getFilters() {
return this.filters;
}
@Override
public Mono<Void> handle(WebServerExchange exchange) {
return new DefaultWebFilterChain().filter(exchange);
}
private class DefaultWebFilterChain implements WebFilterChain {
private int index;
@Override
public Mono<Void> filter(WebServerExchange exchange) {
if (this.index < filters.size()) {
WebFilter filter = filters.get(this.index++);
return filter.filter(exchange, this);
}
else {
return getDelegate().handle(exchange);
}
}
}
}
@@ -1,50 +0,0 @@
/*
* Copyright 2002-2015 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.web.server;
import reactor.Mono;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
/**
* Contract for interception-style, chained processing of HTTP requests.
*
* <p>Filters may be used to implement cross-cutting, application-agnostic
* requirements such as security, timeouts, and others.
*
* <p>{@link FilterChainHttpHandler} provides a way of constructing a chain of
* {@link HttpFilter}s followed by a target {@link HttpHandler}.
*
* @author Rossen Stoyanchev
* @see FilterChainHttpHandler
*/
public interface HttpFilter {
/**
* Process the given request and optionally delegate to the next HttpFilter.
*
* @param request current HTTP request.
* @param response current HTTP response.
* @param chain provides a way to delegate to the next HttpFilter.
* @return {@code Mono<Void>} to indicate when request processing is complete.
*/
Mono<Void> filter(ServerHttpRequest request, ServerHttpResponse response,
HttpFilterChain chain);
}
@@ -0,0 +1,38 @@
/*
* Copyright 2002-2015 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.web.server;
import reactor.Mono;
/**
* Contract for handling exceptions during web server exchange processing.
*
* @author Rossen Stoyanchev
*/
public interface WebExceptionHandler {
/**
* Handle the given exception. A completion signal through the return value
* indicates error handling is complete while an error signal indicates the
* exception is still not handled.
*
* @param exchange the current exchange
* @param ex the exception to handle
* @return {@code Mono<Void>} to indicate when exception handling is complete
*/
Mono<Void> handle(WebServerExchange exchange, Throwable ex);
}
@@ -0,0 +1,40 @@
/*
* Copyright 2002-2015 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.web.server;
import reactor.Mono;
/**
* Contract for interception-style, chained processing of Web requests that may
* be used to implement cross-cutting, application-agnostic requirements such
* as security, timeouts, and others.
*
* @author Rossen Stoyanchev
*/
public interface WebFilter {
/**
* Process the Web request and (optionally) delegate to the next
* {@code WebFilter} through the given {@link WebFilterChain}.
*
* @param exchange the current server exchange
* @param chain provides a way to delegate to the next filter
* @return {@code Mono<Void>} to indicate when request processing is complete
*/
Mono<Void> filter(WebServerExchange exchange, WebFilterChain chain);
}
@@ -17,23 +17,19 @@ package org.springframework.web.server;
import reactor.Mono;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
/**
* Represents a chain of {@link HttpFilter}s allowing each {@link HttpFilter} to
* delegate to the next in the chain.
* Contract to allow a {@link WebFilter} to delegate to the next in the chain.
*
* @author Rossen Stoyanchev
*/
public interface HttpFilterChain {
public interface WebFilterChain {
/**
* Delegate to the next {@code WebFilter} in the chain.
*
* @param request current HTTP request.
* @param response current HTTP response.
* @return {@code Mono<Void>} to indicate when request handling is complete.
* @param exchange the current server exchange
* @return {@code Mono<Void>} to indicate when request handling is complete
*/
Mono<Void> filter(ServerHttpRequest request, ServerHttpResponse response);
Mono<Void> filter(WebServerExchange exchange);
}
@@ -0,0 +1,42 @@
/*
* Copyright 2002-2015 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.web.server;
import reactor.Mono;
/**
* Contract to handle a web server exchange.
*
* <p>Use {@link WebToHttpHandlerAdapter} to adapt a {@code WebHandler} to an
* {@link org.springframework.http.server.reactive.HttpHandler HttpHandler}.
* The {@link WebToHttpHandlerBuilder} provides a convenient way to do that while
* also optionally configuring one or more filters and/or exception handlers.
*
* @author Rossen Stoyanchev
* @see WebToHttpHandlerBuilder
*/
public interface WebHandler {
/**
* Handle the web server exchange.
*
* @param exchange the current server exchange
* @return {@code Mono<Void>} to indicate when request handling is complete
*/
Mono<Void> handle(WebServerExchange exchange);
}
@@ -17,34 +17,32 @@ package org.springframework.web.server;
import reactor.Mono;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.Assert;
/**
* Base class for a {@link WebHandler} that decorates and delegates to another.
*
* @author Rossen Stoyanchev
*/
public class HttpHandlerDecorator implements HttpHandler {
public class WebHandlerDecorator implements WebHandler {
private final HttpHandler delegate;
private final WebHandler delegate;
public HttpHandlerDecorator(HttpHandler delegate) {
public WebHandlerDecorator(WebHandler delegate) {
Assert.notNull(delegate, "'delegate' must not be null");
this.delegate = delegate;
}
public HttpHandler getDelegate() {
public WebHandler getDelegate() {
return this.delegate;
}
@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
return this.delegate.handle(request, response);
public Mono<Void> handle(WebServerExchange exchange) {
return this.delegate.handle(exchange);
}
@Override
@@ -15,24 +15,33 @@
*/
package org.springframework.web.server;
import reactor.Mono;
import java.util.Map;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
/**
* Handle any exception by setting the response status to 500.
* Contract for an HTTP request-response interaction. Provides access to the HTTP
* request and response and also exposes additional server-side processing
* related properties and features such as request attributes.
*
* @author Rossen Stoyanchev
*/
public class InternalServerErrorExceptionHandler implements HttpExceptionHandler {
public interface WebServerExchange {
/**
* @return the current HTTP request
*/
ServerHttpRequest getRequest();
@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response, Throwable ex) {
response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
return Mono.empty();
}
/**
* @return the current HTTP response
*/
ServerHttpResponse getResponse();
/**
* @return mutable map of request attributes for the current exchange
*/
Map<String, Object> getAttributes();
}
@@ -22,26 +22,26 @@ import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
/**
* A contract for resolving exceptions from HTTP request handling.
*
* <p>{@link ErrorHandlingHttpHandler} provides a way of applying a list
* {@link HttpExceptionHandler}s to a target {@link HttpHandler}.
* Adapt {@link WebHandler} to {@link HttpHandler} also creating the
* {@link WebServerExchange} before invoking the target {@code WebHandler}.
*
* @author Rossen Stoyanchev
* @see ErrorHandlingHttpHandler
*/
public interface HttpExceptionHandler {
public class WebToHttpHandlerAdapter extends WebHandlerDecorator implements HttpHandler {
/**
* Handle the given exception and return a completion Publisher to indicate
* when error handling is complete, or send an error signal if the exception
* was not handled.
*
* @param request the current request
* @param response the current response
* @param ex the exception to handle
* @return {@code Mono<Void>} to indicate when exception handling is complete.
*/
Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response, Throwable ex);
public WebToHttpHandlerAdapter(WebHandler delegate) {
super(delegate);
}
@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
WebServerExchange exchange = createWebServerExchange(request, response);
return getDelegate().handle(exchange);
}
protected WebServerExchange createWebServerExchange(ServerHttpRequest request, ServerHttpResponse response) {
return new DefaultWebServerExchange(request, response);
}
}
@@ -0,0 +1,84 @@
/*
* Copyright 2002-2015 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.web.server;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Assist with building an
* {@link org.springframework.http.server.reactive.HttpHandler HttpHandler} to
* invoke a target {@link WebHandler} with an optional chain of
* {@link WebFilter}s and one or more {@link WebExceptionHandler}s.
*
* <p>Effective this sets up the following {@code WebHandler} delegation:<br>
* {@link WebToHttpHandlerAdapter} {@code -->}
* {@link ExceptionHandlingWebHandler} {@code -->}
* {@link FilteringWebHandler}
*
* @author Rossen Stoyanchev
*/
public class WebToHttpHandlerBuilder {
private final WebHandler targetHandler;
private final List<WebFilter> filters = new ArrayList<>();
private final List<WebExceptionHandler> exceptionHandlers = new ArrayList<>();
private WebToHttpHandlerBuilder(WebHandler targetHandler) {
Assert.notNull(targetHandler, "'targetHandler' must not be null");
this.targetHandler = targetHandler;
}
public static WebToHttpHandlerBuilder webHandler(WebHandler webHandler) {
return new WebToHttpHandlerBuilder(webHandler);
}
public WebToHttpHandlerBuilder filters(WebFilter... filters) {
if (!ObjectUtils.isEmpty(filters)) {
this.filters.addAll(Arrays.asList(filters));
}
return this;
}
public WebToHttpHandlerBuilder exceptionHandlers(WebExceptionHandler... exceptionHandlers) {
if (!ObjectUtils.isEmpty(exceptionHandlers)) {
this.exceptionHandlers.addAll(Arrays.asList(exceptionHandlers));
}
return this;
}
public WebToHttpHandlerAdapter build() {
WebHandler webHandler = this.targetHandler;
if (!this.exceptionHandlers.isEmpty()) {
WebExceptionHandler[] array = new WebExceptionHandler[this.exceptionHandlers.size()];
webHandler = new ExceptionHandlingWebHandler(webHandler, this.exceptionHandlers.toArray(array));
}
if (!this.filters.isEmpty()) {
WebFilter[] array = new WebFilter[this.filters.size()];
webHandler = new FilteringWebHandler(webHandler, this.filters.toArray(array));
}
return new WebToHttpHandlerAdapter(webHandler);
}
}