Skip to content

Commit a712826

Browse files
committed
Fix NullPointerException when equals or hashCode are called on proxy instance
They look something like this: java.lang.NullPointerException at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:88) at feign.$Proxy16.equals(Unknown Source) In my particular instance, I had a proxy created by Feign registered in a Spring application context, and it resulted in a NullPointerException on application shutdown.
1 parent 6b5c77a commit a712826

3 files changed

Lines changed: 41 additions & 2 deletions

File tree

CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
### Version 4.4.1
2+
* Fix NullPointerException on calling equals and hashCode.
3+
14
### Version 4.4
25
* Support overriding default HostnameVerifier.
36
* Support GZIP content encoding for request bodies.

core/src/main/java/feign/ReflectiveFeign.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,17 @@ static class FeignInvocationHandler implements InvocationHandler {
8585
}
8686

8787
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
88+
if ("equals".equals(method.getName())) {
89+
try {
90+
Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
91+
return equals(otherHandler);
92+
} catch (IllegalArgumentException e) {
93+
return false;
94+
}
95+
}
96+
if ("hashCode".equals(method.getName())) {
97+
return hashCode();
98+
}
8899
return methodToHandler.get(method).invoke(args);
89100
}
90101

@@ -93,10 +104,15 @@ static class FeignInvocationHandler implements InvocationHandler {
93104
}
94105

95106
@Override public boolean equals(Object obj) {
96-
if (this == obj)
107+
if (obj == null) {
108+
return false;
109+
}
110+
if (this == obj) {
97111
return true;
98-
if (FeignInvocationHandler.class != obj.getClass())
112+
}
113+
if (FeignInvocationHandler.class != obj.getClass()) {
99114
return false;
115+
}
100116
FeignInvocationHandler that = FeignInvocationHandler.class.cast(obj);
101117
return this.target.equals(that.target);
102118
}

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import static dagger.Provides.Type.SET;
5252
import static feign.Util.UTF_8;
5353
import static org.testng.Assert.assertEquals;
54+
import static org.testng.Assert.assertFalse;
5455
import static org.testng.Assert.assertNull;
5556
import static org.testng.Assert.assertTrue;
5657
import static org.testng.Assert.fail;
@@ -135,6 +136,10 @@ public void iterableQueryParams() throws IOException, InterruptedException {
135136
}
136137
}
137138

139+
interface OtherTestInterface {
140+
@RequestLine("POST /") String post();
141+
}
142+
138143
@Test
139144
public void observableVoid() throws IOException, InterruptedException {
140145
final MockWebServer server = new MockWebServer();
@@ -629,4 +634,19 @@ static class DisableHostnameVerification {
629634
server.shutdown();
630635
}
631636
}
637+
638+
@Test public void equalsAndHashCodeWork() {
639+
TestInterface i1 = Feign.create(TestInterface.class, "http://localhost:8080", new TestInterface.Module());
640+
TestInterface i2 = Feign.create(TestInterface.class, "http://localhost:8080", new TestInterface.Module());
641+
TestInterface i3 = Feign.create(TestInterface.class, "http://localhost:8888", new TestInterface.Module());
642+
OtherTestInterface i4 = Feign.create(OtherTestInterface.class, "http://localhost:8080");
643+
644+
assertTrue(i1.equals(i1));
645+
assertTrue(i1.equals(i2));
646+
assertFalse(i1.equals(i3));
647+
assertFalse(i1.equals(i4));
648+
649+
assertEquals(i1.hashCode(), i1.hashCode());
650+
assertEquals(i1.hashCode(), i2.hashCode());
651+
}
632652
}

0 commit comments

Comments
 (0)