Refactor Router to RoutingFunctions

This commit refactors the Router into a RoutingFunctions class, by:

  - Renaming the class :)
  - Moving all Configuration logic into a separate, top-level
  Configuration class with mutable builder.
This commit is contained in:
Arjen Poutsma
2016-09-13 20:13:36 +02:00
parent aaa1281809
commit 91bde2e6b2
20 changed files with 481 additions and 351 deletions

View File

@@ -28,7 +28,7 @@ public abstract class AbstractRoutingFunctionIntegrationTests
@Override
protected final HttpHandler createHttpHandler() {
RoutingFunction<?> routingFunction = routingFunction();
return Router.toHttpHandler(routingFunction);
return RoutingFunctions.toHttpHandler(routingFunction);
}
protected abstract RoutingFunction<?> routingFunction();

View File

@@ -0,0 +1,105 @@
/*
* 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.
* 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.function;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.http.MediaType;
import org.springframework.http.ReactiveHttpInputMessage;
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.HttpMessageWriter;
import static org.junit.Assert.assertTrue;
/**
* @author Arjen Poutsma
*/
public class ConfigurationTests {
@Test
public void toConfiguration() throws Exception {
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("messageWriter", DummyMessageWriter.class);
applicationContext.registerSingleton("messageReader", DummyMessageReader.class);
applicationContext.refresh();
Configuration configuration = Configuration.toConfiguration(applicationContext);
assertTrue(configuration.messageReaders().get()
.allMatch(r -> r instanceof DummyMessageReader));
assertTrue(configuration.messageWriters().get()
.allMatch(r -> r instanceof DummyMessageWriter));
}
private static class DummyMessageWriter implements HttpMessageWriter<Object> {
@Override
public boolean canWrite(ResolvableType type, MediaType mediaType, Map<String, Object> hints) {
return false;
}
@Override
public List<MediaType> getWritableMediaTypes() {
return Collections.emptyList();
}
@Override
public Mono<Void> write(Publisher<?> inputStream, ResolvableType type,
MediaType contentType,
ReactiveHttpOutputMessage outputMessage,
Map<String, Object> hints) {
return Mono.empty();
}
}
private static class DummyMessageReader implements HttpMessageReader<Object> {
@Override
public boolean canRead(ResolvableType type, MediaType mediaType, Map<String, Object> hints) {
return false;
}
@Override
public List<MediaType> getReadableMediaTypes() {
return Collections.emptyList();
}
@Override
public Flux<Object> read(ResolvableType type, ReactiveHttpInputMessage inputMessage,
Map<String, Object> hints) {
return Flux.empty();
}
@Override
public Mono<Object> readMono(ResolvableType type, ReactiveHttpInputMessage inputMessage,
Map<String, Object> hints) {
return Mono.empty();
}
}
}

View File

@@ -1,40 +0,0 @@
/*
* 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.
* 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.function;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* @author Arjen Poutsma
*/
public class DefaultConfigurationTests {
private DefaultConfiguration configuration = new DefaultConfiguration();
@Test
public void messageReaders() throws Exception {
assertEquals(4, configuration.messageReaders().get().count());
}
@Test
public void messageWriters() throws Exception {
assertEquals(4, configuration.messageWriters().get().count());
}
}

View File

