Skip to content

Commit aecd01a

Browse files
authored
Merge pull request #3449 from hutiefang76/codex/openfeign-2782-builder-clone
Fix fluent methods on cloned builders
2 parents 2a37c7e + 5a21b0f commit aecd01a

2 files changed

Lines changed: 48 additions & 31 deletions

File tree

core/src/main/java/feign/BaseBuilder.java

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@
4040

4141
public abstract class BaseBuilder<B extends BaseBuilder<B, T>, T> implements Cloneable {
4242

43-
private final B thisB;
44-
4543
protected List<RequestInterceptor> requestInterceptors = new ArrayList<>();
4644
protected List<ResponseInterceptor> responseInterceptors = new ArrayList<>();
4745
protected List<MethodInterceptor> methodInterceptors = new ArrayList<>();
@@ -64,43 +62,47 @@ public abstract class BaseBuilder<B extends BaseBuilder<B, T>, T> implements Clo
6462

6563
public BaseBuilder() {
6664
super();
67-
thisB = (B) this;
65+
}
66+
67+
@SuppressWarnings("unchecked")
68+
private B thisB() {
69+
return (B) this;
6870
}
6971

7072
public B logLevel(Logger.Level logLevel) {
7173
this.logLevel = logLevel;
72-
return thisB;
74+
return thisB();
7375
}
7476

7577
public B contract(Contract contract) {
7678
this.contract = contract;
77-
return thisB;
79+
return thisB();
7880
}
7981

8082
public B retryer(Retryer retryer) {
8183
this.retryer = retryer;
82-
return thisB;
84+
return thisB();
8385
}
8486

8587
public B logger(Logger logger) {
8688
this.logger = logger;
87-
return thisB;
89+
return thisB();
8890
}
8991

9092
public B encoder(Encoder encoder) {
9193
this.encoder = encoder;
92-
return thisB;
94+
return thisB();
9395
}
9496

9597
public B decoder(Decoder decoder) {
9698
this.decoder = decoder;
97-
return thisB;
99+
return thisB();
98100
}
99101

100102
public B codec(Codec codec) {
101103
this.encoder = codec.encoder();
102104
this.decoder = codec.decoder();
103-
return thisB;
105+
return thisB();
104106
}
105107

106108
/**
@@ -115,23 +117,23 @@ public B codec(Codec codec) {
115117
*/
116118
public B doNotCloseAfterDecode() {
117119
this.closeAfterDecode = false;
118-
return thisB;
120+
return thisB();
119121
}
120122

121123
public B decodeVoid() {
122124
this.decodeVoid = true;
123-
return thisB;
125+
return thisB();
124126
}
125127

126128
public B queryMapEncoder(QueryMapEncoder queryMapEncoder) {
127129
this.queryMapEncoder = queryMapEncoder;
128-
return thisB;
130+
return thisB();
129131
}
130132

131133
/** Allows to map the response before passing it to the decoder. */
132134
public B mapAndDecode(ResponseMapper mapper, Decoder decoder) {
133135
this.decoder = new ResponseMappingDecoder(mapper, decoder);
134-
return thisB;
136+
return thisB();
135137
}
136138

137139
/**
@@ -151,7 +153,7 @@ public B mapAndDecode(ResponseMapper mapper, Decoder decoder) {
151153
*/
152154
public B dismiss404() {
153155
this.dismiss404 = true;
154-
return thisB;
156+
return thisB();
155157
}
156158

157159
/**
@@ -173,23 +175,23 @@ public B dismiss404() {
173175
@Deprecated
174176
public B decode404() {
175177
this.dismiss404 = true;
176-
return thisB;
178+
return thisB();
177179
}
178180

179181
public B errorDecoder(ErrorDecoder errorDecoder) {
180182
this.errorDecoder = errorDecoder;
181-
return thisB;
183+
return thisB();
182184
}
183185

184186
public B options(Options options) {
185187
this.options = options;
186-
return thisB;
188+
return thisB();
187189
}
188190

189191
/** Adds a single request interceptor to the builder. */
190192
public B requestInterceptor(RequestInterceptor requestInterceptor) {
191193
this.requestInterceptors.add(requestInterceptor);
192-
return thisB;
194+
return thisB();
193195
}
194196

195197
/**
@@ -201,7 +203,7 @@ public B requestInterceptors(Iterable<RequestInterceptor> requestInterceptors) {
201203
for (RequestInterceptor requestInterceptor : requestInterceptors) {
202204
this.requestInterceptors.add(requestInterceptor);
203205
}
204-
return thisB;
206+
return thisB();
205207
}
206208

207209
/**
@@ -213,13 +215,13 @@ public B responseInterceptors(Iterable<ResponseInterceptor> responseInterceptors
213215
for (ResponseInterceptor responseInterceptor : responseInterceptors) {
214216
this.responseInterceptors.add(responseInterceptor);
215217
}
216-
return thisB;
218+
return thisB();
217219
}
218220

219221
/** Adds a single response interceptor to the builder. */
220222
public B responseInterceptor(ResponseInterceptor responseInterceptor) {
221223
this.responseInterceptors.add(responseInterceptor);
222-
return thisB;
224+
return thisB();
223225
}
224226

225227
/**
@@ -230,7 +232,7 @@ public B responseInterceptor(ResponseInterceptor responseInterceptor) {
230232
@Experimental
231233
public B methodInterceptor(MethodInterceptor methodInterceptor) {
232234
this.methodInterceptors.add(methodInterceptor);
233-
return thisB;
235+
return thisB();
234236
}
235237

236238
/** Sets the full set of method interceptors, overwriting any previously configured. */
@@ -240,33 +242,33 @@ public B methodInterceptors(Iterable<MethodInterceptor> methodInterceptors) {
240242
for (MethodInterceptor methodInterceptor : methodInterceptors) {
241243
this.methodInterceptors.add(methodInterceptor);
242244
}
243-
return thisB;
245+
return thisB();
244246
}
245247

246248
/** Allows you to override how reflective dispatch works inside of Feign. */
247249
public B invocationHandlerFactory(InvocationHandlerFactory invocationHandlerFactory) {
248250
this.invocationHandlerFactory = invocationHandlerFactory;
249-
return thisB;
251+
return thisB();
250252
}
251253

252254
public B exceptionPropagationPolicy(ExceptionPropagationPolicy propagationPolicy) {
253255
this.propagationPolicy = propagationPolicy;
254-
return thisB;
256+
return thisB();
255257
}
256258

257259
public B addCapability(Capability capability) {
258260
this.capabilities.add(capability);
259-
return thisB;
261+
return thisB();
260262
}
261263

262264
@SuppressWarnings("unchecked")
263265
B enrich() {
264266
if (capabilities.isEmpty()) {
265-
return thisB;
267+
return thisB();
266268
}
267269

268270
try {
269-
B clone = (B) thisB.clone();
271+
B clone = (B) thisB().clone();
270272

271273
getFieldsToEnrich()
272274
.forEach(
@@ -367,8 +369,6 @@ List<Field> getFieldsToEnrich() {
367369
.filter(field -> !field.isSynthetic())
368370
// and capabilities itself
369371
.filter(field -> !Objects.equals(field.getName(), "capabilities"))
370-
// and thisB helper field
371-
.filter(field -> !Objects.equals(field.getName(), "thisB"))
372372
// interceptor lists are enriched per-element then as a whole via custom types
373373
.filter(field -> !Objects.equals(field.getName(), "requestInterceptors"))
374374
.filter(field -> !Objects.equals(field.getName(), "responseInterceptors"))

core/src/test/java/feign/BaseBuilderTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import static org.junit.jupiter.api.Assertions.assertNotSame;
2020
import static org.mockito.Mockito.RETURNS_MOCKS;
2121

22+
import feign.codec.Decoder;
2223
import java.lang.reflect.Field;
2324
import java.util.List;
2425
import java.util.concurrent.atomic.AtomicInteger;
@@ -64,6 +65,22 @@ void checkEnrichTouchesAllBuilderFields()
6465
Feign.builder().requestInterceptor(_ -> {}).responseInterceptor((ic, c) -> c.next(ic)), 10);
6566
}
6667

68+
@Test
69+
void clonedBuilderFluentMethodsReturnClone() throws CloneNotSupportedException {
70+
CloneableFeignBuilder original = new CloneableFeignBuilder();
71+
Feign.Builder clone = original.copy();
72+
73+
assertThat(clone.requestInterceptor(_ -> {})).isSameAs(clone);
74+
assertThat(clone.decoder(Mockito.mock(Decoder.class))).isSameAs(clone);
75+
}
76+
77+
private static final class CloneableFeignBuilder extends Feign.Builder {
78+
79+
Feign.Builder copy() throws CloneNotSupportedException {
80+
return (Feign.Builder) super.clone();
81+
}
82+
}
83+
6784
@Test
6885
void capabilityCanProvideResponseInterceptorWhenNoneConfigured() {
6986
AtomicInteger enrichCalls = new AtomicInteger();

0 commit comments

Comments
 (0)