public List<MethodMetadata> parseAndValidatateMetadata(Class<?> targetType) { List<MethodMetadata> metas = this.delegate.parseAndValidatateMetadata(targetType); for (int i = 0; metas != null && i < metas.size(); i++) { MethodMetadata meta = metas.get(i); if (meta.returnType() == void.class) { meta.returnType(Void.class); } } return metas == null ? new ArrayList<MethodMetadata>() : metas; }
@Override public List<MethodMetadata> parseAndValidatateMetadata(Class<?> targetType) { List<MethodMetadata> metadatas = delegate.parseAndValidatateMetadata(targetType); metadatas.forEach(metadata -> metadataMap .put(targetType.getName() + metadata.configKey(), metadata)); return metadatas; }
MethodMetadata data = new MethodMetadata(); data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType())); data.configKey(Feign.configKey(targetType, method)); processAnnotationOnMethod(data, methodAnnotation, method); checkState(data.template().method() != null, "Method %s not annotated with HTTP method type (ex. GET, POST)", method.getName()); data.urlIndex(i); } else if (!isHttpAnnotation) { checkState(data.formParams().isEmpty(), "Body parameters cannot be used with form parameters."); checkState(data.bodyIndex() == null, "Method has too many Body parameters: %s", method); data.bodyIndex(i); data.bodyType(Types.resolve(targetType, targetType, method.getGenericParameterTypes()[i])); if (data.headerMapIndex() != null) { checkState(Map.class.isAssignableFrom(parameterTypes[data.headerMapIndex()]), "HeaderMap parameter must be a Map: %s", parameterTypes[data.headerMapIndex()]); if (data.queryMapIndex() != null) { checkState(Map.class.isAssignableFrom(parameterTypes[data.queryMapIndex()]), "QueryMap parameter must be a Map: %s", parameterTypes[data.queryMapIndex()]);
public RetryReactiveHttpClient(ReactiveHttpClient reactiveClient, MethodMetadata methodMetadata, Function<Flux<Throwable>, Publisher<Throwable>> retryFunction) { this.reactiveClient = reactiveClient; this.feignMethodTag = methodMetadata.configKey().substring(0, methodMetadata.configKey().indexOf('(')); this.retryFunction = wrapWithLog(retryFunction, feignMethodTag); final Type returnType = methodMetadata.returnType(); returnPublisherType = ((ParameterizedType) returnType).getRawType(); }
public static WebReactiveHttpClient webClient(MethodMetadata methodMetadata, WebClient webClient) { final Type returnType = methodMetadata.returnType(); Type returnPublisherType = ((ParameterizedType) returnType).getRawType(); ParameterizedTypeReference<Object> returnActualType = ParameterizedTypeReference.forType( resolveLastTypeParameter(returnType, (Class<?>) returnPublisherType)); ParameterizedTypeReference<Object> bodyActualType = ofNullable( getBodyActualType(methodMetadata.bodyType())) .map(type -> ParameterizedTypeReference.forType(type)) .orElse(null); return new WebReactiveHttpClient(webClient, bodyActualType, returnPublisherType, returnActualType); }
public Map<String, MethodHandler> apply(Target key) { List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type()); Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>(); for (MethodMetadata md : metadata) { BuildTemplateByResolvingArgs buildTemplate; if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) { buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder); } else if (md.bodyIndex() != null) { buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder); } else { buildTemplate = new BuildTemplateByResolvingArgs(md); } result.put(md.configKey(), factory.create(key, md, buildTemplate, options, decoder, errorDecoder)); } return result; } }
public WebReactiveHttpClient(MethodMetadata methodMetadata, WebClient webClient, ReactiveHttpRequestInterceptor requestInterceptor, ReactiveStatusHandler statusHandler, boolean decode404) { this.webClient = webClient; this.metadata = methodMetadata; this.requestInterceptor = requestInterceptor; this.statusHandler = statusHandler; this.decode404 = decode404; this.logger = new Logger(); this.methodTag = methodMetadata.configKey().substring(0, methodMetadata.configKey().indexOf('(')); Type bodyType = methodMetadata.bodyType(); bodyActualType = getBodyActualType(bodyType); final Type returnType = methodMetadata.returnType(); returnPublisherType = ((ParameterizedType) returnType).getRawType(); returnActualType = ParameterizedTypeReference.forType( resolveLastTypeParameter(returnType, (Class<?>) returnPublisherType)); }
Class<? extends Param.Expander> expander = ((Param) annotation).expander(); if (expander != Param.ToStringExpander.class) { data.indexToExpanderClass().put(paramIndex, expander); if (data.template().url().indexOf(varName) == -1 && !searchMapValuesContainsExact(data.template().queries(), varName) && !searchMapValuesContainsSubstring(data.template().headers(), varName)) { data.formParams().add(name); checkState(data.queryMapIndex() == null, "QueryMap annotation was present on multiple parameters."); data.queryMapIndex(paramIndex); data.queryMapEncoded(QueryMap.class.cast(annotation).encoded()); isHttpAnnotation = true; } else if (annotationType == HeaderMap.class) { checkState(data.headerMapIndex() == null, "HeaderMap annotation was present on multiple parameters."); data.headerMapIndex(paramIndex); isHttpAnnotation = true;
@Override public RequestTemplate create(Object[] argv) { RequestTemplate mutable = new RequestTemplate(metadata.template()); if (metadata.urlIndex() != null) { int urlIndex = metadata.urlIndex(); checkArgument(argv[urlIndex] != null, "URI parameter %s was null", urlIndex); mutable.insert(0, String.valueOf(argv[urlIndex])); for (Entry<Integer, Collection<String>> entry : metadata.indexToName().entrySet()) { int i = entry.getKey(); Object value = argv[entry.getKey()]; if (metadata.queryMapIndex() != null) { if (metadata.headerMapIndex() != null) { template = addHeaderMapHeaders(argv, template);
protected MultiValueMap<String, String> headers(Object[] argv, Map<String, ?> substitutionsMap) { MultiValueMap<String, String> headers = new LinkedMultiValueMap<>(); methodMetadata.template().headers().keySet() .forEach(headerName -> headers.addAll(headerName, headerExpanders.get(headerName).stream() .map(expander -> expander.apply(substitutionsMap)) .collect(toList()))); if (methodMetadata.headerMapIndex() != null) { ((Map<String, String>) argv[methodMetadata.headerMapIndex()]) .forEach(headers::add); } return headers; }
public PublisherClientMethodHandler(Target target, MethodMetadata methodMetadata, PublisherHttpClient publisherClient) { this.target = checkNotNull(target, "target must be not null"); this.methodMetadata = checkNotNull(methodMetadata, "methodMetadata must be not null"); this.publisherClient = checkNotNull(publisherClient, "client must be not null"); this.pathExpander = buildExpandFunction(methodMetadata.template().url()); this.headerExpanders = buildExpanders(methodMetadata.template().headers()); this.queriesAll = new HashMap<>(methodMetadata.template().queries()); if (methodMetadata.formParams() != null) { methodMetadata.formParams() .forEach(param -> add(queriesAll, param, "{" + param + "}")); } this.queryExpanders = buildExpanders(queriesAll); }
protected MultiValueMap<String, String> parameters(Object[] argv) { MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(); methodMetadata.template().queries() .forEach((key, value) -> parameters.addAll(key, (List) value)); if (methodMetadata.formParams() != null) { methodMetadata.formParams() .forEach(param -> parameters.add(param, "{" + param + "}")); } if (methodMetadata.queryMapIndex() != null) { ((Map<String, String>) argv[methodMetadata.queryMapIndex()]) .forEach((key, value) -> parameters.add(key, value)); } return parameters; }
@Override public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) { int parameterIndex = context.getParameterIndex(); Class<?> parameterType = method.getParameterTypes()[parameterIndex]; MethodMetadata data = context.getMethodMetadata(); if (Map.class.isAssignableFrom(parameterType)) { checkState(data.queryMapIndex() == null, "Query map can only be present once."); data.queryMapIndex(parameterIndex); return true; } RequestParam requestParam = ANNOTATION.cast(annotation); String name = requestParam.value(); checkState(emptyToNull(name) != null, "RequestParam.value() was empty on parameter %s", parameterIndex); context.setParameterName(name); Collection<String> query = context.setTemplateParameter(name, data.template().queries().get(name)); data.template().query(name, query); return true; } }
@Override protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) { MethodMetadata data = super.parseAndValidateMetadata(targetType, method); // perform extra validation for HeaderMap and QueryMap annotations Class<?>[] parameterTypes = method.getParameterTypes(); if (data.headerMapIndex() != null) { feign.Util.checkState(Map.class.isAssignableFrom(parameterTypes[data.headerMapIndex()]), "HeaderMap parameter must be a Map: %s", parameterTypes[data.headerMapIndex()]); } if (data.queryMapIndex() != null) { feign.Util.checkState(Map.class.isAssignableFrom(parameterTypes[data.queryMapIndex()]), "QueryMap parameter must be a Map: %s", parameterTypes[data.queryMapIndex()]); } return data; }
protected ReactiveHttpRequest buildRequest(Object[] argv) { Map<String, ?> substitutionsMap = methodMetadata.indexToName().entrySet().stream() .flatMap(e -> e.getValue().stream() .map(v -> new AbstractMap.SimpleImmutableEntry<>(e.getKey(), v))) .collect(Collectors.toMap(Map.Entry::getValue, entry -> argv[entry.getKey()])); HttpMethod method = HttpMethod.resolve(methodMetadata.template().method()); URI uri = defaultUriBuilderFactory.uriString(methodMetadata.template().url()) .queryParams(parameters(argv)).build(substitutionsMap); return new ReactiveHttpRequest(method, uri, headers(argv, substitutionsMap), body(argv)); }
@Override protected RequestTemplate resolve(Object[] argv, RequestTemplate mutable, Map<String, Object> variables) { Object body = argv[metadata.bodyIndex()]; checkArgument(body != null, "Body parameter %s was null", metadata.bodyIndex()); try { 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); } }
@Override public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) { int paramIndex = context.getParameterIndex(); MethodMetadata metadata = context.getMethodMetadata(); if (metadata.queryMapIndex() == null) { metadata.queryMapIndex(paramIndex); metadata.queryMapEncoded(SpringQueryMap.class.cast(annotation).encoded()); } return true; } }
private void configureOptionalExpanders(Class<?> targetType, Method method, MethodMetadata metadata, int index, FluentIterable<Class<?>> paramAnnotations, Class<? extends Expander> emptyExpanderClass, Class<? extends Expander> nullExpanderClass) { if (paramAnnotations.contains(HeaderParam.class)) { metadata.indexToExpanderClass().put(index, emptyExpanderClass); } else if (paramAnnotations.contains(QueryParam.class)) { metadata.indexToExpanderClass().put(index, nullExpanderClass); } else if (paramAnnotations.contains(PathParam.class)) { throw new RuntimeException(String.format( "Cannot use Java8 Optionals with PathParams. (Class: %s, Method: %s, Param: arg%d)", targetType.getName(), method.getName(), index)); } }
BuildTemplateByResolvingArgs(final MethodMetadata metadata) { this.metadata = metadata; if (metadata.indexToExpander() != null) { indexToExpander.putAll(metadata.indexToExpander()); return; } if (metadata.indexToExpanderClass().isEmpty()) { return; } for (final Map.Entry<Integer, Class<? extends Param.Expander>> indexToExpanderClass : metadata .indexToExpanderClass().entrySet()) { try { indexToExpander.put( indexToExpanderClass.getKey(), indexToExpanderClass.getValue().newInstance()); } catch (final InstantiationException | IllegalAccessException exception) { throw new IllegalStateException(exception); } } }