Skip to content

Commit d420939

Browse files
bclozelrstoyanchev
authored andcommitted
Allow ExchangeStrategies customizations in WebClient
Prior to this commit, developers could configure their WebClient to use their custom `ExchangeStrategies`, by providing it in the `WebClient.Builder` chain. Once created, an `ExchangeStrategies` instance is not mutable, which makes it hard for further customizations by other components. In the case of the reported issue, other components would override the default configuration for the codecs maxInMemorySize. This commit makes the `ExchangeStrategies` mutable and uses that fact to further customize them with a new `WebClient.Builder#exchangeStrategies` `Consumer` variant. This commit is also deprecating those mutating variants in favor of a new `WebClient.Builder#exchangeStrategies` that takes a `ExchangeStrategies#Builder` directly and avoids mutation issues altogether. Closes gh-23961
1 parent 17e2a0c commit d420939

File tree

18 files changed

+344
-43
lines changed

18 files changed

+344
-43
lines changed

spring-messaging/src/main/java/org/springframework/messaging/rsocket/RSocketRequester.java

+8-7
Original file line numberDiff line numberDiff line change
@@ -169,18 +169,19 @@ interface Builder {
169169
RSocketRequester.Builder setupMetadata(Object value, @Nullable MimeType mimeType);
170170

171171
/**
172-
* Provide {@link RSocketStrategies} to use.
173-
* <p>By default this is based on default settings of
174-
* {@link RSocketStrategies.Builder} but may be further customized via
175-
* {@link #rsocketStrategies(Consumer)}.
172+
* Provide the {@link RSocketStrategies} to use.
173+
* <p>This is useful for changing the default settings, yet still allowing
174+
* further customizations via {@link #rsocketStrategies(Consumer)}.
175+
* If not set, defaults are obtained from {@link RSocketStrategies#builder()}.
176+
* @param strategies the strategies to use
176177
*/
177178
RSocketRequester.Builder rsocketStrategies(@Nullable RSocketStrategies strategies);
178179

179180
/**
180181
* Customize the {@link RSocketStrategies}.
181-
* <p>By default this starts out as {@link RSocketStrategies#builder()}.
182-
* However if strategies were {@link #rsocketStrategies(RSocketStrategies) set}
183-
* explicitly, then they are {@link RSocketStrategies#mutate() mutated}.
182+
* <p>Allows further customization on {@link RSocketStrategies},
183+
* mutating them if they were {@link #rsocketStrategies(RSocketStrategies) set},
184+
* or starting from {@link RSocketStrategies#builder()} defaults}.
184185
*/
185186
RSocketRequester.Builder rsocketStrategies(Consumer<RSocketStrategies.Builder> configurer);
186187

spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClientBuilder.java

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -137,11 +137,24 @@ public WebTestClient.Builder filters(Consumer<List<ExchangeFilterFunction>> filt
137137
}
138138

139139
@Override
140+
@Deprecated
140141
public WebTestClient.Builder exchangeStrategies(ExchangeStrategies strategies) {
141142
this.webClientBuilder.exchangeStrategies(strategies);
142143
return this;
143144
}
144145

146+
@Override
147+
public WebTestClient.Builder exchangeStrategies(ExchangeStrategies.Builder strategies) {
148+
this.webClientBuilder.exchangeStrategies(strategies);
149+
return this;
150+
}
151+
152+
@Override
153+
public WebTestClient.Builder exchangeStrategies(Consumer<ExchangeStrategies.Builder> configurer) {
154+
this.webClientBuilder.exchangeStrategies(configurer);
155+
return this;
156+
}
157+
145158
@Override
146159
public WebTestClient.Builder responseTimeout(Duration timeout) {
147160
this.responseTimeout = timeout;

spring-test/src/main/java/org/springframework/test/web/reactive/server/WebTestClient.java

+27-3
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
* perform integration tests on an embedded WebFlux server.
8585
*
8686
* @author Rossen Stoyanchev
87+
* @author Brian Clozel
8788
* @since 5.0
8889
* @see StatusAssertions
8990
* @see HeaderAssertions
@@ -443,11 +444,34 @@ interface Builder {
443444

444445
/**
445446
* Configure the {@link ExchangeStrategies} to use.
446-
* <p>By default {@link ExchangeStrategies#withDefaults()} is used.
447+
* <p>This is useful for changing the default settings, yet still allowing
448+
* further customizations via {@link #exchangeStrategies(Consumer)}.
449+
* By default {@link ExchangeStrategies#withDefaults()} is used.
447450
* @param strategies the strategies to use
451+
* @deprecated as of 5.1 in favor of {@link #exchangeStrategies(ExchangeStrategies.Builder)}
448452
*/
453+
@Deprecated
449454
Builder exchangeStrategies(ExchangeStrategies strategies);
450455

456+
/**
457+
* Configure the {@link ExchangeStrategies.Builder} to use.
458+
* <p>This is useful for changing the default settings, yet still allowing
459+
* further customizations via {@link #exchangeStrategies(Consumer)}.
460+
* By default {@link ExchangeStrategies#builder()} is used.
461+
* @param strategies the strategies to use
462+
* @since 5.1.12
463+
*/
464+
Builder exchangeStrategies(ExchangeStrategies.Builder strategies);
465+
466+
/**
467+
* Customize the {@link ExchangeStrategies}.
468+
* <p>Allows further customization on {@link ExchangeStrategies},
469+
* mutating them if they were {@link #exchangeStrategies(ExchangeStrategies) set},
470+
* or starting from {@link ExchangeStrategies#withDefaults() defaults}.
471+
* @since 5.1.12
472+
*/
473+
Builder exchangeStrategies(Consumer<ExchangeStrategies.Builder> configurer);
474+
451475
/**
452476
* Max amount of time to wait for responses.
453477
* <p>By default 5 seconds.
@@ -928,7 +952,7 @@ interface BodyContentSpec {
928952
* @since 5.1
929953
* @see #xpath(String, Map, Object...)
930954
*/
931-
default XpathAssertions xpath(String expression, Object... args){
955+
default XpathAssertions xpath(String expression, Object... args) {
932956
return xpath(expression, null, args);
933957
}
934958

@@ -942,7 +966,7 @@ default XpathAssertions xpath(String expression, Object... args){
942966
* @param args arguments to parameterize the expression
943967
* @since 5.1
944968
*/
945-
XpathAssertions xpath(String expression, @Nullable Map<String, String> namespaces, Object... args);
969+
XpathAssertions xpath(String expression, @Nullable Map<String, String> namespaces, Object... args);
946970

947971
/**
948972
* Assert the response body content with the given {@link Consumer}.

spring-web/src/main/java/org/springframework/http/codec/ClientCodecConfigurer.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -63,6 +63,11 @@ public interface ClientCodecConfigurer extends CodecConfigurer {
6363
@Override
6464
ClientDefaultCodecs defaultCodecs();
6565

66+
/**
67+
* Clone this {@link ClientCodecConfigurer}.
68+
*/
69+
@Override
70+
ClientCodecConfigurer clone();
6671

6772
/**
6873
* Static factory method for a {@code ClientCodecConfigurer}.

spring-web/src/main/java/org/springframework/http/codec/CodecConfigurer.java

+6
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ public interface CodecConfigurer {
8787
*/
8888
List<HttpMessageWriter<?>> getWriters();
8989

90+
/**
91+
* Clone this {@link CodecConfigurer}.
92+
* @since 5.1.12
93+
*/
94+
CodecConfigurer clone();
95+
9096

9197
/**
9298
* Customize or replace the HTTP message readers and writers registered by

spring-web/src/main/java/org/springframework/http/codec/support/BaseCodecConfigurer.java

+35-4
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,14 @@
3434
* client and server specific variants.
3535
*
3636
* @author Rossen Stoyanchev
37+
* @author Brian Clozel
3738
* @since 5.0
3839
*/
3940
class BaseCodecConfigurer implements CodecConfigurer {
4041

41-
private final BaseDefaultCodecs defaultCodecs;
42+
protected final BaseDefaultCodecs defaultCodecs;
4243

43-
private final DefaultCustomCodecs customCodecs = new DefaultCustomCodecs();
44+
protected final DefaultCustomCodecs customCodecs;
4445

4546

4647
/**
@@ -50,6 +51,16 @@ class BaseCodecConfigurer implements CodecConfigurer {
5051
BaseCodecConfigurer(BaseDefaultCodecs defaultCodecs) {
5152
Assert.notNull(defaultCodecs, "'defaultCodecs' is required");
5253
this.defaultCodecs = defaultCodecs;
54+
this.customCodecs = new DefaultCustomCodecs();
55+
}
56+
57+
/**
58+
* Constructor with another {@link BaseCodecConfigurer} to copy
59+
* the configuration from.
60+
*/
61+
BaseCodecConfigurer(BaseCodecConfigurer other) {
62+
this.defaultCodecs = other.cloneDefaultCodecs();
63+
this.customCodecs = new DefaultCustomCodecs(other.customCodecs);
5364
}
5465

5566

@@ -87,6 +98,17 @@ public List<HttpMessageWriter<?>> getWriters() {
8798
return getWritersInternal(false);
8899
}
89100

101+
102+
@Override
103+
public CodecConfigurer clone() {
104+
return new BaseCodecConfigurer(this);
105+
}
106+
107+
protected BaseDefaultCodecs cloneDefaultCodecs() {
108+
return new BaseDefaultCodecs(this.defaultCodecs);
109+
}
110+
111+
90112
/**
91113
* Internal method that returns the configured writers.
92114
* @param forMultipart whether to returns writers for general use ("false"),
@@ -110,7 +132,7 @@ protected List<HttpMessageWriter<?>> getWritersInternal(boolean forMultipart) {
110132
/**
111133
* Default implementation of {@code CustomCodecs}.
112134
*/
113-
private static final class DefaultCustomCodecs implements CustomCodecs {
135+
protected static final class DefaultCustomCodecs implements CustomCodecs {
114136

115137
private final List<HttpMessageReader<?>> typedReaders = new ArrayList<>();
116138

@@ -121,6 +143,16 @@ private static final class DefaultCustomCodecs implements CustomCodecs {
121143
private final List<HttpMessageWriter<?>> objectWriters = new ArrayList<>();
122144

123145

146+
DefaultCustomCodecs() {
147+
}
148+
149+
DefaultCustomCodecs(DefaultCustomCodecs other) {
150+
other.typedReaders.addAll(this.typedReaders);
151+
other.typedWriters.addAll(this.typedWriters);
152+
other.objectReaders.addAll(this.objectReaders);
153+
other.objectWriters.addAll(this.objectWriters);
154+
}
155+
124156
@Override
125157
public void decoder(Decoder<?> decoder) {
126158
reader(new DecoderHttpMessageReader<>(decoder));
@@ -143,7 +175,6 @@ public void writer(HttpMessageWriter<?> writer) {
143175
(canWriteObject ? this.objectWriters : this.typedWriters).add(writer);
144176
}
145177

146-
147178
// Package private accessors...
148179

149180
List<HttpMessageReader<?>> getTypedReaders() {

spring-web/src/main/java/org/springframework/http/codec/support/BaseDefaultCodecs.java

+15
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,21 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
106106
private boolean registerDefaults = true;
107107

108108

109+
BaseDefaultCodecs() {
110+
}
111+
112+
protected BaseDefaultCodecs(BaseDefaultCodecs other) {
113+
this.jackson2JsonDecoder = other.jackson2JsonDecoder;
114+
this.jackson2JsonEncoder = other.jackson2JsonEncoder;
115+
this.protobufDecoder = other.protobufDecoder;
116+
this.protobufEncoder = other.protobufEncoder;
117+
this.jaxb2Decoder = other.jaxb2Decoder;
118+
this.jaxb2Encoder = other.jaxb2Encoder;
119+
this.maxInMemorySize = other.maxInMemorySize;
120+
this.enableLoggingRequestDetails = other.enableLoggingRequestDetails;
121+
this.registerDefaults = other.registerDefaults;
122+
}
123+
109124
@Override
110125
public void jackson2JsonDecoder(Decoder<?> decoder) {
111126
this.jackson2JsonDecoder = decoder;

spring-web/src/main/java/org/springframework/http/codec/support/ClientDefaultCodecsImpl.java

+31-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,6 +49,17 @@ class ClientDefaultCodecsImpl extends BaseDefaultCodecs implements ClientCodecCo
4949
private Supplier<List<HttpMessageWriter<?>>> partWritersSupplier;
5050

5151

52+
ClientDefaultCodecsImpl() {
53+
}
54+
55+
ClientDefaultCodecsImpl(ClientDefaultCodecsImpl other) {
56+
super(other);
57+
this.multipartCodecs = new DefaultMultipartCodecs(other.multipartCodecs);
58+
this.sseDecoder = other.sseDecoder;
59+
this.partWritersSupplier = other.partWritersSupplier;
60+
}
61+
62+
5263
/**
5364
* Set a supplier for part writers to use when
5465
* {@link #multipartCodecs()} are not explicitly configured.
@@ -73,6 +84,14 @@ public void serverSentEventDecoder(Decoder<?> decoder) {
7384
this.sseDecoder = decoder;
7485
}
7586

87+
@Override
88+
public ClientDefaultCodecsImpl clone() {
89+
ClientDefaultCodecsImpl codecs = new ClientDefaultCodecsImpl();
90+
codecs.multipartCodecs = this.multipartCodecs;
91+
codecs.sseDecoder = this.sseDecoder;
92+
codecs.partWritersSupplier = this.partWritersSupplier;
93+
return codecs;
94+
}
7695

7796
@Override
7897
protected void extendObjectReaders(List<HttpMessageReader<?>> objectReaders) {
@@ -116,6 +135,17 @@ private static class DefaultMultipartCodecs implements ClientCodecConfigurer.Mul
116135

117136
private final List<HttpMessageWriter<?>> writers = new ArrayList<>();
118137

138+
139+
DefaultMultipartCodecs() {
140+
}
141+
142+
DefaultMultipartCodecs(@Nullable DefaultMultipartCodecs other) {
143+
if (other != null) {
144+
this.writers.addAll(other.writers);
145+
}
146+
}
147+
148+
119149
@Override
120150
public ClientCodecConfigurer.MultipartCodecs encoder(Encoder<?> encoder) {
121151
writer(new EncoderHttpMessageWriter<>(encoder));

spring-web/src/main/java/org/springframework/http/codec/support/DefaultClientCodecConfigurer.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,14 +26,30 @@
2626
*/
2727
public class DefaultClientCodecConfigurer extends BaseCodecConfigurer implements ClientCodecConfigurer {
2828

29+
2930
public DefaultClientCodecConfigurer() {
3031
super(new ClientDefaultCodecsImpl());
3132
((ClientDefaultCodecsImpl) defaultCodecs()).setPartWritersSupplier(() -> getWritersInternal(true));
3233
}
3334

35+
private DefaultClientCodecConfigurer(DefaultClientCodecConfigurer other) {
36+
super(other);
37+
}
38+
39+
3440
@Override
3541
public ClientDefaultCodecs defaultCodecs() {
3642
return (ClientDefaultCodecs) super.defaultCodecs();
3743
}
3844

45+
@Override
46+
public DefaultClientCodecConfigurer clone() {
47+
return new DefaultClientCodecConfigurer(this);
48+
}
49+
50+
@Override
51+
protected BaseDefaultCodecs cloneDefaultCodecs() {
52+
return new ClientDefaultCodecsImpl((ClientDefaultCodecsImpl) defaultCodecs());
53+
}
54+
3955
}

spring-web/src/main/java/org/springframework/http/codec/support/DefaultServerCodecConfigurer.java

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,13 +26,28 @@
2626
*/
2727
public class DefaultServerCodecConfigurer extends BaseCodecConfigurer implements ServerCodecConfigurer {
2828

29+
2930
public DefaultServerCodecConfigurer() {
3031
super(new ServerDefaultCodecsImpl());
3132
}
3233

34+
private DefaultServerCodecConfigurer(BaseCodecConfigurer other) {
35+
super(other);
36+
}
37+
38+
3339
@Override
3440
public ServerDefaultCodecs defaultCodecs() {
3541
return (ServerDefaultCodecs) super.defaultCodecs();
3642
}
3743

44+
@Override
45+
public DefaultServerCodecConfigurer clone() {
46+
return new DefaultServerCodecConfigurer(this);
47+
}
48+
49+
@Override
50+
protected BaseDefaultCodecs cloneDefaultCodecs() {
51+
return new ServerDefaultCodecsImpl((ServerDefaultCodecsImpl) defaultCodecs());
52+
}
3853
}

0 commit comments

Comments
 (0)