@@ -115,7 +115,7 @@ public class DefaultRequestTests {
@Test
public void pathVariables() throws Exception {
Map<String, String> pathVariables = Collections.singletonMap("foo", "bar");
when(mockExchange.getAttribute(Router.URI_TEMPLATE_VARIABLES_ATTRIBUTE)).thenReturn(Optional.of(pathVariables));
when(mockExchange.getAttribute(RoutingFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE)).thenReturn(Optional.of(pathVariables));
assertEquals(pathVariables, defaultRequest.pathVariables());
}
@@ -161,9 +161,10 @@ public class DefaultRequestTests {
Set<HttpMessageReader<?>> messageReaders = Collections
.singleton(new DecoderHttpMessageReader<String>(new StringDecoder()));
when(mockExchange.getAttribute(Router.HTTP_MESSAGE_READERS_ATTRIBUTE))
.thenReturn(Optional.of(
(Supplier<Stream<HttpMessageReader<?>>>) messageReaders::stream));
Configuration mockConfig = mock(Configuration.class);
when(mockConfig.messageReaders()).thenReturn(messageReaders::stream);
when(mockExchange.getAttribute(RoutingFunctions.CONFIGURATION_ATTRIBUTE))
.thenReturn(Optional.of(mockConfig));
assertEquals(body, defaultRequest.body().stream());

View File

@@ -18,7 +18,9 @@ package org.springframework.web.reactive.function;
import java.net.URI;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -218,11 +220,13 @@ public class DefaultResponseBuilderTests {
MockServerHttpResponse response = new MockServerHttpResponse();
ServerWebExchange exchange =
new DefaultServerWebExchange(request, response, new MockWebSessionManager());
Set<HttpMessageWriter<?>>
messageWriters = Collections
.singleton(new EncoderHttpMessageWriter<CharSequence>(new CharSequenceEncoder()));
exchange.getAttributes().put(Router.HTTP_MESSAGE_WRITERS_ATTRIBUTE,
(Supplier<Stream<HttpMessageWriter<?>>>) messageWriters::stream);
List<HttpMessageWriter<?>> messageWriters = new ArrayList<>();
messageWriters.add(new EncoderHttpMessageWriter<CharSequence>(new CharSequenceEncoder()));
Configuration mockConfig = mock(Configuration.class);
when(mockConfig.messageWriters()).thenReturn(messageWriters::stream);
exchange.getAttributes().put(RoutingFunctions.CONFIGURATION_ATTRIBUTE, mockConfig);
result.writeTo(exchange).block();
assertNotNull(response.getBody());
@@ -239,11 +243,13 @@ public class DefaultResponseBuilderTests {
MockServerHttpResponse response = new MockServerHttpResponse();
ServerWebExchange exchange =
new DefaultServerWebExchange(request, response, new MockWebSessionManager());
Set<HttpMessageWriter<?>>
messageWriters = Collections
.singleton(new EncoderHttpMessageWriter<CharSequence>(new CharSequenceEncoder()));
exchange.getAttributes().put(Router.HTTP_MESSAGE_WRITERS_ATTRIBUTE,
(Supplier<Stream<HttpMessageWriter<?>>>) messageWriters::stream);
List<HttpMessageWriter<?>> messageWriters = new ArrayList<>();
messageWriters.add(new EncoderHttpMessageWriter<CharSequence>(new CharSequenceEncoder()));
Configuration mockConfig = mock(Configuration.class);
when(mockConfig.messageWriters()).thenReturn(messageWriters::stream);
exchange.getAttributes().put(RoutingFunctions.CONFIGURATION_ATTRIBUTE, mockConfig);
result.writeTo(exchange).block();
assertEquals(HttpStatus.NOT_ACCEPTABLE, response.getStatusCode());
@@ -259,10 +265,13 @@ public class DefaultResponseBuilderTests {
MockServerHttpResponse response = new MockServerHttpResponse();
ServerWebExchange exchange =
new DefaultServerWebExchange(request, response, new MockWebSessionManager());
Set<HttpMessageWriter<?>> messageWriters = Collections
.singleton(new EncoderHttpMessageWriter<CharSequence>(new CharSequenceEncoder()));
exchange.getAttributes().put(Router.HTTP_MESSAGE_WRITERS_ATTRIBUTE,
(Supplier<Stream<HttpMessageWriter<?>>>) messageWriters::stream);
List<HttpMessageWriter<?>> messageWriters = new ArrayList<>();
messageWriters.add(new EncoderHttpMessageWriter<CharSequence>(new CharSequenceEncoder()));
Configuration mockConfig = mock(Configuration.class);
when(mockConfig.messageWriters()).thenReturn(messageWriters::stream);
exchange.getAttributes().put(RoutingFunctions.CONFIGURATION_ATTRIBUTE, mockConfig);
result.writeTo(exchange).block();
assertNotNull(response.getBody());
@@ -311,10 +320,13 @@ public class DefaultResponseBuilderTests {
View view = mock(View.class);
when(viewResolver.resolveViewName("view", Locale.ENGLISH)).thenReturn(Mono.just(view));
when(view.render(model, null, exchange)).thenReturn(Mono.empty());
exchange.getAttributes().put(Router.VIEW_RESOLVERS_ATTRIBUTE,
(Supplier<Stream<ViewResolver>>) () -> Collections
.singleton(viewResolver).stream());
List<ViewResolver> viewResolvers = new ArrayList<>();
viewResolvers.add(viewResolver);
Configuration mockConfig = mock(Configuration.class);
when(mockConfig.viewResolvers()).thenReturn(viewResolvers::stream);
exchange.getAttributes().put(RoutingFunctions.CONFIGURATION_ATTRIBUTE, mockConfig);
result.writeTo(exchange).block();
}

View File

@@ -50,7 +50,7 @@ import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
import static org.junit.Assert.assertEquals;
import static org.springframework.web.reactive.function.Router.route;
import static org.springframework.web.reactive.function.RoutingFunctions.route;
/**
* Tests the use of {@link HandlerFunction} and {@link RoutingFunction} in a
@@ -119,8 +119,8 @@ public class DispatcherHandlerIntegrationTests extends AbstractHttpHandlerIntegr
@Bean
public HandlerMapping handlerMapping(RoutingFunction<?> routingFunction,
ApplicationContext applicationContext) {
return Router.toHandlerMapping(routingFunction,
new Router.Configuration() {
return RoutingFunctions.toHandlerMapping(routingFunction,
new org.springframework.web.reactive.function.Configuration() {
@Override
public Supplier<Stream<HttpMessageReader<?>>> messageReaders() {
return () -> getMessageReaders().stream();

View File

@@ -35,7 +35,7 @@ import org.springframework.web.client.RestTemplate;
import static org.junit.Assert.assertEquals;
import static org.springframework.web.reactive.function.RequestPredicates.GET;
import static org.springframework.web.reactive.function.RequestPredicates.POST;
import static org.springframework.web.reactive.function.Router.route;
import static org.springframework.web.reactive.function.RoutingFunctions.route;
/**
* @author Arjen Poutsma

View File

@@ -57,7 +57,7 @@ public class RouterTests {
RequestPredicate requestPredicate = mock(RequestPredicate.class);
when(requestPredicate.test(request)).thenReturn(true);
RoutingFunction<Void> result = Router.route(requestPredicate, handlerFunction);
RoutingFunction<Void> result = RoutingFunctions.route(requestPredicate, handlerFunction);
assertNotNull(result);
Optional<HandlerFunction<Void>> resultHandlerFunction = result.route(request);
@@ -73,7 +73,7 @@ public class RouterTests {
RequestPredicate requestPredicate = mock(RequestPredicate.class);
when(requestPredicate.test(request)).thenReturn(false);
RoutingFunction<Void> result = Router.route(requestPredicate, handlerFunction);
RoutingFunction<Void> result = RoutingFunctions.route(requestPredicate, handlerFunction);
assertNotNull(result);
Optional<HandlerFunction<Void>> resultHandlerFunction = result.route(request);
@@ -89,7 +89,7 @@ public class RouterTests {
RequestPredicate requestPredicate = mock(RequestPredicate.class);
when(requestPredicate.test(request)).thenReturn(true);
RoutingFunction<Void> result = Router.subroute(requestPredicate, routingFunction);
RoutingFunction<Void> result = RoutingFunctions.subroute(requestPredicate, routingFunction);
assertNotNull(result);
Optional<HandlerFunction<Void>> resultHandlerFunction = result.route(request);
@@ -106,7 +106,7 @@ public class RouterTests {
RequestPredicate requestPredicate = mock(RequestPredicate.class);
when(requestPredicate.test(request)).thenReturn(false);
RoutingFunction<Void> result = Router.subroute(requestPredicate, routingFunction);
RoutingFunction<Void> result = RoutingFunctions.subroute(requestPredicate, routingFunction);
assertNotNull(result);
Optional<HandlerFunction<Void>> resultHandlerFunction = result.route(request);
@@ -128,7 +128,7 @@ public class RouterTests {
RequestPredicate requestPredicate = mock(RequestPredicate.class);
when(requestPredicate.test(request)).thenReturn(false);
Router.Configuration configuration = mock(Router.Configuration.class);
Configuration configuration = mock(Configuration.class);
when(configuration.messageReaders()).thenReturn(
() -> Collections.<HttpMessageReader<?>>emptyList().stream());
when(configuration.messageWriters()).thenReturn(
@@ -136,72 +136,13 @@ public class RouterTests {
when(configuration.viewResolvers()).thenReturn(
() -> Collections.<ViewResolver>emptyList().stream());
HttpHandler result = Router.toHttpHandler(routingFunction, configuration);
HttpHandler result = RoutingFunctions.toHttpHandler(routingFunction, configuration);
assertNotNull(result);
MockServerHttpRequest httpRequest = new MockServerHttpRequest(HttpMethod.GET, "http://localhost");
MockServerHttpRequest httpRequest =
new MockServerHttpRequest(HttpMethod.GET, "http://localhost");
MockServerHttpResponse serverHttpResponse = new MockServerHttpResponse();
result.handle(httpRequest, serverHttpResponse);
}
@Test
public void toConfiguration() throws Exception {
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("messageWriter", DummyMessageWriter.class);
applicationContext.registerSingleton("messageReader", DummyMessageReader.class);
applicationContext.refresh();
Router.Configuration configuration = Router.toConfiguration(applicationContext);
assertTrue(configuration.messageReaders().get()
.allMatch(r -> r instanceof DummyMessageReader));
assertTrue(configuration.messageWriters().get()
.allMatch(r -> r instanceof DummyMessageWriter));
}
private static class DummyMessageWriter implements HttpMessageWriter<Object> {
@Override
public boolean canWrite(ResolvableType elementType, MediaType mediaType, Map<String, Object> hints) {
return false;
}
@Override
public List<MediaType> getWritableMediaTypes() {
return Collections.emptyList();
}
@Override
public Mono<Void> write(Publisher<?> inputStream, ResolvableType elementType,
MediaType mediaType,
ReactiveHttpOutputMessage outputMessage,
Map<String, Object> hints) {
return Mono.empty();
}
}
private static class DummyMessageReader implements HttpMessageReader<Object> {
@Override
public boolean canRead(ResolvableType elementType, MediaType mediaType, Map<String, Object> hints) {
return false;
}
@Override
public List<MediaType> getReadableMediaTypes() {
return Collections.emptyList();
}
@Override
public Flux<Object> read(ResolvableType elementType, ReactiveHttpInputMessage inputMessage,
Map<String, Object> hints) {
return Flux.empty();
}
@Override
public Mono<Object> readMono(ResolvableType elementType, ReactiveHttpInputMessage inputMessage,
Map<String, Object> hints) {
return Mono.empty();
}
}
}
}

View File

@@ -32,7 +32,7 @@ import org.springframework.web.client.reactive.WebClient;
import static org.springframework.web.client.reactive.ClientWebRequestBuilders.get;
import static org.springframework.web.client.reactive.ResponseExtractors.bodyStream;
import static org.springframework.web.reactive.function.Router.route;
import static org.springframework.web.reactive.function.RoutingFunctions.route;
/**
* @author Arjen Poutsma