Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions core/src/main/java/feign/AsyncFeign.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*/
package feign;

import feign.InvocationHandlerFactory.MethodHandler;
import feign.ReflectiveFeign.ParseHandlersByName;
import feign.Logger.Level;
import feign.Request.Options;
Expand Down Expand Up @@ -205,15 +206,15 @@ public AsyncFeign<C> build() {
AsyncResponseHandler.class,
capabilities);

final SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(stageExecution(activeContextHolder, client), retryer,
requestInterceptors,
responseInterceptor, logger, logLevel, dismiss404, closeAfterDecode,
propagationPolicy, true);
final MethodHandler.Factory methodHandlerFactory =
new AsynchronousMethodHandler.Factory(stageExecution(activeContextHolder, client),
retryer, requestInterceptors,
responseInterceptor, logger, logLevel,
propagationPolicy);
final ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder,
stageDecode(activeContextHolder, logger, logLevel, responseHandler), queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
errorDecoder, methodHandlerFactory);
final ReflectiveFeign feign =
new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
return new ReflectiveAsyncFeign<>(feign, defaultContextSupplier, activeContextHolder,
Expand Down
176 changes: 176 additions & 0 deletions core/src/main/java/feign/AsynchronousMethodHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/*
* Copyright 2012-2022 The Feign Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package feign;

import feign.InvocationHandlerFactory.MethodHandler;
import feign.Request.Options;
import feign.codec.Decoder;
import feign.codec.ErrorDecoder;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import static feign.ExceptionPropagationPolicy.UNWRAP;
import static feign.FeignException.errorExecuting;
import static feign.Util.checkNotNull;

final class AsynchronousMethodHandler implements MethodHandler {
Copy link
Collaborator Author

@wplong11 wplong11 Sep 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this PR, The AsynchronousMethodHandler is just copy and paste of the SynchronousMethodHandler. Then refactored in the PR #1757


private final MethodMetadata metadata;
private final Target<?> target;
private final Client client;
private final Retryer retryer;
private final List<RequestInterceptor> requestInterceptors;
private final ResponseInterceptor responseInterceptor;
private final Logger logger;
private final Logger.Level logLevel;
private final RequestTemplate.Factory buildTemplateFromArgs;
private final Options options;
private final ExceptionPropagationPolicy propagationPolicy;
private final Decoder decoder;


private AsynchronousMethodHandler(Target<?> target, Client client, Retryer retryer,
List<RequestInterceptor> requestInterceptors, ResponseInterceptor responseInterceptor,
Logger logger, Logger.Level logLevel, MethodMetadata metadata,
RequestTemplate.Factory buildTemplateFromArgs, Options options,
Decoder decoder, ExceptionPropagationPolicy propagationPolicy) {

this.target = checkNotNull(target, "target");
this.client = checkNotNull(client, "client for %s", target);
this.retryer = checkNotNull(retryer, "retryer for %s", target);
this.requestInterceptors =
checkNotNull(requestInterceptors, "requestInterceptors for %s", target);
this.logger = checkNotNull(logger, "logger for %s", target);
this.logLevel = checkNotNull(logLevel, "logLevel for %s", target);
this.metadata = checkNotNull(metadata, "metadata for %s", target);
this.buildTemplateFromArgs = checkNotNull(buildTemplateFromArgs, "metadata for %s", target);
this.options = checkNotNull(options, "options for %s", target);
this.propagationPolicy = propagationPolicy;
this.responseInterceptor = responseInterceptor;
this.decoder = decoder;
}

@Override
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Options options = findOptions(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
return executeAndDecode(template, options);
} catch (RetryableException e) {
try {
retryer.continueOrPropagate(e);
} catch (RetryableException th) {
Throwable cause = th.getCause();
if (propagationPolicy == UNWRAP && cause != null) {
throw cause;
} else {
throw th;
}
}
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}

Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
Request request = targetRequest(template);

if (logLevel != Logger.Level.NONE) {
logger.logRequest(metadata.configKey(), logLevel, request);
}

Response response;
long start = System.nanoTime();
try {
response = client.execute(request, options);
// ensure the request is set. TODO: remove in Feign 12
response = response.toBuilder()
.request(request)
.requestTemplate(template)
.build();
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
}
throw errorExecuting(request, e);
}
return responseInterceptor
.aroundDecode(new InvocationContext(decoder, metadata.returnType(), response));
}

private long elapsedTime(long start) {
return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
}

private Request targetRequest(RequestTemplate template) {
for (RequestInterceptor interceptor : requestInterceptors) {
interceptor.apply(template);
}
return target.apply(template);
}

private Options findOptions(Object[] argv) {
if (argv == null || argv.length == 0) {
return this.options;
}
return Stream.of(argv)
.filter(Options.class::isInstance)
.map(Options.class::cast)
.findFirst()
.orElse(this.options);
}

static class Factory implements MethodHandler.Factory {

private final Client client;
private final Retryer retryer;
private final List<RequestInterceptor> requestInterceptors;
private final ResponseInterceptor responseInterceptor;
private final Logger logger;
private final Logger.Level logLevel;
private final ExceptionPropagationPolicy propagationPolicy;

Factory(Client client, Retryer retryer, List<RequestInterceptor> requestInterceptors,
ResponseInterceptor responseInterceptor,
Logger logger, Logger.Level logLevel,
ExceptionPropagationPolicy propagationPolicy) {
this.client = checkNotNull(client, "client");
this.retryer = checkNotNull(retryer, "retryer");
this.requestInterceptors = checkNotNull(requestInterceptors, "requestInterceptors");
this.responseInterceptor = responseInterceptor;
this.logger = checkNotNull(logger, "logger");
this.logLevel = checkNotNull(logLevel, "logLevel");
this.propagationPolicy = propagationPolicy;
}

public MethodHandler create(Target<?> target,
MethodMetadata md,
RequestTemplate.Factory buildTemplateFromArgs,
Options options,
Decoder decoder,
ErrorDecoder errorDecoder) {
return new AsynchronousMethodHandler(target, client, retryer, requestInterceptors,
responseInterceptor, logger, logLevel, md, buildTemplateFromArgs, options, decoder,
propagationPolicy);
}
}
}
14 changes: 3 additions & 11 deletions core/src/main/java/feign/Feign.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.codec.ErrorDecoder;
import feign.InvocationHandlerFactory.MethodHandler;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
Expand Down Expand Up @@ -95,7 +96,6 @@ public static String configKey(Method method) {
public static class Builder extends BaseBuilder<Builder> {

private Client client = new Client.Default(null, null);
private boolean forceDecoding = false;

@Override
public Builder logLevel(Logger.Level logLevel) {
Expand Down Expand Up @@ -189,14 +189,6 @@ public Builder addCapability(Capability capability) {
return super.addCapability(capability);
}

/**
* Internal - used to indicate that the decoder should be immediately called
*/
public /* FIXME should not be public */ Builder forceDecoding() {
this.forceDecoding = true;
return this;
}

public <T> T target(Class<T> apiType, String url) {
return target(new HardCodedTarget<>(apiType, url));
}
Expand All @@ -208,10 +200,10 @@ public <T> T target(Target<T> target) {
public Feign build() {
super.enrich();

SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
MethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors,
responseInterceptor, logger, logLevel, dismiss404, closeAfterDecode,
propagationPolicy, forceDecoding);
propagationPolicy);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
Expand Down
11 changes: 11 additions & 0 deletions core/src/main/java/feign/InvocationHandlerFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
*/
package feign;

import feign.codec.Decoder;
import feign.codec.ErrorDecoder;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
Expand All @@ -31,6 +33,15 @@ public interface InvocationHandlerFactory {
interface MethodHandler {

Object invoke(Object[] argv) throws Throwable;

interface Factory {
MethodHandler create(Target<?> target,
MethodMetadata md,
RequestTemplate.Factory buildTemplateFromArgs,
Request.Options options,
Decoder decoder,
ErrorDecoder errorDecoder);
}
}

static final class Default implements InvocationHandlerFactory {
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/java/feign/ReflectiveFeign.java
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ static final class ParseHandlersByName {
private final Decoder decoder;
private final ErrorDecoder errorDecoder;
private final QueryMapEncoder queryMapEncoder;
private final SynchronousMethodHandler.Factory factory;
private final MethodHandler.Factory factory;

ParseHandlersByName(
Contract contract,
Expand All @@ -137,7 +137,7 @@ static final class ParseHandlersByName {
Decoder decoder,
QueryMapEncoder queryMapEncoder,
ErrorDecoder errorDecoder,
SynchronousMethodHandler.Factory factory) {
MethodHandler.Factory factory) {
this.contract = contract;
this.options = options;
this.factory = factory;
Expand Down
32 changes: 6 additions & 26 deletions core/src/main/java/feign/SynchronousMethodHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,6 @@ final class SynchronousMethodHandler implements MethodHandler {
private final RequestTemplate.Factory buildTemplateFromArgs;
private final Options options;
private final ExceptionPropagationPolicy propagationPolicy;

// only one of decoder and asyncResponseHandler will be non-null
private final Decoder decoder;
private final AsyncResponseHandler asyncResponseHandler;


Expand All @@ -53,8 +50,7 @@ private SynchronousMethodHandler(Target<?> target, Client client, Retryer retrye
Logger logger, Logger.Level logLevel, MethodMetadata metadata,
RequestTemplate.Factory buildTemplateFromArgs, Options options,
Decoder decoder, ErrorDecoder errorDecoder, boolean dismiss404,
boolean closeAfterDecode, ExceptionPropagationPolicy propagationPolicy,
boolean forceDecoding) {
boolean closeAfterDecode, ExceptionPropagationPolicy propagationPolicy) {

this.target = checkNotNull(target, "target");
this.client = checkNotNull(client, "client for %s", target);
Expand All @@ -68,17 +64,8 @@ private SynchronousMethodHandler(Target<?> target, Client client, Retryer retrye
this.options = checkNotNull(options, "options for %s", target);
this.propagationPolicy = propagationPolicy;
this.responseInterceptor = responseInterceptor;

if (forceDecoding) {
// internal only: usual handling will be short-circuited, and all responses will be passed to
// decoder directly!
this.decoder = decoder;
this.asyncResponseHandler = null;
} else {
this.decoder = null;
this.asyncResponseHandler = new AsyncResponseHandler(logLevel, logger, decoder, errorDecoder,
dismiss404, closeAfterDecode, responseInterceptor);
}
this.asyncResponseHandler = new AsyncResponseHandler(logLevel, logger, decoder, errorDecoder,
dismiss404, closeAfterDecode, responseInterceptor);
}

@Override
Expand Down Expand Up @@ -132,11 +119,6 @@ Object executeAndDecode(RequestTemplate template, Options options) throws Throwa
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);

if (decoder != null) {
return responseInterceptor
.aroundDecode(new InvocationContext(decoder, metadata.returnType(), response));
}

CompletableFuture<Object> resultFuture = new CompletableFuture<>();
asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
metadata.returnType(), elapsedTime);
Expand Down Expand Up @@ -175,7 +157,7 @@ Options findOptions(Object[] argv) {
.orElse(this.options);
}

static class Factory {
static class Factory implements MethodHandler.Factory {

private final Client client;
private final Retryer retryer;
Expand All @@ -186,12 +168,11 @@ static class Factory {
private final boolean dismiss404;
private final boolean closeAfterDecode;
private final ExceptionPropagationPolicy propagationPolicy;
private final boolean forceDecoding;

Factory(Client client, Retryer retryer, List<RequestInterceptor> requestInterceptors,
ResponseInterceptor responseInterceptor,
Logger logger, Logger.Level logLevel, boolean dismiss404, boolean closeAfterDecode,
ExceptionPropagationPolicy propagationPolicy, boolean forceDecoding) {
ExceptionPropagationPolicy propagationPolicy) {
this.client = checkNotNull(client, "client");
this.retryer = checkNotNull(retryer, "retryer");
this.requestInterceptors = checkNotNull(requestInterceptors, "requestInterceptors");
Expand All @@ -201,7 +182,6 @@ static class Factory {
this.dismiss404 = dismiss404;
this.closeAfterDecode = closeAfterDecode;
this.propagationPolicy = propagationPolicy;
this.forceDecoding = forceDecoding;
}

public MethodHandler create(Target<?> target,
Expand All @@ -212,7 +192,7 @@ public MethodHandler create(Target<?> target,
ErrorDecoder errorDecoder) {
return new SynchronousMethodHandler(target, client, retryer, requestInterceptors,
responseInterceptor, logger, logLevel, md, buildTemplateFromArgs, options, decoder,
errorDecoder, dismiss404, closeAfterDecode, propagationPolicy, forceDecoding);
errorDecoder, dismiss404, closeAfterDecode, propagationPolicy);
}
}
}