Browse Source

Non-blocking HandlerMapping chain

pull/1111/head
Rossen Stoyanchev 10 years ago
parent
commit
71d1d11fac
  1. 42
      spring-web-reactive/src/main/java/org/springframework/web/reactive/DispatcherHandler.java
  2. 59
      spring-web-reactive/src/main/java/org/springframework/web/reactive/HandlerNotFoundException.java
  3. 12
      spring-web-reactive/src/main/java/org/springframework/web/reactive/handler/SimpleUrlHandlerMapping.java
  4. 27
      spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/RequestMappingHandlerMapping.java
  5. 22
      spring-web-reactive/src/test/java/org/springframework/web/reactive/handler/SimpleUrlHandlerMappingIntegrationTests.java

42
spring-web-reactive/src/main/java/org/springframework/web/reactive/DispatcherHandler.java

@ -19,6 +19,7 @@ package org.springframework.web.reactive; @@ -19,6 +19,7 @@ package org.springframework.web.reactive;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -30,7 +31,6 @@ import org.springframework.beans.factory.BeanFactoryUtils; @@ -30,7 +31,6 @@ 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.HttpStatus;
import org.springframework.http.server.ReactiveHttpHandler;
import org.springframework.http.server.ReactiveServerHttpRequest;
import org.springframework.http.server.ReactiveServerHttpResponse;
@ -76,6 +76,7 @@ public class DispatcherHandler implements ReactiveHttpHandler, ApplicationContex @@ -76,6 +76,7 @@ public class DispatcherHandler implements ReactiveHttpHandler, ApplicationContex
context, HandlerMapping.class, true, false);
this.handlerMappings = new ArrayList<>(mappingBeans.values());
this.handlerMappings.add(new NotFoundHandlerMapping());
AnnotationAwareOrderComparator.sort(this.handlerMappings);
Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
@ -98,13 +99,9 @@ public class DispatcherHandler implements ReactiveHttpHandler, ApplicationContex @@ -98,13 +99,9 @@ public class DispatcherHandler implements ReactiveHttpHandler, ApplicationContex
logger.debug("Processing " + request.getMethod() + " request for [" + request.getURI() + "]");
}
Publisher<Object> handlerPublisher = getHandler(request);
if (handlerPublisher == null) {
// No exception handling mechanism yet
response.setStatusCode(HttpStatus.NOT_FOUND);
response.writeHeaders();
return Publishers.empty();
}
Publisher<HandlerMapping> mappings = Publishers.from(this.handlerMappings);
Publisher<Object> handlerPublisher = Publishers.concatMap(mappings, m -> m.getHandler(request));
handlerPublisher = first(handlerPublisher);
Publisher<HandlerResult> resultPublisher = Publishers.concatMap(handlerPublisher, handler -> {
HandlerAdapter handlerAdapter = getHandlerAdapter(handler);
@ -117,16 +114,6 @@ public class DispatcherHandler implements ReactiveHttpHandler, ApplicationContex @@ -117,16 +114,6 @@ public class DispatcherHandler implements ReactiveHttpHandler, ApplicationContex
});
}
protected Publisher<Object> getHandler(ReactiveServerHttpRequest request) {
for (HandlerMapping handlerMapping : this.handlerMappings) {
Publisher<Object> handlerPublisher = handlerMapping.getHandler(request);
if (handlerPublisher != null) {
return handlerPublisher;
}
}
return null;
}
protected HandlerAdapter getHandlerAdapter(Object handler) {
for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
if (handlerAdapter.supports(handler)) {
@ -145,4 +132,21 @@ public class DispatcherHandler implements ReactiveHttpHandler, ApplicationContex @@ -145,4 +132,21 @@ public class DispatcherHandler implements ReactiveHttpHandler, ApplicationContex
throw new IllegalStateException("No HandlerResultHandler: " + handlerResult.getValue());
}
}
private static <E> Publisher<E> first(Publisher<E> source) {
return Publishers.lift(source, (e, subscriber) -> {
subscriber.onNext(e);
subscriber.onComplete();
});
}
private static class NotFoundHandlerMapping implements HandlerMapping {
@Override
public Publisher<Object> getHandler(ReactiveServerHttpRequest request) {
return Publishers.error(new HandlerNotFoundException(request.getMethod(),
request.getURI().getPath(), request.getHeaders()));
}
}
}

59
spring-web-reactive/src/main/java/org/springframework/web/reactive/HandlerNotFoundException.java

@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
/*
* 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.reactive;
import org.springframework.core.NestedRuntimeException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
/**
* @author Rossen Stoyanchev
*/
public class HandlerNotFoundException extends NestedRuntimeException {
private final HttpMethod method;
private final String requestURL;
private final HttpHeaders headers;
/**
* Constructor for NoHandlerFoundException.
* @param method the HTTP method
* @param requestURL the HTTP request URL
* @param headers the HTTP request headers
*/
public HandlerNotFoundException(HttpMethod method, String requestURL, HttpHeaders headers) {
super("No handler found for " + method + " " + requestURL);
this.method = method;
this.requestURL = requestURL;
this.headers = headers;
}
public HttpMethod getMethod() {
return this.method;
}
public String getRequestURL() {
return this.requestURL;
}
public HttpHeaders getHeaders() {
return this.headers;
}
}

