Skip to content

Commit bdc278d

Browse files
wplong11velo
andauthored
Support kotlin coroutines (#1706)
* Support kotlin coroutines Resolves: #1565 Inspired by PlaytikaOSS/feign-reactive#486 ## TODO - [ ] Separate Kotlin support module - [ ] Enhance test case - [ ] Refactoring - [ ] Clean up pom.xml * Apply optional dependency to kotlin support related dependency * Seperate Kotlin support module * Remove unused code from ClassUtils.java * Remove unused code from ClassUtils.java * Refactor KotlinDetector * Move ClassUtils location into KotlinDetector * Move KotlinDetector location * Format code * First attempt to move kotlin work to it's own isolated module * Coroutine Feign using AyncFeign * Coroutine Feign using AyncFeign * Refactor suspending function detect logic - Remove KotlinDetector.java - Add Method.isSuspend extension function * Cleanup CoroutineFeignTest test code format * Fix suspend function contract parsing error when using http body * Rename test names to be meaningful * Add Github Example With Coroutine - Copy of GithubExample * Remove unnecessary dependency https://github.com/OpenFeign/feign/pull/1706/files#r965389041 Co-authored-by: Marvin Froeder <[email protected]> Co-authored-by: Marvin Froeder <[email protected]>
1 parent 1746ec3 commit bdc278d

File tree

18 files changed

+1209
-30
lines changed

18 files changed

+1209
-30
lines changed

core/pom.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
<dependency>
5555
<groupId>com.fasterxml.jackson.core</groupId>
5656
<artifactId>jackson-databind</artifactId>
57-
<version>${jackson.version}</version>
5857
<scope>test</scope>
5958
</dependency>
6059

core/src/main/java/feign/AsyncInvocation.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,45 +20,45 @@
2020
* A specific invocation of an APU
2121
*/
2222
@Experimental
23-
class AsyncInvocation<C> {
23+
public class AsyncInvocation<C> {
2424

2525
private final C context;
2626
private final MethodInfo methodInfo;
2727
private final long startNanos;
2828
private CompletableFuture<Response> responseFuture;
2929

30-
AsyncInvocation(C context, MethodInfo methodInfo) {
30+
public AsyncInvocation(C context, MethodInfo methodInfo) {
3131
super();
3232
this.context = context;
3333
this.methodInfo = methodInfo;
3434
this.startNanos = System.nanoTime();
3535
}
3636

37-
C context() {
37+
public C context() {
3838
return context;
3939
}
4040

41-
String configKey() {
41+
public String configKey() {
4242
return methodInfo.configKey();
4343
}
4444

45-
long startNanos() {
45+
public long startNanos() {
4646
return startNanos;
4747
}
4848

49-
Type underlyingType() {
49+
public Type underlyingType() {
5050
return methodInfo.underlyingReturnType();
5151
}
5252

53-
boolean isAsyncReturnType() {
53+
public boolean isAsyncReturnType() {
5454
return methodInfo.isAsyncReturnType();
5555
}
5656

57-
void setResponseFuture(CompletableFuture<Response> responseFuture) {
57+
public void setResponseFuture(CompletableFuture<Response> responseFuture) {
5858
this.responseFuture = responseFuture;
5959
}
6060

61-
CompletableFuture<Response> responseFuture() {
61+
public CompletableFuture<Response> responseFuture() {
6262
return responseFuture;
6363
}
6464
}

core/src/main/java/feign/AsyncResponseHandler.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
* handling
2828
*/
2929
@Experimental
30-
class AsyncResponseHandler {
30+
public class AsyncResponseHandler {
3131

3232
private static final long MAX_RESPONSE_BUFFER_SIZE = 8192L;
3333

@@ -41,8 +41,9 @@ class AsyncResponseHandler {
4141

4242
private final ResponseInterceptor responseInterceptor;
4343

44-
AsyncResponseHandler(Level logLevel, Logger logger, Decoder decoder, ErrorDecoder errorDecoder,
45-
boolean dismiss404, boolean closeAfterDecode, ResponseInterceptor responseInterceptor) {
44+
public AsyncResponseHandler(Level logLevel, Logger logger, Decoder decoder,
45+
ErrorDecoder errorDecoder, boolean dismiss404, boolean closeAfterDecode,
46+
ResponseInterceptor responseInterceptor) {
4647
super();
4748
this.logLevel = logLevel;
4849
this.logger = logger;
@@ -54,14 +55,15 @@ class AsyncResponseHandler {
5455
}
5556

5657
boolean isVoidType(Type returnType) {
57-
return Void.class == returnType || void.class == returnType;
58+
return Void.class == returnType || void.class == returnType
59+
|| returnType.getTypeName().equals("kotlin.Unit");
5860
}
5961

60-
void handleResponse(CompletableFuture<Object> resultFuture,
61-
String configKey,
62-
Response response,
63-
Type returnType,
64-
long elapsedTime) {
62+
public void handleResponse(CompletableFuture<Object> resultFuture,
63+
String configKey,
64+
Response response,
65+
Type returnType,
66+
long elapsedTime) {
6567
// copied fairly liberally from SynchronousMethodHandler
6668
boolean shouldClose = true;
6769

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method me
130130
data.ignoreParamater(i);
131131
}
132132

133+
if ("kotlin.coroutines.Continuation".equals(parameterTypes[i].getName())) {
134+
data.ignoreParamater(i);
135+
}
136+
133137
if (parameterTypes[i] == URI.class) {
134138
data.urlIndex(i);
135139
} else if (!isHttpAnnotation

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ public Builder addCapability(Capability capability) {
192192
/**
193193
* Internal - used to indicate that the decoder should be immediately called
194194
*/
195-
Builder forceDecoding() {
195+
public /* FIXME should not be public */ Builder forceDecoding() {
196196
this.forceDecoding = true;
197197
return this;
198198
}

core/src/main/java/feign/Logger.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,16 @@
1313
*/
1414
package feign;
1515

16+
import static feign.Util.UTF_8;
17+
import static feign.Util.decodeOrDefault;
18+
import static feign.Util.valuesOrEmpty;
19+
import static java.util.Objects.nonNull;
1620
import java.io.IOException;
1721
import java.io.PrintWriter;
1822
import java.io.StringWriter;
1923
import java.util.logging.FileHandler;
2024
import java.util.logging.LogRecord;
2125
import java.util.logging.SimpleFormatter;
22-
import static feign.Util.*;
23-
import static java.util.Objects.nonNull;
2426

2527
/**
2628
* Simple logging abstraction for debug messages. Adapted from {@code retrofit.RestAdapter.Log}.
@@ -137,10 +139,10 @@ protected Response logAndRebufferResponse(String configKey,
137139
return response;
138140
}
139141

140-
protected IOException logIOException(String configKey,
141-
Level logLevel,
142-
IOException ioe,
143-
long elapsedTime) {
142+
public IOException logIOException(String configKey,
143+
Level logLevel,
144+
IOException ioe,
145+
long elapsedTime) {
144146
log(configKey, "<--- ERROR %s: %s (%sms)", ioe.getClass().getSimpleName(), ioe.getMessage(),
145147
elapsedTime);
146148
if (logLevel.ordinal() >= Level.FULL.ordinal()) {
@@ -217,7 +219,7 @@ public JavaLogger() {
217219

218220
/**
219221
* Constructor for JavaLogger class
220-
*
222+
*
221223
* @param loggerName a name for the logger. This should be a dot-separated name and should
222224
* normally be based on the package name or class name of the subsystem, such as java.net
223225
* or javax.swing

core/src/main/java/feign/MethodInfo.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919
import java.util.concurrent.CompletableFuture;
2020

2121
@Experimental
22-
class MethodInfo {
22+
public class MethodInfo {
2323
private final String configKey;
2424
private final Type underlyingReturnType;
2525
private final boolean asyncReturnType;
2626

27-
MethodInfo(String configKey, Type underlyingReturnType, boolean asyncReturnType) {
27+
protected MethodInfo(String configKey, Type underlyingReturnType, boolean asyncReturnType) {
2828
this.configKey = configKey;
2929
this.underlyingReturnType = underlyingReturnType;
3030
this.asyncReturnType = asyncReturnType;

core/src/main/java/feign/Types.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ static Type getSupertype(Type context, Class<?> contextRawType, Class<?> superty
199199
getGenericSupertype(context, contextRawType, supertype));
200200
}
201201

202-
static Type resolve(Type context, Class<?> contextRawType, Type toResolve) {
202+
public static Type resolve(Type context, Class<?> contextRawType, Type toResolve) {
203203
// This implementation is made a little more complicated in an attempt to avoid object-creation.
204204
while (true) {
205205
if (toResolve instanceof TypeVariable) {
@@ -350,14 +350,17 @@ static final class ParameterizedTypeImpl implements ParameterizedType {
350350
}
351351
}
352352

353+
@Override
353354
public Type[] getActualTypeArguments() {
354355
return typeArguments.clone();
355356
}
356357

358+
@Override
357359
public Type getRawType() {
358360
return rawType;
359361
}
360362

363+
@Override
361364
public Type getOwnerType() {
362365
return ownerType;
363366
}
@@ -395,6 +398,7 @@ private static final class GenericArrayTypeImpl implements GenericArrayType {
395398
this.componentType = componentType;
396399
}
397400

401+
@Override
398402
public Type getGenericComponentType() {
399403
return componentType;
400404
}
@@ -454,10 +458,12 @@ static final class WildcardTypeImpl implements WildcardType {
454458
}
455459
}
456460

461+
@Override
457462
public Type[] getUpperBounds() {
458463
return new Type[] {upperBound};
459464
}
460465

466+
@Override
461467
public Type[] getLowerBounds() {
462468
return lowerBound != null ? new Type[] {lowerBound} : EMPTY_TYPE_ARRAY;
463469
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
GitHub Example With Coroutine
2+
===================
3+
4+
This is an example of a simple json client.
5+
6+
=== Building example with Gradle
7+
Install and run `gradle` to produce `build/github`
8+
9+
=== Building example with Maven
10+
Install and run `mvn` to produce `target/github`

0 commit comments

Comments
 (0)