diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ClientRequest.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ClientRequest.java index 608d17084a..a36077f248 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ClientRequest.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ClientRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -30,12 +30,12 @@ import org.springframework.web.reactive.function.BodyInserter; /** * Represents a typed, immutable, client-side HTTP request, as executed by the - * {@link WebClient}. Instances of this interface can be created via static - * builder methods in this class. + * {@link ExchangeFunction}. Instances of this interface can be created via static + * builder methods. * *

Note that applications are more likely to perform requests through - * {@link WebClientOperations} rather than using this directly. - * : + * {@link WebClient} rather than using this directly. + * * @param the type of the body that this request contains * @author Brian Clozel * @author Arjen Poutsma @@ -75,7 +75,7 @@ public interface ClientRequest { * @param strategies the strategies to use when writing * @return {@code Mono} to indicate when writing is complete */ - Mono writeTo(ClientHttpRequest request, WebClientStrategies strategies); + Mono writeTo(ClientHttpRequest request, ExchangeStrategies strategies); // Static builder methods diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ClientResponse.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ClientResponse.java index 8441aec995..476536cc54 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ClientResponse.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ClientResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -30,7 +30,7 @@ import org.springframework.http.client.reactive.ClientHttpResponse; import org.springframework.web.reactive.function.BodyExtractor; /** - * Represents an HTTP response, as returned by the {@link WebClient}. + * Represents an HTTP response, as returned by the {@link ExchangeFunction}. * Access to headers and body is offered by {@link Headers} and * {@link #body(BodyExtractor)}, {@link #bodyToMono(Class)}, {@link #bodyToFlux(Class)} * respectively. diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilder.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilder.java index e0f65d6db8..dadceae1c5 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilder.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -154,7 +154,7 @@ class DefaultClientRequestBuilder implements ClientRequest.Builder { } @Override - public Mono writeTo(ClientHttpRequest request, WebClientStrategies strategies) { + public Mono writeTo(ClientHttpRequest request, ExchangeStrategies strategies) { HttpHeaders requestHeaders = request.getHeaders(); if (!this.headers.isEmpty()) { this.headers.entrySet().stream() diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java index 25f2c1c38d..44ab32b5c8 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -49,10 +49,10 @@ class DefaultClientResponse implements ClientResponse { private final Headers headers; - private final WebClientStrategies strategies; + private final ExchangeStrategies strategies; - public DefaultClientResponse(ClientHttpResponse response, WebClientStrategies strategies) { + public DefaultClientResponse(ClientHttpResponse response, ExchangeStrategies strategies) { this.response = response; this.strategies = strategies; this.headers = new DefaultHeaders(); diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientStrategiesBuilder.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultExchangeStrategiesBuilder.java similarity index 85% rename from spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientStrategiesBuilder.java rename to spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultExchangeStrategiesBuilder.java index d3f1c2473d..95f7ccf6e7 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientStrategiesBuilder.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultExchangeStrategiesBuilder.java @@ -46,22 +46,22 @@ import org.springframework.util.Assert; import org.springframework.util.ClassUtils; /** - * Default implementation of {@link WebClientStrategies.Builder}. + * Default implementation of {@link ExchangeStrategies.Builder}. * * @author Arjen Poutsma * @since 5.0 */ -class DefaultWebClientStrategiesBuilder implements WebClientStrategies.Builder { +class DefaultExchangeStrategiesBuilder implements ExchangeStrategies.Builder { private static final boolean jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", - DefaultWebClientStrategiesBuilder.class.getClassLoader()) && + DefaultExchangeStrategiesBuilder.class.getClassLoader()) && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", - DefaultWebClientStrategiesBuilder.class.getClassLoader()); + DefaultExchangeStrategiesBuilder.class.getClassLoader()); private static final boolean jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", - DefaultWebClientStrategiesBuilder.class.getClassLoader()); + DefaultExchangeStrategiesBuilder.class.getClassLoader()); private final List> messageReaders = new ArrayList<>(); @@ -100,43 +100,43 @@ class DefaultWebClientStrategiesBuilder implements WebClientStrategies.Builder { } @Override - public WebClientStrategies.Builder messageReader(HttpMessageReader messageReader) { + public ExchangeStrategies.Builder messageReader(HttpMessageReader messageReader) { Assert.notNull(messageReader, "'messageReader' must not be null"); this.messageReaders.add(messageReader); return this; } @Override - public WebClientStrategies.Builder decoder(Decoder decoder) { + public ExchangeStrategies.Builder decoder(Decoder decoder) { Assert.notNull(decoder, "'decoder' must not be null"); return messageReader(new DecoderHttpMessageReader<>(decoder)); } @Override - public WebClientStrategies.Builder messageWriter(HttpMessageWriter messageWriter) { + public ExchangeStrategies.Builder messageWriter(HttpMessageWriter messageWriter) { Assert.notNull(messageWriter, "'messageWriter' must not be null"); this.messageWriters.add(messageWriter); return this; } @Override - public WebClientStrategies.Builder encoder(Encoder encoder) { + public ExchangeStrategies.Builder encoder(Encoder encoder) { Assert.notNull(encoder, "'encoder' must not be null"); return messageWriter(new EncoderHttpMessageWriter<>(encoder)); } @Override - public WebClientStrategies build() { - return new DefaultWebClientStrategies(this.messageReaders, this.messageWriters); + public ExchangeStrategies build() { + return new DefaultExchangeStrategies(this.messageReaders, this.messageWriters); } - private static class DefaultWebClientStrategies implements WebClientStrategies { + private static class DefaultExchangeStrategies implements ExchangeStrategies { private final List> messageReaders; private final List> messageWriters; - public DefaultWebClientStrategies( + public DefaultExchangeStrategies( List> messageReaders, List> messageWriters) { this.messageReaders = unmodifiableCopy(messageReaders); diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientOperations.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java similarity index 88% rename from spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientOperations.java rename to spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java index 95d5ffe9a8..248c92eeb0 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientOperations.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.web.reactive.function.client; import java.net.URI; @@ -38,26 +39,26 @@ import org.springframework.web.util.UriBuilderFactory; /** - * Default implementation of {@link WebClientOperations}. + * Default implementation of {@link WebClient}. * * @author Rossen Stoyanchev * @since 5.0 */ -class DefaultWebClientOperations implements WebClientOperations { +class DefaultWebClient implements WebClient { - private final WebClient webClient; + private final ExchangeFunction exchangeFunction; private final UriBuilderFactory uriBuilderFactory; - DefaultWebClientOperations(WebClient webClient, UriBuilderFactory factory) { - this.webClient = webClient; + DefaultWebClient(ExchangeFunction exchangeFunction, UriBuilderFactory factory) { + this.exchangeFunction = exchangeFunction; this.uriBuilderFactory = (factory != null ? factory : new DefaultUriBuilderFactory()); } - private WebClient getWebClient() { - return this.webClient; + private ExchangeFunction getExchangeFunction() { + return this.exchangeFunction; } private UriBuilderFactory getUriBuilderFactory() { @@ -107,9 +108,9 @@ class DefaultWebClientOperations implements WebClientOperations { @Override - public WebClientOperations filter(ExchangeFilterFunction filterFunction) { - WebClient filteredWebClient = this.webClient.filter(filterFunction); - return new DefaultWebClientOperations(filteredWebClient, this.uriBuilderFactory); + public WebClient filter(ExchangeFilterFunction filterFunction) { + ExchangeFunction filteredExchangeFunction = this.exchangeFunction.filter(filterFunction); + return new DefaultWebClient(filteredExchangeFunction, this.uriBuilderFactory); } @@ -219,19 +220,19 @@ class DefaultWebClientOperations implements WebClientOperations { @Override public Mono exchange() { ClientRequest request = this.requestBuilder.headers(this.headers).build(); - return getWebClient().exchange(request); + return getExchangeFunction().exchange(request); } @Override public Mono exchange(BodyInserter inserter) { ClientRequest request = this.requestBuilder.headers(this.headers).body(inserter); - return getWebClient().exchange(request); + return getExchangeFunction().exchange(request); } @Override public > Mono exchange(S publisher, Class elementClass) { ClientRequest request = this.requestBuilder.headers(this.headers).body(publisher, elementClass); - return getWebClient().exchange(request); + return getExchangeFunction().exchange(request); } } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientBuilder.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientBuilder.java index b6100a0bb3..e3ed0817b3 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientBuilder.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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,101 +16,37 @@ package org.springframework.web.reactive.function.client; -import java.util.logging.Level; - -import reactor.core.publisher.Mono; - -import org.springframework.http.client.reactive.ClientHttpConnector; import org.springframework.util.Assert; +import org.springframework.web.util.UriBuilderFactory; /** * Default implementation of {@link WebClient.Builder}. * - * @author Arjen Poutsma + * @author Rossen Stoyanchev * @since 5.0 */ class DefaultWebClientBuilder implements WebClient.Builder { - private ClientHttpConnector clientHttpConnector; + private final ExchangeFunction exchangeFunction; - private WebClientStrategies strategies = WebClientStrategies.withDefaults(); - - private ExchangeFilterFunction filter = new NoOpFilter(); + private UriBuilderFactory uriBuilderFactory; - public DefaultWebClientBuilder(ClientHttpConnector clientHttpConnector) { - this.clientHttpConnector = clientHttpConnector; + public DefaultWebClientBuilder(ExchangeFunction exchangeFunction) { + Assert.notNull(exchangeFunction, "'exchangeFunction' must not be null"); + this.exchangeFunction = exchangeFunction; } - @Override - public WebClient.Builder strategies(WebClientStrategies strategies) { - Assert.notNull(strategies, "'strategies' must not be null"); - this.strategies = strategies; - return this; - } @Override - public WebClient.Builder filter(ExchangeFilterFunction filter) { - Assert.notNull(filter, "'filter' must not be null"); - this.filter = filter.andThen(this.filter); + public WebClient.Builder uriBuilderFactory(UriBuilderFactory uriBuilderFactory) { + this.uriBuilderFactory = uriBuilderFactory; return this; } @Override public WebClient build() { - return new DefaultWebClient(this.clientHttpConnector, this.strategies, this.filter); - } - - private final static class DefaultWebClient implements WebClient { - - private final ClientHttpConnector clientHttpConnector; - - private final WebClientStrategies strategies; - - private final ExchangeFilterFunction filter; - - public DefaultWebClient( - ClientHttpConnector clientHttpConnector, - WebClientStrategies strategies, - ExchangeFilterFunction filter) { - this.clientHttpConnector = clientHttpConnector; - this.strategies = strategies; - this.filter = filter; - } - - @Override - public Mono exchange(ClientRequest request) { - Assert.notNull(request, "'request' must not be null"); - - return this.filter.filter(request, this::exchangeInternal); - } - - private Mono exchangeInternal(ClientRequest request) { - return this.clientHttpConnector - .connect(request.method(), request.url(), - clientHttpRequest -> request - .writeTo(clientHttpRequest, this.strategies)) - .log("org.springframework.web.client.reactive", Level.FINE) - .map(clientHttpResponse -> new DefaultClientResponse(clientHttpResponse, - this.strategies)); - } - - @Override - public WebClient filter(ExchangeFilterFunction filter) { - Assert.notNull(filter, "'filter' must not be null"); - - ExchangeFilterFunction composedFilter = filter.andThen(this.filter); - - return new DefaultWebClient(this.clientHttpConnector, this.strategies, composedFilter); - } - } - - private class NoOpFilter implements ExchangeFilterFunction { - - @Override - public Mono filter(ClientRequest request, ExchangeFunction next) { - return next.exchange(request); - } + return new DefaultWebClient(this.exchangeFunction, this.uriBuilderFactory); } } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientOperationsBuilder.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientOperationsBuilder.java deleted file mode 100644 index f8c7749837..0000000000 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientOperationsBuilder.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2002-2017 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.client; - -import org.springframework.util.Assert; -import org.springframework.web.util.UriBuilderFactory; - -/** - * Default implementation of {@link WebClientOperations.Builder}. - * - * @author Rossen Stoyanchev - * @since 5.0 - */ -class DefaultWebClientOperationsBuilder implements WebClientOperations.Builder { - - private final WebClient webClient; - - private UriBuilderFactory uriBuilderFactory; - - - public DefaultWebClientOperationsBuilder(WebClient webClient) { - Assert.notNull(webClient, "WebClient is required"); - this.webClient = webClient; - } - - - @Override - public WebClientOperations.Builder uriBuilderFactory(UriBuilderFactory uriBuilderFactory) { - this.uriBuilderFactory = uriBuilderFactory; - return this; - } - - @Override - public WebClientOperations build() { - return new DefaultWebClientOperations(this.webClient, this.uriBuilderFactory); - } - -} diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunction.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunction.java index 337dfe824f..7e18483640 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunction.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -18,9 +18,21 @@ package org.springframework.web.reactive.function.client; import reactor.core.publisher.Mono; +import org.springframework.util.Assert; + /** * Represents a function that exchanges a {@linkplain ClientRequest request} for a (delayed) - * {@linkplain ClientResponse}. + * {@linkplain ClientResponse}. Can be used as an alternative to {@link WebClient}. + *

