Skip to content

Commit f4a7892

Browse files
committed
FIX: interceptors disapearing on BaseBuilder#clone
1 parent 8fc3505 commit f4a7892

File tree

2 files changed

+133
-29
lines changed

2 files changed

+133
-29
lines changed

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

Lines changed: 44 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,6 @@
3232
import java.util.stream.Collectors;
3333

3434
public abstract class BaseBuilder<B extends BaseBuilder<B, T>, T> implements Cloneable {
35-
36-
private final B thisB;
37-
3835
protected final List<RequestInterceptor> requestInterceptors = new ArrayList<>();
3936
protected final List<ResponseInterceptor> responseInterceptors = new ArrayList<>();
4037
protected Logger.Level logLevel = Logger.Level.NONE;
@@ -56,37 +53,42 @@ public abstract class BaseBuilder<B extends BaseBuilder<B, T>, T> implements Clo
5653

5754
public BaseBuilder() {
5855
super();
59-
thisB = (B) this;
6056
}
6157

58+
@SuppressWarnings("unchecked")
6259
public B logLevel(Logger.Level logLevel) {
6360
this.logLevel = logLevel;
64-
return thisB;
61+
return (B) this;
6562
}
6663

64+
@SuppressWarnings("unchecked")
6765
public B contract(Contract contract) {
6866
this.contract = contract;
69-
return thisB;
67+
return (B) this;
7068
}
7169

70+
@SuppressWarnings("unchecked")
7271
public B retryer(Retryer retryer) {
7372
this.retryer = retryer;
74-
return thisB;
73+
return (B) this;
7574
}
7675

76+
@SuppressWarnings("unchecked")
7777
public B logger(Logger logger) {
7878
this.logger = logger;
79-
return thisB;
79+
return (B) this;
8080
}
8181

82+
@SuppressWarnings("unchecked")
8283
public B encoder(Encoder encoder) {
8384
this.encoder = encoder;
84-
return thisB;
85+
return (B) this;
8586
}
8687

88+
@SuppressWarnings("unchecked")
8789
public B decoder(Decoder decoder) {
8890
this.decoder = decoder;
89-
return thisB;
91+
return (B) this;
9092
}
9193

9294
/**
@@ -99,25 +101,29 @@ public B decoder(Decoder decoder) {
99101
*
100102
* @since 9.6
101103
*/
104+
@SuppressWarnings("unchecked")
102105
public B doNotCloseAfterDecode() {
103106
this.closeAfterDecode = false;
104-
return thisB;
107+
return (B) this;
105108
}
106109

110+
@SuppressWarnings("unchecked")
107111
public B decodeVoid() {
108112
this.decodeVoid = true;
109-
return thisB;
113+
return (B) this;
110114
}
111115

116+
@SuppressWarnings("unchecked")
112117
public B queryMapEncoder(QueryMapEncoder queryMapEncoder) {
113118
this.queryMapEncoder = queryMapEncoder;
114-
return thisB;
119+
return (B) this;
115120
}
116121

117122
/** Allows to map the response before passing it to the decoder. */
123+
@SuppressWarnings("unchecked")
118124
public B mapAndDecode(ResponseMapper mapper, Decoder decoder) {
119125
this.decoder = new ResponseMappingDecoder(mapper, decoder);
120-
return thisB;
126+
return (B) this;
121127
}
122128

123129
/**
@@ -135,9 +141,10 @@ public B mapAndDecode(ResponseMapper mapper, Decoder decoder) {
135141
*
136142
* @since 11.9
137143
*/
144+
@SuppressWarnings("unchecked")
138145
public B dismiss404() {
139146
this.dismiss404 = true;
140-
return thisB;
147+
return (B) this;
141148
}
142149

143150
/**
@@ -157,81 +164,91 @@ public B dismiss404() {
157164
* @deprecated use {@link #dismiss404()} instead.
158165
*/
159166
@Deprecated
167+
@SuppressWarnings("unchecked")
160168
public B decode404() {
161169
this.dismiss404 = true;
162-
return thisB;
170+
return (B) this;
163171
}
164172

173+
@SuppressWarnings("unchecked")
165174
public B errorDecoder(ErrorDecoder errorDecoder) {
166175
this.errorDecoder = errorDecoder;
167-
return thisB;
176+
return (B) this;
168177
}
169178

179+
@SuppressWarnings("unchecked")
170180
public B options(Options options) {
171181
this.options = options;
172-
return thisB;
182+
return (B) this;
173183
}
174184

175185
/** Adds a single request interceptor to the builder. */
186+
@SuppressWarnings("unchecked")
176187
public B requestInterceptor(RequestInterceptor requestInterceptor) {
177188
this.requestInterceptors.add(requestInterceptor);
178-
return thisB;
189+
return (B) this;
179190
}
180191

181192
/**
182193
* Sets the full set of request interceptors for the builder, overwriting any previous
183194
* interceptors.
184195
*/
196+
@SuppressWarnings("unchecked")
185197
public B requestInterceptors(Iterable<RequestInterceptor> requestInterceptors) {
186198
this.requestInterceptors.clear();
187199
for (RequestInterceptor requestInterceptor : requestInterceptors) {
188200
this.requestInterceptors.add(requestInterceptor);
189201
}
190-
return thisB;
202+
return (B) this;
191203
}
192204

193205
/**
194206
* Sets the full set of request interceptors for the builder, overwriting any previous
195207
* interceptors.
196208
*/
209+
@SuppressWarnings("unchecked")
197210
public B responseInterceptors(Iterable<ResponseInterceptor> responseInterceptors) {
198211
this.responseInterceptors.clear();
199212
for (ResponseInterceptor responseInterceptor : responseInterceptors) {
200213
this.responseInterceptors.add(responseInterceptor);
201214
}
202-
return thisB;
215+
return (B) this;
203216
}
204217

205218
/** Adds a single response interceptor to the builder. */
219+
@SuppressWarnings("unchecked")
206220
public B responseInterceptor(ResponseInterceptor responseInterceptor) {
207221
this.responseInterceptors.add(responseInterceptor);
208-
return thisB;
222+
return (B) this;
209223
}
210224

211225
/** Allows you to override how reflective dispatch works inside of Feign. */
226+
@SuppressWarnings("unchecked")
212227
public B invocationHandlerFactory(InvocationHandlerFactory invocationHandlerFactory) {
213228
this.invocationHandlerFactory = invocationHandlerFactory;
214-
return thisB;
229+
return (B) this;
215230
}
216231

232+
@SuppressWarnings("unchecked")
217233
public B exceptionPropagationPolicy(ExceptionPropagationPolicy propagationPolicy) {
218234
this.propagationPolicy = propagationPolicy;
219-
return thisB;
235+
return (B) this;
220236
}
221237

238+
@SuppressWarnings("unchecked")
222239
public B addCapability(Capability capability) {
223240
this.capabilities.add(capability);
224-
return thisB;
241+
return (B) this;
225242
}
226243

227244
@SuppressWarnings("unchecked")
228245
B enrich() {
229246
if (capabilities.isEmpty()) {
230-
return thisB;
247+
return (B) this;
231248
}
232249

233250
try {
234-
B clone = (B) thisB.clone();
251+
B clone = (B) this.clone();
235252

236253
getFieldsToEnrich()
237254
.forEach(
@@ -274,8 +291,6 @@ List<Field> getFieldsToEnrich() {
274291
.filter(field -> !field.isSynthetic())
275292
// and capabilities itself
276293
.filter(field -> !Objects.equals(field.getName(), "capabilities"))
277-
// and thisB helper field
278-
.filter(field -> !Objects.equals(field.getName(), "thisB"))
279294
// skip primitive types
280295
.filter(field -> !field.getType().isPrimitive())
281296
// skip enumerations

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

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,93 @@ void checkEnrichTouchesAllBuilderFields()
6767
.responseInterceptor((ic, c) -> c.next(ic)),
6868
12);
6969
}
70+
71+
@Test
72+
void checkCloneDontLooseInterceptors() {
73+
Feign.Builder originalBuilder =
74+
Feign.builder()
75+
.requestInterceptor(new FirstRequestInterceptor())
76+
.addCapability(
77+
new Capability() {
78+
@Override
79+
public RequestInterceptor enrich(RequestInterceptor requestInterceptor) {
80+
return new DecoratingRequestInterceptor(requestInterceptor);
81+
}
82+
});
83+
84+
// There is one interceptor FirstRequestInterceptor
85+
assertThat(originalBuilder.requestInterceptors)
86+
.isNotNull()
87+
.isNotEmpty()
88+
.hasSize(1)
89+
.first()
90+
.isInstanceOf(FirstRequestInterceptor.class);
91+
92+
Feign.Builder enrichedBuilder = originalBuilder.enrich();
93+
94+
// Original builder should have one interceptor FirstRequestInterceptor
95+
assertThat(originalBuilder.requestInterceptors)
96+
.isNotNull()
97+
.isNotEmpty()
98+
.hasSize(1)
99+
.first()
100+
.isInstanceOf(FirstRequestInterceptor.class);
101+
102+
// enrichedBuilder should have one interceptor DecoratingRequestInterceptor
103+
assertThat(enrichedBuilder.requestInterceptors)
104+
.isNotNull()
105+
.isNotEmpty()
106+
.hasSize(1)
107+
.first()
108+
.isInstanceOf(DecoratingRequestInterceptor.class);
109+
110+
Feign.Builder enrichedBuilderWithInterceptor =
111+
enrichedBuilder.requestInterceptor(new SecondRequestInterceptor());
112+
113+
// Original builder should have one interceptor FirstRequestInterceptor
114+
assertThat(originalBuilder.requestInterceptors)
115+
.isNotNull()
116+
.isNotEmpty()
117+
.hasSize(1)
118+
.first()
119+
.isInstanceOf(FirstRequestInterceptor.class);
120+
121+
// enrichedBuilder should have two interceptors
122+
assertThat(enrichedBuilder.requestInterceptors).isNotNull().isNotEmpty().hasSize(2);
123+
assertThat(enrichedBuilder.requestInterceptors.get(0))
124+
.isInstanceOf(DecoratingRequestInterceptor.class);
125+
assertThat(enrichedBuilder.requestInterceptors.get(1))
126+
.isInstanceOf(SecondRequestInterceptor.class);
127+
128+
// enrichedBuilderWithInterceptor should have two interceptors
129+
assertThat(enrichedBuilderWithInterceptor.requestInterceptors)
130+
.isNotNull()
131+
.isNotEmpty()
132+
.hasSize(2);
133+
assertThat(enrichedBuilderWithInterceptor.requestInterceptors.get(0))
134+
.isInstanceOf(DecoratingRequestInterceptor.class);
135+
assertThat(enrichedBuilderWithInterceptor.requestInterceptors.get(1))
136+
.isInstanceOf(SecondRequestInterceptor.class);
137+
}
138+
139+
static final class FirstRequestInterceptor implements RequestInterceptor {
140+
@Override
141+
public void apply(final RequestTemplate template) {}
142+
}
143+
144+
static final class SecondRequestInterceptor implements RequestInterceptor {
145+
@Override
146+
public void apply(final RequestTemplate template) {}
147+
}
148+
149+
static final class DecoratingRequestInterceptor implements RequestInterceptor {
150+
RequestInterceptor delegate;
151+
152+
DecoratingRequestInterceptor(RequestInterceptor delegate) {
153+
this.delegate = delegate;
154+
}
155+
156+
@Override
157+
public void apply(final RequestTemplate template) {}
158+
}
70159
}

0 commit comments

Comments
 (0)