Skip to content

Commit

Permalink
Extract AsynchronousMethodHandler from SynchronousMethodHandler
Browse files Browse the repository at this point in the history
  • Loading branch information
wplong11 committed Sep 19, 2022
1 parent 0efefdd commit 4845d15
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 41 deletions.
12 changes: 6 additions & 6 deletions core/src/main/java/feign/AsyncFeign.java
Original file line number Diff line number Diff line change
Expand Up @@ -206,15 +206,15 @@ public AsyncFeign<C> build() {
AsyncResponseHandler.class,
capabilities);

final MethodHandler.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 {

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);
}
}
}
11 changes: 1 addition & 10 deletions core/src/main/java/feign/Feign.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,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 @@ -190,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 @@ -212,7 +203,7 @@ public Feign build() {
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
30 changes: 5 additions & 25 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 @@ -186,12 +168,11 @@ static class Factory implements MethodHandler.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 implements MethodHandler.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);
}
}
}

0 comments on commit 4845d15

Please sign in to comment.