/* * 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 dagger.ObjectGraph; import dagger.Provides; import feign.Logger.NoOpLogger; import feign.Request.Options; import feign.Target.HardCodedTarget; import feign.codec.Decoder; import feign.codec.Encoder; import feign.codec.ErrorDecoder; import javax.inject.Inject; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSocketFactory; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; /** * 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 { /** * 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 Builder builder() { return new Builder(); } public static T create(Class apiType, String url, Object... modules) { return create(new HardCodedTarget(apiType, url), modules); } /** * Shortcut to {@link #newInstance(Target) create} a single {@code targeted} * http api using {@link ReflectiveFeign reflection}. */ public static T create(Target target, Object... modules) { return create(modules).newInstance(target); } /** * Returns a {@link ReflectiveFeign reflective} factory for generating * {@link Target targeted} http apis. */ public static Feign create(Object... modules) { return ObjectGraph.create(modulesForGraph(modules).toArray()).get(Feign.class); } /** * Returns an {@link ObjectGraph Dagger ObjectGraph} that can inject a * {@link ReflectiveFeign reflective} Feign. */ public static ObjectGraph createObjectGraph(Object... modules) { return ObjectGraph.create(modulesForGraph(modules).toArray()); } @SuppressWarnings("rawtypes") // incomplete as missing Encoder/Decoder @dagger.Module(injects = {Feign.class, Builder.class}, complete = false, includes = ReflectiveFeign.Module.class) public static class Defaults { @Provides Contract contract() { return new Contract.Default(); } @Provides Logger.Level logLevel() { return Logger.Level.NONE; } @Provides Logger noOp() { return new NoOpLogger(); } @Provides Retryer retryer() { return new Retryer.Default(); } @Provides ErrorDecoder errorDecoder() { return new ErrorDecoder.Default(); } @Provides Options options() { return new Options(); } @Provides SSLSocketFactory sslSocketFactory() { return SSLSocketFactory.class.cast(SSLSocketFactory.getDefault()); } @Provides HostnameVerifier hostnameVerifier() { return HttpsURLConnection.getDefaultHostnameVerifier(); } @Provides feign.Client httpClient(feign.Client.Default client) { return client; } } /** *
* Configuration keys are formatted as unresolved see tags. *
* For example. *
    *
  • {@code Route53}: would match a class such as * {@code denominator.route53.Route53} *
  • {@code Route53#list()}: would match a method such as * {@code denominator.route53.Route53#list()} *
  • {@code Route53#listAt(Marker)}: would match a method such as * {@code denominator.route53.Route53#listAt(denominator.route53.Marker)} *
  • {@code Route53#listByNameAndType(String, String)}: would match a * method such as {@code denominator.route53.Route53#listAt(String, String)} *
*
* Note that there is no whitespace expected in a key! */ public static String configKey(Method method) { StringBuilder builder = new StringBuilder(); builder.append(method.getDeclaringClass().getSimpleName()); builder.append('#').append(method.getName()).append('('); for (Class param : method.getParameterTypes()) builder.append(param.getSimpleName()).append(','); if (method.getParameterTypes().length > 0) builder.deleteCharAt(builder.length() - 1); return builder.append(')').toString(); } private static List modulesForGraph(Object... modules) { List modulesForGraph = new ArrayList(2); modulesForGraph.add(new Defaults()); if (modules != null) for (Object module : modules) modulesForGraph.add(module); return modulesForGraph; } @dagger.Module(injects = Feign.class, includes = ReflectiveFeign.Module.class) public static class Builder { private final Set requestInterceptors = new LinkedHashSet(); @Inject Logger.Level logLevel; @Inject Contract contract; @Inject Client client; @Inject Retryer retryer; @Inject Logger logger; Encoder encoder = new Encoder.Default(); Decoder decoder = new Decoder.Default(); @Inject ErrorDecoder errorDecoder; @Inject Options options; Builder() { ObjectGraph.create(new Defaults()).inject(this); } 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 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; } public T target(Class apiType, String url) { return target(new HardCodedTarget(apiType, url)); } public T target(Target target) { return ObjectGraph.create(this).get(Feign.class).newInstance(target); } @Provides Logger.Level logLevel() { return logLevel; } @Provides Contract contract() { return contract; } @Provides Client client() { return client; } @Provides Retryer retryer() { return retryer; } @Provides Logger logger() { return logger; } @Provides Encoder encoder() { return encoder; } @Provides Decoder decoder() { return decoder; } @Provides ErrorDecoder errorDecoder() { return errorDecoder; } @Provides Options options() { return options; } @Provides(type = Provides.Type.SET_VALUES) Set requestInterceptors() { return requestInterceptors; } } }