From df7c8e550d3218ee413bebbc8508d0c6ced5d951 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 21 Apr 2016 20:35:47 -0400 Subject: [PATCH] Updates to WebHandler support Rename two classes each adapting to WebHandler to avoid confusing them: 1. HttpWebHandlerAdapter adapts from the low level HttpHandler to any WebHandler (e.g. DispatcherHandler). 2. SimpleHandlerAdapter adapts the plain WebHandler for use within the DispatcherHandler. This commit also fixes an issue in WebHttpHandlerBuilder to ensure that WebExceptionHandler's are inserted before and not after WebFilter's. --- ...Adapter.java => SimpleHandlerAdapter.java} | 14 ++-- .../web/server/WebHandler.java | 7 +- ...dapter.java => HttpWebHandlerAdapter.java} | 6 +- .../server/adapter/WebHttpHandlerBuilder.java | 66 +++++++------------ .../server/handler/FilteringWebHandler.java | 6 +- .../result/WebHandlerIntegrationTests.java | 13 ++-- .../handler/FilteringWebHandlerTests.java | 42 +++++++++++- 7 files changed, 83 insertions(+), 71 deletions(-) rename spring-web-reactive/src/main/java/org/springframework/web/reactive/result/{WebHandlerHandlerAdapter.java => SimpleHandlerAdapter.java} (75%) rename spring-web-reactive/src/main/java/org/springframework/web/server/adapter/{WebHttpHandlerAdapter.java => HttpWebHandlerAdapter.java} (94%) diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/WebHandlerHandlerAdapter.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/SimpleHandlerAdapter.java similarity index 75% rename from spring-web-reactive/src/main/java/org/springframework/web/reactive/result/WebHandlerHandlerAdapter.java rename to spring-web-reactive/src/main/java/org/springframework/web/reactive/result/SimpleHandlerAdapter.java index c9f8932eb46..a4836426e85 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/WebHandlerHandlerAdapter.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/SimpleHandlerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -16,7 +16,6 @@ package org.springframework.web.reactive.result; -import org.reactivestreams.Publisher; import reactor.core.publisher.Mono; import org.springframework.core.ResolvableType; @@ -27,15 +26,16 @@ import org.springframework.web.server.WebHandler; import org.springframework.web.server.ServerWebExchange; /** - * Adapter to use a {@link WebHandler} through the {@link DispatcherHandler}. + * HandlerAdapter that allows using the plain {@link WebHandler} contract with + * the generic {@link DispatcherHandler}. * * @author Rossen Stoyanchev * @author Sebastien Deleuze */ -public class WebHandlerHandlerAdapter implements HandlerAdapter { +public class SimpleHandlerAdapter implements HandlerAdapter { - private static final ResolvableType PUBLISHER_VOID = ResolvableType.forClassWithGenerics( - Publisher.class, Void.class); + private static final ResolvableType MONO_VOID = ResolvableType.forClassWithGenerics( + Mono.class, Void.class); @Override @@ -47,7 +47,7 @@ public class WebHandlerHandlerAdapter implements HandlerAdapter { public Mono handle(ServerWebExchange exchange, Object handler) { WebHandler webHandler = (WebHandler) handler; Mono mono = webHandler.handle(exchange); - return Mono.just(new HandlerResult(webHandler, mono, PUBLISHER_VOID)); + return Mono.just(new HandlerResult(webHandler, mono, MONO_VOID)); } } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/WebHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/server/WebHandler.java index b2c8045b193..45e2da95957 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/server/WebHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/server/WebHandler.java @@ -18,19 +18,18 @@ package org.springframework.web.server; import reactor.core.publisher.Mono; -import org.springframework.web.server.adapter.WebHttpHandlerAdapter; +import org.springframework.web.server.adapter.HttpWebHandlerAdapter; import org.springframework.web.server.adapter.WebHttpHandlerBuilder; /** - * Contract to handle a web server exchange. + * Contract to handle a web request. * - *

Use {@link WebHttpHandlerAdapter} to adapt a {@code WebHandler} to an + *

Use {@link HttpWebHandlerAdapter} to adapt a {@code WebHandler} to an * {@link org.springframework.http.server.reactive.HttpHandler HttpHandler}. * The {@link WebHttpHandlerBuilder} provides a convenient way to do that while * also optionally configuring one or more filters and/or exception handlers. * * @author Rossen Stoyanchev - * @see WebHttpHandlerBuilder */ public interface WebHandler { diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/adapter/WebHttpHandlerAdapter.java b/spring-web-reactive/src/main/java/org/springframework/web/server/adapter/HttpWebHandlerAdapter.java similarity index 94% rename from spring-web-reactive/src/main/java/org/springframework/web/server/adapter/WebHttpHandlerAdapter.java rename to spring-web-reactive/src/main/java/org/springframework/web/server/adapter/HttpWebHandlerAdapter.java index fab3ffcdcbc..da9deadaab1 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/server/adapter/WebHttpHandlerAdapter.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/server/adapter/HttpWebHandlerAdapter.java @@ -38,15 +38,15 @@ import org.springframework.web.server.session.WebSessionManager; * * @author Rossen Stoyanchev */ -public class WebHttpHandlerAdapter extends WebHandlerDecorator implements HttpHandler { +public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHandler { - private static Log logger = LogFactory.getLog(WebHttpHandlerAdapter.class); + private static Log logger = LogFactory.getLog(HttpWebHandlerAdapter.class); private WebSessionManager sessionManager = new DefaultWebSessionManager(); - public WebHttpHandlerAdapter(WebHandler delegate) { + public HttpWebHandlerAdapter(WebHandler delegate) { super(delegate); } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/adapter/WebHttpHandlerBuilder.java b/spring-web-reactive/src/main/java/org/springframework/web/server/adapter/WebHttpHandlerBuilder.java index 38dfda1b545..8893bf0460c 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/server/adapter/WebHttpHandlerBuilder.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/server/adapter/WebHttpHandlerBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -31,28 +31,20 @@ import org.springframework.web.server.handler.FilteringWebHandler; import org.springframework.web.server.session.WebSessionManager; /** - * Build an {@link org.springframework.http.server.reactive.HttpHandler HttpHandler} - * to handle requests with a chain of {@link #filters(WebFilter...) web filters}, - * a target {@link #webHandler(WebHandler) web handler}, and apply one or more - * {@link #exceptionHandlers(WebExceptionHandler...) exception handlers}. - * - *

Effective this sets up the following {@code WebHandler} delegation:
- * {@link WebHttpHandlerAdapter} {@code -->} - * {@link ExceptionHandlingWebHandler} {@code -->} - * {@link FilteringWebHandler} {@code -->} - * {@link WebHandler} + * Builder for an {@link HttpHandler} that adapts to a target {@link WebHandler} + * along with a chain of {@link WebFilter}s and a set of + * {@link WebExceptionHandler}s. * *

Example usage: *

- * WebFilter myFilter = ... ;
- * WebHandler myHandler = ... ;
+ * WebFilter filter = ... ;
+ * WebHandler webHandler = ... ;
+ * WebExceptionHandler exceptionHandler = ...;
  *
- * HttpHandler httpHandler = WebToHttpHandlerBuilder.webHandler(myHandler)
- *         .filters(myFilter)
- *         .exceptionHandlers(new ResponseStatusExceptionHandler())
+ * HttpHandler httpHandler = WebHttpHandlerBuilder.webHandler(webHandler)
+ *         .filters(filter)
+ *         .exceptionHandlers(exceptionHandler)
  *         .build();
- *
- * // Configure the HttpServer with the created httpHandler
  * 
* * @author Rossen Stoyanchev @@ -70,7 +62,7 @@ public class WebHttpHandlerBuilder { /** * Private constructor. - * See static factory method {@link #webHandler(WebHandler)}. + * See factory method {@link #webHandler(WebHandler)}. */ private WebHttpHandlerBuilder(WebHandler targetHandler) { Assert.notNull(targetHandler, "'targetHandler' must not be null"); @@ -80,10 +72,10 @@ public class WebHttpHandlerBuilder { /** * Factory method to create a new builder instance. - * @param targetHandler the target handler to process requests with + * @param webHandler the target handler for the request */ - public static WebHttpHandlerBuilder webHandler(WebHandler targetHandler) { - return new WebHttpHandlerBuilder(targetHandler); + public static WebHttpHandlerBuilder webHandler(WebHandler webHandler) { + return new WebHttpHandlerBuilder(webHandler); } @@ -114,6 +106,7 @@ public class WebHttpHandlerBuilder { * {@link ServerWebExchange WebServerExchange} * created for each HTTP request. * @param sessionManager the session manager + * @see HttpWebHandlerAdapter#setSessionManager(WebSessionManager) */ public WebHttpHandlerBuilder sessionManager(WebSessionManager sessionManager) { this.sessionManager = sessionManager; @@ -124,35 +117,20 @@ public class WebHttpHandlerBuilder { * Build the {@link HttpHandler}. */ public HttpHandler build() { - WebHandler handler = createWebHandler(); - return adaptWebHandler(handler); - } - - /** - * Create the final (decorated) {@link WebHandler} to use. - */ - protected WebHandler createWebHandler() { 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 webHandler; - } - - /** - * Adapt the {@link WebHandler} to {@link HttpHandler}. - */ - protected WebHttpHandlerAdapter adaptWebHandler(WebHandler handler) { - WebHttpHandlerAdapter adapter = new WebHttpHandlerAdapter(handler); + if (!this.exceptionHandlers.isEmpty()) { + WebExceptionHandler[] array = new WebExceptionHandler[this.exceptionHandlers.size()]; + webHandler = new ExceptionHandlingWebHandler(webHandler, this.exceptionHandlers.toArray(array)); + } + HttpWebHandlerAdapter httpHandler = new HttpWebHandlerAdapter(webHandler); if (this.sessionManager != null) { - adapter.setSessionManager(this.sessionManager); + httpHandler.setSessionManager(this.sessionManager); } - return adapter; + return httpHandler; } } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/handler/FilteringWebHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/server/handler/FilteringWebHandler.java index 8dc1d7396a7..de12581575a 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/server/handler/FilteringWebHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/server/handler/FilteringWebHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -27,8 +27,8 @@ import org.springframework.web.server.WebHandler; import org.springframework.web.server.ServerWebExchange; /** - * WebHandler that delegates to a chain of {@link WebFilter} instances followed - * by a target {@link WebHandler}. + * WebHandler that delegates to a chain of {@link WebFilter} instances and then + * to the target {@link WebHandler}. * * @author Rossen Stoyanchev */ diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/WebHandlerIntegrationTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/WebHandlerIntegrationTests.java index dc9a56baf3b..7eee8878ed3 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/WebHandlerIntegrationTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/WebHandlerIntegrationTests.java @@ -48,7 +48,7 @@ import static org.junit.Assert.assertEquals; /** - * Integration tests with simple WebHandler's processing requests. + * Integration tests with requests mapped to plain {@link WebHandler}s. * * @author Rossen Stoyanchev */ @@ -61,9 +61,9 @@ public class WebHandlerIntegrationTests extends AbstractHttpHandlerIntegrationTe protected HttpHandler createHttpHandler() { StaticApplicationContext wac = new StaticApplicationContext(); - wac.registerSingleton("hm", TestHandlerMapping.class); - wac.registerSingleton("ha", WebHandlerHandlerAdapter.class); - wac.registerSingleton("rh", SimpleResultHandler.class); + wac.registerSingleton("handlerMapping", TestSimpleUrlHandlerMapping.class); + wac.registerSingleton("handlerAdapter", SimpleHandlerAdapter.class); + wac.registerSingleton("resultHandler", SimpleResultHandler.class); wac.refresh(); DispatcherHandler dispatcherHandler = new DispatcherHandler(); @@ -129,9 +129,9 @@ public class WebHandlerIntegrationTests extends AbstractHttpHandlerIntegrationTe } - private static class TestHandlerMapping extends SimpleUrlHandlerMapping { + private static class TestSimpleUrlHandlerMapping extends SimpleUrlHandlerMapping { - public TestHandlerMapping() { + public TestSimpleUrlHandlerMapping() { Map map = new HashMap<>(); map.put("/foo", new FooHandler()); map.put("/bar", new BarHandler()); @@ -140,7 +140,6 @@ public class WebHandlerIntegrationTests extends AbstractHttpHandlerIntegrationTe } } - private static DataBuffer asDataBuffer(String text) { return new DefaultDataBufferAllocator().allocateBuffer().write(text.getBytes(StandardCharsets.UTF_8)); } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/server/handler/FilteringWebHandlerTests.java b/spring-web-reactive/src/test/java/org/springframework/web/server/handler/FilteringWebHandlerTests.java index 3ba4e6966dc..57078bbe02c 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/server/handler/FilteringWebHandlerTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/server/handler/FilteringWebHandlerTests.java @@ -23,20 +23,25 @@ import org.apache.commons.logging.LogFactory; import org.junit.Before; import org.junit.Test; import reactor.core.publisher.Mono; +import reactor.core.test.TestSubscriber; import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.HttpHandler; import org.springframework.http.server.reactive.MockServerHttpRequest; import org.springframework.http.server.reactive.MockServerHttpResponse; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.server.WebExceptionHandler; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; import org.springframework.web.server.WebHandler; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.adapter.WebHttpHandlerBuilder; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; /** @@ -47,9 +52,9 @@ public class FilteringWebHandlerTests { private static Log logger = LogFactory.getLog(FilteringWebHandlerTests.class); - private ServerHttpRequest request; + private MockServerHttpRequest request; - private ServerHttpResponse response; + private MockServerHttpResponse response; @Before @@ -108,6 +113,20 @@ public class FilteringWebHandlerTests { assertTrue(webHandler.invoked()); } + @Test + public void handleErrorFromFilter() throws Exception { + TestExceptionHandler exceptionHandler = new TestExceptionHandler(); + HttpHandler handler = WebHttpHandlerBuilder.webHandler(new StubWebHandler()) + .filters(new ExceptionFilter()).exceptionHandlers(exceptionHandler).build(); + handler.handle(this.request, this.response).get(); + + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, this.response.getStatus()); + + Throwable savedException = exceptionHandler.ex; + assertNotNull(savedException); + assertEquals("boo", savedException.getMessage()); + } + private HttpHandler createHttpHandler(StubWebHandler webHandler, WebFilter... filters) { return WebHttpHandlerBuilder.webHandler(webHandler).filters(filters).build(); } @@ -117,7 +136,6 @@ public class FilteringWebHandlerTests { private volatile boolean invoked; - public boolean invoked() { return this.invoked; } @@ -156,6 +174,24 @@ public class FilteringWebHandlerTests { } } + private static class ExceptionFilter implements WebFilter { + + @Override + public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + return Mono.error(new IllegalStateException("boo")); + } + } + + private static class TestExceptionHandler implements WebExceptionHandler { + + private Throwable ex; + + @Override + public Mono handle(ServerWebExchange exchange, Throwable ex) { + this.ex = ex; + return Mono.error(ex); + } + } private static class StubWebHandler implements WebHandler {