Browse Source

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.
pull/1111/head
Rossen Stoyanchev 10 years ago
parent
commit
df7c8e550d
  1. 14
      spring-web-reactive/src/main/java/org/springframework/web/reactive/result/SimpleHandlerAdapter.java
  2. 7
      spring-web-reactive/src/main/java/org/springframework/web/server/WebHandler.java
  3. 6
      spring-web-reactive/src/main/java/org/springframework/web/server/adapter/HttpWebHandlerAdapter.java
  4. 66
      spring-web-reactive/src/main/java/org/springframework/web/server/adapter/WebHttpHandlerBuilder.java
  5. 6
      spring-web-reactive/src/main/java/org/springframework/web/server/handler/FilteringWebHandler.java
  6. 13
      spring-web-reactive/src/test/java/org/springframework/web/reactive/result/WebHandlerIntegrationTests.java
  7. 42
      spring-web-reactive/src/test/java/org/springframework/web/server/handler/FilteringWebHandlerTests.java

14
spring-web-reactive/src/main/java/org/springframework/web/reactive/result/WebHandlerHandlerAdapter.java → spring-web-reactive/src/main/java/org/springframework/web/reactive/result/SimpleHandlerAdapter.java

@ -1,5 +1,5 @@ @@ -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 @@ @@ -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; @@ -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 { @@ -47,7 +47,7 @@ public class WebHandlerHandlerAdapter implements HandlerAdapter {
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
WebHandler webHandler = (WebHandler) handler;
Mono<Void> mono = webHandler.handle(exchange);
return Mono.just(new HandlerResult(webHandler, mono, PUBLISHER_VOID));
return Mono.just(new HandlerResult(webHandler, mono, MONO_VOID));
}
}

7
spring-web-reactive/src/main/java/org/springframework/web/server/WebHandler.java

@ -18,19 +18,18 @@ package org.springframework.web.server; @@ -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.
*
* <p>Use {@link WebHttpHandlerAdapter} to adapt a {@code WebHandler} to an
* <p>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 {

6
spring-web-reactive/src/main/java/org/springframework/web/server/adapter/WebHttpHandlerAdapter.java → spring-web-reactive/src/main/java/org/springframework/web/server/adapter/HttpWebHandlerAdapter.java

@ -38,15 +38,15 @@ import org.springframework.web.server.session.WebSessionManager; @@ -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);
}

66
spring-web-reactive/src/main/java/org/springframework/web/server/adapter/WebHttpHandlerBuilder.java

@ -1,5 +1,5 @@ @@ -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; @@ -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}.
*
* <p>Effective this sets up the following {@code WebHandler} delegation:<br>
* {@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.
*
* <p>Example usage:
* <pre>
* 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
* </pre>
*
* @author Rossen Stoyanchev
@ -70,7 +62,7 @@ public class WebHttpHandlerBuilder { @@ -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 { @@ -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 { @@ -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 { @@ -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;
}
}

6
spring-web-reactive/src/main/java/org/springframework/web/server/handler/FilteringWebHandler.java

@ -1,5 +1,5 @@ @@ -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; @@ -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
*/

13
spring-web-reactive/src/test/java/org/springframework/web/reactive/result/WebHandlerIntegrationTests.java

@ -48,7 +48,7 @@ import static org.junit.Assert.assertEquals; @@ -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 @@ -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 @@ -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<String, Object> map = new HashMap<>();
map.put("/foo", new FooHandler());
map.put("/bar", new BarHandler());
@ -140,7 +140,6 @@ public class WebHandlerIntegrationTests extends AbstractHttpHandlerIntegrationTe @@ -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));
}

42
spring-web-reactive/src/test/java/org/springframework/web/server/handler/FilteringWebHandlerTests.java

@ -23,20 +23,25 @@ import org.apache.commons.logging.LogFactory; @@ -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 { @@ -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 { @@ -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 { @@ -117,7 +136,6 @@ public class FilteringWebHandlerTests {
private volatile boolean invoked;
public boolean invoked() {
return this.invoked;
}
@ -156,6 +174,24 @@ public class FilteringWebHandlerTests { @@ -156,6 +174,24 @@ public class FilteringWebHandlerTests {
}
}
private static class ExceptionFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return Mono.error(new IllegalStateException("boo"));
}
}
private static class TestExceptionHandler implements WebExceptionHandler {
private Throwable ex;
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
this.ex = ex;
return Mono.error(ex);
}
}
private static class StubWebHandler implements WebHandler {

Loading…
Cancel
Save