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

feign.Feign Maven / Gradle / Ivy

There is a newer version: 9.7.0
Show newest version
/*
 * Copyright 2013 Netflix, Inc.
 *
 * 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 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;

/**
 * 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 ErrorDecoder errorDecoder = new ErrorDecoder.Default(); private Options options = new Options(); private InvocationHandlerFactory invocationHandlerFactory = new InvocationHandlerFactory.Default(); private boolean decode404; 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; } /** * 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; } 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() { SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger, logLevel, decode404); ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, errorDecoder, synchronousMethodHandlerFactory); return new ReflectiveFeign(handlersByName, invocationHandlerFactory); } } static class ResponseMappingDecoder implements Decoder { private final ResponseMapper mapper; private final Decoder delegate; 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