package feign.hystrix;
import com.netflix.hystrix.HystrixCommand;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
import feign.Client;
import feign.Contract;
import feign.Feign;
import feign.InvocationHandlerFactory;
import feign.Logger;
import feign.Request;
import feign.RequestInterceptor;
import feign.ResponseMapper;
import feign.Retryer;
import feign.Target;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.codec.ErrorDecoder;
/**
* Allows Feign interfaces to return HystrixCommand or rx.Observable or rx.Single objects. Also
* decorates normal Feign methods with circuit breakers, but calls {@link HystrixCommand#execute()}
* directly.
*/
public final class HystrixFeign {
public static Builder builder() {
return new Builder();
}
public static final class Builder extends Feign.Builder {
private Contract contract = new Contract.Default();
private SetterFactory setterFactory = new SetterFactory.Default();
/**
* Allows you to override hystrix properties such as thread pools and command keys.
*/
public Builder setterFactory(SetterFactory setterFactory) {
this.setterFactory = setterFactory;
return this;
}
/**
* @see #target(Class, String, Object)
*/
public <T> T target(Target<T> target, T fallback) {
return build(fallback != null ? new FallbackFactory.Default<T>(fallback) : null)
.newInstance(target);
}
/**
* @see #target(Class, String, FallbackFactory)
*/
public <T> T target(Target<T> target, FallbackFactory<? extends T> fallbackFactory) {
return build(fallbackFactory).newInstance(target);
}
/**
* Like {@link Feign#newInstance(Target)}, except with {@link HystrixCommand#getFallback()
* fallback} support.
*
* <p>Fallbacks are known values, which you return when there's an error invoking an http
* method. For example, you can return a cached result as opposed to raising an error to the
* caller. To use this feature, pass a safe implementation of your target interface as the last
* parameter.
*
* Here's an example:
* <pre>
* {@code
*
* // When dealing with fallbacks, it is less tedious to keep interfaces small.
* interface GitHub {
* @RequestLine("GET /repos/{owner}/{repo}/contributors")
* List<String> contributors(@Param("owner") String owner, @Param("repo") String repo);
* }
*
* // This instance will be invoked if there are errors of any kind.
* GitHub fallback = (owner, repo) -> {
* if (owner.equals("Netflix") && repo.equals("feign")) {
* return Arrays.asList("stuarthendren"); // inspired this approach!
* } else {
* return Collections.emptyList();
* }
* };
*
* GitHub github = HystrixFeign.builder()
* ...
* .target(GitHub.class, "https://api.github.com", fallback);
* }</pre>
*
* @see #target(Target, Object)
*/
public <T> T target(Class<T> apiType, String url, T fallback) {
return target(new Target.HardCodedTarget<T>(apiType, url), fallback);
}
/**
* Same as {@link #target(Class, String, T)}, except you can inspect a source exception before
* creating a fallback object.
*/
public <T> T target(Class<T> apiType, String url, FallbackFactory<? extends T> fallbackFactory) {
return target(new Target.HardCodedTarget<T>(apiType, url), fallbackFactory);
}
@Override
public Feign.Builder invocationHandlerFactory(InvocationHandlerFactory invocationHandlerFactory) {
throw new UnsupportedOperationException();
}
@Override
public Builder contract(Contract contract) {
this.contract = contract;
return this;
}
@Override
public Feign build() {
return build(null);
}
/** Configures components needed for hystrix integration. */
Feign build(final FallbackFactory<?> nullableFallbackFactory) {
super.invocationHandlerFactory(new InvocationHandlerFactory() {
@Override public InvocationHandler create(Target target,
Map<Method, MethodHandler> dispatch) {
return new HystrixInvocationHandler(target, dispatch, setterFactory, nullableFallbackFactory);
}
});
super.contract(new HystrixDelegatingContract(contract));
return super.build();
}
// Covariant overrides to support chaining to new fallback method.
@Override
public Builder logLevel(Logger.Level logLevel) {
return (Builder) super.logLevel(logLevel);
}
@Override
public Builder client(Client client) {
return (Builder) super.client(client);
}
@Override
public Builder retryer(Retryer retryer) {
return (Builder) super.retryer(retryer);
}
@Override
public Builder logger(Logger logger) {
return (Builder) super.logger(logger);
}
@Override
public Builder encoder(Encoder encoder) {
return (Builder) super.encoder(encoder);
}
@Override
public Builder decoder(Decoder decoder) {
return (Builder) super.decoder(decoder);
}
@Override
public Builder mapAndDecode(ResponseMapper mapper, Decoder decoder) {
return (Builder) super.mapAndDecode(mapper, decoder);
}
@Override
public Builder decode404() {
return (Builder) super.decode404();
}
@Override
public Builder errorDecoder(ErrorDecoder errorDecoder) {
return (Builder) super.errorDecoder(errorDecoder);
}
@Override
public Builder options(Request.Options options) {
return (Builder) super.options(options);
}
@Override
public Builder requestInterceptor(RequestInterceptor requestInterceptor) {
return (Builder) super.requestInterceptor(requestInterceptor);
}
@Override
public Builder requestInterceptors(Iterable<RequestInterceptor> requestInterceptors) {
return (Builder) super.requestInterceptors(requestInterceptors);
}
}
}