Skip to content

Commit 92c0c36

Browse files
author
adriancole
committed
provide encoder/decoder in a module that addsTo = Feign.Defaults.class
1 parent 0d947b8 commit 92c0c36

9 files changed

Lines changed: 122 additions & 102 deletions

File tree

CHANGES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* Decoders/Encoders are now more flexible, having access to the Response/RequestTemplate respectively.
77
* Added Feign.Builder to simplify client customizations without using Dagger.
88
* Gson type adapters can be registered as Dagger set bindings.
9-
* New Defaults.WithoutCodec to avoid binding collisions.
9+
* `Feign.create(...)` now requires specifying an encoder and decoder.
1010

1111
### Version 4.4.1
1212
* Fix NullPointerException on calling equals and hashCode.

core/src/main/java/feign/Feign.java

Lines changed: 31 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
package feign;
1717

1818

19-
import dagger.Module;
2019
import dagger.ObjectGraph;
2120
import dagger.Provides;
2221
import feign.Logger.NoOpLogger;
@@ -86,61 +85,43 @@ public static ObjectGraph createObjectGraph(Object... modules) {
8685
}
8786

8887
@SuppressWarnings("rawtypes")
89-
@dagger.Module(complete = false, injects = {Feign.class, Builder.class},
90-
includes = {Defaults.WithoutCodec.class, Defaults.Codec.class}, library = true)
88+
// incomplete as missing Encoder/Decoder
89+
@dagger.Module(injects = {Feign.class, Builder.class}, complete = false, includes = ReflectiveFeign.Module.class)
9190
public static class Defaults {
91+
@Provides Contract contract() {
92+
return new Contract.Default();
93+
}
9294

93-
@dagger.Module(includes = {Defaults.Client.class}, library = true)
94-
public static class WithoutCodec {
95-
@Provides Contract contract() {
96-
return new Contract.Default();
97-
}
98-
99-
@Provides Logger.Level logLevel() {
100-
return Logger.Level.NONE;
101-
}
102-
103-
@Provides Logger noOp() {
104-
return new NoOpLogger();
105-
}
106-
107-
@Provides Retryer retryer() {
108-
return new Retryer.Default();
109-
}
95+
@Provides Logger.Level logLevel() {
96+
return Logger.Level.NONE;
97+
}
11098

111-
@Provides ErrorDecoder errorDecoder() {
112-
return new ErrorDecoder.Default();
113-
}
99+
@Provides Logger noOp() {
100+
return new NoOpLogger();
114101
}
115102

116-
@dagger.Module(library = true)
117-
public static class Client {
118-
@Provides SSLSocketFactory sslSocketFactory() {
119-
return SSLSocketFactory.class.cast(SSLSocketFactory.getDefault());
120-
}
103+
@Provides Retryer retryer() {
104+
return new Retryer.Default();
105+
}
121106

122-
@Provides HostnameVerifier hostnameVerifier() {
123-
return HttpsURLConnection.getDefaultHostnameVerifier();
124-
}
107+
@Provides ErrorDecoder errorDecoder() {
108+
return new ErrorDecoder.Default();
109+
}
125110

126-
@Provides feign.Client httpClient(feign.Client.Default client) {
127-
return client;
128-
}
111+
@Provides Options options() {
112+
return new Options();
113+
}
129114

130-
@Provides Options options() {
131-
return new Options();
132-
}
115+
@Provides SSLSocketFactory sslSocketFactory() {
116+
return SSLSocketFactory.class.cast(SSLSocketFactory.getDefault());
133117
}
134118

135-
@dagger.Module(library = true)
136-
public static class Codec {
137-
@Provides Encoder defaultEncoder() {
138-
return new Encoder.Default();
139-
}
119+
@Provides HostnameVerifier hostnameVerifier() {
120+
return HttpsURLConnection.getDefaultHostnameVerifier();
121+
}
140122

141-
@Provides Decoder defaultDecoder() {
142-
return new Decoder.Default();
143-
}
123+
@Provides feign.Client httpClient(feign.Client.Default client) {
124+
return client;
144125
}
145126
}
146127

@@ -176,24 +157,24 @@ public static String configKey(Method method) {
176157
}
177158

178159
private static List<Object> modulesForGraph(Object... modules) {
179-
List<Object> modulesForGraph = new ArrayList<Object>(3);
160+
List<Object> modulesForGraph = new ArrayList<Object>(2);
180161
modulesForGraph.add(new Defaults());
181-
modulesForGraph.add(new ReflectiveFeign.Module());
182162
if (modules != null)
183163
for (Object module : modules)
184164
modulesForGraph.add(module);
185165
return modulesForGraph;
186166
}
187167

168+
@dagger.Module(injects = Feign.class, includes = ReflectiveFeign.Module.class)
188169
public static class Builder {
189170
private final Set<RequestInterceptor> requestInterceptors = new LinkedHashSet<RequestInterceptor>();
190171
@Inject Logger.Level logLevel;
191172
@Inject Contract contract;
192173
@Inject Client client;
193174
@Inject Retryer retryer;
194175
@Inject Logger logger;
195-
@Inject Encoder encoder;
196-
@Inject Decoder decoder;
176+
Encoder encoder = new Encoder.Default();
177+
Decoder decoder = new Decoder.Default();
197178
@Inject ErrorDecoder errorDecoder;
198179
@Inject Options options;
199180

@@ -270,35 +251,7 @@ public <T> T target(Class<T> apiType, String url) {
270251
}
271252

272253
public <T> T target(Target<T> target) {
273-
BuilderModule module = new BuilderModule(this);
274-
return create(module).newInstance(target);
275-
}
276-
}
277-
278-
@Module(library = true, overrides = true, addsTo = Defaults.class)
279-
static class BuilderModule {
280-
private final Logger.Level logLevel;
281-
private final Contract contract;
282-
private final Client client;
283-
private final Retryer retryer;
284-
private final Logger logger;
285-
private final Encoder encoder;
286-
private final Decoder decoder;
287-
private final ErrorDecoder errorDecoder;
288-
private final Options options;
289-
private final Set<RequestInterceptor> requestInterceptors;
290-
291-
BuilderModule(Builder builder) {
292-
this.logLevel = builder.logLevel;
293-
this.contract = builder.contract;
294-
this.client = builder.client;
295-
this.retryer = builder.retryer;
296-
this.logger = builder.logger;
297-
this.encoder = builder.encoder;
298-
this.decoder = builder.decoder;
299-
this.errorDecoder = builder.errorDecoder;
300-
this.options = builder.options;
301-
this.requestInterceptors = builder.requestInterceptors;
254+
return ObjectGraph.create(this).get(Feign.class).newInstance(target);
302255
}
303256

304257
@Provides Logger.Level logLevel() {

core/src/test/java/feign/FeignTest.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,12 @@ void login(
7373

7474
@RequestLine("GET /?1={1}&2={2}") Response queryParams(@Named("1") String one, @Named("2") Iterable<String> twos);
7575

76-
@dagger.Module(overrides = true, library = true)
76+
@dagger.Module(injects = Feign.class, addsTo = Feign.Defaults.class)
7777
static class Module {
78+
@Provides Decoder defaultDecoder() {
79+
return new Decoder.Default();
80+
}
81+
7882
@Provides Encoder defaultEncoder() {
7983
return new Encoder() {
8084
@Override public void encode(Object object, RequestTemplate template) {
@@ -400,7 +404,7 @@ public void doesntRetryAfterResponseIsSent() throws IOException, InterruptedExce
400404
}
401405
}
402406

403-
@Module(injects = Client.Default.class, overrides = true, addsTo = Feign.Defaults.class)
407+
@Module(overrides = true, includes = TestInterface.Module.class)
404408
static class TrustSSLSockets {
405409
@Provides SSLSocketFactory trustingSSLSocketFactory() {
406410
return TrustingSSLSocketFactory.get();
@@ -415,14 +419,14 @@ static class TrustSSLSockets {
415419

416420
try {
417421
TestInterface api = Feign.create(TestInterface.class, "https://localhost:" + server.getPort(),
418-
new TestInterface.Module(), new TrustSSLSockets());
422+
new TrustSSLSockets());
419423
api.post();
420424
} finally {
421425
server.shutdown();
422426
}
423427
}
424428

425-
@Module(injects = Client.Default.class, overrides = true, addsTo = Feign.Defaults.class)
429+
@Module(overrides = true, includes = TrustSSLSockets.class)
426430
static class DisableHostnameVerification {
427431
@Provides HostnameVerifier acceptAllHostnameVerifier() {
428432
return new AcceptAllHostnameVerifier();
@@ -437,7 +441,7 @@ static class DisableHostnameVerification {
437441

438442
try {
439443
TestInterface api = Feign.create(TestInterface.class, "https://localhost:" + server.getPort(),
440-
new TestInterface.Module(), new TrustSSLSockets(), new DisableHostnameVerification());
444+
new DisableHostnameVerification());
441445
api.post();
442446
} finally {
443447
server.shutdown();
@@ -465,7 +469,7 @@ static class DisableHostnameVerification {
465469
TestInterface i1 = Feign.create(TestInterface.class, "http://localhost:8080", new TestInterface.Module());
466470
TestInterface i2 = Feign.create(TestInterface.class, "http://localhost:8080", new TestInterface.Module());
467471
TestInterface i3 = Feign.create(TestInterface.class, "http://localhost:8888", new TestInterface.Module());
468-
OtherTestInterface i4 = Feign.create(OtherTestInterface.class, "http://localhost:8080");
472+
OtherTestInterface i4 = Feign.create(OtherTestInterface.class, "http://localhost:8080", new TestInterface.Module());
469473

470474
assertTrue(i1.equals(i1));
471475
assertTrue(i1.equals(i2));

core/src/test/java/feign/LoggerTest.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.google.mockwebserver.MockResponse;
2020
import com.google.mockwebserver.MockWebServer;
2121
import dagger.Provides;
22+
import feign.codec.Decoder;
2223
import feign.codec.Encoder;
2324
import org.testng.annotations.BeforeMethod;
2425
import org.testng.annotations.DataProvider;
@@ -122,7 +123,7 @@ public void levelEmits(final Logger.Level logLevel, List<String> expectedMessage
122123
}
123124
}
124125

125-
static @dagger.Module(overrides = true, library = true) class DefaultModule {
126+
static @dagger.Module(injects = Feign.class, overrides = true, addsTo = Feign.Defaults.class) class DefaultModule {
126127
final Logger logger;
127128
final Logger.Level logLevel;
128129

@@ -131,12 +132,12 @@ public void levelEmits(final Logger.Level logLevel, List<String> expectedMessage
131132
this.logLevel = logLevel;
132133
}
133134

135+
@Provides Decoder defaultDecoder() {
136+
return new Decoder.Default();
137+
}
138+
134139
@Provides Encoder defaultEncoder() {
135-
return new Encoder() {
136-
@Override public void encode(Object object, RequestTemplate template) {
137-
template.body(object.toString());
138-
}
139-
};
140+
return new Encoder.Default();
140141
}
141142

142143
@Provides @Singleton Logger logger() {

gson/src/main/java/feign/gson/GsonModule.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.google.gson.stream.JsonReader;
2727
import com.google.gson.stream.JsonWriter;
2828
import dagger.Provides;
29+
import feign.Feign;
2930
import feign.RequestTemplate;
3031
import feign.Response;
3132
import feign.codec.Decoder;
@@ -79,7 +80,7 @@
7980
* }
8081
* </pre>
8182
*/
82-
@dagger.Module(library = true)
83+
@dagger.Module(injects = Feign.class, addsTo = Feign.Defaults.class)
8384
public final class GsonModule {
8485

8586
@Provides Encoder encoder(GsonCodec codec) {

gson/src/test/java/feign/gson/GsonModuleTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242

4343
@Test
4444
public class GsonModuleTest {
45-
@Module(includes = GsonModule.class, library = true, injects = EncoderAndDecoderBindings.class)
45+
@Module(includes = GsonModule.class, injects = EncoderAndDecoderBindings.class)
4646
static class EncoderAndDecoderBindings {
4747
@Inject Encoder encoder;
4848
@Inject Decoder decoder;
@@ -56,7 +56,7 @@ static class EncoderAndDecoderBindings {
5656
assertEquals(bindings.decoder.getClass(), GsonModule.GsonCodec.class);
5757
}
5858

59-
@Module(includes = GsonModule.class, library = true, injects = EncoderBindings.class)
59+
@Module(includes = GsonModule.class, injects = EncoderBindings.class)
6060
static class EncoderBindings {
6161
@Inject Encoder encoder;
6262
}
@@ -115,7 +115,7 @@ static class Zone extends LinkedHashMap<String, Object> {
115115
private static final long serialVersionUID = 1L;
116116
}
117117

118-
@Module(includes = GsonModule.class, library = true, injects = DecoderBindings.class)
118+
@Module(includes = GsonModule.class, injects = DecoderBindings.class)
119119
static class DecoderBindings {
120120
@Inject Decoder decoder;
121121
}
@@ -144,7 +144,7 @@ static class DecoderBindings {
144144
+ " }\n"//
145145
+ "]\n";
146146

147-
@Module(includes = GsonModule.class, library = true, injects = CustomTypeAdapter.class)
147+
@Module(includes = GsonModule.class, injects = CustomTypeAdapter.class)
148148
static class CustomTypeAdapter {
149149
@Provides(type = Provides.Type.SET) TypeAdapter upperZone() {
150150
return new TypeAdapter<Zone>() {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2013 Netflix, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package feign.gson.examples;
17+
18+
import feign.Feign;
19+
import feign.RequestLine;
20+
import feign.gson.GsonModule;
21+
22+
import javax.inject.Named;
23+
import java.util.List;
24+
25+
/**
26+
* adapted from {@code com.example.retrofit.GitHubClient}
27+
*/
28+
public class GitHubExample {
29+
30+
interface GitHub {
31+
@RequestLine("GET /repos/{owner}/{repo}/contributors")
32+
List<Contributor> contributors(@Named("owner") String owner, @Named("repo") String repo);
33+
}
34+
35+
static class Contributor {
36+
String login;
37+
int contributions;
38+
}
39+
40+
public static void main(String... args) throws InterruptedException {
41+
GitHub github = Feign.create(GitHub.class, "https://api.github.com", new GsonModule());
42+
43+
System.out.println("Let's fetch and print a list of the contributors to this library.");
44+
List<Contributor> contributors = github.contributors("netflix", "feign");
45+
for (Contributor contributor : contributors) {
46+
System.out.println(contributor.login + " (" + contributor.contributions + ")");
47+
}
48+
}
49+
}

ribbon/src/test/java/feign/ribbon/LoadBalancingTargetTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public void loadBalancingDefaultPolicyRoundRobin() throws IOException, Interrupt
5151

5252
try {
5353
LoadBalancingTarget<TestInterface> target = LoadBalancingTarget.create(TestInterface.class, "http://" + name);
54-
TestInterface api = Feign.create(target);
54+
TestInterface api = Feign.builder().target(target);
5555

5656
api.post();
5757
api.post();

0 commit comments

Comments
 (0)