12
spring-web-reactive/src/main/java/org/springframework/web/reactive/handler/SimpleUrlHandlerMapping.java

@ -21,6 +21,7 @@ import java.util.Map; @@ -21,6 +21,7 @@ import java.util.Map;
import org.reactivestreams.Publisher;
import reactor.Publishers;
import reactor.core.publisher.PublisherFactory;
import org.springframework.http.server.ReactiveServerHttpRequest;
import org.springframework.web.reactive.HandlerMapping;
@ -43,9 +44,14 @@ public class SimpleUrlHandlerMapping implements HandlerMapping { @@ -43,9 +44,14 @@ public class SimpleUrlHandlerMapping implements HandlerMapping {
@Override
public Publisher<Object> getHandler(ReactiveServerHttpRequest request) {
String path = request.getURI().getPath();
Object handler = this.handlerMap.get(path);
return (handler != null ? Publishers.just(handler) : null);
return PublisherFactory.create(subscriber -> {
String path = request.getURI().getPath();
Object handler = this.handlerMap.get(path);
if (handler != null) {
subscriber.onNext(handler);
}
subscriber.onComplete();
});
}
}

27
spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/RequestMappingHandlerMapping.java

@ -28,19 +28,19 @@ import java.util.TreeSet; @@ -28,19 +28,19 @@ import java.util.TreeSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.reactivestreams.Publisher;
import reactor.Publishers;
import reactor.core.publisher.PublisherFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.server.ReactiveServerHttpRequest;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
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;
/**
@ -95,18 +95,21 @@ public class RequestMappingHandlerMapping implements HandlerMapping, @@ -95,18 +95,21 @@ public class RequestMappingHandlerMapping implements HandlerMapping,
@Override
public Publisher<Object> getHandler(ReactiveServerHttpRequest request) {
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : this.methodMap.entrySet()) {
RequestMappingInfo info = entry.getKey();
if (info.matchesRequest(request)) {
HandlerMethod handlerMethod = entry.getValue();
if (logger.isDebugEnabled()) {
logger.debug("Mapped " + request.getMethod() + " " +
request.getURI().getPath() + " to [" + handlerMethod + "]");
return PublisherFactory.create(subscriber -> {
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : this.methodMap.entrySet()) {
RequestMappingInfo info = entry.getKey();
if (info.matchesRequest(request)) {
HandlerMethod handlerMethod = entry.getValue();
if (logger.isDebugEnabled()) {
logger.debug("Mapped " + request.getMethod() + " " +
request.getURI().getPath() + " to [" + handlerMethod + "]");
}
subscriber.onNext(handlerMethod);
break;
}
return Publishers.just(handlerMethod);
}
}
return null;
subscriber.onComplete();
});
}

22
spring-web-reactive/src/test/java/org/springframework/web/reactive/handler/SimpleUrlHandlerMappingIntegrationTests.java

@ -21,6 +21,7 @@ import java.nio.charset.Charset; @@ -21,6 +21,7 @@ import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import org.junit.Ignore;
import org.junit.Test;
import org.reactivestreams.Publisher;
import reactor.io.buffer.Buffer;
@ -31,11 +32,14 @@ import org.springframework.http.RequestEntity; @@ -31,11 +32,14 @@ import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.http.server.ReactiveServerHttpRequest;
import org.springframework.http.server.ReactiveServerHttpResponse;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.reactive.DispatcherHandler;
import org.springframework.http.server.AbstractHttpHandlerIntegrationTests;
import org.springframework.http.server.ReactiveHttpHandler;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.reactive.method.annotation.RequestMappingHandlerMapping;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@ -89,6 +93,24 @@ public class SimpleUrlHandlerMappingIntegrationTests extends AbstractHttpHandler @@ -89,6 +93,24 @@ public class SimpleUrlHandlerMappingIntegrationTests extends AbstractHttpHandler
assertArrayEquals("bar".getBytes(UTF_8), response.getBody());
}
// TODO: remove @Ignore after 404 default handling
@Test
@Ignore
public void testNotFound() throws Exception {
RestTemplate restTemplate = new RestTemplate();
URI url = new URI("http://localhost:" + port + "/oops");
RequestEntity<Void> request = RequestEntity.get(url).build();
try {
restTemplate.exchange(request, byte[].class);
}
catch (HttpClientErrorException ex) {
assertEquals(HttpStatus.NOT_FOUND, ex.getStatusCode());
}
}
private static class TestHandlerMapping extends SimpleUrlHandlerMapping {

Loading…
Cancel
Save