All Downloads are FREE. Search and download functionalities are using the official Maven repository.

feign.Feign Maven / Gradle / Ivy

There is a newer version: 13.5
Show newest version
/**
 * Copyright 2012-2020 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 java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import feign.Logger.Level;
import feign.Logger.NoOpLogger;
import feign.ReflectiveFeign.ParseHandlersByName;
import feign.Request.Options;
import feign.Target.HardCodedTarget;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.codec.ErrorDecoder;
import feign.querymap.FieldQueryMapEncoder;
import static feign.ExceptionPropagationPolicy.NONE;

/**
 * Feign's purpose is to ease development against http apis that feign restfulness. 
* In implementation, Feign is a {@link Feign#newInstance factory} for generating {@link Target * targeted} http apis. */ public abstract class Feign { public static Builder builder() { return new Builder(); } /** * Configuration keys are formatted as unresolved see * tags. This method exposes that format, in case you need to create the same value as * {@link MethodMetadata#configKey()} for correlation purposes. * *

* Here are some sample encodings: * *

   * 
    *
  • {@code Route53}: would match a class {@code route53.Route53}
  • *
  • {@code Route53#list()}: would match a method {@code route53.Route53#list()}
  • *
  • {@code Route53#listAt(Marker)}: would match a method {@code * route53.Route53#listAt(Marker)}
  • *
  • {@code Route53#listByNameAndType(String, String)}: would match a method {@code * route53.Route53#listAt(String, String)}
  • *
*
* * Note that there is no whitespace expected in a key! * * @param targetType {@link feign.Target#type() type} of the Feign interface. * @param method invoked method, present on {@code type} or its super. * @see MethodMetadata#configKey() */ public static String configKey(Class targetType, Method method) { StringBuilder builder = new StringBuilder(); builder.append(targetType.getSimpleName()); builder.append('#').append(method.getName()).append('('); for (Type param : method.getGenericParameterTypes()) { param = Types.resolve(targetType, targetType, param); builder.append(Types.getRawType(param).getSimpleName()).append(','); } if (method.getParameterTypes().length > 0) { builder.deleteCharAt(builder.length() - 1); } return builder.append(')').toString(); } /** * @deprecated use {@link #configKey(Class, Method)} instead. */ @Deprecated public static String configKey(Method method) { return configKey(method.getDeclaringClass(), method); } /** * Returns a new instance of an HTTP API, defined by annotations in the {@link Feign Contract}, * for the specified {@code target}. You should cache this result. */ public abstract T newInstance(Target target); public static class Builder { private final List requestInterceptors = new ArrayList(); private Logger.Level logLevel = Logger.Level.NONE; private Contract contract = new Contract.Default(); private Client client = new Client.Default(null, null); private Retryer retryer = new Retryer.Default(); private Logger logger = new NoOpLogger(); private Encoder encoder = new Encoder.Default(); private Decoder decoder = new Decoder.Default(); private QueryMapEncoder queryMapEncoder = new FieldQueryMapEncoder(); private ErrorDecoder errorDecoder = new ErrorDecoder.Default(); private Options options = new Options(); private InvocationHandlerFactory invocationHandlerFactory = new InvocationHandlerFactory.Default(); private boolean decode404; private boolean closeAfterDecode = true; private ExceptionPropagationPolicy propagationPolicy = NONE; private boolean forceDecoding = false; private List capabilities = new ArrayList<>(); public Builder logLevel(Logger.Level logLevel) { this.logLevel = logLevel; return this; } public Builder contract(Contract contract) { this.contract = contract; return this; } public Builder client(Client client) { this.client = client; return this; } public Builder retryer(Retryer retryer) { this.retryer = retryer; return this; } public Builder logger(Logger logger) { this.logger = logger; return this; } public Builder encoder(Encoder encoder) { this.encoder = encoder; return this; } public Builder decoder(Decoder decoder) { this.decoder = decoder; return this; } public Builder queryMapEncoder(QueryMapEncoder queryMapEncoder) { this.queryMapEncoder = queryMapEncoder; return this; } /** * Allows to map the response before passing it to the decoder. */ public Builder mapAndDecode(ResponseMapper mapper, Decoder decoder) { this.decoder = new ResponseMappingDecoder(mapper, decoder); return this; } /** * This flag indicates that the {@link #decoder(Decoder) decoder} should process responses with * 404 status, specifically returning null or empty instead of throwing {@link FeignException}. * *

* All first-party (ex gson) decoders return well-known empty values defined by * {@link Util#emptyValueOf}. To customize further, wrap an existing {@link #decoder(Decoder) * decoder} or make your own. * *

* This flag only works with 404, as opposed to all or arbitrary status codes. This was an * explicit decision: 404 -> empty is safe, common and doesn't complicate redirection, retry or * fallback policy. If your server returns a different status for not-found, correct via a * custom {@link #client(Client) client}. * * @since 8.12 */ public Builder decode404() { this.decode404 = true; return this; } public Builder errorDecoder(ErrorDecoder errorDecoder) { this.errorDecoder = errorDecoder; return this; } public Builder options(Options options) { this.options = options; return this; } /** * Adds a single request interceptor to the builder. */ public Builder requestInterceptor(RequestInterceptor requestInterceptor) { this.requestInterceptors.add(requestInterceptor); return this; } /** * Sets the full set of request interceptors for the builder, overwriting any previous * interceptors. */ public Builder requestInterceptors(Iterable requestInterceptors) { this.requestInterceptors.clear(); for (RequestInterceptor requestInterceptor : requestInterceptors) { this.requestInterceptors.add(requestInterceptor); } return this; } /** * Allows you to override how reflective dispatch works inside of Feign. */ public Builder invocationHandlerFactory(InvocationHandlerFactory invocationHandlerFactory) { this.invocationHandlerFactory = invocationHandlerFactory; return this; } /** * This flag indicates that the response should not be automatically closed upon completion of * decoding the message. This should be set if you plan on processing the response into a * lazy-evaluated construct, such as a {@link java.util.Iterator}. * *

* Feign standard decoders do not have built in support for this flag. If you are using this * flag, you MUST also use a custom Decoder, and be sure to close all resources appropriately * somewhere in the Decoder (you can use {@link Util#ensureClosed} for convenience). * * @since 9.6 * */ public Builder doNotCloseAfterDecode() { this.closeAfterDecode = false; return this; } public Builder exceptionPropagationPolicy(ExceptionPropagationPolicy propagationPolicy) { this.propagationPolicy = propagationPolicy; return this; } public Builder addCapability(Capability capability) { this.capabilities.add(capability); return this; } /** * Internal - used to indicate that the decoder should be immediately called */ Builder forceDecoding() { this.forceDecoding = true; return this; } public T target(Class apiType, String url) { return target(new HardCodedTarget(apiType, url)); } public T target(Target target) { return build().newInstance(target); } public Feign build() { Client client = Capability.enrich(this.client, capabilities); Retryer retryer = Capability.enrich(this.retryer, capabilities); List requestInterceptors = this.requestInterceptors.stream() .map(ri -> Capability.enrich(ri, capabilities)) .collect(Collectors.toList()); Logger logger = Capability.enrich(this.logger, capabilities); Contract contract = Capability.enrich(this.contract, capabilities); Options options = Capability.enrich(this.options, capabilities); Encoder encoder = Capability.enrich(this.encoder, capabilities); Decoder decoder = Capability.enrich(this.decoder, capabilities); InvocationHandlerFactory invocationHandlerFactory = Capability.enrich(this.invocationHandlerFactory, capabilities); QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities); SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger, logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding); ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, errorDecoder, synchronousMethodHandlerFactory); return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder); } } public static class ResponseMappingDecoder implements Decoder { private final ResponseMapper mapper; private final Decoder delegate; public ResponseMappingDecoder(ResponseMapper mapper, Decoder decoder) { this.mapper = mapper; this.delegate = decoder; } @Override public Object decode(Response response, Type type) throws IOException { return delegate.decode(mapper.map(response, type), type); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy