Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move responsibility for resolving RequestTemplateFactory #1850

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion core/src/main/java/feign/AsyncFeign.java
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,11 @@ public AsyncFeign<C> build() {
client, retryer, requestInterceptors,
responseHandler, logger, logLevel,
propagationPolicy, methodInfoResolver,
new RequestTemplateFactoryResolver(encoder, queryMapEncoder),
options, decoder, errorDecoder);
final ParseHandlersByName<C> handlersByName =
new ParseHandlersByName<>(contract, encoder, queryMapEncoder, methodHandlerFactory);
new ParseHandlersByName<>(contract,
methodHandlerFactory);
final ReflectiveFeign<C> feign =
new ReflectiveFeign<>(handlersByName, invocationHandlerFactory, defaultContextSupplier);
return new AsyncFeign<>(feign);
Expand Down
7 changes: 6 additions & 1 deletion core/src/main/java/feign/AsynchronousMethodHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ static class Factory<C> implements MethodHandler.Factory<C> {
private final Logger.Level logLevel;
private final ExceptionPropagationPolicy propagationPolicy;
private final MethodInfoResolver methodInfoResolver;
private final RequestTemplateFactoryResolver requestTemplateFactoryResolver;
private final Options options;
private final Decoder decoder;
private final ErrorDecoder errorDecoder;
Expand All @@ -255,6 +256,7 @@ static class Factory<C> implements MethodHandler.Factory<C> {
Logger logger, Logger.Level logLevel,
ExceptionPropagationPolicy propagationPolicy,
MethodInfoResolver methodInfoResolver,
RequestTemplateFactoryResolver requestTemplateFactoryResolver,
Options options,
Decoder decoder,
ErrorDecoder errorDecoder) {
Expand All @@ -266,15 +268,18 @@ static class Factory<C> implements MethodHandler.Factory<C> {
this.logLevel = checkNotNull(logLevel, "logLevel");
this.propagationPolicy = propagationPolicy;
this.methodInfoResolver = methodInfoResolver;
this.requestTemplateFactoryResolver =
checkNotNull(requestTemplateFactoryResolver, "requestTemplateFactoryResolver");
this.options = checkNotNull(options, "options");
this.errorDecoder = checkNotNull(errorDecoder, "errorDecoder");
this.decoder = checkNotNull(decoder, "decoder");
}

public MethodHandler create(Target<?> target,
MethodMetadata md,
RequestTemplate.Factory buildTemplateFromArgs,
C requestContext) {
final RequestTemplate.Factory buildTemplateFromArgs =
requestTemplateFactoryResolver.resolve(target, md);
return new AsynchronousMethodHandler<C>(target, client, retryer, requestInterceptors,
logger, logLevel, md, buildTemplateFromArgs, options, responseHandler,
propagationPolicy, requestContext,
Expand Down
6 changes: 4 additions & 2 deletions core/src/main/java/feign/Feign.java
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,11 @@ public Feign build() {
dismiss404, closeAfterDecode, responseInterceptor);
MethodHandler.Factory<Object> synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors,
responseHandler, logger, logLevel, propagationPolicy, options);
responseHandler, logger, logLevel, propagationPolicy,
new RequestTemplateFactoryResolver(encoder, queryMapEncoder),
options);
ParseHandlersByName<Object> handlersByName =
new ParseHandlersByName<>(contract, encoder, queryMapEncoder,
new ParseHandlersByName<>(contract,
synchronousMethodHandlerFactory);
return new ReflectiveFeign<>(handlersByName, invocationHandlerFactory, () -> null);
}
Expand Down
1 change: 0 additions & 1 deletion core/src/main/java/feign/InvocationHandlerFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ interface MethodHandler {
interface Factory<C> {
MethodHandler create(Target<?> target,
MethodMetadata md,
RequestTemplate.Factory buildTemplateFromArgs,
C requestContext);
}
}
Expand Down
256 changes: 1 addition & 255 deletions core/src/main/java/feign/ReflectiveFeign.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
*/
package feign;

import static feign.Util.checkArgument;
import static feign.Util.checkNotNull;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
Expand All @@ -22,12 +21,8 @@
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.CompletableFuture;
import feign.InvocationHandlerFactory.MethodHandler;
import feign.Param.Expander;
import feign.codec.*;
import feign.template.UriUtils;

public class ReflectiveFeign<C> extends Feign {

Expand Down Expand Up @@ -121,19 +116,13 @@ public String toString() {
static final class ParseHandlersByName<C> {

private final Contract contract;
private final Encoder encoder;
private final QueryMapEncoder queryMapEncoder;
private final MethodHandler.Factory<C> factory;

ParseHandlersByName(
Contract contract,
Encoder encoder,
QueryMapEncoder queryMapEncoder,
MethodHandler.Factory<C> factory) {
this.contract = contract;
this.factory = factory;
this.queryMapEncoder = queryMapEncoder;
this.encoder = checkNotNull(encoder, "encoder");
}

public Map<Method, MethodHandler> apply(Target target, C requestContext) {
Expand Down Expand Up @@ -169,250 +158,7 @@ private MethodHandler createMethodHandler(final Target<?> target,
};
}

BuildTemplateByResolvingArgs buildTemplate = getBuildTemplate(target, md);
return factory.create(target, md, buildTemplate, requestContext);
}

private BuildTemplateByResolvingArgs getBuildTemplate(Target target, MethodMetadata md) {
if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
return new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
} else if (md.bodyIndex() != null || md.alwaysEncodeBody()) {
return new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
} else {
return new BuildTemplateByResolvingArgs(md, queryMapEncoder, target);
}
}
}

private static class BuildTemplateByResolvingArgs implements RequestTemplate.Factory {

private final QueryMapEncoder queryMapEncoder;

protected final MethodMetadata metadata;
protected final Target<?> target;
private final Map<Integer, Expander> indexToExpander = new LinkedHashMap<Integer, Expander>();

private BuildTemplateByResolvingArgs(MethodMetadata metadata, QueryMapEncoder queryMapEncoder,
Target target) {
this.metadata = metadata;
this.target = target;
this.queryMapEncoder = queryMapEncoder;
if (metadata.indexToExpander() != null) {
indexToExpander.putAll(metadata.indexToExpander());
return;
}
if (metadata.indexToExpanderClass().isEmpty()) {
return;
}
for (Entry<Integer, Class<? extends Expander>> indexToExpanderClass : metadata
.indexToExpanderClass().entrySet()) {
try {
indexToExpander
.put(indexToExpanderClass.getKey(), indexToExpanderClass.getValue().newInstance());
} catch (InstantiationException e) {
throw new IllegalStateException(e);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
}

@Override
public RequestTemplate create(Object[] argv) {
RequestTemplate mutable = RequestTemplate.from(metadata.template());
mutable.feignTarget(target);
if (metadata.urlIndex() != null) {
int urlIndex = metadata.urlIndex();
checkArgument(argv[urlIndex] != null, "URI parameter %s was null", urlIndex);
mutable.target(String.valueOf(argv[urlIndex]));
}
Map<String, Object> varBuilder = new LinkedHashMap<String, Object>();
for (Entry<Integer, Collection<String>> entry : metadata.indexToName().entrySet()) {
int i = entry.getKey();
Object value = argv[entry.getKey()];
if (value != null) { // Null values are skipped.
if (indexToExpander.containsKey(i)) {
value = expandElements(indexToExpander.get(i), value);
}
for (String name : entry.getValue()) {
varBuilder.put(name, value);
}
}
}

RequestTemplate template = resolve(argv, mutable, varBuilder);
if (metadata.queryMapIndex() != null) {
// add query map parameters after initial resolve so that they take
// precedence over any predefined values
Object value = argv[metadata.queryMapIndex()];
Map<String, Object> queryMap = toQueryMap(value);
template = addQueryMapQueryParameters(queryMap, template);
}

if (metadata.headerMapIndex() != null) {
// add header map parameters for a resolution of the user pojo object
Object value = argv[metadata.headerMapIndex()];
Map<String, Object> headerMap = toQueryMap(value);
template = addHeaderMapHeaders(headerMap, template);
}

return template;
}

private Map<String, Object> toQueryMap(Object value) {
if (value instanceof Map) {
return (Map<String, Object>) value;
}
try {
return queryMapEncoder.encode(value);
} catch (EncodeException e) {
throw new IllegalStateException(e);
}
}

private Object expandElements(Expander expander, Object value) {
if (value instanceof Iterable) {
return expandIterable(expander, (Iterable) value);
}
return expander.expand(value);
}

private List<String> expandIterable(Expander expander, Iterable value) {
List<String> values = new ArrayList<String>();
for (Object element : value) {
if (element != null) {
values.add(expander.expand(element));
}
}
return values;
}

@SuppressWarnings("unchecked")
private RequestTemplate addHeaderMapHeaders(Map<String, Object> headerMap,
RequestTemplate mutable) {
for (Entry<String, Object> currEntry : headerMap.entrySet()) {
Collection<String> values = new ArrayList<String>();

Object currValue = currEntry.getValue();
if (currValue instanceof Iterable<?>) {
Iterator<?> iter = ((Iterable<?>) currValue).iterator();
while (iter.hasNext()) {
Object nextObject = iter.next();
values.add(nextObject == null ? null : nextObject.toString());
}
} else {
values.add(currValue == null ? null : currValue.toString());
}

mutable.header(currEntry.getKey(), values);
}
return mutable;
}

@SuppressWarnings("unchecked")
private RequestTemplate addQueryMapQueryParameters(Map<String, Object> queryMap,
RequestTemplate mutable) {
for (Entry<String, Object> currEntry : queryMap.entrySet()) {
Collection<String> values = new ArrayList<String>();

Object currValue = currEntry.getValue();
if (currValue instanceof Iterable<?>) {
Iterator<?> iter = ((Iterable<?>) currValue).iterator();
while (iter.hasNext()) {
Object nextObject = iter.next();
values.add(nextObject == null ? null : UriUtils.encode(nextObject.toString()));
}
} else if (currValue instanceof Object[]) {
for (Object value : (Object[]) currValue) {
values.add(value == null ? null : UriUtils.encode(value.toString()));
}
} else {
if (currValue != null) {
values.add(UriUtils.encode(currValue.toString()));
}
}

if (values.size() > 0) {
mutable.query(UriUtils.encode(currEntry.getKey()), values);
}
}
return mutable;
}

protected RequestTemplate resolve(Object[] argv,
RequestTemplate mutable,
Map<String, Object> variables) {
return mutable.resolve(variables);
}
}

private static class BuildFormEncodedTemplateFromArgs extends BuildTemplateByResolvingArgs {

private final Encoder encoder;

private BuildFormEncodedTemplateFromArgs(MethodMetadata metadata, Encoder encoder,
QueryMapEncoder queryMapEncoder, Target target) {
super(metadata, queryMapEncoder, target);
this.encoder = encoder;
}

@Override
protected RequestTemplate resolve(Object[] argv,
RequestTemplate mutable,
Map<String, Object> variables) {
Map<String, Object> formVariables = new LinkedHashMap<String, Object>();
for (Entry<String, Object> entry : variables.entrySet()) {
if (metadata.formParams().contains(entry.getKey())) {
formVariables.put(entry.getKey(), entry.getValue());
}
}
try {
encoder.encode(formVariables, Encoder.MAP_STRING_WILDCARD, mutable);
} catch (EncodeException e) {
throw e;
} catch (RuntimeException e) {
throw new EncodeException(e.getMessage(), e);
}
return super.resolve(argv, mutable, variables);
}
}

private static class BuildEncodedTemplateFromArgs extends BuildTemplateByResolvingArgs {

private final Encoder encoder;

private BuildEncodedTemplateFromArgs(MethodMetadata metadata, Encoder encoder,
QueryMapEncoder queryMapEncoder, Target target) {
super(metadata, queryMapEncoder, target);
this.encoder = encoder;
}

@Override
protected RequestTemplate resolve(Object[] argv,
RequestTemplate mutable,
Map<String, Object> variables) {

boolean alwaysEncodeBody = mutable.methodMetadata().alwaysEncodeBody();

Object body = null;
if (!alwaysEncodeBody) {
body = argv[metadata.bodyIndex()];
checkArgument(body != null, "Body parameter %s was null", metadata.bodyIndex());
}

try {
if (alwaysEncodeBody) {
body = argv == null ? new Object[0] : argv;
encoder.encode(body, Object[].class, mutable);
} else {
encoder.encode(body, metadata.bodyType(), mutable);
}
} catch (EncodeException e) {
throw e;
} catch (RuntimeException e) {
throw new EncodeException(e.getMessage(), e);
}
return super.resolve(argv, mutable, variables);
return factory.create(target, md, requestContext);
}
}

Expand Down