Skip to content

Commit b294a4c

Browse files
authored
Allow to ignore methods on provided interface (OpenFeign#2218)
* Allow to ignore methods on provided interface * Throw an UnsupportedOperationException if an ignored method was called
1 parent e37d7d5 commit b294a4c

6 files changed

Lines changed: 68 additions & 14 deletions

File tree

core/src/main/java/feign/Contract.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ public List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType) {
6262
for (final Method method : targetType.getMethods()) {
6363
if (method.getDeclaringClass() == Object.class
6464
|| (method.getModifiers() & Modifier.STATIC) != 0
65-
|| Util.isDefault(method)) {
65+
|| Util.isDefault(method)
66+
|| method.isAnnotationPresent(FeignIgnore.class)) {
6667
continue;
6768
}
6869
final MethodMetadata metadata = parseAndValidateMetadata(targetType, method);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2012-2023 The Feign Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License
10+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
* or implied. See the License for the specific language governing permissions and limitations under
12+
* the License.
13+
*/
14+
package feign;
15+
16+
import static java.lang.annotation.ElementType.*;
17+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
18+
19+
import java.lang.annotation.Retention;
20+
21+
/** Indicates that method will be ignored */
22+
@Retention(RUNTIME)
23+
@java.lang.annotation.Target({METHOD})
24+
public @interface FeignIgnore {}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
9494
return hashCode();
9595
} else if ("toString".equals(method.getName())) {
9696
return toString();
97+
} else if (!dispatch.containsKey(method)) {
98+
throw new UnsupportedOperationException(
99+
String.format("Method \"%s\" should not be called", method.getName()));
97100
}
98101

99102
return dispatch.get(method).invoke(args);

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import okio.Buffer;
5353
import org.assertj.core.data.MapEntry;
5454
import org.assertj.core.util.Maps;
55+
import org.junit.Assert;
5556
import org.junit.Rule;
5657
import org.junit.Test;
5758
import org.junit.rules.ExpectedException;
@@ -1224,6 +1225,19 @@ public void responseInterceptorChainOrder() throws Exception {
12241225
assertEquals("ResponseInterceptor did not extract the response body", body, api.post());
12251226
}
12261227

1228+
@Test
1229+
public void testCallIgnoredMethod() throws Exception {
1230+
TestInterface api = new TestInterfaceBuilder().target("http://localhost:" + server.getPort());
1231+
1232+
try {
1233+
api.ignore();
1234+
Assert.fail("No exception thrown");
1235+
} catch (Exception e) {
1236+
assertThat(e.getClass()).isEqualTo(UnsupportedOperationException.class);
1237+
assertThat(e.getMessage()).isEqualTo("Method \"ignore\" should not be called");
1238+
}
1239+
}
1240+
12271241
interface TestInterface {
12281242

12291243
@RequestLine("POST /")
@@ -1331,6 +1345,9 @@ void queryMapPropertyInheritenceWithBeanMapEncoder(
13311345
@Headers("Custom: {complex}")
13321346
void supportComplexHttpHeaders(@Param("complex") String complex);
13331347

1348+
@FeignIgnore
1349+
String ignore();
1350+
13341351
class ClockToMillis implements Param.Expander {
13351352

13361353
@Override

reactive/src/main/java/feign/reactive/ReactiveInvocationHandler.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
4747
return hashCode();
4848
} else if ("toString".equals(method.getName())) {
4949
return toString();
50+
} else if (!dispatch.containsKey(method)) {
51+
throw new UnsupportedOperationException(
52+
String.format("Method \"%s\" should not be called", method.getName()));
5053
}
5154
return this.invoke(method, this.dispatch.get(method), args);
5255
}

reactive/src/test/java/feign/reactive/ReactiveFeignIntegrationTest.java

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,9 @@
2323
import static org.mockito.Mockito.verify;
2424
import static org.mockito.Mockito.when;
2525

26-
import feign.Client;
27-
import feign.Logger;
26+
import feign.*;
2827
import feign.Logger.Level;
29-
import feign.Param;
30-
import feign.QueryMap;
31-
import feign.QueryMapEncoder;
32-
import feign.Request;
3328
import feign.Request.Options;
34-
import feign.RequestInterceptor;
35-
import feign.RequestLine;
36-
import feign.RequestTemplate;
37-
import feign.Response;
38-
import feign.ResponseMapper;
39-
import feign.RetryableException;
40-
import feign.Retryer;
4129
import feign.codec.Decoder;
4230
import feign.codec.ErrorDecoder;
4331
import feign.jackson.JacksonDecoder;
@@ -53,6 +41,7 @@
5341
import javax.ws.rs.Path;
5442
import okhttp3.mockwebserver.MockResponse;
5543
import okhttp3.mockwebserver.MockWebServer;
44+
import org.junit.Assert;
5645
import org.junit.Rule;
5746
import org.junit.Test;
5847
import org.junit.rules.ExpectedException;
@@ -72,6 +61,20 @@ private String getServerUrl() {
7261
return "http://localhost:" + this.webServer.getPort();
7362
}
7463

64+
@Test
65+
public void testCallIgnoredMethod() throws Exception {
66+
TestReactorService service =
67+
ReactorFeign.builder().target(TestReactorService.class, this.getServerUrl());
68+
69+
try {
70+
service.ignore().subscribe();
71+
Assert.fail("No exception thrown");
72+
} catch (Exception e) {
73+
assertThat(e.getClass()).isEqualTo(UnsupportedOperationException.class);
74+
assertThat(e.getMessage()).isEqualTo("Method \"ignore\" should not be called");
75+
}
76+
}
77+
7578
@Test
7679
public void testDefaultMethodsNotProxied() {
7780
TestReactorService service =
@@ -323,6 +326,9 @@ interface TestReactorService {
323326

324327
@RequestLine("GET /users")
325328
Mono<List<User>> usersMono();
329+
330+
@FeignIgnore
331+
Mono<String> ignore();
326332
}
327333

328334
interface TestReactiveXService {

0 commit comments

Comments
 (0)