For example: + *

+ * ExchangeFunction exchangeFunction = ExchangeFunctions.create(new ReactorClientHttpConnector());
+ * ClientRequest<Void> request = ClientRequest.method(HttpMethod.GET,
+ *   "http://example.com/resource").build();
+ *
+ * Mono<String> result = exchangeFunction
+ *   .exchange(request)
+ *   .then(response -> response.bodyToMono(String.class));
+ * 
* * @author Arjen Poutsma * @since 5.0 @@ -31,8 +43,21 @@ public interface ExchangeFunction { /** * Exchange the given request for a response mono. * @param request the request to exchange - * @return the response, wrapped in a {@code Mono} + * @return the delayed response */ Mono exchange(ClientRequest request); + /** + * Filters this exchange function with the given {@code ExchangeFilterFunction}, resulting in a + * filtered {@code ExchangeFunction}. + * @param filter the filter to apply to this exchange + * @return the filtered exchange + * @see ExchangeFilterFunction#apply(ExchangeFunction) + */ + default ExchangeFunction filter(ExchangeFilterFunction filter) { + Assert.notNull(filter, "'filter' must not be null"); + + return filter.apply(this); + } + } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunctions.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunctions.java new file mode 100644 index 0000000000..f2dd3dcb94 --- /dev/null +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunctions.java @@ -0,0 +1,87 @@ +/* + * Copyright 2002-2017 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.client; + +import java.util.logging.Level; + +import reactor.core.publisher.Mono; + +import org.springframework.http.client.reactive.ClientHttpConnector; +import org.springframework.util.Assert; + +/** + * Exposes request-response exchange functionality, such as to + * {@linkplain #create(ClientHttpConnector) create} an {@code ExchangeFunction} given a + * {@code ClientHttpConnector}. + * + * @author Arjen Poutsma + * @since 5.0 + */ +public abstract class ExchangeFunctions { + + /** + * Create a new {@link ExchangeFunction} with the given connector. This method uses + * {@linkplain ExchangeStrategies#withDefaults() default strategies}. + * @param connector the connector to create connections + * @return the created function + */ + public static ExchangeFunction create(ClientHttpConnector connector) { + return create(connector, ExchangeStrategies.withDefaults()); + } + + /** + * Create a new {@link ExchangeFunction} with the given connector and strategies. + * @param connector the connector to create connections + * @param strategies the strategies to use + * @return the created function + */ + public static ExchangeFunction create(ClientHttpConnector connector, + ExchangeStrategies strategies) { + Assert.notNull(connector, "'connector' must not be null"); + Assert.notNull(strategies, "'strategies' must not be null"); + + return new DefaultExchangeFunction(connector, strategies); + } + + + private static class DefaultExchangeFunction implements ExchangeFunction { + + private final ClientHttpConnector connector; + + private final ExchangeStrategies strategies; + + public DefaultExchangeFunction( + ClientHttpConnector connector, + ExchangeStrategies strategies) { + this.connector = connector; + this.strategies = strategies; + } + + @Override + public Mono exchange(ClientRequest request) { + Assert.notNull(request, "'request' must not be null"); + + return this.connector + .connect(request.method(), request.url(), + clientHttpRequest -> request.writeTo(clientHttpRequest, this.strategies)) + .log("org.springframework.web.reactive.function.client", Level.FINE) + .map(clientHttpResponse -> new DefaultClientResponse(clientHttpResponse, + this.strategies)); + } + } + +} diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/WebClientStrategies.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ExchangeStrategies.java similarity index 78% rename from spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/WebClientStrategies.java rename to spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ExchangeStrategies.java index 7d40486e87..345e8191a6 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/WebClientStrategies.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/ExchangeStrategies.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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,17 +27,17 @@ import org.springframework.http.codec.HttpMessageWriter; import org.springframework.util.Assert; /** - * Defines the strategies to be used by the {@link WebClient}. An instance of + * Defines the strategies for invoking {@link ExchangeFunction}s. An instance of * this class is immutable; instances are typically created through the mutable {@link Builder}: * either through {@link #builder()} to set up default strategies, or {@link #empty()} to start from - * scratch. Alternatively, {@code WebClientStrategies} instances can be created through + * scratch. Alternatively, {@code ExchangeStrategies} instances can be created through * {@link #of(Supplier, Supplier)}. * * @author Brian Clozel * @author Arjen Poutsma * @since 5.0 */ -public interface WebClientStrategies { +public interface ExchangeStrategies { // Instance methods @@ -59,38 +59,38 @@ public interface WebClientStrategies { // Static methods /** - * Return a new {@code WebClientStrategies} with default initialization. - * @return the new {@code WebClientStrategies} + * Return a new {@code ExchangeStrategies} with default initialization. + * @return the new {@code ExchangeStrategies} */ - static WebClientStrategies withDefaults() { + static ExchangeStrategies withDefaults() { return builder().build(); } /** - * Return a new {@code WebClientStrategies} based on the given + * Return a new {@code ExchangeStrategies} based on the given * {@linkplain ApplicationContext application context}. * The returned supplier will search for all {@link HttpMessageReader}, and * {@link HttpMessageWriter} instances in the given application context and return them for * {@link #messageReaders()}, and {@link #messageWriters()} respectively. * @param applicationContext the application context to base the strategies on - * @return the new {@code WebClientStrategies} + * @return the new {@code ExchangeStrategies} */ - static WebClientStrategies of(ApplicationContext applicationContext) { + static ExchangeStrategies of(ApplicationContext applicationContext) { return builder(applicationContext).build(); } /** - * Return a new {@code WebClientStrategies} described by the given supplier functions. + * Return a new {@code ExchangeStrategies} described by the given supplier functions. * All provided supplier function parameters can be {@code null} to indicate an empty * stream is to be returned. * @param messageReaders the supplier function for {@link HttpMessageReader} instances (can be {@code null}) * @param messageWriters the supplier function for {@link HttpMessageWriter} instances (can be {@code null}) - * @return the new {@code WebClientStrategies} + * @return the new {@code ExchangeStrategies} */ - static WebClientStrategies of(Supplier>> messageReaders, + static ExchangeStrategies of(Supplier>> messageReaders, Supplier>> messageWriters) { - return new WebClientStrategies() { + return new ExchangeStrategies() { @Override public Supplier>> messageReaders() { return checkForNull(messageReaders); @@ -109,11 +109,11 @@ public interface WebClientStrategies { // Builder methods /** - * Return a mutable builder for a {@code WebClientStrategies} with default initialization. + * Return a mutable builder for a {@code ExchangeStrategies} with default initialization. * @return the builder */ static Builder builder() { - DefaultWebClientStrategiesBuilder builder = new DefaultWebClientStrategiesBuilder(); + DefaultExchangeStrategiesBuilder builder = new DefaultExchangeStrategiesBuilder(); builder.defaultConfiguration(); return builder; } @@ -128,22 +128,22 @@ public interface WebClientStrategies { */ static Builder builder(ApplicationContext applicationContext) { Assert.notNull(applicationContext, "ApplicationContext must not be null"); - DefaultWebClientStrategiesBuilder builder = new DefaultWebClientStrategiesBuilder(); + DefaultExchangeStrategiesBuilder builder = new DefaultExchangeStrategiesBuilder(); builder.applicationContext(applicationContext); return builder; } /** - * Return a mutable, empty builder for a {@code WebClientStrategies}. + * Return a mutable, empty builder for a {@code ExchangeStrategies}. * @return the builder */ static Builder empty() { - return new DefaultWebClientStrategiesBuilder(); + return new DefaultExchangeStrategiesBuilder(); } /** - * A mutable builder for a {@link WebClientStrategies}. + * A mutable builder for a {@link ExchangeStrategies}. */ interface Builder { @@ -180,10 +180,10 @@ public interface WebClientStrategies { Builder encoder(Encoder encoder); /** - * Builds the {@link WebClientStrategies}. + * Builds the {@link ExchangeStrategies}. * @return the built strategies */ - WebClientStrategies build(); + ExchangeStrategies build(); } } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/WebClient.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/WebClient.java index 8ecd734f40..b78c66b734 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/WebClient.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/WebClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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,43 +16,97 @@ package org.springframework.web.reactive.function.client; +import java.net.URI; +import java.nio.charset.Charset; +import java.time.ZonedDateTime; +import java.util.function.Function; + +import org.reactivestreams.Publisher; import reactor.core.publisher.Mono; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.http.client.reactive.ClientHttpConnector; -import org.springframework.util.Assert; +import org.springframework.http.client.reactive.ClientHttpRequest; +import org.springframework.util.MultiValueMap; +import org.springframework.web.reactive.function.BodyInserter; +import org.springframework.web.util.UriBuilderFactory; /** - * Reactive Web client supporting the HTTP/1.1 protocol. Main entry point is through the - * {@link #exchange(ClientRequest)} method. + * The main class for performing Web requests. * - *

For example: *

- * WebClient client = WebClient.create(new ReactorClientHttpConnector());
- * ClientRequest<Void> request = ClientRequest.GET("http://example.com/resource").build();
  *
- * Mono<String> result = client
- *   .exchange(request)
- *   .then(response -> response.bodyToMono(String.class));
+ * // Create ExchangeFunction (application-wide)
+ *
+ * ClientHttpConnector connector = new ReactorClientHttpConnector();
+ * ExchangeFunction exchangeFunction = ExchangeFunctions.create(connector);
+ *
+ * // Create WebClient (per base URI)
+ *
+ * String baseUri = "http://abc.com";
+ * UriBuilderFactory factory = new DefaultUriBuilderFactory(baseUri);
+ * WebClient operations = WebClient.create(exchangeFunction, factory);
+ *
+ * // Perform requests...
+ *
+ * Mono result = operations.get()
+ *     .uri("/foo")
+ *     .exchange()
+ *     .then(response -> response.bodyToMono(String.class));
  * 
* - * @author Brian Clozel + * @author Rossen Stoyanchev * @author Arjen Poutsma * @since 5.0 */ -public interface WebClient extends ExchangeFunction { +public interface WebClient { /** - * Exchange the given request for a response mono. Invoking this method performs the actual - * HTTP request/response exchange. - * @param request the request to exchange - * @return the response, wrapped in a {@code Mono} + * Prepare an HTTP GET request. + * @return a spec for specifying the target URL */ - @Override - Mono exchange(ClientRequest request); + UriSpec get(); /** - * Filters this client with the given {@code ExchangeFilterFunction}, resulting in a filtered - * {@code WebClient}. + * Prepare an HTTP HEAD request. + * @return a spec for specifying the target URL + */ + UriSpec head(); + + /** + * Prepare an HTTP POST request. + * @return a spec for specifying the target URL + */ + UriSpec post(); + + /** + * Prepare an HTTP PUT request. + * @return a spec for specifying the target URL + */ + UriSpec put(); + + /** + * Prepare an HTTP PATCH request. + * @return a spec for specifying the target URL + */ + UriSpec patch(); + + /** + * Prepare an HTTP DELETE request. + * @return a spec for specifying the target URL + */ + UriSpec delete(); + + /** + * Prepare an HTTP OPTIONS request. + * @return a spec for specifying the target URL + */ + UriSpec options(); + + + /** + * Filter the client with the given {@code ExchangeFilterFunction}. * @param filterFunction the filter to apply to this client * @return the filtered client * @see ExchangeFilterFunction#apply(ExchangeFunction) @@ -60,52 +114,204 @@ public interface WebClient extends ExchangeFunction { WebClient filter(ExchangeFilterFunction filterFunction); + // Static, factory methods + /** - * Create a new instance of {@code WebClient} with the given connector. This method uses - * {@linkplain WebClientStrategies#withDefaults() default strategies}. - * @param connector the connector to create connections - * @return the created client + * Create {@code WebClient} that uses the given {@link ClientHttpConnector}. + * @param connector the underlying connector to use */ static WebClient create(ClientHttpConnector connector) { - return builder(connector).build(); + return create(ExchangeFunctions.create(connector)); + } /** - * Return a builder for a {@code WebClient}. - * @param connector the connector to create connections - * @return a web client builder + * Create {@code WebClient} that uses the given {@link ClientHttpConnector} and + * {@link ExchangeStrategies}. + * @param connector the underlying connector to use + * @param strategies the strategies to use */ - static Builder builder(ClientHttpConnector connector) { - Assert.notNull(connector, "'connector' must not be null"); - return new DefaultWebClientBuilder(connector); + static WebClient create(ClientHttpConnector connector, ExchangeStrategies strategies) { + return create(ExchangeFunctions.create(connector, strategies)); } + /** + * Create {@code WebClient} that wraps the given {@link ExchangeFunction}. + * @param exchangeFunction the underlying exchange function to use + */ + static WebClient create(ExchangeFunction exchangeFunction) { + return builder(exchangeFunction).build(); + } + + /** + * Create {@code WebClient} with a builder for additional + * configuration options. + * @param exchangeFunction the underlying exchange function to use + */ + static WebClient.Builder builder(ExchangeFunction exchangeFunction) { + return new DefaultWebClientBuilder(exchangeFunction); + } + + /** * A mutable builder for a {@link WebClient}. */ interface Builder { /** - * Replaces the default strategies with the ones provided by the given - * {@code WebClientStrategies}. - * @param strategies the strategies to use - * @return this builder + * Configure a {@code UriBuilderFactory} for use with this client for + * example to define a common "base" URI. + * @param uriBuilderFactory the URI builder factory */ - Builder strategies(WebClientStrategies strategies); + Builder uriBuilderFactory(UriBuilderFactory uriBuilderFactory); /** - * Adds a filter function before the currently registered filters (if any). - * @param filter the filter to add - * @return this builder - */ - Builder filter(ExchangeFilterFunction filter); - - /** - * Builds the {@code WebClient}. - * @return the built client + * Builder the {@link WebClient} instance. */ WebClient build(); + } + /** + * Contract for specifying the URI for a request. + */ + interface UriSpec { + + /** + * Specify the URI using an absolute, fully constructed {@link URI}. + */ + HeaderSpec uri(URI uri); + + /** + * Specify the URI for the request using a URI template and URI variables. + * If a {@link UriBuilderFactory} was configured for the client (e.g. + * with a base URI) it will be used to expand the URI template. + * @see Builder#uriBuilderFactory(UriBuilderFactory) + */ + HeaderSpec uri(String uri, Object... uriVariables); + + /** + * Build the URI for the request using the {@link UriBuilderFactory} + * configured for this client. + * @see Builder#uriBuilderFactory(UriBuilderFactory) + */ + HeaderSpec uri(Function uriFunction); + + } + + /** + * Contract for specifying request headers leading up to the exchange. + */ + interface HeaderSpec { + + /** + * Set the list of acceptable {@linkplain MediaType media types}, as + * specified by the {@code Accept} header. + * @param acceptableMediaTypes the acceptable media types + * @return this builder + */ + HeaderSpec accept(MediaType... acceptableMediaTypes); + + /** + * Set the list of acceptable {@linkplain Charset charsets}, as specified + * by the {@code Accept-Charset} header. + * @param acceptableCharsets the acceptable charsets + * @return this builder + */ + HeaderSpec acceptCharset(Charset... acceptableCharsets); + + /** + * Set the length of the body in bytes, as specified by the + * {@code Content-Length} header. + * @param contentLength the content length + * @return this builder + * @see HttpHeaders#setContentLength(long) + */ + HeaderSpec contentLength(long contentLength); + + /** + * Set the {@linkplain MediaType media type} of the body, as specified + * by the {@code Content-Type} header. + * @param contentType the content type + * @return this builder + * @see HttpHeaders#setContentType(MediaType) + */ + HeaderSpec contentType(MediaType contentType); + + /** + * Add a cookie with the given name and value. + * @param name the cookie name + * @param value the cookie value + * @return this builder + */ + HeaderSpec cookie(String name, String value); + + /** + * Copy the given cookies into the entity's cookies map. + * + * @param cookies the existing cookies to copy from + * @return this builder + */ + HeaderSpec cookies(MultiValueMap cookies); + + /** + * Set the value of the {@code If-Modified-Since} header. + *

The date should be specified as the number of milliseconds since + * January 1, 1970 GMT. + * @param ifModifiedSince the new value of the header + * @return this builder + */ + HeaderSpec ifModifiedSince(ZonedDateTime ifModifiedSince); + + /** + * Set the values of the {@code If-None-Match} header. + * @param ifNoneMatches the new value of the header + * @return this builder + */ + HeaderSpec ifNoneMatch(String... ifNoneMatches); + + /** + * Add the given, single header value under the given name. + * @param headerName the header name + * @param headerValues the header value(s) + * @return this builder + */ + HeaderSpec header(String headerName, String... headerValues); + + /** + * Copy the given headers into the entity's headers map. + * @param headers the existing headers to copy from + * @return this builder + */ + HeaderSpec headers(HttpHeaders headers); + + /** + * Perform the request without a request body. + * @return a {@code Mono} with the response + */ + Mono exchange(); + + /** + * Set the body of the request to the given {@code BodyInserter} and + * perform the request. + * @param inserter the {@code BodyInserter} that writes to the request + * @param the type contained in the body + * @return a {@code Mono} with the response + */ + Mono exchange(BodyInserter inserter); + + /** + * Set the body of the request to the given {@code Publisher} and + * perform the request. + * @param publisher the {@code Publisher} to write to the request + * @param elementClass the class of elements contained in the publisher + * @param the type of the elements contained in the publisher + * @param the type of the {@code Publisher} + * @return a {@code Mono} with the response + */ + > Mono exchange(S publisher, Class elementClass); + + } + } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/WebClientOperations.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/WebClientOperations.java deleted file mode 100644 index 7200f8fb8c..0000000000 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/function/client/WebClientOperations.java +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright 2002-2017 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.client; - -import java.net.URI; -import java.nio.charset.Charset; -import java.time.ZonedDateTime; -import java.util.function.Function; - -import org.reactivestreams.Publisher; -import reactor.core.publisher.Mono; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.client.reactive.ClientHttpRequest; -import org.springframework.util.MultiValueMap; -import org.springframework.web.reactive.function.BodyInserter; -import org.springframework.web.util.UriBuilderFactory; - -/** - * The main class for performing requests through a WebClient. - * - *

- *
- * // Create WebClient (application-wide)
- *
- * ClientHttpConnector connector = new ReactorClientHttpConnector();
- * WebClient webClient = WebClient.create(connector);
- *
- * // Create WebClientOperations (per base URI)
- *
- * String baseUri = "http://abc.com";
- * UriBuilderFactory factory = new DefaultUriBuilderFactory(baseUri);
- * WebClientOperations operations = WebClientOperations.create(webClient, factory);
- *
- * // Perform requests...
- *
- * Mono result = operations.get()
- *     .uri("/foo")
- *     .exchange()
- *     .then(response -> response.bodyToMono(String.class));
- * 
- * - * @author Rossen Stoyanchev - * @since 5.0 - */ -public interface WebClientOperations { - - /** - * Prepare an HTTP GET request. - * @return a spec for specifying the target URL - */ - UriSpec get(); - - /** - * Prepare an HTTP HEAD request. - * @return a spec for specifying the target URL - */ - UriSpec head(); - - /** - * Prepare an HTTP POST request. - * @return a spec for specifying the target URL - */ - UriSpec post(); - - /** - * Prepare an HTTP PUT request. - * @return a spec for specifying the target URL - */ - UriSpec put(); - - /** - * Prepare an HTTP PATCH request. - * @return a spec for specifying the target URL - */ - UriSpec patch(); - - /** - * Prepare an HTTP DELETE request. - * @return a spec for specifying the target URL - */ - UriSpec delete(); - - /** - * Prepare an HTTP OPTIONS request. - * @return a spec for specifying the target URL - */ - UriSpec options(); - - - /** - * Filter the client with the given {@code ExchangeFilterFunction}. - * @param filterFunction the filter to apply to this client - * @return the filtered client - * @see ExchangeFilterFunction#apply(ExchangeFunction) - */ - WebClientOperations filter(ExchangeFilterFunction filterFunction); - - - // Static, factory methods - - /** - * Create {@link WebClientOperations} that wraps the given {@link WebClient}. - * @param webClient the underlying client to use - */ - static WebClientOperations create(WebClient webClient) { - return builder(webClient).build(); - } - - /** - * Create {@link WebClientOperations} with a builder for additional - * configuration options. - * @param webClient the underlying client to use - */ - static WebClientOperations.Builder builder(WebClient webClient) { - return new DefaultWebClientOperationsBuilder(webClient); - } - - - /** - * A mutable builder for a {@link WebClientOperations}. - */ - interface Builder { - - /** - * Configure a {@code UriBuilderFactory} for use with this client for - * example to define a common "base" URI. - * @param uriBuilderFactory the URI builder factory - */ - Builder uriBuilderFactory(UriBuilderFactory uriBuilderFactory); - - /** - * Builder the {@link WebClient} instance. - */ - WebClientOperations build(); - - } - - - /** - * Contract for specifying the URI for a request. - */ - interface UriSpec { - - /** - * Specify the URI using an absolute, fully constructed {@link URI}. - */ - HeaderSpec uri(URI uri); - - /** - * Specify the URI for the request using a URI template and URI variables. - * If a {@link UriBuilderFactory} was configured for the client (e.g. - * with a base URI) it will be used to expand the URI template. - * @see Builder#uriBuilderFactory(UriBuilderFactory) - */ - HeaderSpec uri(String uri, Object... uriVariables); - - /** - * Build the URI for the request using the {@link UriBuilderFactory} - * configured for this client. - * @see Builder#uriBuilderFactory(UriBuilderFactory) - */ - HeaderSpec uri(Function uriFunction); - - } - - /** - * Contract for specifying request headers leading up to the exchange. - */ - interface HeaderSpec { - - /** - * Set the list of acceptable {@linkplain MediaType media types}, as - * specified by the {@code Accept} header. - * @param acceptableMediaTypes the acceptable media types - * @return this builder - */ - HeaderSpec accept(MediaType... acceptableMediaTypes); - - /** - * Set the list of acceptable {@linkplain Charset charsets}, as specified - * by the {@code Accept-Charset} header. - * @param acceptableCharsets the acceptable charsets - * @return this builder - */ - HeaderSpec acceptCharset(Charset... acceptableCharsets); - - /** - * Set the length of the body in bytes, as specified by the - * {@code Content-Length} header. - * @param contentLength the content length - * @return this builder - * @see HttpHeaders#setContentLength(long) - */ - HeaderSpec contentLength(long contentLength); - - /** - * Set the {@linkplain MediaType media type} of the body, as specified - * by the {@code Content-Type} header. - * @param contentType the content type - * @return this builder - * @see HttpHeaders#setContentType(MediaType) - */ - HeaderSpec contentType(MediaType contentType); - - /** - * Add a cookie with the given name and value. - * @param name the cookie name - * @param value the cookie value - * @return this builder - */ - HeaderSpec cookie(String name, String value); - - /** - * Copy the given cookies into the entity's cookies map. - * - * @param cookies the existing cookies to copy from - * @return this builder - */ - HeaderSpec cookies(MultiValueMap cookies); - - /** - * Set the value of the {@code If-Modified-Since} header. - *

The date should be specified as the number of milliseconds since - * January 1, 1970 GMT. - * @param ifModifiedSince the new value of the header - * @return this builder - */ - HeaderSpec ifModifiedSince(ZonedDateTime ifModifiedSince); - - /** - * Set the values of the {@code If-None-Match} header. - * @param ifNoneMatches the new value of the header - * @return this builder - */ - HeaderSpec ifNoneMatch(String... ifNoneMatches); - - /** - * Add the given, single header value under the given name. - * @param headerName the header name - * @param headerValues the header value(s) - * @return this builder - */ - HeaderSpec header(String headerName, String... headerValues); - - /** - * Copy the given headers into the entity's headers map. - * @param headers the existing headers to copy from - * @return this builder - */ - HeaderSpec headers(HttpHeaders headers); - - /** - * Perform the request without a request body. - * @return a {@code Mono} with the response - */ - Mono exchange(); - - /** - * Set the body of the request to the given {@code BodyInserter} and - * perform the request. - * @param inserter the {@code BodyInserter} that writes to the request - * @param the type contained in the body - * @return a {@code Mono} with the response - */ - Mono exchange(BodyInserter inserter); - - /** - * Set the body of the request to the given {@code Publisher} and - * perform the request. - * @param publisher the {@code Publisher} to write to the request - * @param elementClass the class of elements contained in the publisher - * @param the type of the elements contained in the publisher - * @param the type of the {@code Publisher} - * @return a {@code Mono} with the response - */ - > Mono exchange(S publisher, Class elementClass); - - } - -} diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/FlushingIntegrationTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/FlushingIntegrationTests.java index 18810cfd57..aca5c1614b 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/FlushingIntegrationTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/FlushingIntegrationTests.java @@ -36,19 +36,20 @@ import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.http.server.reactive.bootstrap.RxNettyHttpServer; import org.springframework.web.reactive.function.BodyExtractors; +import org.springframework.web.reactive.function.client.ExchangeFunction; +import org.springframework.web.reactive.function.client.ExchangeFunctions; import org.springframework.web.reactive.function.client.WebClient; -import org.springframework.web.reactive.function.client.WebClientOperations; import org.springframework.web.util.DefaultUriBuilderFactory; import org.springframework.web.util.UriBuilderFactory; -import static org.junit.Assert.*; +import static org.junit.Assert.assertTrue; /** * @author Sebastien Deleuze */ public class FlushingIntegrationTests extends AbstractHttpHandlerIntegrationTests { - private WebClientOperations operations; + private WebClient webClient; @Before @@ -58,15 +59,15 @@ public class FlushingIntegrationTests extends AbstractHttpHandlerIntegrationTest super.setup(); - WebClient client = WebClient.create(new ReactorClientHttpConnector()); + ExchangeFunction exchange = ExchangeFunctions.create(new ReactorClientHttpConnector()); UriBuilderFactory factory = new DefaultUriBuilderFactory("http://localhost:" + this.port); - this.operations = WebClientOperations.builder(client).uriBuilderFactory(factory).build(); + this.webClient = WebClient.builder(exchange).uriBuilderFactory(factory).build(); } @Test public void writeAndFlushWith() throws Exception { - Mono result = this.operations.get() + Mono result = this.webClient.get() .uri("/write-and-flush") .exchange() .flatMap(response -> response.body(BodyExtractors.toFlux(String.class))) @@ -81,7 +82,7 @@ public class FlushingIntegrationTests extends AbstractHttpHandlerIntegrationTest @Test // SPR-14991 public void writeAndAutoFlushOnComplete() { - Mono result = this.operations.get() + Mono result = this.webClient.get() .uri("/write-and-complete") .exchange() .flatMap(response -> response.bodyToFlux(String.class)) @@ -95,7 +96,7 @@ public class FlushingIntegrationTests extends AbstractHttpHandlerIntegrationTest @Test // SPR-14992 public void writeAndAutoFlushBeforeComplete() { - Flux result = this.operations.get() + Flux result = this.webClient.get() .uri("/write-and-never-complete") .exchange() .flatMap(response -> response.bodyToFlux(String.class)); diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilderTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilderTests.java index b16ad3c0c7..749210bf09 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilderTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -83,7 +83,7 @@ public class DefaultClientRequestBuilderTests { .build(); MockClientHttpRequest request = new MockClientHttpRequest(GET, "/"); - WebClientStrategies strategies = mock(WebClientStrategies.class); + ExchangeStrategies strategies = mock(ExchangeStrategies.class); result.writeTo(request, strategies).block(); @@ -110,7 +110,7 @@ public class DefaultClientRequestBuilderTests { List> messageWriters = new ArrayList<>(); messageWriters.add(new EncoderHttpMessageWriter<>(new CharSequenceEncoder())); - WebClientStrategies strategies = mock(WebClientStrategies.class); + ExchangeStrategies strategies = mock(ExchangeStrategies.class); when(strategies.messageWriters()).thenReturn(messageWriters::stream); MockClientHttpRequest request = new MockClientHttpRequest(GET, "/"); diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/DefaultClientResponseTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/DefaultClientResponseTests.java index 49ef89f9d8..aee80b6283 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/DefaultClientResponseTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/DefaultClientResponseTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -55,7 +55,7 @@ public class DefaultClientResponseTests { private ClientHttpResponse mockResponse; - private WebClientStrategies mockWebClientStrategies; + private ExchangeStrategies mockExchangeStrategies; private DefaultClientResponse defaultClientResponse; @@ -63,9 +63,9 @@ public class DefaultClientResponseTests { @Before public void createMocks() { mockResponse = mock(ClientHttpResponse.class); - mockWebClientStrategies = mock(WebClientStrategies.class); + mockExchangeStrategies = mock(ExchangeStrategies.class); - defaultClientResponse = new DefaultClientResponse(mockResponse, mockWebClientStrategies); + defaultClientResponse = new DefaultClientResponse(mockResponse, mockExchangeStrategies); } @Test @@ -110,7 +110,7 @@ public class DefaultClientResponseTests { Set> messageReaders = Collections .singleton(new DecoderHttpMessageReader(new StringDecoder())); - when(mockWebClientStrategies.messageReaders()).thenReturn(messageReaders::stream); + when(mockExchangeStrategies.messageReaders()).thenReturn(messageReaders::stream); Mono resultMono = defaultClientResponse.body(toMono(String.class)); assertEquals("foo", resultMono.block()); @@ -131,7 +131,7 @@ public class DefaultClientResponseTests { Set> messageReaders = Collections .singleton(new DecoderHttpMessageReader(new StringDecoder())); - when(mockWebClientStrategies.messageReaders()).thenReturn(messageReaders::stream); + when(mockExchangeStrategies.messageReaders()).thenReturn(messageReaders::stream); Mono resultMono = defaultClientResponse.bodyToMono(String.class); assertEquals("foo", resultMono.block()); @@ -146,7 +146,7 @@ public class DefaultClientResponseTests { Set> messageReaders = Collections .singleton(new DecoderHttpMessageReader(new StringDecoder())); - when(mockWebClientStrategies.messageReaders()).thenReturn(messageReaders::stream); + when(mockExchangeStrategies.messageReaders()).thenReturn(messageReaders::stream); Mono resultMono = defaultClientResponse.bodyToMono(String.class); @@ -170,7 +170,7 @@ public class DefaultClientResponseTests { Set> messageReaders = Collections .singleton(new DecoderHttpMessageReader(new StringDecoder())); - when(mockWebClientStrategies.messageReaders()).thenReturn(messageReaders::stream); + when(mockExchangeStrategies.messageReaders()).thenReturn(messageReaders::stream); Flux resultFlux = defaultClientResponse.bodyToFlux(String.class); Mono> result = resultFlux.collectList(); @@ -186,7 +186,7 @@ public class DefaultClientResponseTests { Set> messageReaders = Collections .singleton(new DecoderHttpMessageReader(new StringDecoder())); - when(mockWebClientStrategies.messageReaders()).thenReturn(messageReaders::stream); + when(mockExchangeStrategies.messageReaders()).thenReturn(messageReaders::stream); Flux resultFlux = defaultClientResponse.bodyToFlux(String.class); StepVerifier.create(resultFlux) diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/WebClientStrategiesTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/ExchangeStrategiesTests.java similarity index 92% rename from spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/WebClientStrategiesTests.java rename to spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/ExchangeStrategiesTests.java index e5a13bbca0..b9ea5a3d4c 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/WebClientStrategiesTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/ExchangeStrategiesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -42,11 +42,11 @@ import static org.junit.Assert.assertTrue; /** * @author Arjen Poutsma */ -public class WebClientStrategiesTests { +public class ExchangeStrategiesTests { @Test public void empty() { - WebClientStrategies strategies = WebClientStrategies.empty().build(); + ExchangeStrategies strategies = ExchangeStrategies.empty().build(); assertEquals(Optional.empty(), strategies.messageReaders().get().findFirst()); assertEquals(Optional.empty(), strategies.messageWriters().get().findFirst()); } @@ -56,7 +56,7 @@ public class WebClientStrategiesTests { HttpMessageReader messageReader = new DummyMessageReader(); HttpMessageWriter messageWriter = new DummyMessageWriter(); - WebClientStrategies strategies = WebClientStrategies.of( + ExchangeStrategies strategies = ExchangeStrategies.of( () -> Stream.of(messageReader), () -> Stream.of(messageWriter)); @@ -74,7 +74,7 @@ public class WebClientStrategiesTests { applicationContext.registerSingleton("messageReader", DummyMessageReader.class); applicationContext.refresh(); - WebClientStrategies strategies = WebClientStrategies.of(applicationContext); + ExchangeStrategies strategies = ExchangeStrategies.of(applicationContext); assertTrue(strategies.messageReaders().get() .allMatch(r -> r instanceof DummyMessageReader)); assertTrue(strategies.messageWriters().get() diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java index 5dabfc061c..209cf4c93c 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -43,7 +43,7 @@ import static org.junit.Assert.assertThat; import static org.springframework.web.reactive.function.BodyInserters.fromObject; /** - * Integration tests using a {@link WebClient} through {@link WebClientOperations}. + * Integration tests using a {@link ExchangeFunction} through {@link WebClient}. * * @author Brian Clozel * @author Rossen Stoyanchev @@ -52,17 +52,17 @@ public class WebClientIntegrationTests { private MockWebServer server; - private WebClientOperations operations; + private WebClient webClient; @Before public void setup() { this.server = new MockWebServer(); - WebClient webClient = WebClient.create(new ReactorClientHttpConnector()); + ExchangeFunction exchangeFunction = ExchangeFunctions.create(new ReactorClientHttpConnector()); UriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(this.server.url("/").toString()); - this.operations = WebClientOperations.builder(webClient) + this.webClient = WebClient.builder(exchangeFunction) .uriBuilderFactory(uriBuilderFactory) .build(); } @@ -77,7 +77,7 @@ public class WebClientIntegrationTests { public void headers() throws Exception { this.server.enqueue(new MockResponse().setHeader("Content-Type", "text/plain").setBody("Hello Spring!")); - Mono result = this.operations.get() + Mono result = this.webClient.get() .uri("/greeting?name=Spring") .exchange() .map(response -> response.headers().asHttpHeaders()); @@ -101,7 +101,7 @@ public class WebClientIntegrationTests { public void plainText() throws Exception { this.server.enqueue(new MockResponse().setBody("Hello Spring!")); - Mono result = this.operations.get() + Mono result = this.webClient.get() .uri("/greeting?name=Spring") .header("X-Test-Header", "testvalue") .exchange() @@ -125,7 +125,7 @@ public class WebClientIntegrationTests { this.server.enqueue(new MockResponse().setHeader("Content-Type", "application/json") .setBody(content)); - Mono result = this.operations.get() + Mono result = this.webClient.get() .uri("/json") .accept(MediaType.APPLICATION_JSON) .exchange() @@ -147,7 +147,7 @@ public class WebClientIntegrationTests { this.server.enqueue(new MockResponse().setHeader("Content-Type", "application/json") .setBody("{\"bar\":\"barbar\",\"foo\":\"foofoo\"}")); - Mono result = this.operations.get() + Mono result = this.webClient.get() .uri("/pojo") .accept(MediaType.APPLICATION_JSON) .exchange() @@ -169,7 +169,7 @@ public class WebClientIntegrationTests { this.server.enqueue(new MockResponse().setHeader("Content-Type", "application/json") .setBody("[{\"bar\":\"bar1\",\"foo\":\"foo1\"},{\"bar\":\"bar2\",\"foo\":\"foo2\"}]")); - Flux result = this.operations.get() + Flux result = this.webClient.get() .uri("/pojos") .accept(MediaType.APPLICATION_JSON) .exchange() @@ -193,7 +193,7 @@ public class WebClientIntegrationTests { .setHeader("Content-Type", "application/json") .setBody("{\"bar\":\"BARBAR\",\"foo\":\"FOOFOO\"}")); - Mono result = this.operations.post() + Mono result = this.webClient.post() .uri("/pojo/capitalize") .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON) @@ -219,7 +219,7 @@ public class WebClientIntegrationTests { this.server.enqueue(new MockResponse() .setHeader("Content-Type", "text/plain").setBody("test")); - Mono result = this.operations.get() + Mono result = this.webClient.get() .uri("/test") .cookie("testkey", "testvalue") .exchange() @@ -241,7 +241,7 @@ public class WebClientIntegrationTests { this.server.enqueue(new MockResponse().setResponseCode(404) .setHeader("Content-Type", "text/plain").setBody("Not Found")); - Mono result = this.operations.get().uri("/greeting?name=Spring").exchange(); + Mono result = this.webClient.get().uri("/greeting?name=Spring").exchange(); StepVerifier.create(result) .consumeNextWith(response -> assertEquals(HttpStatus.NOT_FOUND, response.statusCode())) @@ -258,7 +258,7 @@ public class WebClientIntegrationTests { public void buildFilter() throws Exception { this.server.enqueue(new MockResponse().setHeader("Content-Type", "text/plain").setBody("Hello Spring!")); - WebClientOperations filteredClient = this.operations.filter( + WebClient filteredClient = this.webClient.filter( (request, next) -> { ClientRequest filteredRequest = ClientRequest.from(request).header("foo", "bar").build(); return next.exchange(filteredRequest); @@ -284,7 +284,7 @@ public class WebClientIntegrationTests { public void filter() throws Exception { this.server.enqueue(new MockResponse().setHeader("Content-Type", "text/plain").setBody("Hello Spring!")); - WebClientOperations filteredClient = this.operations.filter( + WebClient filteredClient = this.webClient.filter( (request, next) -> { ClientRequest filteredRequest = ClientRequest.from(request).header("foo", "bar").build(); return next.exchange(filteredRequest); diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/server/SseHandlerFunctionIntegrationTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/server/SseHandlerFunctionIntegrationTests.java index e58fc19431..a4d923bbd7 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/server/SseHandlerFunctionIntegrationTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/function/server/SseHandlerFunctionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -26,8 +26,9 @@ import reactor.test.StepVerifier; import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.http.codec.ServerSentEvent; +import org.springframework.web.reactive.function.client.ExchangeFunction; +import org.springframework.web.reactive.function.client.ExchangeFunctions; import org.springframework.web.reactive.function.client.WebClient; -import org.springframework.web.reactive.function.client.WebClientOperations; import org.springframework.web.util.DefaultUriBuilderFactory; import org.springframework.web.util.UriBuilderFactory; @@ -44,15 +45,16 @@ import static org.springframework.web.reactive.function.server.RouterFunctions.r */ public class SseHandlerFunctionIntegrationTests extends AbstractRouterFunctionIntegrationTests { - private WebClientOperations operations; + private WebClient webClient; @Before public void setup() throws Exception { super.setup(); - WebClient client = WebClient.create(new ReactorClientHttpConnector()); + ExchangeFunction exchangeFunction = + ExchangeFunctions.create(new ReactorClientHttpConnector()); UriBuilderFactory factory = new DefaultUriBuilderFactory("http://localhost:" + this.port); - this.operations = WebClientOperations.builder(client).uriBuilderFactory(factory).build(); + this.webClient = WebClient.builder(exchangeFunction).uriBuilderFactory(factory).build(); } @Override @@ -65,7 +67,7 @@ public class SseHandlerFunctionIntegrationTests extends AbstractRouterFunctionIn @Test public void sseAsString() throws Exception { - Flux result = this.operations.get() + Flux result = this.webClient.get() .uri("/string") .accept(TEXT_EVENT_STREAM) .exchange() @@ -79,7 +81,7 @@ public class SseHandlerFunctionIntegrationTests extends AbstractRouterFunctionIn } @Test public void sseAsPerson() throws Exception { - Flux result = this.operations.get() + Flux result = this.webClient.get() .uri("/person") .accept(TEXT_EVENT_STREAM) .exchange() @@ -94,7 +96,7 @@ public class SseHandlerFunctionIntegrationTests extends AbstractRouterFunctionIn @Test public void sseAsEvent() throws Exception { - Flux> result = this.operations.get() + Flux> result = this.webClient.get() .uri("/event") .accept(TEXT_EVENT_STREAM) .exchange() diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/SseIntegrationTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/SseIntegrationTests.java index 83387cc0a2..252ac12491 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/SseIntegrationTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/SseIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -35,8 +35,9 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.reactive.DispatcherHandler; import org.springframework.web.reactive.config.EnableWebReactive; +import org.springframework.web.reactive.function.client.ExchangeFunction; +import org.springframework.web.reactive.function.client.ExchangeFunctions; import org.springframework.web.reactive.function.client.WebClient; -import org.springframework.web.reactive.function.client.WebClientOperations; import org.springframework.web.server.adapter.WebHttpHandlerBuilder; import org.springframework.web.util.DefaultUriBuilderFactory; import org.springframework.web.util.UriBuilderFactory; @@ -55,16 +56,16 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests { private AnnotationConfigApplicationContext wac; - private WebClientOperations operations; + private WebClient webClient; @Override @Before public void setup() throws Exception { super.setup(); - WebClient client = WebClient.create(new ReactorClientHttpConnector()); + ExchangeFunction exchangeFunction = ExchangeFunctions.create(new ReactorClientHttpConnector()); UriBuilderFactory factory = new DefaultUriBuilderFactory("http://localhost:" + this.port + "/sse"); - this.operations = WebClientOperations.builder(client).uriBuilderFactory(factory).build(); + this.webClient = WebClient.builder(exchangeFunction).uriBuilderFactory(factory).build(); } @@ -79,7 +80,7 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests { @Test public void sseAsString() throws Exception { - Flux result = this.operations.get() + Flux result = this.webClient.get() .uri("/string") .accept(TEXT_EVENT_STREAM) .exchange() @@ -93,7 +94,7 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests { } @Test public void sseAsPerson() throws Exception { - Flux result = this.operations.get() + Flux result = this.webClient.get() .uri("/person") .accept(TEXT_EVENT_STREAM) .exchange() @@ -109,7 +110,7 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests { @Test public void sseAsEvent() throws Exception { ResolvableType type = forClassWithGenerics(ServerSentEvent.class, String.class); - Flux> result = this.operations.get() + Flux> result = this.webClient.get() .uri("/event") .accept(TEXT_EVENT_STREAM) .exchange() @@ -136,7 +137,7 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests { @Test public void sseAsEventWithoutAcceptHeader() throws Exception { - Flux> result = this.operations.get() + Flux> result = this.webClient.get() .uri("/event") .accept(TEXT_EVENT_STREAM) .exchange()