diff --git a/benchmark/src/main/java/feign/benchmark/DecoderIteratorsBenchmark.java b/benchmark/src/main/java/feign/benchmark/DecoderIteratorsBenchmark.java index 04b12d5b74..79ed9ad717 100644 --- a/benchmark/src/main/java/feign/benchmark/DecoderIteratorsBenchmark.java +++ b/benchmark/src/main/java/feign/benchmark/DecoderIteratorsBenchmark.java @@ -21,7 +21,6 @@ import feign.jackson.JacksonIteratorDecoder; import feign.stream.StreamDecoder; import org.openjdk.jmh.annotations.*; - import java.lang.reflect.Type; import java.util.Collection; import java.util.Collections; @@ -89,18 +88,15 @@ public void buildDecoder() { switch (api) { case "list": decoder = new JacksonDecoder(); - type = new TypeReference>() { - }.getType(); + type = new TypeReference>() {}.getType(); break; case "iterator": decoder = JacksonIteratorDecoder.create(); - type = new TypeReference>() { - }.getType(); + type = new TypeReference>() {}.getType(); break; case "stream": decoder = StreamDecoder.create(JacksonIteratorDecoder.create()); - type = new TypeReference>() { - }.getType(); + type = new TypeReference>() {}.getType(); break; default: throw new IllegalStateException("Unknown api: " + api); diff --git a/benchmark/src/main/java/feign/benchmark/FeignTestInterface.java b/benchmark/src/main/java/feign/benchmark/FeignTestInterface.java index 11639d67e2..1c7ab6f58f 100644 --- a/benchmark/src/main/java/feign/benchmark/FeignTestInterface.java +++ b/benchmark/src/main/java/feign/benchmark/FeignTestInterface.java @@ -14,7 +14,6 @@ package feign.benchmark; import java.util.List; - import feign.Body; import feign.Headers; import feign.Param; @@ -41,7 +40,8 @@ Response mixedParams(@Param("domainId") int id, @RequestLine("POST /") @Body("%7B\"customer_name\": \"{customer_name}\", \"user_name\": \"{user_name}\", \"password\": \"{password}\"%7D") - void form(@Param("customer_name") String customer, @Param("user_name") String user, + void form(@Param("customer_name") String customer, + @Param("user_name") String user, @Param("password") String password); @RequestLine("POST /") diff --git a/benchmark/src/main/java/feign/benchmark/RealRequestBenchmarks.java b/benchmark/src/main/java/feign/benchmark/RealRequestBenchmarks.java index 7451bf1f55..0fcbd7307d 100644 --- a/benchmark/src/main/java/feign/benchmark/RealRequestBenchmarks.java +++ b/benchmark/src/main/java/feign/benchmark/RealRequestBenchmarks.java @@ -15,7 +15,6 @@ import okhttp3.OkHttpClient; import okhttp3.Request; - import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -27,10 +26,8 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.annotations.Warmup; - import java.io.IOException; import java.util.concurrent.TimeUnit; - import feign.Feign; import feign.Response; import io.netty.buffer.ByteBuf; diff --git a/benchmark/src/main/java/feign/benchmark/WhatShouldWeCacheBenchmarks.java b/benchmark/src/main/java/feign/benchmark/WhatShouldWeCacheBenchmarks.java index 832776f2d2..a66d676590 100644 --- a/benchmark/src/main/java/feign/benchmark/WhatShouldWeCacheBenchmarks.java +++ b/benchmark/src/main/java/feign/benchmark/WhatShouldWeCacheBenchmarks.java @@ -23,14 +23,12 @@ import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; - import java.io.IOException; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; - import feign.Client; import feign.Contract; import feign.Feign; diff --git a/core/src/main/java/feign/Body.java b/core/src/main/java/feign/Body.java index 8dd770b708..94bb3f3948 100644 --- a/core/src/main/java/feign/Body.java +++ b/core/src/main/java/feign/Body.java @@ -16,18 +16,21 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.util.Map; - import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; /** * A possibly templated body of a PUT or POST command. variables wrapped in curly braces are - * expanded before the request is submitted.
ex.
+ * expanded before the request is submitted.
+ * ex.
+ * *
  * @Body("<v01:getResourceRecordsOfZone><zoneName>{zoneName}</zoneName><rrType>0</rrType></v01:getResourceRecordsOfZone>")
  * List<Record> listByZone(@Param("zoneName") String zoneName);
  * 
- *
Note that if you'd like curly braces literally in the body, urlencode them first. + * + *
+ * Note that if you'd like curly braces literally in the body, urlencode them first. * * @see RequestTemplate#expand(String, Map) */ diff --git a/core/src/main/java/feign/Client.java b/core/src/main/java/feign/Client.java index c7faa57e59..02fb8d5995 100644 --- a/core/src/main/java/feign/Client.java +++ b/core/src/main/java/feign/Client.java @@ -14,7 +14,6 @@ package feign; import static java.lang.String.format; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -26,13 +25,10 @@ import java.util.Map; import java.util.zip.DeflaterOutputStream; import java.util.zip.GZIPOutputStream; - import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSocketFactory; - import feign.Request.Options; - import static feign.Util.CONTENT_ENCODING; import static feign.Util.CONTENT_LENGTH; import static feign.Util.ENCODING_DEFLATE; @@ -73,8 +69,7 @@ public Response execute(Request request, Options options) throws IOException { } HttpURLConnection convertAndSend(Request request, Options options) throws IOException { - final HttpURLConnection - connection = + final HttpURLConnection connection = (HttpURLConnection) new URL(request.url()).openConnection(); if (connection instanceof HttpsURLConnection) { HttpsURLConnection sslCon = (HttpsURLConnection) connection; @@ -92,11 +87,9 @@ HttpURLConnection convertAndSend(Request request, Options options) throws IOExce connection.setRequestMethod(request.method()); Collection contentEncodingValues = request.headers().get(CONTENT_ENCODING); - boolean - gzipEncodedRequest = + boolean gzipEncodedRequest = contentEncodingValues != null && contentEncodingValues.contains(ENCODING_GZIP); - boolean - deflateEncodedRequest = + boolean deflateEncodedRequest = contentEncodingValues != null && contentEncodingValues.contains(ENCODING_DEFLATE); boolean hasAcceptHeader = false; @@ -174,11 +167,11 @@ Response convertResponse(HttpURLConnection connection) throws IOException { stream = connection.getInputStream(); } return Response.builder() - .status(status) - .reason(reason) - .headers(headers) - .body(stream, length) - .build(); + .status(status) + .reason(reason) + .headers(headers) + .body(stream, length) + .build(); } } } diff --git a/core/src/main/java/feign/CollectionFormat.java b/core/src/main/java/feign/CollectionFormat.java index a4b7e391bc..829d78d023 100644 --- a/core/src/main/java/feign/CollectionFormat.java +++ b/core/src/main/java/feign/CollectionFormat.java @@ -18,8 +18,10 @@ /** * Various ways to encode collections in URL parameters. * - *

These specific cases are inspired by the - * OpenAPI specification.

+ *

+ * These specific cases are inspired by the OpenAPI + * specification. + *

*/ public enum CollectionFormat { /** Comma separated values, eg foo=bar,baz */ @@ -43,18 +45,24 @@ public enum CollectionFormat { /** * Joins the field and possibly multiple values with the given separator. * - *

Calling EXPLODED.join("foo", ["bar"]) will return "foo=bar".

+ *

+ * Calling EXPLODED.join("foo", ["bar"]) will return "foo=bar". + *

* - *

Calling CSV.join("foo", ["bar", "baz"]) will return "foo=bar,baz".

+ *

+ * Calling CSV.join("foo", ["bar", "baz"]) will return "foo=bar,baz". + *

* - *

Null values are treated somewhat specially. With EXPLODED, the field - * is repeated without any "=" for backwards compatibility. With all other - * formats, null values are not included in the joined value list.

+ *

+ * Null values are treated somewhat specially. With EXPLODED, the field is repeated without any + * "=" for backwards compatibility. With all other formats, null values are not included in the + * joined value list. + *

* * @param field The field name corresponding to these values. * @param values A collection of value strings for the given field. - * @return The formatted char sequence of the field and joined values. If the - * value collection is empty, an empty char sequence will be returned. + * @return The formatted char sequence of the field and joined values. If the value collection is + * empty, an empty char sequence will be returned. */ CharSequence join(String field, Collection values) { StringBuilder builder = new StringBuilder(); diff --git a/core/src/main/java/feign/Contract.java b/core/src/main/java/feign/Contract.java index b5877b45ae..212dca20ed 100644 --- a/core/src/main/java/feign/Contract.java +++ b/core/src/main/java/feign/Contract.java @@ -24,7 +24,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; - import static feign.Util.checkState; import static feign.Util.emptyToNull; @@ -46,13 +45,13 @@ abstract class BaseContract implements Contract { @Override public List parseAndValidatateMetadata(Class targetType) { checkState(targetType.getTypeParameters().length == 0, "Parameterized types unsupported: %s", - targetType.getSimpleName()); + targetType.getSimpleName()); checkState(targetType.getInterfaces().length <= 1, "Only single inheritance supported: %s", - targetType.getSimpleName()); + targetType.getSimpleName()); if (targetType.getInterfaces().length == 1) { checkState(targetType.getInterfaces()[0].getInterfaces().length == 0, - "Only single-level inheritance supported: %s", - targetType.getSimpleName()); + "Only single-level inheritance supported: %s", + targetType.getSimpleName()); } Map result = new LinkedHashMap(); for (Method method : targetType.getMethods()) { @@ -63,7 +62,7 @@ public List parseAndValidatateMetadata(Class targetType) { } MethodMetadata metadata = parseAndValidateMetadata(targetType, method); checkState(!result.containsKey(metadata.configKey()), "Overrides unsupported: %s", - metadata.configKey()); + metadata.configKey()); result.put(metadata.configKey(), metadata); } return new ArrayList(result.values()); @@ -85,7 +84,7 @@ protected MethodMetadata parseAndValidateMetadata(Class targetType, Method me data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType())); data.configKey(Feign.configKey(targetType, method)); - if(targetType.getInterfaces().length == 1) { + if (targetType.getInterfaces().length == 1) { processAnnotationOnClass(data, targetType.getInterfaces()[0]); } processAnnotationOnClass(data, targetType); @@ -95,8 +94,8 @@ protected MethodMetadata parseAndValidateMetadata(Class targetType, Method me processAnnotationOnMethod(data, methodAnnotation, method); } checkState(data.template().method() != null, - "Method %s not annotated with HTTP method type (ex. GET, POST)", - method.getName()); + "Method %s not annotated with HTTP method type (ex. GET, POST)", + method.getName()); Class[] parameterTypes = method.getParameterTypes(); Type[] genericParameterTypes = method.getGenericParameterTypes(); @@ -111,7 +110,7 @@ protected MethodMetadata parseAndValidateMetadata(Class targetType, Method me data.urlIndex(i); } else if (!isHttpAnnotation) { checkState(data.formParams().isEmpty(), - "Body parameters cannot be used with form parameters."); + "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, genericParameterTypes[i])); @@ -119,7 +118,8 @@ protected MethodMetadata parseAndValidateMetadata(Class targetType, Method me } if (data.headerMapIndex() != null) { - checkMapString("HeaderMap", parameterTypes[data.headerMapIndex()], genericParameterTypes[data.headerMapIndex()]); + checkMapString("HeaderMap", parameterTypes[data.headerMapIndex()], + genericParameterTypes[data.headerMapIndex()]); } if (data.queryMapIndex() != null) { @@ -133,7 +133,7 @@ protected MethodMetadata parseAndValidateMetadata(Class targetType, Method me private static void checkMapString(String name, Class type, Type genericType) { checkState(Map.class.isAssignableFrom(type), - "%s parameter must be a Map: %s", name, type); + "%s parameter must be a Map: %s", name, type); checkMapKeys(name, genericType); } @@ -168,29 +168,30 @@ private static void checkMapKeys(String name, Type genericType) { /** - * Called by parseAndValidateMetadata twice, first on the declaring class, then on the - * target type (unless they are the same). + * Called by parseAndValidateMetadata twice, first on the declaring class, then on the target + * type (unless they are the same). * - * @param data metadata collected so far relating to the current java method. - * @param clz the class to process + * @param data metadata collected so far relating to the current java method. + * @param clz the class to process */ protected abstract void processAnnotationOnClass(MethodMetadata data, Class clz); /** - * @param data metadata collected so far relating to the current java method. + * @param data metadata collected so far relating to the current java method. * @param annotation annotations present on the current method annotation. - * @param method method currently being processed. + * @param method method currently being processed. */ - protected abstract void processAnnotationOnMethod(MethodMetadata data, Annotation annotation, + protected abstract void processAnnotationOnMethod(MethodMetadata data, + Annotation annotation, Method method); /** - * @param data metadata collected so far relating to the current java method. + * @param data metadata collected so far relating to the current java method. * @param annotations annotations present on the current parameter annotation. - * @param paramIndex if you find a name in {@code annotations}, call {@link - * #nameParam(MethodMetadata, String, int)} with this as the last parameter. + * @param paramIndex if you find a name in {@code annotations}, call + * {@link #nameParam(MethodMetadata, String, int)} with this as the last parameter. * @return true if you called {@link #nameParam(MethodMetadata, String, int)} after finding an - * http-relevant annotation. + * http-relevant annotation. */ protected abstract boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, @@ -213,8 +214,7 @@ protected Collection addTemplatedParam(Collection possiblyNull, * links a parameter name to its index in the method signature. */ protected void nameParam(MethodMetadata data, String name, int i) { - Collection - names = + Collection names = data.indexToName().containsKey(i) ? data.indexToName().get(i) : new ArrayList(); names.add(name); data.indexToName().put(i, names); @@ -227,7 +227,7 @@ protected void processAnnotationOnClass(MethodMetadata data, Class targetType if (targetType.isAnnotationPresent(Headers.class)) { String[] headersOnType = targetType.getAnnotation(Headers.class).value(); checkState(headersOnType.length > 0, "Headers annotation was empty on type %s.", - targetType.getName()); + targetType.getName()); Map> headers = toMap(headersOnType); headers.putAll(data.template().headers()); data.template().headers(null); // to clear @@ -236,13 +236,14 @@ protected void processAnnotationOnClass(MethodMetadata data, Class targetType } @Override - protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, + protected void processAnnotationOnMethod(MethodMetadata data, + Annotation methodAnnotation, Method method) { Class annotationType = methodAnnotation.annotationType(); if (annotationType == RequestLine.class) { String requestLine = RequestLine.class.cast(methodAnnotation).value(); checkState(emptyToNull(requestLine) != null, - "RequestLine annotation was empty on method %s.", method.getName()); + "RequestLine annotation was empty on method %s.", method.getName()); if (requestLine.indexOf(' ') == -1) { checkState(requestLine.indexOf('/') == -1, "RequestLine annotation didn't start with an HTTP verb on method %s.", @@ -261,12 +262,13 @@ protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodA } data.template().decodeSlash(RequestLine.class.cast(methodAnnotation).decodeSlash()); - data.template().collectionFormat(RequestLine.class.cast(methodAnnotation).collectionFormat()); + data.template() + .collectionFormat(RequestLine.class.cast(methodAnnotation).collectionFormat()); } else if (annotationType == Body.class) { String body = Body.class.cast(methodAnnotation).value(); checkState(emptyToNull(body) != null, "Body annotation was empty on method %s.", - method.getName()); + method.getName()); if (body.indexOf('{') == -1) { data.template().body(body); } else { @@ -275,13 +277,14 @@ protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodA } else if (annotationType == Headers.class) { String[] headersOnMethod = Headers.class.cast(methodAnnotation).value(); checkState(headersOnMethod.length > 0, "Headers annotation was empty on method %s.", - method.getName()); + method.getName()); data.template().headers(toMap(headersOnMethod)); } } @Override - protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, + protected boolean processAnnotationsOnParameter(MethodMetadata data, + Annotation[] annotations, int paramIndex) { boolean isHttpAnnotation = false; for (Annotation annotation : annotations) { @@ -289,7 +292,8 @@ protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[ if (annotationType == Param.class) { Param paramAnnotation = (Param) annotation; String name = paramAnnotation.value(); - checkState(emptyToNull(name) != null, "Param annotation was empty on param %s.", paramIndex); + checkState(emptyToNull(name) != null, "Param annotation was empty on param %s.", + paramIndex); nameParam(data, name, paramIndex); Class expander = paramAnnotation.expander(); if (expander != Param.ToStringExpander.class) { @@ -304,12 +308,14 @@ protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[ data.formParams().add(name); } } else if (annotationType == QueryMap.class) { - checkState(data.queryMapIndex() == null, "QueryMap annotation was present on multiple parameters."); + 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."); + checkState(data.headerMapIndex() == null, + "HeaderMap annotation was present on multiple parameters."); data.headerMapIndex(paramIndex); isHttpAnnotation = true; } @@ -336,8 +342,7 @@ private static boolean searchMapValuesContainsSubstring(Map> toMap(String[] input) { - Map> - result = + Map> result = new LinkedHashMap>(input.length); for (String header : input) { int colon = header.indexOf(':'); diff --git a/core/src/main/java/feign/DefaultMethodHandler.java b/core/src/main/java/feign/DefaultMethodHandler.java index b67e0b7dc8..1afd1c1101 100644 --- a/core/src/main/java/feign/DefaultMethodHandler.java +++ b/core/src/main/java/feign/DefaultMethodHandler.java @@ -15,19 +15,18 @@ import feign.InvocationHandlerFactory.MethodHandler; import org.jvnet.animal_sniffer.IgnoreJRERequirement; - import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles.Lookup; import java.lang.reflect.Field; import java.lang.reflect.Method; /** - * Handles default methods by directly invoking the default method code on the interface. - * The bindTo method must be called on the result before invoke is called. + * Handles default methods by directly invoking the default method code on the interface. The bindTo + * method must be called on the result before invoke is called. */ @IgnoreJRERequirement final class DefaultMethodHandler implements MethodHandler { - // Uses Java 7 MethodHandle based reflection. As default methods will only exist when + // Uses Java 7 MethodHandle based reflection. As default methods will only exist when // run on a Java 8 JVM this will not affect use on legacy JVMs. // When Feign upgrades to Java 7, remove the @IgnoreJRERequirement annotation. private final MethodHandle unboundHandle; @@ -51,24 +50,27 @@ public DefaultMethodHandler(Method defaultMethod) { } /** - * Bind this handler to a proxy object. After bound, DefaultMethodHandler#invoke will act as if it was called - * on the proxy object. Must be called once and only once for a given instance of DefaultMethodHandler + * Bind this handler to a proxy object. After bound, DefaultMethodHandler#invoke will act as if it + * was called on the proxy object. Must be called once and only once for a given instance of + * DefaultMethodHandler */ public void bindTo(Object proxy) { - if(handle != null) { - throw new IllegalStateException("Attempted to rebind a default method handler that was already bound"); + if (handle != null) { + throw new IllegalStateException( + "Attempted to rebind a default method handler that was already bound"); } handle = unboundHandle.bindTo(proxy); } /** - * Invoke this method. DefaultMethodHandler#bindTo must be called before the first - * time invoke is called. + * Invoke this method. DefaultMethodHandler#bindTo must be called before the first time invoke is + * called. */ @Override public Object invoke(Object[] argv) throws Throwable { - if(handle == null) { - throw new IllegalStateException("Default method handler invoked before proxy has been bound."); + if (handle == null) { + throw new IllegalStateException( + "Default method handler invoked before proxy has been bound."); } return handle.invokeWithArguments(argv); } diff --git a/core/src/main/java/feign/Feign.java b/core/src/main/java/feign/Feign.java index 379ee46673..fbd49486cb 100644 --- a/core/src/main/java/feign/Feign.java +++ b/core/src/main/java/feign/Feign.java @@ -18,7 +18,6 @@ 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; @@ -28,8 +27,8 @@ 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 + * 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 { @@ -39,11 +38,13 @@ public static Builder builder() { } /** - * Configuration keys are formatted as unresolved see tags. This method exposes that format, in case you need to create the same value as + * 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: + *

+ * Here are some sample encodings: * *

    * 
    @@ -161,11 +162,13 @@ public Builder mapAndDecode(ResponseMapper mapper, Decoder decoder) { * 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. + *

    + * 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 + *

    + * 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}. @@ -216,15 +219,14 @@ public Builder invocationHandlerFactory(InvocationHandlerFactory invocationHandl } /** - * 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}. + * 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). + *

    + * 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 * @@ -245,10 +247,10 @@ public T target(Target target) { public Feign build() { SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger, - logLevel, decode404, closeAfterDecode); + logLevel, decode404, closeAfterDecode); ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, - errorDecoder, synchronousMethodHandlerFactory); + errorDecoder, synchronousMethodHandlerFactory); return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder); } } diff --git a/core/src/main/java/feign/FeignException.java b/core/src/main/java/feign/FeignException.java index 794d02b28e..59cf7c8665 100644 --- a/core/src/main/java/feign/FeignException.java +++ b/core/src/main/java/feign/FeignException.java @@ -14,7 +14,6 @@ package feign; import java.io.IOException; - import static java.lang.String.format; /** diff --git a/core/src/main/java/feign/HeaderMap.java b/core/src/main/java/feign/HeaderMap.java index 539dddd011..ad434c7bae 100644 --- a/core/src/main/java/feign/HeaderMap.java +++ b/core/src/main/java/feign/HeaderMap.java @@ -16,51 +16,46 @@ import java.lang.annotation.Retention; import java.util.List; import java.util.Map; - import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; /** - * A template parameter that can be applied to a Map that contains header - * entries, where the keys are Strings that are the header field names and the - * values are the header field values. The headers specified by the map will be - * applied to the request after all other processing, and will take precedence - * over any previously specified header parameters. - *
    - * This parameter is useful in cases where different header fields and values - * need to be set on an API method on a per-request basis in a thread-safe manner - * and independently of Feign client construction. A concrete example of a case - * like this are custom metadata header fields (e.g. as "x-amz-meta-*" or - * "x-goog-meta-*") where the header field names are dynamic and the range of keys - * cannot be determined a priori. The {@link Headers} annotation does not allow this - * because the header fields that it defines are static (it is not possible to add or - * remove fields on a per-request basis), and doing this using a custom {@link Target} - * or {@link RequestInterceptor} can be cumbersome (it requires more code for per-method - * customization, it is difficult to implement in a thread-safe manner and it requires - * customization when the Feign client for the API is built). - *
    + * A template parameter that can be applied to a Map that contains header entries, where the keys + * are Strings that are the header field names and the values are the header field values. The + * headers specified by the map will be applied to the request after all other processing, and will + * take precedence over any previously specified header parameters.
    + * This parameter is useful in cases where different header fields and values need to be set on an + * API method on a per-request basis in a thread-safe manner and independently of Feign client + * construction. A concrete example of a case like this are custom metadata header fields (e.g. as + * "x-amz-meta-*" or "x-goog-meta-*") where the header field names are dynamic and the range of keys + * cannot be determined a priori. The {@link Headers} annotation does not allow this because the + * header fields that it defines are static (it is not possible to add or remove fields on a + * per-request basis), and doing this using a custom {@link Target} or {@link RequestInterceptor} + * can be cumbersome (it requires more code for per-method customization, it is difficult to + * implement in a thread-safe manner and it requires customization when the Feign client for the API + * is built).
    + * *
      * ...
      * @RequestLine("GET /servers/{serverId}")
      * void get(@Param("serverId") String serverId, @HeaderMap Map);
      * ...
      * 
    - * The annotated parameter must be an instance of {@link Map}, and the keys must - * be Strings. The header field value of a key will be the value of its toString - * method, except in the following cases: - *
    + * + * The annotated parameter must be an instance of {@link Map}, and the keys must be Strings. The + * header field value of a key will be the value of its toString method, except in the following + * cases:
    *
    *
      - *
    • if the value is null, the value will remain null (rather than converting - * to the String "null") - *
    • if the value is an {@link Iterable}, it is converted to a {@link List} of - * String objects where each value in the list is either null if the original - * value was null or the value's toString representation otherwise. + *
    • if the value is null, the value will remain null (rather than converting to the String + * "null") + *
    • if the value is an {@link Iterable}, it is converted to a {@link List} of String objects + * where each value in the list is either null if the original value was null or the value's + * toString representation otherwise. *
    *
    - * Once this conversion is applied, the query keys and resulting String values - * follow the same contract as if they were set using - * {@link RequestTemplate#header(String, String...)}. + * Once this conversion is applied, the query keys and resulting String values follow the same + * contract as if they were set using {@link RequestTemplate#header(String, String...)}. */ @Retention(RUNTIME) @java.lang.annotation.Target(PARAMETER) diff --git a/core/src/main/java/feign/Headers.java b/core/src/main/java/feign/Headers.java index 75552bca9e..c2431c9b85 100644 --- a/core/src/main/java/feign/Headers.java +++ b/core/src/main/java/feign/Headers.java @@ -15,13 +15,14 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; - import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; /** - * Expands headers supplied in the {@code value}. Variables to the the right of the colon are expanded.
    + * Expands headers supplied in the {@code value}. Variables to the the right of the colon are + * expanded.
    + * *
      * @Headers("Content-Type: application/xml")
      * interface SoapApi {
    @@ -37,13 +38,21 @@
      * }) void post(@Param("token") String token);
      * ...
      * 
    - *
    Notes: + * + *
    + * Notes: *
      - *
    • If you'd like curly braces literally in the header, urlencode them first.
    • - *
    • Headers do not overwrite each other. All headers with the same name will be included - * in the request.
    • + *
    • If you'd like curly braces literally in the header, urlencode them first.
    • + *
    • Headers do not overwrite each other. All headers with the same name will be included in the + * request.
    • *
    - *
    Relationship to JAXRS

    The following two forms are identical.

    Feign: + *
    + * Relationship to JAXRS
    + *
    + * The following two forms are identical.
    + *
    + * Feign: + * *
      * @RequestLine("POST /")
      * @Headers({
    @@ -51,7 +60,10 @@
      * }) void post(@Named("token") String token);
      * ...
      * 
    - *
    JAX-RS: + * + *
    + * JAX-RS: + * *
      * @POST @Path("/")
      * void post(@HeaderParam("X-Ping") String token);
    diff --git a/core/src/main/java/feign/Logger.java b/core/src/main/java/feign/Logger.java
    index 3ce3188f70..e34dd551c6 100644
    --- a/core/src/main/java/feign/Logger.java
    +++ b/core/src/main/java/feign/Logger.java
    @@ -19,13 +19,12 @@
     import java.util.logging.FileHandler;
     import java.util.logging.LogRecord;
     import java.util.logging.SimpleFormatter;
    -
     import static feign.Util.UTF_8;
     import static feign.Util.decodeOrDefault;
     import static feign.Util.valuesOrEmpty;
     
     /**
    - * Simple logging abstraction for debug messages.  Adapted from {@code retrofit.RestAdapter.Log}.
    + * Simple logging abstraction for debug messages. Adapted from {@code retrofit.RestAdapter.Log}.
      */
     public abstract class Logger {
     
    @@ -39,8 +38,8 @@ protected static String methodTag(String configKey) {
        * request and response text.
        *
        * @param configKey value of {@link Feign#configKey(Class, java.lang.reflect.Method)}
    -   * @param format    {@link java.util.Formatter format string}
    -   * @param args      arguments applied to {@code format}
    +   * @param format {@link java.util.Formatter format string}
    +   * @param args arguments applied to {@code format}
        */
       protected abstract void log(String configKey, String format, Object... args);
     
    @@ -58,8 +57,7 @@ protected void logRequest(String configKey, Level logLevel, Request request) {
           if (request.body() != null) {
             bodyLength = request.body().length;
             if (logLevel.ordinal() >= Level.FULL.ordinal()) {
    -          String
    -              bodyText =
    +          String bodyText =
                   request.charset() != null ? new String(request.body(), request.charset()) : null;
               log(configKey, ""); // CRLF
               log(configKey, "%s", bodyText != null ? bodyText : "Binary data");
    @@ -73,10 +71,14 @@ protected void logRetry(String configKey, Level logLevel) {
         log(configKey, "---> RETRYING");
       }
     
    -  protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response,
    -                                            long elapsedTime) throws IOException {
    -    String reason = response.reason() != null && logLevel.compareTo(Level.NONE) > 0 ?
    -        " " + response.reason() : "";
    +  protected Response logAndRebufferResponse(String configKey,
    +                                            Level logLevel,
    +                                            Response response,
    +                                            long elapsedTime)
    +      throws IOException {
    +    String reason =
    +        response.reason() != null && logLevel.compareTo(Level.NONE) > 0 ? " " + response.reason()
    +            : "";
         int status = response.status();
         log(configKey, "<--- HTTP/1.1 %s%s (%sms)", status, reason, elapsedTime);
         if (logLevel.ordinal() >= Level.HEADERS.ordinal()) {
    @@ -108,7 +110,10 @@ protected Response logAndRebufferResponse(String configKey, Level logLevel, Resp
         return response;
       }
     
    -  protected IOException logIOException(String configKey, Level logLevel, IOException ioe, long elapsedTime) {
    +  protected IOException logIOException(String configKey,
    +                                       Level logLevel,
    +                                       IOException ioe,
    +                                       long elapsedTime) {
         log(configKey, "<--- ERROR %s: %s (%sms)", ioe.getClass().getSimpleName(), ioe.getMessage(),
             elapsedTime);
         if (logLevel.ordinal() >= Level.FULL.ordinal()) {
    @@ -157,8 +162,7 @@ protected void log(String configKey, String format, Object... args) {
        */
       public static class JavaLogger extends Logger {
     
    -    final java.util.logging.Logger
    -        logger =
    +    final java.util.logging.Logger logger =
             java.util.logging.Logger.getLogger(Logger.class.getName());
     
         @Override
    @@ -169,8 +173,11 @@ protected void logRequest(String configKey, Level logLevel, Request request) {
         }
     
         @Override
    -    protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response,
    -                                              long elapsedTime) throws IOException {
    +    protected Response logAndRebufferResponse(String configKey,
    +                                              Level logLevel,
    +                                              Response response,
    +                                              long elapsedTime)
    +        throws IOException {
           if (logger.isLoggable(java.util.logging.Level.FINE)) {
             return super.logAndRebufferResponse(configKey, logLevel, response, elapsedTime);
           }
    @@ -185,8 +192,8 @@ protected void log(String configKey, String format, Object... args) {
         }
     
         /**
    -     * Helper that configures java.util.logging to sanely log messages at FINE level without additional
    -     * formatting.
    +     * Helper that configures java.util.logging to sanely log messages at FINE level without
    +     * additional formatting.
          */
         public JavaLogger appendToFile(String logfile) {
           logger.setLevel(java.util.logging.Level.FINE);
    @@ -209,17 +216,18 @@ public String format(LogRecord record) {
       public static class NoOpLogger extends Logger {
     
         @Override
    -    protected void logRequest(String configKey, Level logLevel, Request request) {
    -    }
    +    protected void logRequest(String configKey, Level logLevel, Request request) {}
     
         @Override
    -    protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response,
    -                                              long elapsedTime) throws IOException {
    +    protected Response logAndRebufferResponse(String configKey,
    +                                              Level logLevel,
    +                                              Response response,
    +                                              long elapsedTime)
    +        throws IOException {
           return response;
         }
     
         @Override
    -    protected void log(String configKey, String format, Object... args) {
    -    }
    +    protected void log(String configKey, String format, Object... args) {}
       }
     }
    diff --git a/core/src/main/java/feign/MethodMetadata.java b/core/src/main/java/feign/MethodMetadata.java
    index 431b3f86c6..d5876ca60c 100644
    --- a/core/src/main/java/feign/MethodMetadata.java
    +++ b/core/src/main/java/feign/MethodMetadata.java
    @@ -20,7 +20,6 @@
     import java.util.LinkedHashMap;
     import java.util.List;
     import java.util.Map;
    -
     import feign.Param.Expander;
     
     public final class MethodMetadata implements Serializable {
    @@ -43,8 +42,7 @@ public final class MethodMetadata implements Serializable {
       private Map indexToEncoded = new LinkedHashMap();
       private transient Map indexToExpander;
     
    -  MethodMetadata() {
    -  }
    +  MethodMetadata() {}
     
       /**
        * Used as a reference to this method. For example, {@link Logger#log(String, String, Object...)
    diff --git a/core/src/main/java/feign/Param.java b/core/src/main/java/feign/Param.java
    index 0ce2ebd7c5..de1cd11366 100644
    --- a/core/src/main/java/feign/Param.java
    +++ b/core/src/main/java/feign/Param.java
    @@ -14,13 +14,12 @@
     package feign;
     
     import java.lang.annotation.Retention;
    -
     import static java.lang.annotation.ElementType.PARAMETER;
     import static java.lang.annotation.RetentionPolicy.RUNTIME;
     
     /**
    - * A named template parameter applied to {@link Headers}, {@linkplain RequestLine} or {@linkplain
    - * Body}
    + * A named template parameter applied to {@link Headers}, {@linkplain RequestLine} or
    + * {@linkplain Body}
      */
     @Retention(RUNTIME)
     @java.lang.annotation.Target(PARAMETER)
    @@ -37,8 +36,8 @@
       Class expander() default ToStringExpander.class;
     
       /**
    -   * Specifies whether argument is already encoded
    -   * The value is ignored for headers (headers are never encoded)
    +   * Specifies whether argument is already encoded The value is ignored for headers (headers are
    +   * never encoded)
        *
        * @see QueryMap#encoded
        */
    diff --git a/core/src/main/java/feign/QueryMap.java b/core/src/main/java/feign/QueryMap.java
    index 67368e4f2f..af8b92be13 100644
    --- a/core/src/main/java/feign/QueryMap.java
    +++ b/core/src/main/java/feign/QueryMap.java
    @@ -16,18 +16,17 @@
     import java.lang.annotation.Retention;
     import java.util.List;
     import java.util.Map;
    -
     import static java.lang.annotation.ElementType.PARAMETER;
     import static java.lang.annotation.RetentionPolicy.RUNTIME;
     
     /**
    - * A template parameter that can be applied to a Map that contains query
    - * parameters, where the keys are Strings that are the parameter names and the
    - * values are the parameter values. The queries specified by the map will be
    - * applied to the request after all other processing, and will take precedence
    - * over any previously specified query parameters. It is not necessary to
    - * reference the parameter map as a variable. 
    + * A template parameter that can be applied to a Map that contains query parameters, where the keys + * are Strings that are the parameter names and the values are the parameter values. The queries + * specified by the map will be applied to the request after all other processing, and will take + * precedence over any previously specified query parameters. It is not necessary to reference the + * parameter map as a variable.
    *
    + * *
      * ...
      * @RequestLine("POST /servers")
    @@ -38,30 +37,29 @@
      * void get(@Param("serverId") String serverId, @Param("count") int count, @QueryMap Map);
      * ...
      * 
    - * The annotated parameter must be an instance of {@link Map}, and the keys must - * be Strings. The query value of a key will be the value of its toString - * method, except in the following cases: + * + * The annotated parameter must be an instance of {@link Map}, and the keys must be Strings. The + * query value of a key will be the value of its toString method, except in the following cases: *
    *
    *
      - *
    • if the value is null, the value will remain null (rather than converting - * to the String "null") - *
    • if the value is an {@link Iterable}, it is converted to a {@link List} of - * String objects where each value in the list is either null if the original - * value was null or the value's toString representation otherwise. + *
    • if the value is null, the value will remain null (rather than converting to the String + * "null") + *
    • if the value is an {@link Iterable}, it is converted to a {@link List} of String objects + * where each value in the list is either null if the original value was null or the value's + * toString representation otherwise. *
    *
    - * Once this conversion is applied, the query keys and resulting String values - * follow the same contract as if they were set using - * {@link RequestTemplate#query(String, String...)}. + * Once this conversion is applied, the query keys and resulting String values follow the same + * contract as if they were set using {@link RequestTemplate#query(String, String...)}. */ @Retention(RUNTIME) @java.lang.annotation.Target(PARAMETER) public @interface QueryMap { - /** - * Specifies whether parameter names and values are already encoded. - * - * @see Param#encoded - */ - boolean encoded() default false; + /** + * Specifies whether parameter names and values are already encoded. + * + * @see Param#encoded + */ + boolean encoded() default false; } diff --git a/core/src/main/java/feign/QueryMapEncoder.java b/core/src/main/java/feign/QueryMapEncoder.java index b6909823b4..fb7de67d6f 100644 --- a/core/src/main/java/feign/QueryMapEncoder.java +++ b/core/src/main/java/feign/QueryMapEncoder.java @@ -32,7 +32,7 @@ public interface QueryMapEncoder { * @param object the object to encode * @return the map represented by the object */ - Map encode (Object object); + Map encode(Object object); class Default implements QueryMapEncoder { @@ -40,7 +40,7 @@ class Default implements QueryMapEncoder { new HashMap, ObjectParamMetadata>(); @Override - public Map encode (Object object) throws EncodeException { + public Map encode(Object object) throws EncodeException { try { ObjectParamMetadata metadata = getMetadata(object.getClass()); Map fieldNameToValue = new HashMap(); @@ -69,7 +69,7 @@ private static class ObjectParamMetadata { private final List objectFields; - private ObjectParamMetadata (List objectFields) { + private ObjectParamMetadata(List objectFields) { this.objectFields = Collections.unmodifiableList(objectFields); } diff --git a/core/src/main/java/feign/ReflectiveFeign.java b/core/src/main/java/feign/ReflectiveFeign.java index 91332e8671..36f14811aa 100644 --- a/core/src/main/java/feign/ReflectiveFeign.java +++ b/core/src/main/java/feign/ReflectiveFeign.java @@ -18,7 +18,6 @@ import java.lang.reflect.Proxy; import java.util.*; import java.util.Map.Entry; - import feign.InvocationHandlerFactory.MethodHandler; import feign.Param.Expander; import feign.Request.Options; @@ -26,7 +25,6 @@ import feign.codec.EncodeException; import feign.codec.Encoder; import feign.codec.ErrorDecoder; - import static feign.Util.checkArgument; import static feign.Util.checkNotNull; @@ -36,7 +34,8 @@ public class ReflectiveFeign extends Feign { private final InvocationHandlerFactory factory; private final QueryMapEncoder queryMapEncoder; - ReflectiveFeign(ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory, QueryMapEncoder queryMapEncoder) { + ReflectiveFeign(ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory, + QueryMapEncoder queryMapEncoder) { this.targetToHandlersByName = targetToHandlersByName; this.factory = factory; this.queryMapEncoder = queryMapEncoder; @@ -56,7 +55,7 @@ public T newInstance(Target target) { for (Method method : target.type().getMethods()) { if (method.getDeclaringClass() == Object.class) { continue; - } else if(Util.isDefault(method)) { + } else if (Util.isDefault(method)) { DefaultMethodHandler handler = new DefaultMethodHandler(method); defaultMethodHandlers.add(handler); methodToHandler.put(method, handler); @@ -65,9 +64,10 @@ public T newInstance(Target target) { } } InvocationHandler handler = factory.create(target, methodToHandler); - T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler); + T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), + new Class[] {target.type()}, handler); - for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) { + for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) { defaultMethodHandler.bindTo(proxy); } return proxy; @@ -87,8 +87,7 @@ static class FeignInvocationHandler implements InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("equals".equals(method.getName())) { try { - Object - otherHandler = + Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null; return equals(otherHandler); } catch (IllegalArgumentException e) { @@ -162,7 +161,7 @@ public Map apply(Target key) { buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder); } result.put(md.configKey(), - factory.create(key, md, buildTemplate, options, decoder, errorDecoder)); + factory.create(key, md, buildTemplate, options, decoder, errorDecoder)); } return result; } @@ -230,15 +229,16 @@ public RequestTemplate create(Object[] argv) { } if (metadata.headerMapIndex() != null) { - template = addHeaderMapHeaders((Map) argv[metadata.headerMapIndex()], template); + template = + addHeaderMapHeaders((Map) argv[metadata.headerMapIndex()], template); } return template; } - private Map toQueryMap (Object value) { - if (value instanceof Map) { - return (Map)value; + private Map toQueryMap(Object value) { + if (value instanceof Map) { + return (Map) value; } try { return queryMapEncoder.encode(value); @@ -257,7 +257,7 @@ private Object expandElements(Expander expander, Object value) { private List expandIterable(Expander expander, Iterable value) { List values = new ArrayList(); for (Object element : value) { - if (element!=null) { + if (element != null) { values.add(expander.expand(element)); } } @@ -265,7 +265,8 @@ private List expandIterable(Expander expander, Iterable value) { } @SuppressWarnings("unchecked") - private RequestTemplate addHeaderMapHeaders(Map headerMap, RequestTemplate mutable) { + private RequestTemplate addHeaderMapHeaders(Map headerMap, + RequestTemplate mutable) { for (Entry currEntry : headerMap.entrySet()) { Collection values = new ArrayList(); @@ -286,7 +287,8 @@ private RequestTemplate addHeaderMapHeaders(Map headerMap, Reque } @SuppressWarnings("unchecked") - private RequestTemplate addQueryMapQueryParameters(Map queryMap, RequestTemplate mutable) { + private RequestTemplate addQueryMapQueryParameters(Map queryMap, + RequestTemplate mutable) { for (Entry currEntry : queryMap.entrySet()) { Collection values = new ArrayList(); @@ -296,18 +298,23 @@ private RequestTemplate addQueryMapQueryParameters(Map queryMap, Iterator iter = ((Iterable) currValue).iterator(); while (iter.hasNext()) { Object nextObject = iter.next(); - values.add(nextObject == null ? null : encoded ? nextObject.toString() : RequestTemplate.urlEncode(nextObject.toString())); + values.add(nextObject == null ? null + : encoded ? nextObject.toString() + : RequestTemplate.urlEncode(nextObject.toString())); } } else { - values.add(currValue == null ? null : encoded ? currValue.toString() : RequestTemplate.urlEncode(currValue.toString())); + values.add(currValue == null ? null + : encoded ? currValue.toString() : RequestTemplate.urlEncode(currValue.toString())); } - mutable.query(true, encoded ? currEntry.getKey() : RequestTemplate.urlEncode(currEntry.getKey()), values); + mutable.query(true, + encoded ? currEntry.getKey() : RequestTemplate.urlEncode(currEntry.getKey()), values); } return mutable; } - protected RequestTemplate resolve(Object[] argv, RequestTemplate mutable, + protected RequestTemplate resolve(Object[] argv, + RequestTemplate mutable, Map variables) { // Resolving which variable names are already encoded using their indices Map variableToEncoded = new LinkedHashMap(); @@ -325,13 +332,15 @@ private static class BuildFormEncodedTemplateFromArgs extends BuildTemplateByRes private final Encoder encoder; - private BuildFormEncodedTemplateFromArgs(MethodMetadata metadata, Encoder encoder, QueryMapEncoder queryMapEncoder) { + private BuildFormEncodedTemplateFromArgs(MethodMetadata metadata, Encoder encoder, + QueryMapEncoder queryMapEncoder) { super(metadata, queryMapEncoder); this.encoder = encoder; } @Override - protected RequestTemplate resolve(Object[] argv, RequestTemplate mutable, + protected RequestTemplate resolve(Object[] argv, + RequestTemplate mutable, Map variables) { Map formVariables = new LinkedHashMap(); for (Entry entry : variables.entrySet()) { @@ -354,13 +363,15 @@ private static class BuildEncodedTemplateFromArgs extends BuildTemplateByResolvi private final Encoder encoder; - private BuildEncodedTemplateFromArgs(MethodMetadata metadata, Encoder encoder, QueryMapEncoder queryMapEncoder) { + private BuildEncodedTemplateFromArgs(MethodMetadata metadata, Encoder encoder, + QueryMapEncoder queryMapEncoder) { super(metadata, queryMapEncoder); this.encoder = encoder; } @Override - protected RequestTemplate resolve(Object[] argv, RequestTemplate mutable, + protected RequestTemplate resolve(Object[] argv, + RequestTemplate mutable, Map variables) { Object body = argv[metadata.bodyIndex()]; checkArgument(body != null, "Body parameter %s was null", metadata.bodyIndex()); diff --git a/core/src/main/java/feign/Request.java b/core/src/main/java/feign/Request.java index 8890d8102e..f3b2aa12c8 100644 --- a/core/src/main/java/feign/Request.java +++ b/core/src/main/java/feign/Request.java @@ -17,7 +17,6 @@ import java.nio.charset.Charset; import java.util.Collection; import java.util.Map; - import static feign.Util.checkNotNull; import static feign.Util.valuesOrEmpty; @@ -30,8 +29,11 @@ public final class Request { * No parameters can be null except {@code body} and {@code charset}. All parameters must be * effectively immutable, via safe copies, not mutating or otherwise. */ - public static Request create(String method, String url, Map> headers, - byte[] body, Charset charset) { + public static Request create(String method, + String url, + Map> headers, + byte[] body, + Charset charset) { return new Request(method, url, headers, body, charset); } @@ -42,7 +44,7 @@ public static Request create(String method, String url, Map> headers, byte[] body, - Charset charset) { + Charset charset) { this.method = checkNotNull(method, "method of %s", url); this.url = checkNotNull(url, "url"); this.headers = checkNotNull(headers, "headers of %s %s", method, url); @@ -66,7 +68,7 @@ public Map> headers() { } /** - * The character set with which the body is encoded, or null if unknown or not applicable. When + * The character set with which the body is encoded, or null if unknown or not applicable. When * this is present, you can use {@code new String(req.body(), req.charset())} to access the body * as a String. */ @@ -75,7 +77,7 @@ public Charset charset() { } /** - * If present, this is the replayable body to send to the server. In some cases, this may be + * If present, this is the replayable body to send to the server. In some cases, this may be * interpretable as text. * * @see #charset() @@ -99,7 +101,10 @@ public String toString() { return builder.toString(); } - /* Controls the per-request settings currently required to be implemented by all {@link Client clients} */ + /* + * Controls the per-request settings currently required to be implemented by all {@link Client + * clients} + */ public static class Options { private final int connectTimeoutMillis; @@ -112,7 +117,7 @@ public Options(int connectTimeoutMillis, int readTimeoutMillis, boolean followRe this.followRedirects = followRedirects; } - public Options(int connectTimeoutMillis, int readTimeoutMillis){ + public Options(int connectTimeoutMillis, int readTimeoutMillis) { this(connectTimeoutMillis, readTimeoutMillis, true); } diff --git a/core/src/main/java/feign/RequestInterceptor.java b/core/src/main/java/feign/RequestInterceptor.java index 8e8deb219b..24493f5620 100644 --- a/core/src/main/java/feign/RequestInterceptor.java +++ b/core/src/main/java/feign/RequestInterceptor.java @@ -15,21 +15,35 @@ /** * Zero or more {@code RequestInterceptors} may be configured for purposes such as adding headers to - * all requests. No guarantees are give with regards to the order that interceptors are applied. + * all requests. No guarantees are give with regards to the order that interceptors are applied. * Once interceptors are applied, {@link Target#apply(RequestTemplate)} is called to create the - * immutable http request sent via {@link Client#execute(Request, feign.Request.Options)}.

    + * immutable http request sent via {@link Client#execute(Request, feign.Request.Options)}.
    + *
    * For example:
    + * *
      * public void apply(RequestTemplate input) {
    - *     input.header("X-Auth", currentToken);
    + *   input.header("X-Auth", currentToken);
      * }
      * 
    - *

    Configuration

    {@code RequestInterceptors} are configured via {@link - * Feign.Builder#requestInterceptors}.

    Implementation notes

    Do not add - * parameters, such as {@code /path/{foo}/bar } in your implementation of {@link - * #apply(RequestTemplate)}.
    Interceptors are applied after the template's parameters are - * {@link RequestTemplate#resolve(java.util.Map) resolved}. This is to ensure that you can - * implement signatures are interceptors.


    Relationship to Retrofit 1.x

    + * + *
    + *
    + * Configuration
    + *
    + * {@code RequestInterceptors} are configured via {@link Feign.Builder#requestInterceptors}.
    + *
    + * Implementation notes
    + *
    + * Do not add parameters, such as {@code /path/{foo}/bar } in your implementation of + * {@link #apply(RequestTemplate)}.
    + * Interceptors are applied after the template's parameters are + * {@link RequestTemplate#resolve(java.util.Map) resolved}. This is to ensure that you can implement + * signatures are interceptors.
    + *
    + *
    + * Relationship to Retrofit 1.x
    + *
    * This class is similar to {@code RequestInterceptor.intercept()}, except that the implementation * can read, remove, or otherwise mutate any part of the request template. */ diff --git a/core/src/main/java/feign/RequestLine.java b/core/src/main/java/feign/RequestLine.java index 076506c104..2398f51dca 100644 --- a/core/src/main/java/feign/RequestLine.java +++ b/core/src/main/java/feign/RequestLine.java @@ -14,13 +14,13 @@ package feign; import java.lang.annotation.Retention; - import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; /** * Expands the request-line supplied in the {@code value}, permitting path and query variables, or * just the http method.
    + * *
      * ...
      * @RequestLine("POST /servers")
    @@ -34,21 +34,33 @@
      * Response getNext(URI nextLink);
      * ...
      * 
    - * HTTP version suffix is optional, but permitted. There are no guarantees this version will impact + * + * HTTP version suffix is optional, but permitted. There are no guarantees this version will impact * that sent by the client.
    + * *
      * @RequestLine("POST /servers HTTP/1.1")
      * ...
      * 
    - *
    Note: Query params do not overwrite each other. All queries with the same - * name will be included in the request.

    Relationship to JAXRS

    The following - * two forms are identical.
    Feign: + * + *
    + * Note: Query params do not overwrite each other. All queries with the same name + * will be included in the request.
    + *
    + * Relationship to JAXRS
    + *
    + * The following two forms are identical.
    + * Feign: + * *
      * @RequestLine("GET /servers/{serverId}?count={count}")
      * void get(@Param("serverId") String serverId, @Param("count") int count);
      * ...
      * 
    - *
    JAX-RS: + * + *
    + * JAX-RS: + * *
      * @GET @Path("/servers/{serverId}")
      * void get(@PathParam("serverId") String serverId, @QueryParam("count") int count);
    @@ -60,6 +72,8 @@
     public @interface RequestLine {
     
       String value();
    +
       boolean decodeSlash() default true;
    +
       CollectionFormat collectionFormat() default CollectionFormat.EXPLODED;
     }
    diff --git a/core/src/main/java/feign/RequestTemplate.java b/core/src/main/java/feign/RequestTemplate.java
    index 35752bbd7e..37c3831934 100644
    --- a/core/src/main/java/feign/RequestTemplate.java
    +++ b/core/src/main/java/feign/RequestTemplate.java
    @@ -27,7 +27,6 @@
     import java.util.List;
     import java.util.Map;
     import java.util.Map.Entry;
    -
     import static feign.Util.CONTENT_LENGTH;
     import static feign.Util.UTF_8;
     import static feign.Util.checkArgument;
    @@ -37,8 +36,12 @@
     import static feign.Util.valuesOrEmpty;
     
     /**
    - * Builds a request to an http target. Not thread safe. 


    relationship to JAXRS - * 2.0

    A combination of {@code javax.ws.rs.client.WebTarget} and {@code + * Builds a request to an http target. Not thread safe.
    + *
    + *
    + * relationship to JAXRS 2.0
    + *
    + * A combination of {@code javax.ws.rs.client.WebTarget} and {@code * javax.ws.rs.client.Invocation.Builder}, ensuring you can modify any part of the request. However, * this object is mutable, so needs to be guarded with the copy constructor. */ @@ -58,8 +61,7 @@ public final class RequestTemplate implements Serializable { private boolean decodeSlash = true; private CollectionFormat collectionFormat = CollectionFormat.EXPLODED; - public RequestTemplate() { - } + public RequestTemplate() {} /* Copy constructor. Use this when making templates. */ public RequestTemplate(RequestTemplate toCopy) { @@ -92,11 +94,12 @@ static String urlEncode(Object arg) { } private static boolean isHttpUrl(CharSequence value) { - return value.length() >= 4 && value.subSequence(0, 3).equals("http".substring(0, 3)); + return value.length() >= 4 && value.subSequence(0, 3).equals("http".substring(0, 3)); } private static CharSequence removeTrailingSlash(CharSequence charSequence) { - if (charSequence != null && charSequence.length() > 0 && charSequence.charAt(charSequence.length() - 1) == '/') { + if (charSequence != null && charSequence.length() > 0 + && charSequence.charAt(charSequence.length() - 1) == '/') { return charSequence.subSequence(0, charSequence.length() - 1); } else { return charSequence; @@ -105,11 +108,11 @@ private static CharSequence removeTrailingSlash(CharSequence charSequence) { /** * Expands a {@code template}, such as {@code username}, using the {@code variables} supplied. Any - * unresolved parameters will remain.
    Note that if you'd like curly braces literally in the - * {@code template}, urlencode them first. + * unresolved parameters will remain.
    + * Note that if you'd like curly braces literally in the {@code template}, urlencode them first. * - * @param template URI template that can be in level 1 RFC6570 - * form. + * @param template URI template that can be in level 1 + * RFC6570 form. * @param variables to the URI template * @return expanded template, leaving any unresolved parameters literal */ @@ -207,9 +210,14 @@ public RequestTemplate resolve(Map unencoded) { /** * Resolves any template parameters in the requests path, query, or headers against the supplied - * unencoded arguments.


    relationship to JAXRS 2.0

    This call is - * similar to {@code javax.ws.rs.client.WebTarget.resolveTemplates(templateValues, true)} , except - * that the template values apply to any part of the request, not just the URL + * unencoded arguments.
    + *
    + *
    + * relationship to JAXRS 2.0
    + *
    + * This call is similar to + * {@code javax.ws.rs.client.WebTarget.resolveTemplates(templateValues, true)} , except that the + * template values apply to any part of the request, not just the URL */ RequestTemplate resolve(Map unencoded, Map alreadyEncoded) { replaceQueryValues(unencoded, alreadyEncoded); @@ -226,7 +234,8 @@ RequestTemplate resolve(Map unencoded, Map alreadyEn } url = new StringBuilder(resolvedUrl); - Map> resolvedHeaders = new LinkedHashMap>(); + Map> resolvedHeaders = + new LinkedHashMap>(); for (String field : headers.keySet()) { Collection resolvedValues = new ArrayList(); for (String value : valuesOrEmpty(headers, field)) { @@ -243,7 +252,9 @@ RequestTemplate resolve(Map unencoded, Map alreadyEn return this; } - private String encodeValueIfNotEncoded(String key, Object objectValue, Map alreadyEncoded) { + private String encodeValueIfNotEncoded(String key, + Object objectValue, + Map alreadyEncoded) { String value = String.valueOf(objectValue); final Boolean isEncoded = alreadyEncoded.get(key); if (isEncoded == null || !isEncoded) { @@ -259,8 +270,7 @@ public Request request() { return Request.create( method, url + queryLine(), Collections.unmodifiableMap(safeCopy), - body, charset - ); + body, charset); } /* @see Request#method() */ @@ -269,7 +279,7 @@ public RequestTemplate method(String method) { checkArgument(method.matches("^[A-Z]+$"), "Invalid HTTP Method: %s", method); return this; } - + /* @see Request#method() */ public String method() { return method; @@ -279,7 +289,7 @@ public RequestTemplate decodeSlash(boolean decodeSlash) { this.decodeSlash = decodeSlash; return this; } - + public boolean decodeSlash() { return decodeSlash; } @@ -302,9 +312,9 @@ public RequestTemplate append(CharSequence value) { /* @see #url() */ public RequestTemplate insert(int pos, CharSequence value) { - if(isHttpUrl(value)) { + if (isHttpUrl(value)) { value = removeTrailingSlash(value); - if(url.length() > 0 && url.charAt(0) != '/') { + if (url.length() > 0 && url.charAt(0) != '/') { url.insert(0, '/'); } } @@ -317,27 +327,36 @@ public String url() { } /** - * Replaces queries with the specified {@code name} with the {@code values} supplied. - *
    Values can be passed in decoded or in url-encoded form depending on the value of the - * {@code encoded} parameter. - *
    When the {@code value} is {@code null}, all queries with the {@code configKey} are - * removed.


    relationship to JAXRS 2.0

    Like {@code WebTarget.query}, - * except the values can be templatized.
    ex.
    + * Replaces queries with the specified {@code name} with the {@code values} supplied.
    + * Values can be passed in decoded or in url-encoded form depending on the value of the + * {@code encoded} parameter.
    + * When the {@code value} is {@code null}, all queries with the {@code configKey} are removed. + *
    + *
    + *
    + * relationship to JAXRS 2.0
    + *
    + * Like {@code WebTarget.query}, except the values can be templatized.
    + * ex.
    + * *
        * template.query("Signature", "{signature}");
        * 
    - *
    Note: behavior of RequestTemplate is not consistent if a query parameter with - * unsafe characters is passed as both encoded and unencoded, although no validation is performed. - *
    ex.
    + * + *
    + * Note: behavior of RequestTemplate is not consistent if a query parameter with unsafe + * characters is passed as both encoded and unencoded, although no validation is performed.
    + * ex.
    + * *
        * template.query(true, "param[]", "value");
        * template.query(false, "param[]", "value");
        * 
    * - * @param encoded whether name and values are already url-encoded - * @param name the name of the query - * @param values can be a single null to imply removing all values. Else no values are expected - * to be null. + * @param encoded whether name and values are already url-encoded + * @param name the name of the query + * @param values can be a single null to imply removing all values. Else no values are expected to + * be null. * @see #queries() */ public RequestTemplate query(boolean encoded, String name, String... values) { @@ -351,6 +370,7 @@ public RequestTemplate query(boolean encoded, String name, Iterable valu /** * Shortcut for {@code query(false, String, String...)} + * * @see #query(boolean, String, String...) */ public RequestTemplate query(String name, String... values) { @@ -359,6 +379,7 @@ public RequestTemplate query(String name, String... values) { /** * Shortcut for {@code query(false, String, Iterable)} + * * @see #query(boolean, String, String...) */ public RequestTemplate query(String name, Iterable values) { @@ -395,8 +416,13 @@ private static String encodeIfNotVariable(String in) { /** * Replaces all existing queries with the newly supplied url decoded queries.
    - *

    relationship to JAXRS 2.0

    Like {@code WebTarget.queries}, except the - * values can be templatized.
    ex.
    + *
    + *
    + * relationship to JAXRS 2.0
    + *
    + * Like {@code WebTarget.queries}, except the values can be templatized.
    + * ex.
    + * *
        * template.queries(ImmutableMultimap.of("Signature", "{signature}"));
        * 
    @@ -439,16 +465,22 @@ public Map> queries() { /** * Replaces headers with the specified {@code configKey} with the {@code values} supplied.
    * When the {@code value} is {@code null}, all headers with the {@code configKey} are removed. - *


    relationship to JAXRS 2.0

    Like {@code WebTarget.queries} and - * {@code javax.ws.rs.client.Invocation.Builder.header}, except the values can be templatized. - *
    ex.
    + *
    + *
    + *
    + * relationship to JAXRS 2.0
    + *
    + * Like {@code WebTarget.queries} and {@code javax.ws.rs.client.Invocation.Builder.header}, except + * the values can be templatized.
    + * ex.
    + * *
        * template.query("X-Application-Version", "{version}");
        * 
    * - * @param name the name of the header + * @param name the name of the header * @param values can be a single null to imply removing all values. Else no values are expected to - * be null. + * be null. * @see #headers() */ public RequestTemplate header(String name, String... values) { @@ -472,9 +504,15 @@ public RequestTemplate header(String name, Iterable values) { } /** - * Replaces all existing headers with the newly supplied headers.


    relationship to - * JAXRS 2.0

    Like {@code Invocation.Builder.headers(MultivaluedMap)}, except the - * values can be templatized.
    ex.
    + * Replaces all existing headers with the newly supplied headers.
    + *
    + *
    + * relationship to JAXRS 2.0
    + *
    + * Like {@code Invocation.Builder.headers(MultivaluedMap)}, except the values can be templatized. + *
    + * ex.
    + * *
        * template.headers(mapOf("X-Application-Version", asList("{version}")));
        * 
    @@ -501,8 +539,8 @@ public Map> headers() { } /** - * replaces the {@link feign.Util#CONTENT_LENGTH} header.
    Usually populated by an {@link - * feign.codec.Encoder}. + * replaces the {@link feign.Util#CONTENT_LENGTH} header.
    + * Usually populated by an {@link feign.codec.Encoder}. * * @see Request#body() */ @@ -516,8 +554,8 @@ public RequestTemplate body(byte[] bodyData, Charset charset) { } /** - * replaces the {@link feign.Util#CONTENT_LENGTH} header.
    Usually populated by an {@link - * feign.codec.Encoder}. + * replaces the {@link feign.Util#CONTENT_LENGTH} header.
    + * Usually populated by an {@link feign.codec.Encoder}. * * @see Request#body() */ @@ -527,7 +565,7 @@ public RequestTemplate body(String bodyText) { } /** - * The character set with which the body is encoded, or null if unknown or not applicable. When + * The character set with which the body is encoded, or null if unknown or not applicable. When * this is present, you can use {@code new String(req.body(), req.charset())} to access the body * as a String. */ @@ -575,17 +613,17 @@ private StringBuilder pullAnyQueriesOutOfUrl(StringBuilder url) { firstQueries.putAll(queries); queries.clear(); } - //Since we decode all queries, we want to use the - //query()-method to re-add them to ensure that all - //logic (such as url-encoding) are executed, giving - //a valid queryLine() + // Since we decode all queries, we want to use the + // query()-method to re-add them to ensure that all + // logic (such as url-encoding) are executed, giving + // a valid queryLine() for (String key : firstQueries.keySet()) { Collection values = firstQueries.get(key); if (allValuesAreNull(values)) { - //Queries where all values are null will - //be ignored by the query(key, value)-method - //So we manually avoid this case here, to ensure that - //we still fulfill the contract (ex. parameters without values) + // Queries where all values are null will + // be ignored by the query(key, value)-method + // So we manually avoid this case here, to ensure that + // we still fulfill the contract (ex. parameters without values) queries.put(urlEncode(key), values); } else { query(key, values); @@ -644,7 +682,8 @@ void replaceQueryValues(Map unencoded, Map alreadyEn values.add(encodedValue); } } else { - String encodedValue = encodeValueIfNotEncoded(entry.getKey(), variableValue, alreadyEncoded); + String encodedValue = + encodeValueIfNotEncoded(entry.getKey(), variableValue, alreadyEncoded); values.add(encodedValue); } } else { diff --git a/core/src/main/java/feign/Response.java b/core/src/main/java/feign/Response.java index dfbbf7eac0..7e846dd1cb 100644 --- a/core/src/main/java/feign/Response.java +++ b/core/src/main/java/feign/Response.java @@ -26,7 +26,6 @@ import java.util.Locale; import java.util.Map; import java.util.TreeMap; - import static feign.Util.UTF_8; import static feign.Util.checkNotNull; import static feign.Util.checkState; @@ -47,73 +46,83 @@ public final class Response implements Closeable { private Response(Builder builder) { checkState(builder.status >= 200, "Invalid status code: %s", builder.status); this.status = builder.status; - this.reason = builder.reason; //nullable + this.reason = builder.reason; // nullable this.headers = Collections.unmodifiableMap(caseInsensitiveCopyOf(builder.headers)); - this.body = builder.body; //nullable - this.request = builder.request; //nullable + this.body = builder.body; // nullable + this.request = builder.request; // nullable } /** - * @deprecated To be removed in Feign 10 + * @deprecated To be removed in Feign 10 */ @Deprecated - public static Response create(int status, String reason, Map> headers, - InputStream inputStream, Integer length) { + public static Response create(int status, + String reason, + Map> headers, + InputStream inputStream, + Integer length) { return Response.builder() - .status(status) - .reason(reason) - .headers(headers) - .body(InputStreamBody.orNull(inputStream, length)) - .build(); + .status(status) + .reason(reason) + .headers(headers) + .body(InputStreamBody.orNull(inputStream, length)) + .build(); } /** - * @deprecated To be removed in Feign 10 + * @deprecated To be removed in Feign 10 */ @Deprecated - public static Response create(int status, String reason, Map> headers, + public static Response create(int status, + String reason, + Map> headers, byte[] data) { return Response.builder() - .status(status) - .reason(reason) - .headers(headers) - .body(ByteArrayBody.orNull(data)) - .build(); + .status(status) + .reason(reason) + .headers(headers) + .body(ByteArrayBody.orNull(data)) + .build(); } /** - * @deprecated To be removed in Feign 10 + * @deprecated To be removed in Feign 10 */ @Deprecated - public static Response create(int status, String reason, Map> headers, - String text, Charset charset) { + public static Response create(int status, + String reason, + Map> headers, + String text, + Charset charset) { return Response.builder() - .status(status) - .reason(reason) - .headers(headers) - .body(ByteArrayBody.orNull(text, charset)) - .build(); + .status(status) + .reason(reason) + .headers(headers) + .body(ByteArrayBody.orNull(text, charset)) + .build(); } /** - * @deprecated To be removed in Feign 10 + * @deprecated To be removed in Feign 10 */ @Deprecated - public static Response create(int status, String reason, Map> headers, + public static Response create(int status, + String reason, + Map> headers, Body body) { return Response.builder() - .status(status) - .reason(reason) - .headers(headers) - .body(body) - .build(); + .status(status) + .reason(reason) + .headers(headers) + .body(body) + .build(); } - public Builder toBuilder(){ + public Builder toBuilder() { return new Builder(this); } - public static Builder builder(){ + public static Builder builder() { return new Builder(); } @@ -124,8 +133,7 @@ public static final class Builder { Body body; Request request; - Builder() { - } + Builder() {} Builder(Response source) { this.status = source.status; @@ -135,7 +143,7 @@ public static final class Builder { this.request = source.request; } - /** @see Response#status*/ + /** @see Response#status */ public Builder status(int status) { this.status = status; return this; @@ -177,11 +185,12 @@ public Builder body(String text, Charset charset) { return this; } - /** @see Response#request - * - * NOTE: will add null check in version 10 which may require changes - * to custom feign.Client or loggers - */ + /** + * @see Response#request + * + * NOTE: will add null check in version 10 which may require changes to custom feign.Client + * or loggers + */ public Builder request(Request request) { this.request = request; return this; @@ -234,14 +243,16 @@ public Request request() { @Override public String toString() { StringBuilder builder = new StringBuilder("HTTP/1.1 ").append(status); - if (reason != null) builder.append(' ').append(reason); + if (reason != null) + builder.append(' ').append(reason); builder.append('\n'); for (String field : headers.keySet()) { for (String value : valuesOrEmpty(headers, field)) { builder.append(field).append(": ").append(value).append('\n'); } } - if (body != null) builder.append('\n').append(body); + if (body != null) + builder.append('\n').append(body); return builder.toString(); } @@ -255,8 +266,11 @@ public interface Body extends Closeable { /** * length in bytes, if known. Null if unknown or greater than {@link Integer#MAX_VALUE}. * - *


    Note
    This is an integer as - * most implementations cannot do bodies greater than 2GB. + *
    + *
    + *
    + * Note
    + * This is an integer as most implementations cannot do bodies greater than 2GB. */ Integer length(); @@ -280,6 +294,7 @@ private static final class InputStreamBody implements Response.Body { private final InputStream inputStream; private final Integer length; + private InputStreamBody(InputStream inputStream, Integer length) { this.inputStream = inputStream; this.length = length; @@ -362,8 +377,7 @@ public Reader asReader() throws IOException { } @Override - public void close() throws IOException { - } + public void close() throws IOException {} @Override public String toString() { @@ -372,7 +386,8 @@ public String toString() { } private static Map> caseInsensitiveCopyOf(Map> headers) { - Map> result = new TreeMap>(String.CASE_INSENSITIVE_ORDER); + Map> result = + new TreeMap>(String.CASE_INSENSITIVE_ORDER); for (Map.Entry> entry : headers.entrySet()) { String headerName = entry.getKey(); diff --git a/core/src/main/java/feign/ResponseMapper.java b/core/src/main/java/feign/ResponseMapper.java index d02af6adb9..18ad6918b8 100644 --- a/core/src/main/java/feign/ResponseMapper.java +++ b/core/src/main/java/feign/ResponseMapper.java @@ -18,9 +18,10 @@ /** * Map function to apply to the response before decoding it. * - *
    {@code
    + * 
    + * {@code
      * new ResponseMapper() {
    - *      @Override
    + *      @Override
      *      public Response map(Response response, Type type) {
      *          try {
      *            return response
    @@ -32,7 +33,8 @@
      *          }
      *      }
      *  };
    - * }
    + * } + *
    */ public interface ResponseMapper { diff --git a/core/src/main/java/feign/RetryableException.java b/core/src/main/java/feign/RetryableException.java index e3e912f744..79b8eafd97 100644 --- a/core/src/main/java/feign/RetryableException.java +++ b/core/src/main/java/feign/RetryableException.java @@ -43,7 +43,7 @@ public RetryableException(String message, Date retryAfter) { /** * Sometimes corresponds to the {@link feign.Util#RETRY_AFTER} header present in {@code 503} - * status. Other times parsed from an application-specific response. Null if unknown. + * status. Other times parsed from an application-specific response. Null if unknown. */ public Date retryAfter() { return retryAfter != null ? new Date(retryAfter) : null; diff --git a/core/src/main/java/feign/Retryer.java b/core/src/main/java/feign/Retryer.java index 0cacec3ffa..7944c67095 100644 --- a/core/src/main/java/feign/Retryer.java +++ b/core/src/main/java/feign/Retryer.java @@ -79,9 +79,9 @@ public void continueOrPropagate(RetryableException e) { } /** - * Calculates the time interval to a retry attempt.
    The interval increases exponentially - * with each attempt, at a rate of nextInterval *= 1.5 (where 1.5 is the backoff factor), to the - * maximum interval. + * Calculates the time interval to a retry attempt.
    + * The interval increases exponentially with each attempt, at a rate of nextInterval *= 1.5 + * (where 1.5 is the backoff factor), to the maximum interval. * * @return time in nanoseconds from now until the next attempt. */ diff --git a/core/src/main/java/feign/SynchronousMethodHandler.java b/core/src/main/java/feign/SynchronousMethodHandler.java index 2dd3cd4a68..1cf33ccbb7 100644 --- a/core/src/main/java/feign/SynchronousMethodHandler.java +++ b/core/src/main/java/feign/SynchronousMethodHandler.java @@ -16,13 +16,11 @@ import java.io.IOException; import java.util.List; import java.util.concurrent.TimeUnit; - import feign.InvocationHandlerFactory.MethodHandler; import feign.Request.Options; import feign.codec.DecodeException; import feign.codec.Decoder; import feign.codec.ErrorDecoder; - import static feign.FeignException.errorExecuting; import static feign.FeignException.errorReading; import static feign.Util.checkNotNull; @@ -47,11 +45,11 @@ final class SynchronousMethodHandler implements MethodHandler { private final boolean closeAfterDecode; private SynchronousMethodHandler(Target target, Client client, Retryer retryer, - List requestInterceptors, Logger logger, - Logger.Level logLevel, MethodMetadata metadata, - RequestTemplate.Factory buildTemplateFromArgs, Options options, - Decoder decoder, ErrorDecoder errorDecoder, boolean decode404, - boolean closeAfterDecode) { + List requestInterceptors, Logger logger, + Logger.Level logLevel, MethodMetadata metadata, + RequestTemplate.Factory buildTemplateFromArgs, Options options, + Decoder decoder, ErrorDecoder errorDecoder, boolean decode404, + boolean closeAfterDecode) { this.target = checkNotNull(target, "target"); this.client = checkNotNull(client, "client for %s", target); this.retryer = checkNotNull(retryer, "retryer for %s", target); @@ -119,7 +117,7 @@ Object executeAndDecode(RequestTemplate template) throws Throwable { return response; } if (response.body().length() == null || - response.body().length() > MAX_RESPONSE_BUFFER_SIZE) { + response.body().length() > MAX_RESPONSE_BUFFER_SIZE) { shouldClose = false; return response; } @@ -186,7 +184,7 @@ static class Factory { private final boolean closeAfterDecode; Factory(Client client, Retryer retryer, List requestInterceptors, - Logger logger, Logger.Level logLevel, boolean decode404, boolean closeAfterDecode) { + Logger logger, Logger.Level logLevel, boolean decode404, boolean closeAfterDecode) { this.client = checkNotNull(client, "client"); this.retryer = checkNotNull(retryer, "retryer"); this.requestInterceptors = checkNotNull(requestInterceptors, "requestInterceptors"); @@ -196,12 +194,15 @@ static class Factory { this.closeAfterDecode = closeAfterDecode; } - public MethodHandler create(Target target, MethodMetadata md, + public MethodHandler create(Target target, + MethodMetadata md, RequestTemplate.Factory buildTemplateFromArgs, - Options options, Decoder decoder, ErrorDecoder errorDecoder) { + Options options, + Decoder decoder, + ErrorDecoder errorDecoder) { return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger, - logLevel, md, buildTemplateFromArgs, options, decoder, - errorDecoder, decode404, closeAfterDecode); + logLevel, md, buildTemplateFromArgs, options, decoder, + errorDecoder, decode404, closeAfterDecode); } } } diff --git a/core/src/main/java/feign/Target.java b/core/src/main/java/feign/Target.java index e2e02b31da..19e6cd3ccc 100644 --- a/core/src/main/java/feign/Target.java +++ b/core/src/main/java/feign/Target.java @@ -17,7 +17,11 @@ import static feign.Util.emptyToNull; /** - *

    relationship to JAXRS 2.0

    Similar to {@code + *
    + *
    + * relationship to JAXRS 2.0
    + *
    + * Similar to {@code * javax.ws.rs.client.WebTarget}, as it produces requests. However, {@link RequestTemplate} is a * closer match to {@code WebTarget}. * @@ -36,15 +40,24 @@ public interface Target { /** * Targets a template to this target, adding the {@link #url() base url} and any target-specific - * headers or query parameters.

    For example:
    + * headers or query parameters.
    + *
    + * For example:
    + * *
        * public Request apply(RequestTemplate input) {
    -   *     input.insert(0, url());
    -   *     input.replaceHeader("X-Auth", currentToken);
    -   *     return input.asRequest();
    +   *   input.insert(0, url());
    +   *   input.replaceHeader("X-Auth", currentToken);
    +   *   return input.asRequest();
        * }
        * 
    - *


    relationship to JAXRS 2.0

    This call is similar to {@code + * + *
    + *
    + *
    + * relationship to JAXRS 2.0
    + *
    + * This call is similar to {@code * javax.ws.rs.client.WebTarget.request()}, except that we expect transient, but necessary * decoration to be applied on invocation. */ @@ -95,8 +108,8 @@ public boolean equals(Object obj) { if (obj instanceof HardCodedTarget) { HardCodedTarget other = (HardCodedTarget) obj; return type.equals(other.type) - && name.equals(other.name) - && url.equals(other.url); + && name.equals(other.name) + && url.equals(other.url); } return false; } @@ -116,7 +129,7 @@ public String toString() { return "HardCodedTarget(type=" + type.getSimpleName() + ", url=" + url + ")"; } return "HardCodedTarget(type=" + type.getSimpleName() + ", name=" + name + ", url=" + url - + ")"; + + ")"; } } @@ -167,7 +180,7 @@ public boolean equals(Object obj) { if (obj instanceof EmptyTarget) { EmptyTarget other = (EmptyTarget) obj; return type.equals(other.type) - && name.equals(other.name); + && name.equals(other.name); } return false; } diff --git a/core/src/main/java/feign/Types.java b/core/src/main/java/feign/Types.java index d2f30514c0..1e642444f6 100644 --- a/core/src/main/java/feign/Types.java +++ b/core/src/main/java/feign/Types.java @@ -69,8 +69,8 @@ static Class getRawType(Type type) { } else { String className = type == null ? "null" : type.getClass().getName(); throw new IllegalArgumentException("Expected a Class, ParameterizedType, or " - + "GenericArrayType, but <" + type + "> is of type " - + className); + + "GenericArrayType, but <" + type + "> is of type " + + className); } } @@ -91,8 +91,8 @@ static boolean equals(Type a, Type b) { ParameterizedType pa = (ParameterizedType) a; ParameterizedType pb = (ParameterizedType) b; return equal(pa.getOwnerType(), pb.getOwnerType()) - && pa.getRawType().equals(pb.getRawType()) - && Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments()); + && pa.getRawType().equals(pb.getRawType()) + && Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments()); } else if (a instanceof GenericArrayType) { if (!(b instanceof GenericArrayType)) { @@ -109,7 +109,7 @@ static boolean equals(Type a, Type b) { WildcardType wa = (WildcardType) a; WildcardType wb = (WildcardType) b; return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds()) - && Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds()); + && Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds()); } else if (a instanceof TypeVariable) { if (!(b instanceof TypeVariable)) { @@ -118,7 +118,7 @@ static boolean equals(Type a, Type b) { TypeVariable va = (TypeVariable) a; TypeVariable vb = (TypeVariable) b; return va.getGenericDeclaration() == vb.getGenericDeclaration() - && va.getName().equals(vb.getName()); + && va.getName().equals(vb.getName()); } else { return false; // This isn't a type we support! @@ -197,7 +197,7 @@ static Type getSupertype(Type context, Class contextRawType, Class superty throw new IllegalArgumentException(); } return resolve(context, contextRawType, - getGenericSupertype(context, contextRawType, supertype)); + getGenericSupertype(context, contextRawType, supertype)); } static Type resolve(Type context, Class contextRawType, Type toResolve) { @@ -214,15 +214,17 @@ static Type resolve(Type context, Class contextRawType, Type toResolve) { Class original = (Class) toResolve; Type componentType = original.getComponentType(); Type newComponentType = resolve(context, contextRawType, componentType); - return componentType == newComponentType ? original : new GenericArrayTypeImpl( - newComponentType); + return componentType == newComponentType ? original + : new GenericArrayTypeImpl( + newComponentType); } else if (toResolve instanceof GenericArrayType) { GenericArrayType original = (GenericArrayType) toResolve; Type componentType = original.getGenericComponentType(); Type newComponentType = resolve(context, contextRawType, componentType); - return componentType == newComponentType ? original : new GenericArrayTypeImpl( - newComponentType); + return componentType == newComponentType ? original + : new GenericArrayTypeImpl( + newComponentType); } else if (toResolve instanceof ParameterizedType) { ParameterizedType original = (ParameterizedType) toResolve; @@ -243,8 +245,8 @@ static Type resolve(Type context, Class contextRawType, Type toResolve) { } return changed - ? new ParameterizedTypeImpl(newOwnerType, original.getRawType(), args) - : original; + ? new ParameterizedTypeImpl(newOwnerType, original.getRawType(), args) + : original; } else if (toResolve instanceof WildcardType) { WildcardType original = (WildcardType) toResolve; @@ -254,12 +256,12 @@ static Type resolve(Type context, Class contextRawType, Type toResolve) { if (originalLowerBound.length == 1) { Type lowerBound = resolve(context, contextRawType, originalLowerBound[0]); if (lowerBound != originalLowerBound[0]) { - return new WildcardTypeImpl(new Type[]{Object.class}, new Type[]{lowerBound}); + return new WildcardTypeImpl(new Type[] {Object.class}, new Type[] {lowerBound}); } } else if (originalUpperBound.length == 1) { Type upperBound = resolve(context, contextRawType, originalUpperBound[0]); if (upperBound != originalUpperBound[0]) { - return new WildcardTypeImpl(new Type[]{upperBound}, EMPTY_TYPE_ARRAY); + return new WildcardTypeImpl(new Type[] {upperBound}, EMPTY_TYPE_ARRAY); } } return original; @@ -271,7 +273,9 @@ static Type resolve(Type context, Class contextRawType, Type toResolve) { } private static Type resolveTypeVariable( - Type context, Class contextRawType, TypeVariable unknown) { + Type context, + Class contextRawType, + TypeVariable unknown) { Class declaredByRaw = declaringClassOf(unknown); // We can't reduce this further. @@ -380,7 +384,7 @@ public Type getGenericComponentType() { @Override public boolean equals(Object o) { return o instanceof GenericArrayType - && Types.equals(this, (GenericArrayType) o); + && Types.equals(this, (GenericArrayType) o); } @Override @@ -433,11 +437,11 @@ static final class WildcardTypeImpl implements WildcardType { } public Type[] getUpperBounds() { - return new Type[]{upperBound}; + return new Type[] {upperBound}; } public Type[] getLowerBounds() { - return lowerBound != null ? new Type[]{lowerBound} : EMPTY_TYPE_ARRAY; + return lowerBound != null ? new Type[] {lowerBound} : EMPTY_TYPE_ARRAY; } @Override diff --git a/core/src/main/java/feign/Util.java b/core/src/main/java/feign/Util.java index 74819d2fc9..5e355926cc 100644 --- a/core/src/main/java/feign/Util.java +++ b/core/src/main/java/feign/Util.java @@ -38,7 +38,6 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; - import static java.lang.String.format; /** @@ -84,7 +83,7 @@ public class Util { */ public static final Type MAP_STRING_WILDCARD = new Types.ParameterizedTypeImpl(null, Map.class, String.class, - new Types.WildcardTypeImpl(new Type[]{Object.class}, new Type[0])); + new Types.WildcardTypeImpl(new Type[] {Object.class}, new Type[0])); private Util() { // no instances } @@ -134,10 +133,12 @@ public static boolean isDefault(Method method) { // Default methods are public non-abstract, non-synthetic, and non-static instance methods // declared in an interface. // method.isDefault() is not sufficient for our usage as it does not check - // for synthetic methods. As a result, it picks up overridden methods as well as actual default methods. + // for synthetic methods. As a result, it picks up overridden methods as well as actual default + // methods. final int SYNTHETIC = 0x00001000; - return ((method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC | SYNTHETIC)) == - Modifier.PUBLIC) && method.getDeclaringClass().isInterface(); + return ((method.getModifiers() + & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC | SYNTHETIC)) == Modifier.PUBLIC) + && method.getDeclaringClass().isInterface(); } /** @@ -183,22 +184,24 @@ public static void ensureClosed(Closeable closeable) { /** * Resolves the last type parameter of the parameterized {@code supertype}, based on the {@code - * genericContext}, into its upper bounds.

    Implementation copied from {@code + * genericContext}, into its upper bounds. + *

    + * Implementation copied from {@code * retrofit.RestMethodInfo}. * * @param genericContext Ex. {@link java.lang.reflect.Field#getGenericType()} - * @param supertype Ex. {@code Decoder.class} + * @param supertype Ex. {@code Decoder.class} * @return in the example above, the type parameter of {@code Decoder}. * @throws IllegalStateException if {@code supertype} cannot be resolved into a parameterized type - * using {@code context}. + * using {@code context}. */ public static Type resolveLastTypeParameter(Type genericContext, Class supertype) throws IllegalStateException { Type resolvedSuperType = Types.getSupertype(genericContext, Types.getRawType(genericContext), supertype); checkState(resolvedSuperType instanceof ParameterizedType, - "could not resolve %s into a parameterized type %s", - genericContext, supertype); + "could not resolve %s into a parameterized type %s", + genericContext, supertype); Type[] types = ParameterizedType.class.cast(resolvedSuperType).getActualTypeArguments(); for (int i = 0; i < types.length; i++) { Type type = types[i]; @@ -214,18 +217,19 @@ public static Type resolveLastTypeParameter(Type genericContext, Class supert * in the following list. * *

      - *
    • {@code [Bb]oolean}
    • - *
    • {@code byte[]}
    • - *
    • {@code Collection}
    • - *
    • {@code Iterator}
    • - *
    • {@code List}
    • - *
    • {@code Map}
    • - *
    • {@code Set}
    • + *
    • {@code [Bb]oolean}
    • + *
    • {@code byte[]}
    • + *
    • {@code Collection}
    • + *
    • {@code Iterator}
    • + *
    • {@code List}
    • + *
    • {@code Map}
    • + *
    • {@code Set}
    • *
    * - *

    When {@link Feign.Builder#decode404() decoding HTTP 404 status}, you'll need to teach - * decoders a default empty value for a type. This method cheaply supports typical types by only - * looking at the raw type (vs type hierarchy). Decorate for sophistication. + *

    + * When {@link Feign.Builder#decode404() decoding HTTP 404 status}, you'll need to teach decoders + * a default empty value for a type. This method cheaply supports typical types by only looking at + * the raw type (vs type hierarchy). Decorate for sophistication. */ public static Object emptyValueOf(Type type) { return EMPTIES.get(Types.getRawType(type)); diff --git a/core/src/main/java/feign/auth/Base64.java b/core/src/main/java/feign/auth/Base64.java index e032bc8d7b..270ce31f8a 100644 --- a/core/src/main/java/feign/auth/Base64.java +++ b/core/src/main/java/feign/auth/Base64.java @@ -16,22 +16,22 @@ import java.io.UnsupportedEncodingException; /** - * copied from okhttp + * copied from okhttp * * @author Alexander Y. Kleymenov */ final class Base64 { public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; - private static final byte[] MAP = new byte[]{ + private static final byte[] MAP = new byte[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; - private Base64() { - } + private Base64() {} public static byte[] decode(byte[] in) { return decode(in, in.length); @@ -51,7 +51,7 @@ public static byte[] decode(byte[] in, int len) { byte chr; // compute the number of the padding characters // and adjust the length of the input - for (; ; len--) { + for (;; len--) { chr = in[len - 1]; // skip the neutral characters if ((chr == '\n') || (chr == '\r') || (chr == ' ') || (chr == '\t')) { @@ -79,18 +79,18 @@ public static byte[] decode(byte[] in, int len) { } if ((chr >= 'A') && (chr <= 'Z')) { // char ASCII value - // A 65 0 - // Z 90 25 (ASCII - 65) + // A 65 0 + // Z 90 25 (ASCII - 65) bits = chr - 65; } else if ((chr >= 'a') && (chr <= 'z')) { // char ASCII value - // a 97 26 - // z 122 51 (ASCII - 71) + // a 97 26 + // z 122 51 (ASCII - 71) bits = chr - 71; } else if ((chr >= '0') && (chr <= '9')) { // char ASCII value - // 0 48 52 - // 9 57 61 (ASCII + 4) + // 0 48 52 + // 9 57 61 (ASCII + 4) bits = chr + 4; } else if (chr == '+') { bits = 62; diff --git a/core/src/main/java/feign/auth/BasicAuthRequestInterceptor.java b/core/src/main/java/feign/auth/BasicAuthRequestInterceptor.java index 202d048365..d8a32a1c4c 100644 --- a/core/src/main/java/feign/auth/BasicAuthRequestInterceptor.java +++ b/core/src/main/java/feign/auth/BasicAuthRequestInterceptor.java @@ -14,10 +14,8 @@ package feign.auth; import java.nio.charset.Charset; - import feign.RequestInterceptor; import feign.RequestTemplate; - import static feign.Util.ISO_8859_1; import static feign.Util.checkNotNull; @@ -45,7 +43,7 @@ public BasicAuthRequestInterceptor(String username, String password) { * * @param username the username to use for authentication * @param password the password to use for authentication - * @param charset the charset to use when encoding the credentials + * @param charset the charset to use when encoding the credentials */ public BasicAuthRequestInterceptor(String username, String password, Charset charset) { checkNotNull(username, "username"); @@ -54,8 +52,9 @@ public BasicAuthRequestInterceptor(String username, String password, Charset cha } /* - * This uses a Sun internal method; if we ever encounter a case where this method is not available, the appropriate - * response would be to pull the necessary portions of Guava's BaseEncoding class into Util. + * This uses a Sun internal method; if we ever encounter a case where this method is not + * available, the appropriate response would be to pull the necessary portions of Guava's + * BaseEncoding class into Util. */ private static String base64Encode(byte[] bytes) { return Base64.encode(bytes); diff --git a/core/src/main/java/feign/codec/DecodeException.java b/core/src/main/java/feign/codec/DecodeException.java index 2baeb84178..687a6ddfea 100644 --- a/core/src/main/java/feign/codec/DecodeException.java +++ b/core/src/main/java/feign/codec/DecodeException.java @@ -14,12 +14,11 @@ package feign.codec; import feign.FeignException; - import static feign.Util.checkNotNull; /** * Similar to {@code javax.websocket.DecodeException}, raised when a problem occurs decoding a - * message. Note that {@code DecodeException} is not an {@code IOException}, nor does it have one + * message. Note that {@code DecodeException} is not an {@code IOException}, nor does it have one * set as its cause. */ public class DecodeException extends FeignException { @@ -35,7 +34,7 @@ public DecodeException(String message) { /** * @param message possibly null reason for the failure. - * @param cause the cause of the error. + * @param cause the cause of the error. */ public DecodeException(String message, Throwable cause) { super(message, checkNotNull(cause, "cause")); diff --git a/core/src/main/java/feign/codec/Decoder.java b/core/src/main/java/feign/codec/Decoder.java index b7d24a00fb..16171872b1 100644 --- a/core/src/main/java/feign/codec/Decoder.java +++ b/core/src/main/java/feign/codec/Decoder.java @@ -15,16 +15,21 @@ import java.io.IOException; import java.lang.reflect.Type; - import feign.Feign; import feign.FeignException; import feign.Response; import feign.Util; /** - * Decodes an HTTP response into a single object of the given {@code type}. Invoked when {@link - * Response#status()} is in the 2xx range and the return type is neither {@code void} nor {@code - * Response}.

    Example Implementation:

    + * Decodes an HTTP response into a single object of the given {@code type}. Invoked when + * {@link Response#status()} is in the 2xx range and the return type is neither {@code void} nor + * {@code + * Response}. + *

    + *

    + * Example Implementation:
    + *

    + * *

      * public class GsonDecoder implements Decoder {
      *   private final Gson gson = new Gson();
    @@ -43,29 +48,32 @@
      *   }
      * }
      * 
    - *

    Implementation Note

    The {@code type} parameter will correspond to the {@link - * java.lang.reflect.Method#getGenericReturnType() generic return type} of an {@link - * feign.Target#type() interface} processed by {@link feign.Feign#newInstance(feign.Target)}. When - * writing your implementation of Decoder, ensure you also test parameterized types such as {@code - * List}. - *

    Note on exception propagation

    Exceptions thrown by {@link Decoder}s get wrapped in - * a {@link DecodeException} unless they are a subclass of {@link FeignException} already, and unless + * + *
    + *

    Implementation Note

    The {@code type} parameter will correspond to the + * {@link java.lang.reflect.Method#getGenericReturnType() generic return type} of an + * {@link feign.Target#type() interface} processed by {@link feign.Feign#newInstance(feign.Target)}. + * When writing your implementation of Decoder, ensure you also test parameterized types such as + * {@code + * List}.
    + *

    Note on exception propagation

    Exceptions thrown by {@link Decoder}s get wrapped in a + * {@link DecodeException} unless they are a subclass of {@link FeignException} already, and unless * the client was configured with {@link Feign.Builder#decode404()}. */ public interface Decoder { /** - * Decodes an http response into an object corresponding to its {@link - * java.lang.reflect.Method#getGenericReturnType() generic return type}. If you need to wrap - * exceptions, please do so via {@link DecodeException}. + * Decodes an http response into an object corresponding to its + * {@link java.lang.reflect.Method#getGenericReturnType() generic return type}. If you need to + * wrap exceptions, please do so via {@link DecodeException}. * * @param response the response to decode - * @param type {@link java.lang.reflect.Method#getGenericReturnType() generic return type} of - * the method corresponding to this {@code response}. + * @param type {@link java.lang.reflect.Method#getGenericReturnType() generic return type} of the + * method corresponding to this {@code response}. * @return instance of {@code type} - * @throws IOException will be propagated safely to the caller. + * @throws IOException will be propagated safely to the caller. * @throws DecodeException when decoding failed due to a checked exception besides IOException. - * @throws FeignException when decoding succeeds, but conveys the operation failed. + * @throws FeignException when decoding succeeds, but conveys the operation failed. */ Object decode(Response response, Type type) throws IOException, DecodeException, FeignException; @@ -74,8 +82,10 @@ public class Default extends StringDecoder { @Override public Object decode(Response response, Type type) throws IOException { - if (response.status() == 404) return Util.emptyValueOf(type); - if (response.body() == null) return null; + if (response.status() == 404) + return Util.emptyValueOf(type); + if (response.body() == null) + return null; if (byte[].class.equals(type)) { return Util.toByteArray(response.body().asInputStream()); } diff --git a/core/src/main/java/feign/codec/EncodeException.java b/core/src/main/java/feign/codec/EncodeException.java index 9cb40cf074..517728366f 100644 --- a/core/src/main/java/feign/codec/EncodeException.java +++ b/core/src/main/java/feign/codec/EncodeException.java @@ -14,12 +14,11 @@ package feign.codec; import feign.FeignException; - import static feign.Util.checkNotNull; /** * Similar to {@code javax.websocket.EncodeException}, raised when a problem occurs encoding a - * message. Note that {@code EncodeException} is not an {@code IOException}, nor does it have one + * message. Note that {@code EncodeException} is not an {@code IOException}, nor does it have one * set as its cause. */ public class EncodeException extends FeignException { @@ -35,7 +34,7 @@ public EncodeException(String message) { /** * @param message possibly null reason for the failure. - * @param cause the cause of the error. + * @param cause the cause of the error. */ public EncodeException(String message, Throwable cause) { super(message, checkNotNull(cause, "cause")); diff --git a/core/src/main/java/feign/codec/Encoder.java b/core/src/main/java/feign/codec/Encoder.java index 7d5a43f3b6..b5f556ef4a 100644 --- a/core/src/main/java/feign/codec/Encoder.java +++ b/core/src/main/java/feign/codec/Encoder.java @@ -14,22 +14,24 @@ package feign.codec; import java.lang.reflect.Type; - import feign.RequestTemplate; import feign.Util; - import static java.lang.String.format; /** * Encodes an object into an HTTP request body. Like {@code javax.websocket.Encoder}. {@code * Encoder} is used when a method parameter has no {@code @Param} annotation. For example:
    *

    + * *

      * @POST
      * @Path("/")
      * void create(User user);
      * 
    - * Example implementation:

    + * + * Example implementation:
    + *

    + * *

      * public class GsonEncoder implements Encoder {
      *   private final Gson gson;
    @@ -45,16 +47,20 @@
      * }
      * 
    * - *

    Form encoding

    If any parameters are found in {@link - * feign.MethodMetadata#formParams()}, they will be collected and passed to the Encoder as a map. + *

    + *

    Form encoding

    + *

    + * If any parameters are found in {@link feign.MethodMetadata#formParams()}, they will be collected + * and passed to the Encoder as a map. * - *

    Ex. The following is a form. Notice the parameters aren't consumed in the request line. A map + *

    + * Ex. The following is a form. Notice the parameters aren't consumed in the request line. A map * including "username" and "password" keys will passed to the encoder, and the body type will be * {@link #MAP_STRING_WILDCARD}. + * *

      * @RequestLine("POST /")
    - * Session login(@Param("username") String username, @Param("password") String
    - * password);
    + * Session login(@Param("username") String username, @Param("password") String password);
      * 
    */ public interface Encoder { @@ -64,9 +70,9 @@ public interface Encoder { /** * Converts objects to an appropriate representation in the template. * - * @param object what to encode as the request body. + * @param object what to encode as the request body. * @param bodyType the type the object should be encoded as. {@link #MAP_STRING_WILDCARD} - * indicates form encoding. + * indicates form encoding. * @param template the request template to populate. * @throws EncodeException when encoding failed due to a checked exception. */ diff --git a/core/src/main/java/feign/codec/ErrorDecoder.java b/core/src/main/java/feign/codec/ErrorDecoder.java index 4a52226c5b..6482203f8b 100644 --- a/core/src/main/java/feign/codec/ErrorDecoder.java +++ b/core/src/main/java/feign/codec/ErrorDecoder.java @@ -19,11 +19,9 @@ import java.util.Collection; import java.util.Date; import java.util.Map; - import feign.FeignException; import feign.Response; import feign.RetryableException; - import static feign.FeignException.errorStatus; import static feign.Util.RETRY_AFTER; import static feign.Util.checkNotNull; @@ -35,48 +33,54 @@ * Allows you to massage an exception into a application-specific one. Converting out to a throttle * exception are examples of this in use. * - *

    Ex: + *

    + * Ex: + * *

      * class IllegalArgumentExceptionOn404Decoder implements ErrorDecoder {
      *
      *   @Override
      *   public Exception decode(String methodKey, Response response) {
    - *    if (response.status() == 400)
    - *        throw new IllegalArgumentException("bad zone name");
    - *    return new ErrorDecoder.Default().decode(methodKey, response);
    + *     if (response.status() == 400)
    + *       throw new IllegalArgumentException("bad zone name");
    + *     return new ErrorDecoder.Default().decode(methodKey, response);
      *   }
      *
      * }
      * 
    * - *

    Error handling + *

    + * Error handling * - *

    Responses where {@link Response#status()} is not in the 2xx - * range are classified as errors, addressed by the {@link ErrorDecoder}. That said, certain RPC - * apis return errors defined in the {@link Response#body()} even on a 200 status. For example, in - * the DynECT api, a job still running condition is returned with a 200 status, encoded in json. - * When scenarios like this occur, you should raise an application-specific exception (which may be - * {@link feign.RetryableException retryable}). + *

    + * Responses where {@link Response#status()} is not in the 2xx range are classified as errors, + * addressed by the {@link ErrorDecoder}. That said, certain RPC apis return errors defined in the + * {@link Response#body()} even on a 200 status. For example, in the DynECT api, a job still running + * condition is returned with a 200 status, encoded in json. When scenarios like this occur, you + * should raise an application-specific exception (which may be {@link feign.RetryableException + * retryable}). * - *

    Not Found Semantics - *

    It is commonly the case that 404 (Not Found) status has semantic value in HTTP apis. While - * the default behavior is to raise exeception, users can alternatively enable 404 processing via + *

    + * Not Found Semantics + *

    + * It is commonly the case that 404 (Not Found) status has semantic value in HTTP apis. While the + * default behavior is to raise exeception, users can alternatively enable 404 processing via * {@link feign.Feign.Builder#decode404()}. */ public interface ErrorDecoder { /** - * Implement this method in order to decode an HTTP {@link Response} when {@link - * Response#status()} is not in the 2xx range. Please raise application-specific exceptions where - * possible. If your exception is retryable, wrap or subclass {@link RetryableException} + * Implement this method in order to decode an HTTP {@link Response} when + * {@link Response#status()} is not in the 2xx range. Please raise application-specific exceptions + * where possible. If your exception is retryable, wrap or subclass {@link RetryableException} * - * @param methodKey {@link feign.Feign#configKey} of the java method that invoked the request. - * ex. {@code IAM#getUser()} - * @param response HTTP response where {@link Response#status() status} is greater than or equal - * to {@code 300}. + * @param methodKey {@link feign.Feign#configKey} of the java method that invoked the request. ex. + * {@code IAM#getUser()} + * @param response HTTP response where {@link Response#status() status} is greater than or equal + * to {@code 300}. * @return Exception IOException, if there was a network error reading the response or an - * application-specific exception decoded by the implementation. If the throwable is retryable, it - * should be wrapped, or a subtype of {@link RetryableException} + * application-specific exception decoded by the implementation. If the throwable is + * retryable, it should be wrapped, or a subtype of {@link RetryableException} */ public Exception decode(String methodKey, Response response); @@ -103,13 +107,12 @@ private T firstOrNull(Map> map, String key) { } /** - * Decodes a {@link feign.Util#RETRY_AFTER} header into an absolute date, if possible.
    See Retry-After format + * Decodes a {@link feign.Util#RETRY_AFTER} header into an absolute date, if possible.
    + * See Retry-After format */ static class RetryAfterDecoder { - static final DateFormat - RFC822_FORMAT = + static final DateFormat RFC822_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", US); private final DateFormat rfc822Format; @@ -128,8 +131,8 @@ protected long currentTimeMillis() { /** * returns a date that corresponds to the first time a request can be retried. * - * @param retryAfter String in Retry-After format + * @param retryAfter String in + * Retry-After format */ public Date apply(String retryAfter) { if (retryAfter == null) { diff --git a/core/src/main/java/feign/codec/StringDecoder.java b/core/src/main/java/feign/codec/StringDecoder.java index 194f8f3d74..16f2a6f178 100644 --- a/core/src/main/java/feign/codec/StringDecoder.java +++ b/core/src/main/java/feign/codec/StringDecoder.java @@ -15,10 +15,8 @@ import java.io.IOException; import java.lang.reflect.Type; - import feign.Response; import feign.Util; - import static java.lang.String.format; public class StringDecoder implements Decoder { diff --git a/core/src/test/java/feign/BaseApiTest.java b/core/src/test/java/feign/BaseApiTest.java index b1ec0e54b3..60b807639e 100644 --- a/core/src/test/java/feign/BaseApiTest.java +++ b/core/src/test/java/feign/BaseApiTest.java @@ -14,19 +14,14 @@ package feign; import com.google.gson.reflect.TypeToken; - import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; - import org.junit.Rule; import org.junit.Test; - import java.lang.reflect.Type; import java.util.List; - import feign.codec.Decoder; import feign.codec.Encoder; - import static feign.assertj.MockWebServerAssertions.assertThat; public class BaseApiTest { @@ -74,8 +69,7 @@ public void resolvesParameterizedResult() throws InterruptedException { @Override public Object decode(Response response, Type type) { assertThat(type) - .isEqualTo(new TypeToken>() { - }.getType()); + .isEqualTo(new TypeToken>() {}.getType()); return null; } }) @@ -95,16 +89,14 @@ public void resolvesBodyParameter() throws InterruptedException { @Override public void encode(Object object, Type bodyType, RequestTemplate template) { assertThat(bodyType) - .isEqualTo(new TypeToken>() { - }.getType()); + .isEqualTo(new TypeToken>() {}.getType()); } }) .decoder(new Decoder() { @Override public Object decode(Response response, Type type) { assertThat(type) - .isEqualTo(new TypeToken>() { - }.getType()); + .isEqualTo(new TypeToken>() {}.getType()); return null; } }) diff --git a/core/src/test/java/feign/ContractWithRuntimeInjectionTest.java b/core/src/test/java/feign/ContractWithRuntimeInjectionTest.java index 23342a9081..79882fb2b1 100644 --- a/core/src/test/java/feign/ContractWithRuntimeInjectionTest.java +++ b/core/src/test/java/feign/ContractWithRuntimeInjectionTest.java @@ -15,7 +15,6 @@ import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; - import org.junit.Rule; import org.junit.Test; import org.springframework.beans.factory.BeanFactory; @@ -23,11 +22,9 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; - import java.util.LinkedHashMap; import java.util.List; import java.util.Map; - import static feign.assertj.MockWebServerAssertions.assertThat; public class ContractWithRuntimeInjectionTest { @@ -100,7 +97,8 @@ public List parseAndValidatateMetadata(Class targetType) { List result = super.parseAndValidatateMetadata(targetType); for (MethodMetadata md : result) { Map indexToExpander = new LinkedHashMap(); - for (Map.Entry> entry : md.indexToExpanderClass().entrySet()) { + for (Map.Entry> entry : md.indexToExpanderClass() + .entrySet()) { indexToExpander.put(entry.getKey(), beanFactory.getBean(entry.getValue())); } md.indexToExpander(indexToExpander); diff --git a/core/src/test/java/feign/DefaultContractTest.java b/core/src/test/java/feign/DefaultContractTest.java index fc1960ef31..cfa9fd3558 100644 --- a/core/src/test/java/feign/DefaultContractTest.java +++ b/core/src/test/java/feign/DefaultContractTest.java @@ -14,26 +14,23 @@ package feign; import com.google.gson.reflect.TypeToken; - import org.assertj.core.api.Fail; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; - import java.net.URI; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.SortedMap; - import static feign.assertj.FeignAssertions.assertThat; import static java.util.Arrays.asList; import static org.assertj.core.data.MapEntry.entry; /** - * Tests interfaces defined per {@link Contract.Default} are interpreted into expected {@link feign - * .RequestTemplate template} instances. + * Tests interfaces defined per {@link Contract.Default} are interpreted into expected + * {@link feign .RequestTemplate template} instances. */ public class DefaultContractTest { @@ -64,8 +61,7 @@ public void bodyParamIsGeneric() throws Exception { assertThat(md.bodyIndex()) .isEqualTo(0); assertThat(md.bodyType()) - .isEqualTo(new TypeToken>() { - }.getType()); + .isEqualTo(new TypeToken>() {}.getType()); } @Test @@ -75,8 +71,7 @@ public void bodyParamWithPathParam() throws Exception { assertThat(md.bodyIndex()) .isEqualTo(1); assertThat(md.indexToName()).containsOnly( - entry(0, asList("id")) - ); + entry(0, asList("id"))); } @Test @@ -102,44 +97,38 @@ public void queryParamsInPathExtract() throws Exception { assertThat(parseAndValidateMetadata(WithQueryParamsInPath.class, "one").template()) .hasUrl("/") .hasQueries( - entry("Action", asList("GetUser")) - ); + entry("Action", asList("GetUser"))); assertThat(parseAndValidateMetadata(WithQueryParamsInPath.class, "two").template()) .hasUrl("/") .hasQueries( entry("Action", asList("GetUser")), - entry("Version", asList("2010-05-08")) - ); + entry("Version", asList("2010-05-08"))); assertThat(parseAndValidateMetadata(WithQueryParamsInPath.class, "three").template()) .hasUrl("/") .hasQueries( entry("Action", asList("GetUser")), entry("Version", asList("2010-05-08")), - entry("limit", asList("1")) - ); + entry("limit", asList("1"))); assertThat(parseAndValidateMetadata(WithQueryParamsInPath.class, "twoAndOneEmpty").template()) .hasUrl("/") .hasQueries( - entry("flag", asList(new String[]{null})), + entry("flag", asList(new String[] {null})), entry("Action", asList("GetUser")), - entry("Version", asList("2010-05-08")) - ); + entry("Version", asList("2010-05-08"))); assertThat(parseAndValidateMetadata(WithQueryParamsInPath.class, "oneEmpty").template()) .hasUrl("/") .hasQueries( - entry("flag", asList(new String[]{null})) - ); + entry("flag", asList(new String[] {null}))); assertThat(parseAndValidateMetadata(WithQueryParamsInPath.class, "twoEmpty").template()) .hasUrl("/") .hasQueries( - entry("flag", asList(new String[]{null})), - entry("NoErrors", asList(new String[]{null})) - ); + entry("flag", asList(new String[] {null})), + entry("NoErrors", asList(new String[] {null}))); } @Test @@ -157,8 +146,7 @@ public void headersOnMethodAddsContentTypeHeader() throws Exception { assertThat(md.template()) .hasHeaders( entry("Content-Type", asList("application/xml")), - entry("Content-Length", asList(String.valueOf(md.template().body().length))) - ); + entry("Content-Length", asList(String.valueOf(md.template().body().length)))); } @Test @@ -168,21 +156,19 @@ public void headersOnTypeAddsContentTypeHeader() throws Exception { assertThat(md.template()) .hasHeaders( entry("Content-Type", asList("application/xml")), - entry("Content-Length", asList(String.valueOf(md.template().body().length))) - ); + entry("Content-Length", asList(String.valueOf(md.template().body().length)))); } @Test public void withPathAndURIParam() throws Exception { MethodMetadata md = parseAndValidateMetadata(WithURIParam.class, - "uriParam", String.class, URI.class, String.class); + "uriParam", String.class, URI.class, String.class); assertThat(md.indexToName()) .containsExactly( entry(0, asList("1")), // Skips 1 as it is a url index! - entry(2, asList("2")) - ); + entry(2, asList("2"))); assertThat(md.urlIndex()).isEqualTo(1); } @@ -190,8 +176,8 @@ public void withPathAndURIParam() throws Exception { @Test public void pathAndQueryParams() throws Exception { MethodMetadata md = parseAndValidateMetadata(WithPathAndQueryParams.class, - "recordsByNameAndType", int.class, String.class, - String.class); + "recordsByNameAndType", int.class, String.class, + String.class); assertThat(md.template()) .hasQueries(entry("name", asList("{name}")), entry("type", asList("{type}"))); @@ -199,14 +185,13 @@ public void pathAndQueryParams() throws Exception { assertThat(md.indexToName()).containsExactly( entry(0, asList("domainId")), entry(1, asList("name")), - entry(2, asList("type")) - ); + entry(2, asList("type"))); } @Test public void bodyWithTemplate() throws Exception { MethodMetadata md = parseAndValidateMetadata(FormParams.class, - "login", String.class, String.class, String.class); + "login", String.class, String.class, String.class); assertThat(md.template()) .hasBodyTemplate( @@ -216,7 +201,7 @@ public void bodyWithTemplate() throws Exception { @Test public void formParamsParseIntoIndexToName() throws Exception { MethodMetadata md = parseAndValidateMetadata(FormParams.class, - "login", String.class, String.class, String.class); + "login", String.class, String.class, String.class); assertThat(md.formParams()) .containsExactly("customer_name", "user_name", "password"); @@ -224,8 +209,7 @@ public void formParamsParseIntoIndexToName() throws Exception { assertThat(md.indexToName()).containsExactly( entry(0, asList("customer_name")), entry(1, asList("user_name")), - entry(2, asList("password")) - ); + entry(2, asList("password"))); } /** @@ -234,7 +218,7 @@ public void formParamsParseIntoIndexToName() throws Exception { @Test public void formParamsDoesNotSetBodyType() throws Exception { MethodMetadata md = parseAndValidateMetadata(FormParams.class, - "login", String.class, String.class, String.class); + "login", String.class, String.class, String.class); assertThat(md.bodyType()).isNull(); } @@ -253,7 +237,8 @@ public void headerParamsParseIntoIndexToName() throws Exception { @Test public void headerParamsParseIntoIndexToNameNotAtStart() throws Exception { - MethodMetadata md = parseAndValidateMetadata(HeaderParamsNotAtStart.class, "logout", String.class); + MethodMetadata md = + parseAndValidateMetadata(HeaderParamsNotAtStart.class, "logout", String.class); assertThat(md.template()) .hasHeaders(entry("Authorization", asList("Bearer {authToken}", "Foo"))); @@ -273,35 +258,40 @@ public void customExpander() throws Exception { @Test public void queryMap() throws Exception { - MethodMetadata md = parseAndValidateMetadata(QueryMapTestInterface.class, "queryMap", Map.class); + MethodMetadata md = + parseAndValidateMetadata(QueryMapTestInterface.class, "queryMap", Map.class); assertThat(md.queryMapIndex()).isEqualTo(0); } @Test public void queryMapEncodedDefault() throws Exception { - MethodMetadata md = parseAndValidateMetadata(QueryMapTestInterface.class, "queryMap", Map.class); + MethodMetadata md = + parseAndValidateMetadata(QueryMapTestInterface.class, "queryMap", Map.class); assertThat(md.queryMapEncoded()).isFalse(); } @Test public void queryMapEncodedTrue() throws Exception { - MethodMetadata md = parseAndValidateMetadata(QueryMapTestInterface.class, "queryMapEncoded", Map.class); + MethodMetadata md = + parseAndValidateMetadata(QueryMapTestInterface.class, "queryMapEncoded", Map.class); assertThat(md.queryMapEncoded()).isTrue(); } @Test public void queryMapEncodedFalse() throws Exception { - MethodMetadata md = parseAndValidateMetadata(QueryMapTestInterface.class, "queryMapNotEncoded", Map.class); + MethodMetadata md = + parseAndValidateMetadata(QueryMapTestInterface.class, "queryMapNotEncoded", Map.class); assertThat(md.queryMapEncoded()).isFalse(); } @Test public void queryMapMapSubclass() throws Exception { - MethodMetadata md = parseAndValidateMetadata(QueryMapTestInterface.class, "queryMapMapSubclass", SortedMap.class); + MethodMetadata md = parseAndValidateMetadata(QueryMapTestInterface.class, "queryMapMapSubclass", + SortedMap.class); assertThat(md.queryMapIndex()).isEqualTo(0); } @@ -309,7 +299,8 @@ public void queryMapMapSubclass() throws Exception { @Test public void onlyOneQueryMapAnnotationPermitted() throws Exception { try { - parseAndValidateMetadata(QueryMapTestInterface.class, "multipleQueryMap", Map.class, Map.class); + parseAndValidateMetadata(QueryMapTestInterface.class, "multipleQueryMap", Map.class, + Map.class); Fail.failBecauseExceptionWasNotThrown(IllegalStateException.class); } catch (IllegalStateException ex) { assertThat(ex).hasMessage("QueryMap annotation was present on multiple parameters."); @@ -328,14 +319,16 @@ public void queryMapKeysMustBeStrings() throws Exception { @Test public void queryMapPojoObject() throws Exception { - MethodMetadata md = parseAndValidateMetadata(QueryMapTestInterface.class, "pojoObject", Object.class); + MethodMetadata md = + parseAndValidateMetadata(QueryMapTestInterface.class, "pojoObject", Object.class); assertThat(md.queryMapIndex()).isEqualTo(0); } @Test public void queryMapPojoObjectEncoded() throws Exception { - MethodMetadata md = parseAndValidateMetadata(QueryMapTestInterface.class, "pojoObjectEncoded", Object.class); + MethodMetadata md = + parseAndValidateMetadata(QueryMapTestInterface.class, "pojoObjectEncoded", Object.class); assertThat(md.queryMapIndex()).isEqualTo(0); assertThat(md.queryMapEncoded()).isTrue(); @@ -343,7 +336,8 @@ public void queryMapPojoObjectEncoded() throws Exception { @Test public void queryMapPojoObjectNotEncoded() throws Exception { - MethodMetadata md = parseAndValidateMetadata(QueryMapTestInterface.class, "pojoObjectNotEncoded", Object.class); + MethodMetadata md = + parseAndValidateMetadata(QueryMapTestInterface.class, "pojoObjectNotEncoded", Object.class); assertThat(md.queryMapIndex()).isEqualTo(0); assertThat(md.queryMapEncoded()).isFalse(); @@ -352,7 +346,7 @@ public void queryMapPojoObjectNotEncoded() throws Exception { @Test public void slashAreEncodedWhenNeeded() throws Exception { MethodMetadata md = parseAndValidateMetadata(SlashNeedToBeEncoded.class, - "getQueues", String.class); + "getQueues", String.class); assertThat(md.template().decodeSlash()).isFalse(); @@ -373,7 +367,8 @@ public void onlyOneHeaderMapAnnotationPermitted() throws Exception { @Test public void headerMapSubclass() throws Exception { - MethodMetadata md = parseAndValidateMetadata(HeaderMapInterface.class, "headerMapSubClass", SubClassHeaders.class); + MethodMetadata md = parseAndValidateMetadata(HeaderMapInterface.class, "headerMapSubClass", + SubClassHeaders.class); assertThat(md.headerMapIndex()).isEqualTo(0); } @@ -459,7 +454,8 @@ interface WithURIParam { interface WithPathAndQueryParams { @RequestLine("GET /domains/{domainId}/records?name={name}&type={type}") - Response recordsByNameAndType(@Param("domainId") int id, @Param("name") String nameFilter, + Response recordsByNameAndType(@Param("domainId") int id, + @Param("name") String nameFilter, @Param("type") String typeFilter); } @@ -468,14 +464,16 @@ interface FormParams { @RequestLine("POST /") @Body("%7B\"customer_name\": \"{customer_name}\", \"user_name\": \"{user_name}\", \"password\": \"{password}\"%7D") void login( - @Param("customer_name") String customer, - @Param("user_name") String user, @Param("password") String password); + @Param("customer_name") String customer, + @Param("user_name") String user, + @Param("password") String password); } interface HeaderMapInterface { @RequestLine("POST /") - void multipleHeaderMap(@HeaderMap Map headers, @HeaderMap Map queries); + void multipleHeaderMap(@HeaderMap Map headers, + @HeaderMap Map queries); @RequestLine("POST /") void headerMapSubClass(@HeaderMap SubClassHeaders httpHeaders); @@ -534,7 +532,8 @@ interface QueryMapTestInterface { // invalid @RequestLine("POST /") - void multipleQueryMap(@QueryMap Map mapOne, @QueryMap Map mapTwo); + void multipleQueryMap(@QueryMap Map mapOne, + @QueryMap Map mapTwo); // invalid @RequestLine("POST /") @@ -660,27 +659,22 @@ public void parameterizedBaseApi() throws Exception { .containsOnlyKeys("ParameterizedApi#get(String)", "ParameterizedApi#getAll(Keys)"); assertThat(byConfigKey.get("ParameterizedApi#get(String)").returnType()) - .isEqualTo(new TypeToken>() { - }.getType()); + .isEqualTo(new TypeToken>() {}.getType()); assertThat(byConfigKey.get("ParameterizedApi#get(String)").template()).hasHeaders( entry("Version", asList("1")), - entry("Foo", asList("Bar")) - ); + entry("Foo", asList("Bar"))); assertThat(byConfigKey.get("ParameterizedApi#getAll(Keys)").returnType()) - .isEqualTo(new TypeToken>() { - }.getType()); + .isEqualTo(new TypeToken>() {}.getType()); assertThat(byConfigKey.get("ParameterizedApi#getAll(Keys)").bodyType()) - .isEqualTo(new TypeToken>() { - }.getType()); + .isEqualTo(new TypeToken>() {}.getType()); assertThat(byConfigKey.get("ParameterizedApi#getAll(Keys)").template()).hasHeaders( entry("Version", asList("1")), - entry("Foo", asList("Bar")) - ); + entry("Foo", asList("Bar"))); } @Headers("Authorization: {authHdr}") - interface ParameterizedHeaderExpandApi { + interface ParameterizedHeaderExpandApi { @RequestLine("GET /api/{zoneId}") @Headers("Accept: application/json") String getZone(@Param("zoneId") String vhost, @Param("authHdr") String authHdr); @@ -688,7 +682,8 @@ interface ParameterizedHeaderExpandApi { @Test public void parameterizedHeaderExpandApi() throws Exception { - List md = contract.parseAndValidatateMetadata(ParameterizedHeaderExpandApi.class); + List md = + contract.parseAndValidatateMetadata(ParameterizedHeaderExpandApi.class); assertThat(md).hasSize(1); @@ -697,7 +692,8 @@ public void parameterizedHeaderExpandApi() throws Exception { assertThat(md.get(0).returnType()) .isEqualTo(String.class); assertThat(md.get(0).template()) - .hasHeaders(entry("Authorization", asList("{authHdr}")), entry("Accept", asList("application/json"))); + .hasHeaders(entry("Authorization", asList("{authHdr}")), + entry("Accept", asList("application/json"))); // Ensure that the authHdr expansion was properly detected and did not create a formParam assertThat(md.get(0).formParams()) .isEmpty(); @@ -705,8 +701,7 @@ public void parameterizedHeaderExpandApi() throws Exception { @Test public void parameterizedHeaderNotStartingWithCurlyBraceExpandApi() throws Exception { - List - md = + List md = contract.parseAndValidatateMetadata( ParameterizedHeaderNotStartingWithCurlyBraceExpandApi.class); @@ -746,7 +741,8 @@ interface ParameterizedHeaderExpandInheritedApi extends ParameterizedHeaderBase @Test public void parameterizedHeaderExpandApiBaseClass() throws Exception { - List mds = contract.parseAndValidatateMetadata(ParameterizedHeaderExpandInheritedApi.class); + List mds = + contract.parseAndValidatateMetadata(ParameterizedHeaderExpandInheritedApi.class); Map byConfigKey = new LinkedHashMap(); for (MethodMetadata m : mds) { @@ -755,18 +751,20 @@ public void parameterizedHeaderExpandApiBaseClass() throws Exception { assertThat(byConfigKey) .containsOnlyKeys("ParameterizedHeaderExpandInheritedApi#getZoneAccept(String,String)", - "ParameterizedHeaderExpandInheritedApi#getZone(String,String)"); + "ParameterizedHeaderExpandInheritedApi#getZone(String,String)"); - MethodMetadata md = byConfigKey.get("ParameterizedHeaderExpandInheritedApi#getZoneAccept(String,String)"); + MethodMetadata md = + byConfigKey.get("ParameterizedHeaderExpandInheritedApi#getZoneAccept(String,String)"); assertThat(md.returnType()) .isEqualTo(String.class); assertThat(md.template()) - .hasHeaders(entry("Authorization", asList("{authHdr}")), entry("Accept", asList("application/json"))); + .hasHeaders(entry("Authorization", asList("{authHdr}")), + entry("Accept", asList("application/json"))); // Ensure that the authHdr expansion was properly detected and did not create a formParam assertThat(md.formParams()) .isEmpty(); - md = byConfigKey.get("ParameterizedHeaderExpandInheritedApi#getZone(String,String)"); + md = byConfigKey.get("ParameterizedHeaderExpandInheritedApi#getZone(String,String)"); assertThat(md.returnType()) .isEqualTo(String.class); assertThat(md.template()) @@ -775,11 +773,12 @@ public void parameterizedHeaderExpandApiBaseClass() throws Exception { .isEmpty(); } - private MethodMetadata parseAndValidateMetadata(Class targetType, String method, + private MethodMetadata parseAndValidateMetadata(Class targetType, + String method, Class... parameterTypes) throws NoSuchMethodException { return contract.parseAndValidateMetadata(targetType, - targetType.getMethod(method, parameterTypes)); + targetType.getMethod(method, parameterTypes)); } interface MissingMethod { @@ -791,7 +790,8 @@ interface MissingMethod { @Test public void missingMethod() throws Exception { thrown.expect(IllegalStateException.class); - thrown.expectMessage("RequestLine annotation didn't start with an HTTP verb on method updateSharing"); + thrown.expectMessage( + "RequestLine annotation didn't start with an HTTP verb on method updateSharing"); contract.parseAndValidatateMetadata(MissingMethod.class); } @@ -840,8 +840,7 @@ public void paramIsASubstringOfAQuery() throws Exception { List mds = contract.parseAndValidatateMetadata(SubstringQuery.class); assertThat(mds.get(0).template().queries()).containsExactly( - entry("q", asList("body:{body}")) - ); + entry("q", asList("body:{body}"))); assertThat(mds.get(0).formParams()).isEmpty(); // Prevent issue 424 } } diff --git a/core/src/test/java/feign/DefaultQueryMapEncoderTest.java b/core/src/test/java/feign/DefaultQueryMapEncoderTest.java index 63df04da72..0b8179b839 100644 --- a/core/src/test/java/feign/DefaultQueryMapEncoderTest.java +++ b/core/src/test/java/feign/DefaultQueryMapEncoderTest.java @@ -18,7 +18,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; diff --git a/core/src/test/java/feign/EmptyTargetTest.java b/core/src/test/java/feign/EmptyTargetTest.java index 6f6d838058..821b312143 100644 --- a/core/src/test/java/feign/EmptyTargetTest.java +++ b/core/src/test/java/feign/EmptyTargetTest.java @@ -16,11 +16,8 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; - import java.net.URI; - import feign.Target.EmptyTarget; - import static feign.assertj.FeignAssertions.assertThat; public class EmptyTargetTest { diff --git a/core/src/test/java/feign/FeignBuilderTest.java b/core/src/test/java/feign/FeignBuilderTest.java index 3c78598427..a69b4c681e 100644 --- a/core/src/test/java/feign/FeignBuilderTest.java +++ b/core/src/test/java/feign/FeignBuilderTest.java @@ -16,10 +16,8 @@ import java.util.HashMap; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; - import org.junit.Rule; import org.junit.Test; - import java.io.IOException; import java.io.InputStream; import java.io.Reader; @@ -33,10 +31,8 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; - import feign.codec.Decoder; import feign.codec.Encoder; - import static feign.assertj.MockWebServerAssertions.assertThat; import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; import static org.junit.Assert.assertEquals; @@ -86,13 +82,14 @@ public void testDecode404() throws Exception { - @Test public void testNoFollowRedirect() { - server.enqueue(new MockResponse().setResponseCode(302).addHeader("Location","/")); + @Test + public void testNoFollowRedirect() { + server.enqueue(new MockResponse().setResponseCode(302).addHeader("Location", "/")); String url = "http://localhost:" + server.getPort(); TestInterface noFollowApi = Feign.builder() - .options(new Request.Options(100, 600, false)) - .target(TestInterface.class, url); + .options(new Request.Options(100, 600, false)) + .target(TestInterface.class, url); Response response = noFollowApi.defaultMethodPassthrough(); assertThat(response.status()).isEqualTo(302); @@ -100,11 +97,11 @@ public void testDecode404() throws Exception { .isNotNull() .isEqualTo(Collections.singletonList("/")); - server.enqueue(new MockResponse().setResponseCode(302).addHeader("Location","/")); + server.enqueue(new MockResponse().setResponseCode(302).addHeader("Location", "/")); server.enqueue(new MockResponse().setResponseCode(200)); TestInterface defaultApi = Feign.builder() - .options(new Request.Options(100, 600, true)) - .target(TestInterface.class, url); + .options(new Request.Options(100, 600, true)) + .target(TestInterface.class, url); assertThat(defaultApi.defaultMethodPassthrough().status()).isEqualTo(200); } @@ -205,7 +202,8 @@ public Map encode(Object ignored) { } }; - TestInterface api = Feign.builder().queryMapEncoder(customMapEncoder).target(TestInterface.class, url); + TestInterface api = + Feign.builder().queryMapEncoder(customMapEncoder).target(TestInterface.class, url); api.queryMapEncoded("ignored"); assertThat(server.takeRequest()).hasQueryParams(Arrays.asList("key1=value1", "key2=value2")); @@ -299,11 +297,10 @@ public void testDefaultCallingProxiedMethod() throws Exception { /** * This test ensures that the doNotCloseAfterDecode flag functions. * - * It does so by creating a custom Decoder that lazily retrieves the - * response body when asked for it and pops the value into an Iterator. + * It does so by creating a custom Decoder that lazily retrieves the response body when asked for + * it and pops the value into an Iterator. * - * Without the doNoCloseAfterDecode flag, the test will fail with a - * "stream is closed" exception. + * Without the doNoCloseAfterDecode flag, the test will fail with a "stream is closed" exception. * * @throws Exception */ @@ -340,9 +337,9 @@ public Object next() { }; TestInterface api = Feign.builder() - .decoder(decoder) - .doNotCloseAfterDecode() - .target(TestInterface.class, url); + .decoder(decoder) + .doNotCloseAfterDecode() + .target(TestInterface.class, url); Iterator iterator = api.decodedLazyPost(); assertTrue(iterator.hasNext()); @@ -353,8 +350,8 @@ public Object next() { } /** - * When {@link Feign.Builder#doNotCloseAfterDecode()} is enabled an an exception - * is thrown from the {@link Decoder}, the response should be closed. + * When {@link Feign.Builder#doNotCloseAfterDecode()} is enabled an an exception is thrown from + * the {@link Decoder}, the response should be closed. */ @Test public void testDoNotCloseAfterDecodeDecoderFailure() throws Exception { @@ -370,49 +367,50 @@ public Object decode(Response response, Type type) throws IOException { final AtomicBoolean closed = new AtomicBoolean(); TestInterface api = Feign.builder() - .client(new Client() { - Client client = new Client.Default(null, null); - @Override - public Response execute(Request request, Request.Options options) throws IOException { - final Response original = client.execute(request, options); - return Response.builder() - .status(original.status()) - .headers(original.headers()) - .reason(original.reason()) - .request(original.request()) - .body(new Response.Body() { - @Override - public Integer length() { - return original.body().length(); - } - - @Override - public boolean isRepeatable() { - return original.body().isRepeatable(); - } - - @Override - public InputStream asInputStream() throws IOException { - return original.body().asInputStream(); - } - - @Override - public Reader asReader() throws IOException { - return original.body().asReader(); - } - - @Override - public void close() throws IOException { - closed.set(true); - original.body().close(); - } - }) - .build(); - } - }) - .decoder(angryDecoder) - .doNotCloseAfterDecode() - .target(TestInterface.class, url); + .client(new Client() { + Client client = new Client.Default(null, null); + + @Override + public Response execute(Request request, Request.Options options) throws IOException { + final Response original = client.execute(request, options); + return Response.builder() + .status(original.status()) + .headers(original.headers()) + .reason(original.reason()) + .request(original.request()) + .body(new Response.Body() { + @Override + public Integer length() { + return original.body().length(); + } + + @Override + public boolean isRepeatable() { + return original.body().isRepeatable(); + } + + @Override + public InputStream asInputStream() throws IOException { + return original.body().asInputStream(); + } + + @Override + public Reader asReader() throws IOException { + return original.body().asReader(); + } + + @Override + public void close() throws IOException { + closed.set(true); + original.body().close(); + } + }) + .build(); + } + }) + .decoder(angryDecoder) + .doNotCloseAfterDecode() + .target(TestInterface.class, url); try { api.decodedLazyPost(); fail("Expected an exception"); diff --git a/core/src/test/java/feign/FeignTest.java b/core/src/test/java/feign/FeignTest.java index fcba7548ad..0ddcf744a7 100644 --- a/core/src/test/java/feign/FeignTest.java +++ b/core/src/test/java/feign/FeignTest.java @@ -15,11 +15,9 @@ import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; - import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.SocketPolicy; import okhttp3.mockwebserver.MockWebServer; - import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -29,7 +27,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; - import java.io.IOException; import java.lang.reflect.Type; import java.net.URI; @@ -40,7 +37,6 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.concurrent.atomic.AtomicReference; - import feign.Target.HardCodedTarget; import feign.codec.DecodeException; import feign.codec.Decoder; @@ -49,7 +45,6 @@ import feign.codec.ErrorDecoder; import feign.codec.StringDecoder; import feign.Feign.ResponseMappingDecoder; - import static feign.Util.UTF_8; import static feign.assertj.MockWebServerAssertions.assertThat; import static org.hamcrest.CoreMatchers.isA; @@ -147,8 +142,7 @@ public void encode(Object object, Type bodyType, RequestTemplate template) { server.takeRequest(); - assertThat(encodedType.get()).isEqualTo(new TypeToken>() { - }.getType()); + assertThat(encodedType.get()).isEqualTo(new TypeToken>() {}.getType()); } @Test @@ -203,7 +197,7 @@ public void multipleInterceptor() throws Exception { api.post(); assertThat(server.takeRequest()).hasHeaders("X-Forwarded-For: origin.host.com", - "User-Agent: Feign"); + "User-Agent: Feign"); } @Test @@ -254,9 +248,9 @@ public void headerMap() throws Exception { api.headerMap(headerMap); assertThat(server.takeRequest()) - .hasHeaders( - MapEntry.entry("Content-Type", Arrays.asList("myContent")), - MapEntry.entry("Custom-Header", Arrays.asList("fooValue"))); + .hasHeaders( + MapEntry.entry("Content-Type", Arrays.asList("myContent")), + MapEntry.entry("Custom-Header", Arrays.asList("fooValue"))); } @Test @@ -271,9 +265,9 @@ public void headerMapWithHeaderAnnotations() throws Exception { // header map should be additive for headers provided by annotations assertThat(server.takeRequest()) - .hasHeaders( - MapEntry.entry("Content-Encoding", Arrays.asList("deflate")), - MapEntry.entry("Custom-Header", Arrays.asList("fooValue"))); + .hasHeaders( + MapEntry.entry("Content-Encoding", Arrays.asList("deflate")), + MapEntry.entry("Custom-Header", Arrays.asList("fooValue"))); server.enqueue(new MockResponse()); headerMap.put("Content-Encoding", "overrideFromMap"); @@ -283,9 +277,9 @@ public void headerMapWithHeaderAnnotations() throws Exception { // if header map has entry that collides with annotation, value specified // by header map should be used assertThat(server.takeRequest()) - .hasHeaders( - MapEntry.entry("Content-Encoding", Arrays.asList("overrideFromMap")), - MapEntry.entry("Custom-Header", Arrays.asList("fooValue"))); + .hasHeaders( + MapEntry.entry("Content-Encoding", Arrays.asList("overrideFromMap")), + MapEntry.entry("Custom-Header", Arrays.asList("fooValue"))); } @Test @@ -300,7 +294,7 @@ public void queryMap() throws Exception { api.queryMap(queryMap); assertThat(server.takeRequest()) - .hasPath("/?name=alice&fooKey=fooValue"); + .hasPath("/?name=alice&fooKey=fooValue"); } @Test @@ -317,13 +311,13 @@ public void queryMapIterableValuesExpanded() throws Exception { api.queryMap(queryMap); assertThat(server.takeRequest()) - .hasPath("/?name=Alice&name=Bob&fooKey=fooValue&emptyStringKey="); + .hasPath("/?name=Alice&name=Bob&fooKey=fooValue&emptyStringKey="); } @Test public void queryMapWithQueryParams() throws Exception { TestInterface api = new TestInterfaceBuilder() - .target("http://localhost:" + server.getPort()); + .target("http://localhost:" + server.getPort()); server.enqueue(new MockResponse()); Map queryMap = new LinkedHashMap(); @@ -331,7 +325,7 @@ public void queryMapWithQueryParams() throws Exception { api.queryMapWithQueryParams("alice", queryMap); // query map should be expanded after built-in parameters assertThat(server.takeRequest()) - .hasPath("/?name=alice&fooKey=fooValue"); + .hasPath("/?name=alice&fooKey=fooValue"); server.enqueue(new MockResponse()); queryMap = new LinkedHashMap(); @@ -339,7 +333,7 @@ public void queryMapWithQueryParams() throws Exception { api.queryMapWithQueryParams("alice", queryMap); // query map keys take precedence over built-in parameters assertThat(server.takeRequest()) - .hasPath("/?name=bob"); + .hasPath("/?name=bob"); server.enqueue(new MockResponse()); queryMap = new LinkedHashMap(); @@ -347,7 +341,7 @@ public void queryMapWithQueryParams() throws Exception { api.queryMapWithQueryParams("alice", queryMap); // null value for a query map key removes query parameter assertThat(server.takeRequest()) - .hasPath("/"); + .hasPath("/"); } @Test @@ -422,17 +416,17 @@ public void queryMapPojoWithEmptyParams() throws Exception { @Test public void configKeyFormatsAsExpected() throws Exception { assertEquals("TestInterface#post()", - Feign.configKey(TestInterface.class.getDeclaredMethod("post"))); + Feign.configKey(TestInterface.class.getDeclaredMethod("post"))); assertEquals("TestInterface#uriParam(String,URI,String)", - Feign.configKey(TestInterface.class - .getDeclaredMethod("uriParam", String.class, URI.class, - String.class))); + Feign.configKey(TestInterface.class + .getDeclaredMethod("uriParam", String.class, URI.class, + String.class))); } @Test public void configKeyUsesChildType() throws Exception { assertEquals("List#iterator()", - Feign.configKey(List.class, Iterable.class.getDeclaredMethod("iterator"))); + Feign.configKey(List.class, Iterable.class.getDeclaredMethod("iterator"))); } @Test @@ -526,15 +520,13 @@ public void ensureRetryerClonesItself() { MockRetryer retryer = new MockRetryer(); TestInterface api = Feign.builder() - .retryer(retryer) - .errorDecoder(new ErrorDecoder() - { - @Override - public Exception decode(String methodKey, Response response) - { - return new RetryableException("play it again sam!", null); - } - }).target(TestInterface.class, "http://localhost:" + server.getPort()); + .retryer(retryer) + .errorDecoder(new ErrorDecoder() { + @Override + public Exception decode(String methodKey, Response response) { + return new RetryableException("play it again sam!", null); + } + }).target(TestInterface.class, "http://localhost:" + server.getPort()); api.post(); api.post(); // if retryer instance was reused, this statement will throw an exception @@ -546,11 +538,11 @@ public void whenReturnTypeIsResponseNoErrorHandling() { Map> headers = new LinkedHashMap>(); headers.put("Location", Arrays.asList("http://bar.com")); final Response response = Response.builder() - .status(302) - .reason("Found") - .headers(headers) - .body(new byte[0]) - .build(); + .status(302) + .reason("Found") + .headers(headers) + .body(new byte[0]) + .build(); TestInterface api = Feign.builder() .client(new Client() { // fake client as Client.Default follows redirects. @@ -563,8 +555,7 @@ public Response execute(Request request, Request.Options options) { assertEquals(api.response().headers().get("Location"), Arrays.asList("http://bar.com")); } - private static class MockRetryer implements Retryer - { + private static class MockRetryer implements Retryer { boolean tripped; @Override @@ -578,7 +569,7 @@ public void continueOrPropagate(RetryableException e) { @Override public Retryer clone() { - return new MockRetryer(); + return new MockRetryer(); } } @@ -646,11 +637,9 @@ public void encode(Object object, Type bodyType, RequestTemplate template) { @Test public void equalsHashCodeAndToStringWork() { - Target - t1 = + Target t1 = new HardCodedTarget(TestInterface.class, "http://localhost:8080"); - Target - t2 = + Target t2 = new HardCodedTarget(TestInterface.class, "http://localhost:8888"); Target t3 = new HardCodedTarget(OtherTestInterface.class, "http://localhost:8080"); @@ -689,8 +678,7 @@ public void decodeLogicSupportsByteArray() throws Exception { byte[] expectedResponse = {12, 34, 56}; server.enqueue(new MockResponse().setBody(new Buffer().write(expectedResponse))); - OtherTestInterface - api = + OtherTestInterface api = Feign.builder().target(OtherTestInterface.class, "http://localhost:" + server.getPort()); assertThat(api.binaryResponseBody()) @@ -702,8 +690,7 @@ public void encodeLogicSupportsByteArray() throws Exception { byte[] expectedRequest = {12, 34, 56}; server.enqueue(new MockResponse()); - OtherTestInterface - api = + OtherTestInterface api = Feign.builder().target(OtherTestInterface.class, "http://localhost:" + server.getPort()); api.binaryRequestBody(expectedRequest); @@ -721,12 +708,13 @@ public void encodedQueryParam() throws Exception { api.encodedQueryParam("5.2FSi+"); assertThat(server.takeRequest()) - .hasPath("/?trim=5.2FSi+"); + .hasPath("/?trim=5.2FSi+"); } @Test public void responseMapperIsAppliedBeforeDelegate() throws IOException { - ResponseMappingDecoder decoder = new ResponseMappingDecoder(upperCaseResponseMapper(), new StringDecoder()); + ResponseMappingDecoder decoder = + new ResponseMappingDecoder(upperCaseResponseMapper(), new StringDecoder()); String output = (String) decoder.decode(responseWithText("response"), String.class); assertThat(output).isEqualTo("RESPONSE"); @@ -738,9 +726,9 @@ private ResponseMapper upperCaseResponseMapper() { public Response map(Response response, Type type) { try { return response - .toBuilder() - .body(Util.toString(response.body().asReader()).toUpperCase().getBytes()) - .build(); + .toBuilder() + .body(Util.toString(response.body().asReader()).toUpperCase().getBytes()) + .build(); } catch (IOException e) { throw new RuntimeException(e); } @@ -750,10 +738,10 @@ public Response map(Response response, Type type) { private Response responseWithText(String text) { return Response.builder() - .body(text, Util.UTF_8) - .status(200) - .headers(new HashMap>()) - .build(); + .body(text, Util.UTF_8) + .status(200) + .headers(new HashMap>()) + .build(); } @Test @@ -761,8 +749,8 @@ public void mapAndDecodeExecutesMapFunction() { server.enqueue(new MockResponse().setBody("response!")); TestInterface api = new Feign.Builder() - .mapAndDecode(upperCaseResponseMapper(), new StringDecoder()) - .target(TestInterface.class, "http://localhost:" + server.getPort()); + .mapAndDecode(upperCaseResponseMapper(), new StringDecoder()) + .target(TestInterface.class, "http://localhost:" + server.getPort()); assertEquals(api.post(), "RESPONSE!"); } @@ -778,8 +766,9 @@ interface TestInterface { @RequestLine("POST /") @Body("%7B\"customer_name\": \"{customer_name}\", \"user_name\": \"{user_name}\", \"password\": \"{password}\"%7D") void login( - @Param("customer_name") String customer, @Param("user_name") String user, - @Param("password") String password); + @Param("customer_name") String customer, + @Param("user_name") String user, + @Param("password") String password); @RequestLine("POST /") void body(List contents); @@ -794,8 +783,9 @@ void login( @RequestLine("POST /") void form( - @Param("customer_name") String customer, @Param("user_name") String user, - @Param("password") String password); + @Param("customer_name") String customer, + @Param("user_name") String user, + @Param("password") String password); @RequestLine("GET /{1}/{2}") Response uriParam(@Param("1") String one, URI endpoint, @Param("2") String two); @@ -826,7 +816,8 @@ void form( void queryMapEncoded(@QueryMap(encoded = true) Map queryMap); @RequestLine("GET /?name={name}") - void queryMapWithQueryParams(@Param("name") String name, @QueryMap Map queryMap); + void queryMapWithQueryParams(@Param("name") String name, + @QueryMap Map queryMap); @RequestLine("GET /?trim={trim}") void encodedQueryParam(@Param(value = "trim", encoded = true) String trim); diff --git a/core/src/test/java/feign/LoggerTest.java b/core/src/test/java/feign/LoggerTest.java index d284bc685e..6f8ac7d688 100644 --- a/core/src/test/java/feign/LoggerTest.java +++ b/core/src/test/java/feign/LoggerTest.java @@ -15,7 +15,6 @@ import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; - import org.assertj.core.api.SoftAssertions; import org.junit.Rule; import org.junit.Test; @@ -28,13 +27,11 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import org.junit.runners.model.Statement; - import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; - import feign.Logger.Level; @RunWith(Enclosed.class) @@ -46,7 +43,7 @@ public class LoggerTest { /** Ensure expected exception handling is done before logger rule. */ @Rule - public final RuleChain chain= RuleChain.outerRule( server ).around( logger ).around( thrown ); + public final RuleChain chain = RuleChain.outerRule(server).around(logger).around(thrown); interface SendsStuff { @@ -55,8 +52,9 @@ interface SendsStuff { @Headers("Content-Type: application/json") @Body("%7B\"customer_name\": \"{customer_name}\", \"user_name\": \"{user_name}\", \"password\": \"{password}\"%7D") String login( - @Param("customer_name") String customer, - @Param("user_name") String user, @Param("password") String password); + @Param("customer_name") String customer, + @Param("user_name") String user, + @Param("password") String password); } @RunWith(Parameterized.class) @@ -71,7 +69,7 @@ public LogLevelEmitsTest(Level logLevel, List expectedMessages) { @Parameters public static Iterable data() { - return Arrays.asList(new Object[][]{ + return Arrays.asList(new Object[][] { {Level.NONE, Arrays.asList()}, {Level.BASIC, Arrays.asList( "\\[SendsStuff#login\\] ---> POST http://localhost:[0-9]+/ HTTP/1.1", @@ -124,7 +122,7 @@ public ReasonPhraseOptional(Level logLevel, List expectedMessages) { @Parameters public static Iterable data() { - return Arrays.asList(new Object[][]{ + return Arrays.asList(new Object[][] { {Level.BASIC, Arrays.asList( "\\[SendsStuff#login\\] ---> POST http://localhost:[0-9]+/ HTTP/1.1", "\\[SendsStuff#login\\] <--- HTTP/1.1 200 \\([0-9]+ms\\)")}, @@ -156,7 +154,7 @@ public ReadTimeoutEmitsTest(Level logLevel, List expectedMessages) { @Parameters public static Iterable data() { - return Arrays.asList(new Object[][]{ + return Arrays.asList(new Object[][] { {Level.NONE, Arrays.asList()}, {Level.BASIC, Arrays.asList( "\\[SendsStuff#login\\] ---> POST http://localhost:[0-9]+/ HTTP/1.1", @@ -194,8 +192,10 @@ public void levelEmitsOnReadTimeout() throws IOException, InterruptedException { public void continueOrPropagate(RetryableException e) { throw e; } - @Override public Retryer clone() { - return this; + + @Override + public Retryer clone() { + return this; } }) .target(SendsStuff.class, "http://localhost:" + server.getPort()); @@ -216,7 +216,7 @@ public UnknownHostEmitsTest(Level logLevel, List expectedMessages) { @Parameters public static Iterable data() { - return Arrays.asList(new Object[][]{ + return Arrays.asList(new Object[][] { {Level.NONE, Arrays.asList()}, {Level.BASIC, Arrays.asList( "\\[SendsStuff#login\\] ---> POST http://robofu.abc/ HTTP/1.1", @@ -250,8 +250,10 @@ public void unknownHostEmits() throws IOException, InterruptedException { public void continueOrPropagate(RetryableException e) { throw e; } - @Override public Retryer clone() { - return this; + + @Override + public Retryer clone() { + return this; } }) .target(SendsStuff.class, "http://robofu.abc"); @@ -265,18 +267,18 @@ public void continueOrPropagate(RetryableException e) { @RunWith(Parameterized.class) public static class FormatCharacterTest - extends LoggerTest { + extends LoggerTest { private final Level logLevel; - public FormatCharacterTest( Level logLevel, List expectedMessages) { + public FormatCharacterTest(Level logLevel, List expectedMessages) { this.logLevel = logLevel; logger.expectMessages(expectedMessages); } @Parameters public static Iterable data() { - return Arrays.asList(new Object[][]{ + return Arrays.asList(new Object[][] { {Level.NONE, Arrays.asList()}, {Level.BASIC, Arrays.asList( "\\[SendsStuff#login\\] ---> POST http://sna%fu.abc/ HTTP/1.1", @@ -310,8 +312,10 @@ public void formatCharacterEmits() throws IOException, InterruptedException { public void continueOrPropagate(RetryableException e) { throw e; } - @Override public Retryer clone() { - return this; + + @Override + public Retryer clone() { + return this; } }) .target(SendsStuff.class, "http://sna%fu.abc"); @@ -335,7 +339,7 @@ public RetryEmitsTest(Level logLevel, List expectedMessages) { @Parameters public static Iterable data() { - return Arrays.asList(new Object[][]{ + return Arrays.asList(new Object[][] { {Level.NONE, Arrays.asList()}, {Level.BASIC, Arrays.asList( "\\[SendsStuff#login\\] ---> POST http://robofu.abc/ HTTP/1.1", @@ -367,7 +371,7 @@ public void continueOrPropagate(RetryableException e) { @Override public Retryer clone() { - return this; + return this; } }) .target(SendsStuff.class, "http://robofu.abc"); @@ -398,8 +402,8 @@ public Statement apply(final Statement base, Description description) { public void evaluate() throws Throwable { base.evaluate(); SoftAssertions softly = new SoftAssertions(); - softly.assertThat( messages.size() ).isEqualTo( expectedMessages.size() ); - for (int i = 0; i < messages.size() && i>emptyMap()) - .body(new byte[0]) - .build(); + .status(200) + .headers(Collections.>emptyMap()) + .body(new byte[0]) + .build(); assertThat(response.reason()).isNull(); assertThat(response.toString()).isEqualTo("HTTP/1.1 200\n\n"); @@ -45,10 +43,10 @@ public void canAccessHeadersCaseInsensitively() { List valueList = Collections.singletonList("application/json"); headersMap.put("Content-Type", valueList); Response response = Response.builder() - .status(200) - .headers(headersMap) - .body(new byte[0]) - .build(); + .status(200) + .headers(headersMap) + .body(new byte[0]) + .build(); assertThat(response.headers().get("content-type")).isEqualTo(valueList); assertThat(response.headers().get("Content-Type")).isEqualTo(valueList); } @@ -60,12 +58,13 @@ public void headerValuesWithSameNameOnlyVaryingInCaseAreMerged() { headersMap.put("set-cookie", Arrays.asList("Cookie-C=Value")); Response response = Response.builder() - .status(200) - .headers(headersMap) - .body(new byte[0]) - .build(); + .status(200) + .headers(headersMap) + .body(new byte[0]) + .build(); - List expectedHeaderValue = Arrays.asList("Cookie-A=Value", "Cookie-B=Value", "Cookie-C=Value"); + List expectedHeaderValue = + Arrays.asList("Cookie-A=Value", "Cookie-B=Value", "Cookie-C=Value"); assertThat(response.headers()).containsOnly(entry(("set-cookie"), expectedHeaderValue)); } } diff --git a/core/src/test/java/feign/RetryerTest.java b/core/src/test/java/feign/RetryerTest.java index 29d65ca2b3..fcac120a4c 100644 --- a/core/src/test/java/feign/RetryerTest.java +++ b/core/src/test/java/feign/RetryerTest.java @@ -17,11 +17,8 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; - import java.util.Date; - import feign.Retryer.Default; - import static org.junit.Assert.assertEquals; public class RetryerTest { @@ -79,7 +76,8 @@ public void defaultRetryerFailsOnInterruptedException() { Default retryer = new Retryer.Default(); Thread.currentThread().interrupt(); - RetryableException expected = new RetryableException(null, null, new Date(System.currentTimeMillis() + 5000)); + RetryableException expected = + new RetryableException(null, null, new Date(System.currentTimeMillis() + 5000)); try { retryer.continueOrPropagate(expected); Thread.interrupted(); // reset interrupted flag in case it wasn't diff --git a/core/src/test/java/feign/TargetTest.java b/core/src/test/java/feign/TargetTest.java index 1dde229761..9563fb3bea 100644 --- a/core/src/test/java/feign/TargetTest.java +++ b/core/src/test/java/feign/TargetTest.java @@ -15,12 +15,9 @@ import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; - import org.junit.Rule; import org.junit.Test; - import feign.Target.HardCodedTarget; - import static feign.assertj.MockWebServerAssertions.assertThat; public class TargetTest { @@ -63,8 +60,7 @@ public Request apply(RequestTemplate input) { urlEncoded.method(), urlEncoded.url().replace("%2F", "/"), urlEncoded.headers(), - urlEncoded.body(), urlEncoded.charset() - ); + urlEncoded.body(), urlEncoded.charset()); } }; diff --git a/core/src/test/java/feign/UtilTest.java b/core/src/test/java/feign/UtilTest.java index 6a7c3251f0..a3a2ddc60d 100644 --- a/core/src/test/java/feign/UtilTest.java +++ b/core/src/test/java/feign/UtilTest.java @@ -14,7 +14,6 @@ package feign; import org.junit.Test; - import java.io.Reader; import java.lang.reflect.Type; import java.util.Collection; @@ -23,9 +22,7 @@ import java.util.List; import java.util.Map; import java.util.Set; - import feign.codec.Decoder; - import static feign.Util.resolveLastTypeParameter; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; @@ -76,8 +73,7 @@ public void lastTypeFromInstance() throws Exception { @Test public void lastTypeFromAnonymous() throws Exception { - Parameterized instance = new Parameterized() { - }; + Parameterized instance = new Parameterized() {}; Type last = resolveLastTypeParameter(instance.getClass(), Parameterized.class); assertEquals(Reader.class, last); } diff --git a/core/src/test/java/feign/assertj/FeignAssertions.java b/core/src/test/java/feign/assertj/FeignAssertions.java index 6d35d9da55..e98ba5e9f2 100644 --- a/core/src/test/java/feign/assertj/FeignAssertions.java +++ b/core/src/test/java/feign/assertj/FeignAssertions.java @@ -14,7 +14,6 @@ package feign.assertj; import org.assertj.core.api.Assertions; - import feign.RequestTemplate; public class FeignAssertions extends Assertions { diff --git a/core/src/test/java/feign/assertj/MockWebServerAssertions.java b/core/src/test/java/feign/assertj/MockWebServerAssertions.java index 5a3ce981a8..8c29bcb057 100644 --- a/core/src/test/java/feign/assertj/MockWebServerAssertions.java +++ b/core/src/test/java/feign/assertj/MockWebServerAssertions.java @@ -14,7 +14,6 @@ package feign.assertj; import okhttp3.mockwebserver.RecordedRequest; - import org.assertj.core.api.Assertions; public class MockWebServerAssertions extends Assertions { diff --git a/core/src/test/java/feign/assertj/RecordedRequestAssert.java b/core/src/test/java/feign/assertj/RecordedRequestAssert.java index b10c9c1901..204b44362f 100644 --- a/core/src/test/java/feign/assertj/RecordedRequestAssert.java +++ b/core/src/test/java/feign/assertj/RecordedRequestAssert.java @@ -18,14 +18,12 @@ import java.util.Collections; import okhttp3.Headers; import okhttp3.mockwebserver.RecordedRequest; - import org.assertj.core.api.AbstractAssert; import org.assertj.core.data.MapEntry; import org.assertj.core.internal.ByteArrays; import org.assertj.core.internal.Failures; import org.assertj.core.internal.Maps; import org.assertj.core.internal.Objects; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.ArrayList; @@ -35,9 +33,7 @@ import java.util.Set; import java.util.zip.GZIPInputStream; import java.util.zip.InflaterInputStream; - import feign.Util; - import static org.assertj.core.data.MapEntry.entry; import static org.assertj.core.error.ShouldNotContain.shouldNotContain; diff --git a/core/src/test/java/feign/assertj/RequestTemplateAssert.java b/core/src/test/java/feign/assertj/RequestTemplateAssert.java index ae6004cc71..6562e6421a 100644 --- a/core/src/test/java/feign/assertj/RequestTemplateAssert.java +++ b/core/src/test/java/feign/assertj/RequestTemplateAssert.java @@ -18,9 +18,7 @@ import org.assertj.core.internal.ByteArrays; import org.assertj.core.internal.Maps; import org.assertj.core.internal.Objects; - import feign.RequestTemplate; - import static feign.Util.UTF_8; public final class RequestTemplateAssert diff --git a/core/src/test/java/feign/auth/BasicAuthRequestInterceptorTest.java b/core/src/test/java/feign/auth/BasicAuthRequestInterceptorTest.java index 77ab9591df..8d77c28cd9 100644 --- a/core/src/test/java/feign/auth/BasicAuthRequestInterceptorTest.java +++ b/core/src/test/java/feign/auth/BasicAuthRequestInterceptorTest.java @@ -14,9 +14,7 @@ package feign.auth; import org.junit.Test; - import feign.RequestTemplate; - import static feign.assertj.FeignAssertions.assertThat; import static java.util.Arrays.asList; import static org.assertj.core.data.MapEntry.entry; @@ -26,30 +24,26 @@ public class BasicAuthRequestInterceptorTest { @Test public void addsAuthorizationHeader() { RequestTemplate template = new RequestTemplate(); - BasicAuthRequestInterceptor - interceptor = + BasicAuthRequestInterceptor interceptor = new BasicAuthRequestInterceptor("Aladdin", "open sesame"); interceptor.apply(template); assertThat(template) .hasHeaders( - entry("Authorization", asList("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==")) - ); + entry("Authorization", asList("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="))); } @Test public void addsAuthorizationHeader_longUserAndPassword() { RequestTemplate template = new RequestTemplate(); - BasicAuthRequestInterceptor - interceptor = + BasicAuthRequestInterceptor interceptor = new BasicAuthRequestInterceptor("IOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIO", - "101010101010101010101010101010101010101010"); + "101010101010101010101010101010101010101010"); interceptor.apply(template); assertThat(template) .hasHeaders( entry("Authorization", asList( - "Basic SU9JT0lPSU9JT0lPSU9JT0lPSU9JT0lPSU9JT0lPSU9JT0lPSU86MTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEw")) - ); + "Basic SU9JT0lPSU9JT0lPSU9JT0lPSU9JT0lPSU9JT0lPSU9JT0lPSU86MTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEw"))); } } diff --git a/core/src/test/java/feign/client/AbstractClientTest.java b/core/src/test/java/feign/client/AbstractClientTest.java index 740b9d102b..992c9a0a4b 100644 --- a/core/src/test/java/feign/client/AbstractClientTest.java +++ b/core/src/test/java/feign/client/AbstractClientTest.java @@ -17,11 +17,9 @@ import java.io.IOException; import java.util.Arrays; import java.util.List; - import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; - import feign.Client; import feign.CollectionFormat; import feign.Feign.Builder; @@ -35,308 +33,305 @@ import feign.assertj.MockWebServerAssertions; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; - import static java.util.Arrays.asList; - import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; - import static feign.Util.UTF_8; /** - * {@link AbstractClientTest} can be extended to run a set of tests against any {@link Client} implementation. + * {@link AbstractClientTest} can be extended to run a set of tests against any {@link Client} + * implementation. */ public abstract class AbstractClientTest { - @Rule - public final ExpectedException thrown = ExpectedException.none(); - @Rule - public final MockWebServer server = new MockWebServer(); - - /** - * Create a Feign {@link Builder} with a client configured - */ - public abstract Builder newBuilder(); - - /** - * Some client implementation tests should override this - * test if the PATCH operation is unsupported. - */ - @Test - public void testPatch() throws Exception { - server.enqueue(new MockResponse().setBody("foo")); - server.enqueue(new MockResponse()); - - TestInterface api = newBuilder() - .target(TestInterface.class, "http://localhost:" + server.getPort()); - - assertEquals("foo", api.patch("")); - - MockWebServerAssertions.assertThat(server.takeRequest()) - .hasHeaders("Accept: text/plain", "Content-Length: 0") // Note: OkHttp adds content length. - .hasNoHeaderNamed("Content-Type") - .hasMethod("PATCH"); - } - - @Test - public void parsesRequestAndResponse() throws IOException, InterruptedException { - server.enqueue(new MockResponse().setBody("foo").addHeader("Foo: Bar")); - - TestInterface api = newBuilder() - .target(TestInterface.class, "http://localhost:" + server.getPort()); - - Response response = api.post("foo"); - - assertThat(response.status()).isEqualTo(200); - assertThat(response.reason()).isEqualTo("OK"); - assertThat(response.headers()) - .containsEntry("Content-Length", asList("3")) - .containsEntry("Foo", asList("Bar")); - assertThat(response.body().asInputStream()) - .hasContentEqualTo(new ByteArrayInputStream("foo".getBytes(UTF_8))); - - MockWebServerAssertions.assertThat(server.takeRequest()).hasMethod("POST") - .hasPath("/?foo=bar&foo=baz&qux=") - .hasHeaders("Foo: Bar", "Foo: Baz", "Qux: ", "Accept: */*", "Content-Length: 3") - .hasBody("foo"); - } - - @Test - public void reasonPhraseIsOptional() throws IOException, InterruptedException { - server.enqueue(new MockResponse().setStatus("HTTP/1.1 " + 200)); - - TestInterface api = newBuilder() - .target(TestInterface.class, "http://localhost:" + server.getPort()); - - Response response = api.post("foo"); - - assertThat(response.status()).isEqualTo(200); - assertThat(response.reason()).isNullOrEmpty(); - } - - @Test - public void parsesErrorResponse() throws IOException, InterruptedException { - thrown.expect(FeignException.class); - thrown.expectMessage("status 500 reading TestInterface#get(); content:\n" + "ARGHH"); - - server.enqueue(new MockResponse().setResponseCode(500).setBody("ARGHH")); - - TestInterface api = newBuilder() - .target(TestInterface.class, "http://localhost:" + server.getPort()); - - api.get(); - } - - @Test - public void safeRebuffering() throws IOException, InterruptedException { - server.enqueue(new MockResponse().setBody("foo")); - - TestInterface api = newBuilder() - .logger(new Logger(){ - @Override - protected void log(String configKey, String format, Object... args) { - } - }) - .logLevel(Logger.Level.FULL) // rebuffers the body - .target(TestInterface.class, "http://localhost:" + server.getPort()); - - api.post("foo"); - } - - /** This shows that is a no-op or otherwise doesn't cause an NPE when there's no content. */ - @Test - public void safeRebuffering_noContent() throws IOException, InterruptedException { - server.enqueue(new MockResponse().setResponseCode(204)); - - TestInterface api = newBuilder() - .logger(new Logger(){ - @Override - protected void log(String configKey, String format, Object... args) { - } - }) - .logLevel(Logger.Level.FULL) // rebuffers the body - .target(TestInterface.class, "http://localhost:" + server.getPort()); - - api.post("foo"); - } - - @Test - public void noResponseBodyForPost() { - server.enqueue(new MockResponse()); - - TestInterface api = newBuilder() - .target(TestInterface.class, "http://localhost:" + server.getPort()); - - api.noPostBody(); - } - - @Test - public void noResponseBodyForPut() { - server.enqueue(new MockResponse()); - - TestInterface api = newBuilder() - .target(TestInterface.class, "http://localhost:" + server.getPort()); - - api.noPutBody(); - } - - @Test - public void parsesResponseMissingLength() throws IOException, InterruptedException { - server.enqueue(new MockResponse().setChunkedBody("foo", 1)); - - TestInterface api = newBuilder() - .target(TestInterface.class, "http://localhost:" + server.getPort()); - - Response response = api.post("testing"); - assertThat(response.status()).isEqualTo(200); - assertThat(response.reason()).isEqualTo("OK"); - assertThat(response.body().length()).isNull(); - assertThat(response.body().asInputStream()) - .hasContentEqualTo(new ByteArrayInputStream("foo".getBytes(UTF_8))); - } - - @Test - public void postWithSpacesInPath() throws IOException, InterruptedException { - server.enqueue(new MockResponse().setBody("foo")); - - TestInterface api = newBuilder() - .target(TestInterface.class, "http://localhost:" + server.getPort()); - - Response response = api.post("current documents", "foo"); - - MockWebServerAssertions.assertThat(server.takeRequest()).hasMethod("POST") - .hasPath("/path/current%20documents/resource") - .hasBody("foo"); - } - - @Test - public void testVeryLongResponseNullLength() throws Exception { - server.enqueue(new MockResponse() - .setBody("AAAAAAAA") - .addHeader("Content-Length", Long.MAX_VALUE)); - TestInterface api = newBuilder() - .target(TestInterface.class, "http://localhost:" + server.getPort()); - - Response response = api.post("foo"); - // Response length greater than Integer.MAX_VALUE should be null - assertThat(response.body().length()).isNull(); - } - - @Test - public void testResponseLength() throws Exception { - server.enqueue(new MockResponse() - .setBody("test")); - TestInterface api = newBuilder() - .target(TestInterface.class, "http://localhost:" + server.getPort()); - - Integer expected = 4; - Response response = api.post(""); - Integer actual = response.body().length(); - assertEquals(expected, actual); - } - - @Test - public void testContentTypeWithCharset() throws Exception { - server.enqueue(new MockResponse() - .setBody("AAAAAAAA")); - TestInterface api = newBuilder() - .target(TestInterface.class, "http://localhost:" + server.getPort()); - - Response response = api.postWithContentType("foo", "text/plain;charset=utf-8"); - // Response length should not be null - assertEquals("AAAAAAAA", Util.toString(response.body().asReader())); - } - - @Test - public void testContentTypeWithoutCharset() throws Exception { - server.enqueue(new MockResponse() - .setBody("AAAAAAAA")); - TestInterface api = newBuilder() - .target(TestInterface.class, "http://localhost:" + server.getPort()); - - Response response = api.postWithContentType("foo", "text/plain"); - // Response length should not be null - assertEquals("AAAAAAAA", Util.toString(response.body().asReader())); - } - - @Test - public void testContentTypeDefaultsToRequestCharset() throws Exception { - server.enqueue(new MockResponse().setBody("foo")); - TestInterface api = newBuilder() - .target(TestInterface.class, "http://localhost:" + server.getPort()); - - // should use utf-8 encoding by default - api.postWithContentType("àáâãäåèéêë", "text/plain"); - - MockWebServerAssertions.assertThat(server.takeRequest()).hasMethod("POST") - .hasBody("àáâãäåèéêë"); - } - - @Test - public void testDefaultCollectionFormat() throws Exception { - server.enqueue(new MockResponse().setBody("body")); - - TestInterface api = newBuilder() - .target(TestInterface.class, "http://localhost:" + server.getPort()); - - Response response = api.get(Arrays.asList(new String[] {"bar","baz"})); - - assertThat(response.status()).isEqualTo(200); - assertThat(response.reason()).isEqualTo("OK"); - - MockWebServerAssertions.assertThat(server.takeRequest()).hasMethod("GET") - .hasPath("/?foo=bar&foo=baz"); - } - @Test - public void testAlternativeCollectionFormat() throws Exception { - server.enqueue(new MockResponse().setBody("body")); - - TestInterface api = newBuilder() - .target(TestInterface.class, "http://localhost:" + server.getPort()); - - Response response = api.getCSV(Arrays.asList(new String[] {"bar","baz"})); - - assertThat(response.status()).isEqualTo(200); - assertThat(response.reason()).isEqualTo("OK"); - - // Some HTTP libraries percent-encode commas in query parameters and others don't. - MockWebServerAssertions.assertThat(server.takeRequest()).hasMethod("GET") - .hasOneOfPath("/?foo=bar,baz", "/?foo=bar%2Cbaz"); - } - - public interface TestInterface { - - @RequestLine("POST /?foo=bar&foo=baz&qux=") - @Headers({"Foo: Bar", "Foo: Baz", "Qux: ", "Content-Type: text/plain"}) - Response post(String body); - - @RequestLine("POST /path/{to}/resource") - @Headers("Accept: text/plain") - Response post(@Param("to") String to, String body); - - @RequestLine("GET /") - @Headers("Accept: text/plain") - String get(); - - @RequestLine("GET /?foo={multiFoo}") - Response get(@Param("multiFoo") List multiFoo); - - @RequestLine(value = "GET /?foo={multiFoo}", collectionFormat = CollectionFormat.CSV) - Response getCSV(@Param("multiFoo") List multiFoo); - - @RequestLine("PATCH /") - @Headers("Accept: text/plain") - String patch(String body); + @Rule + public final ExpectedException thrown = ExpectedException.none(); + @Rule + public final MockWebServer server = new MockWebServer(); + + /** + * Create a Feign {@link Builder} with a client configured + */ + public abstract Builder newBuilder(); + + /** + * Some client implementation tests should override this test if the PATCH operation is + * unsupported. + */ + @Test + public void testPatch() throws Exception { + server.enqueue(new MockResponse().setBody("foo")); + server.enqueue(new MockResponse()); + + TestInterface api = newBuilder() + .target(TestInterface.class, "http://localhost:" + server.getPort()); + + assertEquals("foo", api.patch("")); + + MockWebServerAssertions.assertThat(server.takeRequest()) + .hasHeaders("Accept: text/plain", "Content-Length: 0") // Note: OkHttp adds content length. + .hasNoHeaderNamed("Content-Type") + .hasMethod("PATCH"); + } + + @Test + public void parsesRequestAndResponse() throws IOException, InterruptedException { + server.enqueue(new MockResponse().setBody("foo").addHeader("Foo: Bar")); + + TestInterface api = newBuilder() + .target(TestInterface.class, "http://localhost:" + server.getPort()); + + Response response = api.post("foo"); + + assertThat(response.status()).isEqualTo(200); + assertThat(response.reason()).isEqualTo("OK"); + assertThat(response.headers()) + .containsEntry("Content-Length", asList("3")) + .containsEntry("Foo", asList("Bar")); + assertThat(response.body().asInputStream()) + .hasContentEqualTo(new ByteArrayInputStream("foo".getBytes(UTF_8))); + + MockWebServerAssertions.assertThat(server.takeRequest()).hasMethod("POST") + .hasPath("/?foo=bar&foo=baz&qux=") + .hasHeaders("Foo: Bar", "Foo: Baz", "Qux: ", "Accept: */*", "Content-Length: 3") + .hasBody("foo"); + } + + @Test + public void reasonPhraseIsOptional() throws IOException, InterruptedException { + server.enqueue(new MockResponse().setStatus("HTTP/1.1 " + 200)); + + TestInterface api = newBuilder() + .target(TestInterface.class, "http://localhost:" + server.getPort()); + + Response response = api.post("foo"); + + assertThat(response.status()).isEqualTo(200); + assertThat(response.reason()).isNullOrEmpty(); + } + + @Test + public void parsesErrorResponse() throws IOException, InterruptedException { + thrown.expect(FeignException.class); + thrown.expectMessage("status 500 reading TestInterface#get(); content:\n" + "ARGHH"); + + server.enqueue(new MockResponse().setResponseCode(500).setBody("ARGHH")); + + TestInterface api = newBuilder() + .target(TestInterface.class, "http://localhost:" + server.getPort()); + + api.get(); + } + + @Test + public void safeRebuffering() throws IOException, InterruptedException { + server.enqueue(new MockResponse().setBody("foo")); + + TestInterface api = newBuilder() + .logger(new Logger() { + @Override + protected void log(String configKey, String format, Object... args) {} + }) + .logLevel(Logger.Level.FULL) // rebuffers the body + .target(TestInterface.class, "http://localhost:" + server.getPort()); + + api.post("foo"); + } + + /** This shows that is a no-op or otherwise doesn't cause an NPE when there's no content. */ + @Test + public void safeRebuffering_noContent() throws IOException, InterruptedException { + server.enqueue(new MockResponse().setResponseCode(204)); + + TestInterface api = newBuilder() + .logger(new Logger() { + @Override + protected void log(String configKey, String format, Object... args) {} + }) + .logLevel(Logger.Level.FULL) // rebuffers the body + .target(TestInterface.class, "http://localhost:" + server.getPort()); + + api.post("foo"); + } + + @Test + public void noResponseBodyForPost() { + server.enqueue(new MockResponse()); + + TestInterface api = newBuilder() + .target(TestInterface.class, "http://localhost:" + server.getPort()); + + api.noPostBody(); + } + + @Test + public void noResponseBodyForPut() { + server.enqueue(new MockResponse()); + + TestInterface api = newBuilder() + .target(TestInterface.class, "http://localhost:" + server.getPort()); + + api.noPutBody(); + } + + @Test + public void parsesResponseMissingLength() throws IOException, InterruptedException { + server.enqueue(new MockResponse().setChunkedBody("foo", 1)); + + TestInterface api = newBuilder() + .target(TestInterface.class, "http://localhost:" + server.getPort()); + + Response response = api.post("testing"); + assertThat(response.status()).isEqualTo(200); + assertThat(response.reason()).isEqualTo("OK"); + assertThat(response.body().length()).isNull(); + assertThat(response.body().asInputStream()) + .hasContentEqualTo(new ByteArrayInputStream("foo".getBytes(UTF_8))); + } + + @Test + public void postWithSpacesInPath() throws IOException, InterruptedException { + server.enqueue(new MockResponse().setBody("foo")); + + TestInterface api = newBuilder() + .target(TestInterface.class, "http://localhost:" + server.getPort()); + + Response response = api.post("current documents", "foo"); + + MockWebServerAssertions.assertThat(server.takeRequest()).hasMethod("POST") + .hasPath("/path/current%20documents/resource") + .hasBody("foo"); + } + + @Test + public void testVeryLongResponseNullLength() throws Exception { + server.enqueue(new MockResponse() + .setBody("AAAAAAAA") + .addHeader("Content-Length", Long.MAX_VALUE)); + TestInterface api = newBuilder() + .target(TestInterface.class, "http://localhost:" + server.getPort()); + + Response response = api.post("foo"); + // Response length greater than Integer.MAX_VALUE should be null + assertThat(response.body().length()).isNull(); + } + + @Test + public void testResponseLength() throws Exception { + server.enqueue(new MockResponse() + .setBody("test")); + TestInterface api = newBuilder() + .target(TestInterface.class, "http://localhost:" + server.getPort()); + + Integer expected = 4; + Response response = api.post(""); + Integer actual = response.body().length(); + assertEquals(expected, actual); + } + + @Test + public void testContentTypeWithCharset() throws Exception { + server.enqueue(new MockResponse() + .setBody("AAAAAAAA")); + TestInterface api = newBuilder() + .target(TestInterface.class, "http://localhost:" + server.getPort()); + + Response response = api.postWithContentType("foo", "text/plain;charset=utf-8"); + // Response length should not be null + assertEquals("AAAAAAAA", Util.toString(response.body().asReader())); + } + + @Test + public void testContentTypeWithoutCharset() throws Exception { + server.enqueue(new MockResponse() + .setBody("AAAAAAAA")); + TestInterface api = newBuilder() + .target(TestInterface.class, "http://localhost:" + server.getPort()); + + Response response = api.postWithContentType("foo", "text/plain"); + // Response length should not be null + assertEquals("AAAAAAAA", Util.toString(response.body().asReader())); + } + + @Test + public void testContentTypeDefaultsToRequestCharset() throws Exception { + server.enqueue(new MockResponse().setBody("foo")); + TestInterface api = newBuilder() + .target(TestInterface.class, "http://localhost:" + server.getPort()); + + // should use utf-8 encoding by default + api.postWithContentType("àáâãäåèéêë", "text/plain"); + + MockWebServerAssertions.assertThat(server.takeRequest()).hasMethod("POST") + .hasBody("àáâãäåèéêë"); + } + + @Test + public void testDefaultCollectionFormat() throws Exception { + server.enqueue(new MockResponse().setBody("body")); + + TestInterface api = newBuilder() + .target(TestInterface.class, "http://localhost:" + server.getPort()); + + Response response = api.get(Arrays.asList(new String[] {"bar", "baz"})); + + assertThat(response.status()).isEqualTo(200); + assertThat(response.reason()).isEqualTo("OK"); + + MockWebServerAssertions.assertThat(server.takeRequest()).hasMethod("GET") + .hasPath("/?foo=bar&foo=baz"); + } + + @Test + public void testAlternativeCollectionFormat() throws Exception { + server.enqueue(new MockResponse().setBody("body")); + + TestInterface api = newBuilder() + .target(TestInterface.class, "http://localhost:" + server.getPort()); + + Response response = api.getCSV(Arrays.asList(new String[] {"bar", "baz"})); + + assertThat(response.status()).isEqualTo(200); + assertThat(response.reason()).isEqualTo("OK"); + + // Some HTTP libraries percent-encode commas in query parameters and others don't. + MockWebServerAssertions.assertThat(server.takeRequest()).hasMethod("GET") + .hasOneOfPath("/?foo=bar,baz", "/?foo=bar%2Cbaz"); + } + + public interface TestInterface { + + @RequestLine("POST /?foo=bar&foo=baz&qux=") + @Headers({"Foo: Bar", "Foo: Baz", "Qux: ", "Content-Type: text/plain"}) + Response post(String body); + + @RequestLine("POST /path/{to}/resource") + @Headers("Accept: text/plain") + Response post(@Param("to") String to, String body); + + @RequestLine("GET /") + @Headers("Accept: text/plain") + String get(); + + @RequestLine("GET /?foo={multiFoo}") + Response get(@Param("multiFoo") List multiFoo); + + @RequestLine(value = "GET /?foo={multiFoo}", collectionFormat = CollectionFormat.CSV) + Response getCSV(@Param("multiFoo") List multiFoo); + + @RequestLine("PATCH /") + @Headers("Accept: text/plain") + String patch(String body); + + @RequestLine("POST") + String noPostBody(); - @RequestLine("POST") - String noPostBody(); - - @RequestLine("PUT") - String noPutBody(); + @RequestLine("PUT") + String noPutBody(); - @RequestLine("POST /?foo=bar&foo=baz&qux=") - @Headers({"Foo: Bar", "Foo: Baz", "Qux: ", "Content-Type: {contentType}"}) - Response postWithContentType(String body, @Param("contentType") String contentType); - } + @RequestLine("POST /?foo=bar&foo=baz&qux=") + @Headers({"Foo: Bar", "Foo: Baz", "Qux: ", "Content-Type: {contentType}"}) + Response postWithContentType(String body, @Param("contentType") String contentType); + } } diff --git a/core/src/test/java/feign/client/DefaultClientTest.java b/core/src/test/java/feign/client/DefaultClientTest.java index 3bdce9d054..3ebb9b3f68 100644 --- a/core/src/test/java/feign/client/DefaultClientTest.java +++ b/core/src/test/java/feign/client/DefaultClientTest.java @@ -15,19 +15,15 @@ import java.io.IOException; import java.net.ProtocolException; - import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLSession; - import org.junit.Test; - import feign.Client; import feign.Feign; import feign.Feign.Builder; import feign.RetryableException; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.SocketPolicy; - import static org.hamcrest.core.Is.isA; import static org.junit.Assert.assertEquals; @@ -36,68 +32,68 @@ */ public class DefaultClientTest extends AbstractClientTest { - Client disableHostnameVerification = - new Client.Default(TrustingSSLSocketFactory.get(), new HostnameVerifier() { - @Override - public boolean verify(String s, SSLSession sslSession) { - return true; - } - }); - - @Override - public Builder newBuilder() { - return Feign.builder().client(new Client.Default(TrustingSSLSocketFactory.get(), null)); - } - - @Test - public void retriesFailedHandshake() throws IOException, InterruptedException { - server.useHttps(TrustingSSLSocketFactory.get("localhost"), false); - server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); - server.enqueue(new MockResponse()); - - TestInterface api = newBuilder() - .target(TestInterface.class, "https://localhost:" + server.getPort()); - - api.post("foo"); - assertEquals(2, server.getRequestCount()); - } - - @Test - public void canOverrideSSLSocketFactory() throws IOException, InterruptedException { - server.useHttps(TrustingSSLSocketFactory.get("localhost"), false); - server.enqueue(new MockResponse()); - - TestInterface api = newBuilder() - .target(TestInterface.class, "https://localhost:" + server.getPort()); - - api.post("foo"); - } - - /** - * We currently don't include the 60-line - * workaround jersey uses to overcome the lack of support for PATCH. For now, prefer okhttp. - * - * @see java.net.HttpURLConnection#setRequestMethod - */ - @Test - @Override - public void testPatch() throws Exception { - thrown.expect(RetryableException.class); - thrown.expectCause(isA(ProtocolException.class)); - super.testPatch(); - } - - - @Test - public void canOverrideHostnameVerifier() throws IOException, InterruptedException { - server.useHttps(TrustingSSLSocketFactory.get("bad.example.com"), false); - server.enqueue(new MockResponse()); - - TestInterface api = Feign.builder() - .client(disableHostnameVerification) - .target(TestInterface.class, "https://localhost:" + server.getPort()); - - api.post("foo"); - } + Client disableHostnameVerification = + new Client.Default(TrustingSSLSocketFactory.get(), new HostnameVerifier() { + @Override + public boolean verify(String s, SSLSession sslSession) { + return true; + } + }); + + @Override + public Builder newBuilder() { + return Feign.builder().client(new Client.Default(TrustingSSLSocketFactory.get(), null)); + } + + @Test + public void retriesFailedHandshake() throws IOException, InterruptedException { + server.useHttps(TrustingSSLSocketFactory.get("localhost"), false); + server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); + server.enqueue(new MockResponse()); + + TestInterface api = newBuilder() + .target(TestInterface.class, "https://localhost:" + server.getPort()); + + api.post("foo"); + assertEquals(2, server.getRequestCount()); + } + + @Test + public void canOverrideSSLSocketFactory() throws IOException, InterruptedException { + server.useHttps(TrustingSSLSocketFactory.get("localhost"), false); + server.enqueue(new MockResponse()); + + TestInterface api = newBuilder() + .target(TestInterface.class, "https://localhost:" + server.getPort()); + + api.post("foo"); + } + + /** + * We currently don't include the 60-line + * workaround jersey uses to overcome the lack of support for PATCH. For now, prefer okhttp. + * + * @see java.net.HttpURLConnection#setRequestMethod + */ + @Test + @Override + public void testPatch() throws Exception { + thrown.expect(RetryableException.class); + thrown.expectCause(isA(ProtocolException.class)); + super.testPatch(); + } + + + @Test + public void canOverrideHostnameVerifier() throws IOException, InterruptedException { + server.useHttps(TrustingSSLSocketFactory.get("bad.example.com"), false); + server.enqueue(new MockResponse()); + + TestInterface api = Feign.builder() + .client(disableHostnameVerification) + .target(TestInterface.class, "https://localhost:" + server.getPort()); + + api.post("foo"); + } } diff --git a/core/src/test/java/feign/client/TrustingSSLSocketFactory.java b/core/src/test/java/feign/client/TrustingSSLSocketFactory.java index f7c6050577..b25e0e304f 100644 --- a/core/src/test/java/feign/client/TrustingSSLSocketFactory.java +++ b/core/src/test/java/feign/client/TrustingSSLSocketFactory.java @@ -26,7 +26,6 @@ import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Map; - import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; @@ -41,8 +40,7 @@ public final class TrustingSSLSocketFactory extends SSLSocketFactory implements X509TrustManager, X509KeyManager { - private static final Map - sslSocketFactories = + private static final Map sslSocketFactories = new LinkedHashMap(); private static final char[] KEYSTORE_PASSWORD = "password".toCharArray(); private final static String[] ENABLED_CIPHER_SUITES = {"SSL_RSA_WITH_3DES_EDE_CBC_SHA"}; @@ -50,10 +48,11 @@ public final class TrustingSSLSocketFactory extends SSLSocketFactory private final String serverAlias; private final PrivateKey privateKey; private final X509Certificate[] certificateChain; + private TrustingSSLSocketFactory(String serverAlias) { try { SSLContext sc = SSLContext.getInstance("SSL"); - sc.init(new KeyManager[]{this}, new TrustManager[]{this}, new SecureRandom()); + sc.init(new KeyManager[] {this}, new TrustManager[] {this}, new SecureRandom()); this.delegate = sc.getSocketFactory(); } catch (Exception e) { throw new RuntimeException(e); @@ -64,8 +63,7 @@ private TrustingSSLSocketFactory(String serverAlias) { this.certificateChain = null; } else { try { - KeyStore - keyStore = + KeyStore keyStore = loadKeyStore(TrustingSSLSocketFactory.class.getResourceAsStream("/keystore.jks")); this.privateKey = (PrivateKey) keyStore.getKey(serverAlias, KEYSTORE_PASSWORD); Certificate[] rawChain = keyStore.getCertificateChain(serverAlias); @@ -146,11 +144,9 @@ public X509Certificate[] getAcceptedIssuers() { return null; } - public void checkClientTrusted(X509Certificate[] certs, String authType) { - } + public void checkClientTrusted(X509Certificate[] certs, String authType) {} - public void checkServerTrusted(X509Certificate[] certs, String authType) { - } + public void checkServerTrusted(X509Certificate[] certs, String authType) {} @Override public String[] getClientAliases(String keyType, Principal[] issuers) { diff --git a/core/src/test/java/feign/codec/DefaultDecoderTest.java b/core/src/test/java/feign/codec/DefaultDecoderTest.java index f8a3dccaf5..d646d53f26 100644 --- a/core/src/test/java/feign/codec/DefaultDecoderTest.java +++ b/core/src/test/java/feign/codec/DefaultDecoderTest.java @@ -17,16 +17,13 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.w3c.dom.Document; - import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; - import feign.Response; - import static feign.Util.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; @@ -73,18 +70,18 @@ private Response knownResponse() { Map> headers = new HashMap>(); headers.put("Content-Type", Collections.singleton("text/plain")); return Response.builder() - .status(200) - .reason("OK") - .headers(headers) - .body(inputStream, content.length()) - .build(); + .status(200) + .reason("OK") + .headers(headers) + .body(inputStream, content.length()) + .build(); } private Response nullBodyResponse() { return Response.builder() - .status(200) - .reason("OK") - .headers(Collections.>emptyMap()) - .build(); + .status(200) + .reason("OK") + .headers(Collections.>emptyMap()) + .build(); } } diff --git a/core/src/test/java/feign/codec/DefaultEncoderTest.java b/core/src/test/java/feign/codec/DefaultEncoderTest.java index 8fb6a8f06d..bb0185e5bb 100644 --- a/core/src/test/java/feign/codec/DefaultEncoderTest.java +++ b/core/src/test/java/feign/codec/DefaultEncoderTest.java @@ -16,12 +16,9 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; - import java.util.Arrays; import java.util.Date; - import feign.RequestTemplate; - import static feign.Util.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; diff --git a/core/src/test/java/feign/codec/DefaultErrorDecoderTest.java b/core/src/test/java/feign/codec/DefaultErrorDecoderTest.java index 3bbc22c00d..417297b68f 100644 --- a/core/src/test/java/feign/codec/DefaultErrorDecoderTest.java +++ b/core/src/test/java/feign/codec/DefaultErrorDecoderTest.java @@ -16,15 +16,12 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; - import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; - import feign.FeignException; import feign.Response; - import static feign.Util.RETRY_AFTER; import static feign.Util.UTF_8; import static org.assertj.core.api.Assertions.assertThat; @@ -44,10 +41,10 @@ public void throwsFeignException() throws Throwable { thrown.expectMessage("status 500 reading Service#foo()"); Response response = Response.builder() - .status(500) - .reason("Internal server error") - .headers(headers) - .build(); + .status(500) + .reason("Internal server error") + .headers(headers) + .build(); throw errorDecoder.decode("Service#foo()", response); } @@ -58,11 +55,11 @@ public void throwsFeignExceptionIncludingBody() throws Throwable { thrown.expectMessage("status 500 reading Service#foo(); content:\nhello world"); Response response = Response.builder() - .status(500) - .reason("Internal server error") - .headers(headers) - .body("hello world", UTF_8) - .build(); + .status(500) + .reason("Internal server error") + .headers(headers) + .body("hello world", UTF_8) + .build(); throw errorDecoder.decode("Service#foo()", response); } @@ -70,10 +67,10 @@ public void throwsFeignExceptionIncludingBody() throws Throwable { @Test public void testFeignExceptionIncludesStatus() throws Throwable { Response response = Response.builder() - .status(400) - .reason("Bad request") - .headers(headers) - .build(); + .status(400) + .reason("Bad request") + .headers(headers) + .build(); Exception exception = errorDecoder.decode("Service#foo()", response); @@ -88,10 +85,10 @@ public void retryAfterHeaderThrowsRetryableException() throws Throwable { headers.put(RETRY_AFTER, Arrays.asList("Sat, 1 Jan 2000 00:00:00 GMT")); Response response = Response.builder() - .status(503) - .reason("Service Unavailable") - .headers(headers) - .build(); + .status(503) + .reason("Service Unavailable") + .headers(headers) + .build(); throw errorDecoder.decode("Service#foo()", response); } diff --git a/core/src/test/java/feign/codec/RetryAfterDecoderTest.java b/core/src/test/java/feign/codec/RetryAfterDecoderTest.java index 460a619597..80c3aeb949 100644 --- a/core/src/test/java/feign/codec/RetryAfterDecoderTest.java +++ b/core/src/test/java/feign/codec/RetryAfterDecoderTest.java @@ -14,11 +14,8 @@ package feign.codec; import org.junit.Test; - import java.text.ParseException; - import feign.codec.ErrorDecoder.RetryAfterDecoder; - import static feign.codec.ErrorDecoder.RetryAfterDecoder.RFC822_FORMAT; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.junit.Assert.assertEquals; @@ -44,7 +41,7 @@ public void malformDateFailsGracefully() { @Test public void rfc822Parses() throws ParseException { assertEquals(RFC822_FORMAT.parse("Fri, 31 Dec 1999 23:59:59 GMT"), - decoder.apply("Fri, 31 Dec 1999 23:59:59 GMT")); + decoder.apply("Fri, 31 Dec 1999 23:59:59 GMT")); } @Test diff --git a/core/src/test/java/feign/examples/GitHubExample.java b/core/src/test/java/feign/examples/GitHubExample.java index bae2ba33fa..8438ba7c39 100644 --- a/core/src/test/java/feign/examples/GitHubExample.java +++ b/core/src/test/java/feign/examples/GitHubExample.java @@ -15,19 +15,16 @@ import com.google.gson.Gson; import com.google.gson.JsonIOException; - import java.io.IOException; import java.io.Reader; import java.lang.reflect.Type; import java.util.List; - import feign.Feign; import feign.Logger; import feign.Param; import feign.RequestLine; import feign.Response; import feign.codec.Decoder; - import static feign.Util.ensureClosed; /** @@ -62,7 +59,7 @@ static class Contributor { } /** - * Here's how it looks to write a decoder. Note: you can instead use {@code feign-gson}! + * Here's how it looks to write a decoder. Note: you can instead use {@code feign-gson}! */ static class GsonDecoder implements Decoder { diff --git a/gson/src/main/java/feign/gson/DoubleToIntMapTypeAdapter.java b/gson/src/main/java/feign/gson/DoubleToIntMapTypeAdapter.java index 4c4dd03b1e..c84e18df6a 100644 --- a/gson/src/main/java/feign/gson/DoubleToIntMapTypeAdapter.java +++ b/gson/src/main/java/feign/gson/DoubleToIntMapTypeAdapter.java @@ -26,8 +26,7 @@ */ public class DoubleToIntMapTypeAdapter extends TypeAdapter> { private final TypeAdapter> delegate = - new Gson().getAdapter(new TypeToken>() { - }); + new Gson().getAdapter(new TypeToken>() {}); @Override public void write(JsonWriter out, Map value) throws IOException { diff --git a/gson/src/main/java/feign/gson/GsonDecoder.java b/gson/src/main/java/feign/gson/GsonDecoder.java index bb6ad4cc1a..5c3e70438a 100644 --- a/gson/src/main/java/feign/gson/GsonDecoder.java +++ b/gson/src/main/java/feign/gson/GsonDecoder.java @@ -16,16 +16,13 @@ import com.google.gson.Gson; import com.google.gson.JsonIOException; import com.google.gson.TypeAdapter; - import java.io.IOException; import java.io.Reader; import java.lang.reflect.Type; import java.util.Collections; - import feign.Response; import feign.Util; import feign.codec.Decoder; - import static feign.Util.ensureClosed; public class GsonDecoder implements Decoder { @@ -46,8 +43,10 @@ public GsonDecoder(Gson gson) { @Override public Object decode(Response response, Type type) throws IOException { - if (response.status() == 404) return Util.emptyValueOf(type); - if (response.body() == null) return null; + if (response.status() == 404) + return Util.emptyValueOf(type); + if (response.body() == null) + return null; Reader reader = response.body().asReader(); try { return gson.fromJson(reader, type); diff --git a/gson/src/main/java/feign/gson/GsonEncoder.java b/gson/src/main/java/feign/gson/GsonEncoder.java index d9b0e436e0..859ee71838 100644 --- a/gson/src/main/java/feign/gson/GsonEncoder.java +++ b/gson/src/main/java/feign/gson/GsonEncoder.java @@ -15,10 +15,8 @@ import com.google.gson.Gson; import com.google.gson.TypeAdapter; - import java.lang.reflect.Type; import java.util.Collections; - import feign.RequestTemplate; import feign.codec.Encoder; diff --git a/gson/src/main/java/feign/gson/GsonFactory.java b/gson/src/main/java/feign/gson/GsonFactory.java index e75c18235d..e4badcd245 100644 --- a/gson/src/main/java/feign/gson/GsonFactory.java +++ b/gson/src/main/java/feign/gson/GsonFactory.java @@ -17,16 +17,13 @@ import com.google.gson.GsonBuilder; import com.google.gson.TypeAdapter; import com.google.gson.reflect.TypeToken; - import java.lang.reflect.Type; import java.util.Map; - import static feign.Util.resolveLastTypeParameter; final class GsonFactory { - private GsonFactory() { - } + private GsonFactory() {} /** * Registers type adapters by implicit type. Adds one to read numbers in a {@code Map> adapters) { GsonBuilder builder = new GsonBuilder().setPrettyPrinting(); - builder.registerTypeAdapter(new TypeToken>() { - }.getType(), new DoubleToIntMapTypeAdapter()); + builder.registerTypeAdapter(new TypeToken>() {}.getType(), + new DoubleToIntMapTypeAdapter()); for (TypeAdapter adapter : adapters) { Type type = resolveLastTypeParameter(adapter.getClass(), TypeAdapter.class); builder.registerTypeAdapter(type, adapter); diff --git a/gson/src/test/java/feign/gson/GsonCodecTest.java b/gson/src/test/java/feign/gson/GsonCodecTest.java index b34e6c6466..ddfc47038b 100644 --- a/gson/src/test/java/feign/gson/GsonCodecTest.java +++ b/gson/src/test/java/feign/gson/GsonCodecTest.java @@ -17,9 +17,7 @@ import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; - import org.junit.Test; - import java.io.IOException; import java.util.Arrays; import java.util.Collection; @@ -28,10 +26,8 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; - import feign.RequestTemplate; import feign.Response; - import static feign.Util.UTF_8; import static feign.assertj.FeignAssertions.assertThat; import static org.junit.Assert.assertEquals; @@ -48,9 +44,9 @@ public void encodesMapObjectNumericalValuesAsInteger() throws Exception { new GsonEncoder().encode(map, map.getClass(), template); assertThat(template).hasBody("" // - + "{\n" // - + " \"foo\": 1\n" // - + "}"); + + "{\n" // + + " \"foo\": 1\n" // + + "}"); } @Test @@ -59,13 +55,13 @@ public void decodesMapObjectNumericalValuesAsInteger() throws Exception { map.put("foo", 1); Response response = Response.builder() - .status(200) - .reason("OK") - .headers(Collections.>emptyMap()) - .body("{\"foo\": 1}", UTF_8) - .build(); - assertEquals(new GsonDecoder().decode(response, new TypeToken>() { - }.getType()), map); + .status(200) + .reason("OK") + .headers(Collections.>emptyMap()) + .body("{\"foo\": 1}", UTF_8) + .build(); + assertEquals( + new GsonDecoder().decode(response, new TypeToken>() {}.getType()), map); } @Test @@ -76,17 +72,16 @@ public void encodesFormParams() throws Exception { form.put("bar", Arrays.asList(2, 3)); RequestTemplate template = new RequestTemplate(); - new GsonEncoder().encode(form, new TypeToken>() { - }.getType(), template); - - assertThat(template).hasBody("" // - + "{\n" // - + " \"foo\": 1,\n" // - + " \"bar\": [\n" // - + " 2,\n" // - + " 3\n" // - + " ]\n" // - + "}"); + new GsonEncoder().encode(form, new TypeToken>() {}.getType(), template); + + assertThat(template).hasBody("" // + + "{\n" // + + " \"foo\": 1,\n" // + + " \"bar\": [\n" // + + " 2,\n" // + + " 3\n" // + + " ]\n" // + + "}"); } static class Zone extends LinkedHashMap { @@ -117,46 +112,46 @@ public void decodes() throws Exception { zones.add(new Zone("denominator.io.", "ABCD")); Response response = Response.builder() - .status(200) - .reason("OK") - .headers(Collections.>emptyMap()) - .body(zonesJson, UTF_8) - .build(); - assertEquals(zones, new GsonDecoder().decode(response, new TypeToken>() { - }.getType())); + .status(200) + .reason("OK") + .headers(Collections.>emptyMap()) + .body(zonesJson, UTF_8) + .build(); + assertEquals(zones, + new GsonDecoder().decode(response, new TypeToken>() {}.getType())); } @Test public void nullBodyDecodesToNull() throws Exception { Response response = Response.builder() - .status(204) - .reason("OK") - .headers(Collections.>emptyMap()) - .build(); + .status(204) + .reason("OK") + .headers(Collections.>emptyMap()) + .build(); assertNull(new GsonDecoder().decode(response, String.class)); } @Test public void emptyBodyDecodesToNull() throws Exception { Response response = Response.builder() - .status(204) - .reason("OK") - .headers(Collections.>emptyMap()) - .body(new byte[0]) - .build(); + .status(204) + .reason("OK") + .headers(Collections.>emptyMap()) + .body(new byte[0]) + .build(); assertNull(new GsonDecoder().decode(response, String.class)); } private String zonesJson = ""// - + "[\n"// - + " {\n"// - + " \"name\": \"denominator.io.\"\n"// - + " },\n"// - + " {\n"// - + " \"name\": \"denominator.io.\",\n"// - + " \"id\": \"ABCD\"\n"// - + " }\n"// - + "]\n"; + + "[\n"// + + " {\n"// + + " \"name\": \"denominator.io.\"\n"// + + " },\n"// + + " {\n"// + + " \"name\": \"denominator.io.\",\n"// + + " \"id\": \"ABCD\"\n"// + + " }\n"// + + "]\n"; final TypeAdapter upperZone = new TypeAdapter() { @@ -191,13 +186,12 @@ public void customDecoder() throws Exception { Response response = Response.builder() - .status(200) - .reason("OK") - .headers(Collections.>emptyMap()) - .body(zonesJson, UTF_8) - .build(); - assertEquals(zones, decoder.decode(response, new TypeToken>() { - }.getType())); + .status(200) + .reason("OK") + .headers(Collections.>emptyMap()) + .body(zonesJson, UTF_8) + .build(); + assertEquals(zones, decoder.decode(response, new TypeToken>() {}.getType())); } @Test @@ -209,29 +203,28 @@ public void customEncoder() throws Exception { zones.add(new Zone("denominator.io.", "abcd")); RequestTemplate template = new RequestTemplate(); - encoder.encode(zones, new TypeToken>() { - }.getType(), template); + encoder.encode(zones, new TypeToken>() {}.getType(), template); assertThat(template).hasBody("" // - + "[\n" // - + " {\n" // - + " \"name\": \"DENOMINATOR.IO.\"\n" // - + " },\n" // - + " {\n" // - + " \"name\": \"DENOMINATOR.IO.\",\n" // - + " \"id\": \"ABCD\"\n" // - + " }\n" // - + "]"); + + "[\n" // + + " {\n" // + + " \"name\": \"DENOMINATOR.IO.\"\n" // + + " },\n" // + + " {\n" // + + " \"name\": \"DENOMINATOR.IO.\",\n" // + + " \"id\": \"ABCD\"\n" // + + " }\n" // + + "]"); } /** Enabled via {@link feign.Feign.Builder#decode404()} */ @Test public void notFoundDecodesToEmpty() throws Exception { Response response = Response.builder() - .status(404) - .reason("NOT FOUND") - .headers(Collections.>emptyMap()) - .build(); + .status(404) + .reason("NOT FOUND") + .headers(Collections.>emptyMap()) + .build(); assertThat((byte[]) new GsonDecoder().decode(response, byte[].class)).isEmpty(); } } diff --git a/gson/src/test/java/feign/gson/examples/GitHubExample.java b/gson/src/test/java/feign/gson/examples/GitHubExample.java index 3dc33544e0..0b16f68e0d 100644 --- a/gson/src/test/java/feign/gson/examples/GitHubExample.java +++ b/gson/src/test/java/feign/gson/examples/GitHubExample.java @@ -14,7 +14,6 @@ package feign.gson.examples; import java.util.List; - import feign.Feign; import feign.Param; import feign.RequestLine; diff --git a/httpclient/src/main/java/feign/httpclient/ApacheHttpClient.java b/httpclient/src/main/java/feign/httpclient/ApacheHttpClient.java index 86f8cc242a..a63953296d 100644 --- a/httpclient/src/main/java/feign/httpclient/ApacheHttpClient.java +++ b/httpclient/src/main/java/feign/httpclient/ApacheHttpClient.java @@ -29,7 +29,6 @@ import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; - import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -43,17 +42,16 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - import feign.Client; import feign.Request; import feign.Response; import feign.Util; - import static feign.Util.UTF_8; /** * This module directs Feign's http requests to Apache's * HttpClient. Ex. + * *

      * GitHub github = Feign.builder().client(new ApacheHttpClient()).target(GitHub.class,
      * "https://api.github.com");
    @@ -86,29 +84,30 @@ public Response execute(Request request, Request.Options options) throws IOExcep
         return toFeignResponse(httpResponse).toBuilder().request(request).build();
       }
     
    -  HttpUriRequest toHttpUriRequest(Request request, Request.Options options) throws
    -          UnsupportedEncodingException, MalformedURLException, URISyntaxException {
    +  HttpUriRequest toHttpUriRequest(Request request, Request.Options options)
    +      throws UnsupportedEncodingException, MalformedURLException, URISyntaxException {
         RequestBuilder requestBuilder = RequestBuilder.create(request.method());
     
    -    //per request timeouts
    +    // per request timeouts
         RequestConfig requestConfig = RequestConfig
    -            .custom()
    -            .setConnectTimeout(options.connectTimeoutMillis())
    -            .setSocketTimeout(options.readTimeoutMillis())
    -            .build();
    +        .custom()
    +        .setConnectTimeout(options.connectTimeoutMillis())
    +        .setSocketTimeout(options.readTimeoutMillis())
    +        .build();
         requestBuilder.setConfig(requestConfig);
     
         URI uri = new URIBuilder(request.url()).build();
     
         requestBuilder.setUri(uri.getScheme() + "://" + uri.getAuthority() + uri.getRawPath());
     
    -    //request query params
    -    List queryParams = URLEncodedUtils.parse(uri, requestBuilder.getCharset().name());
    -    for (NameValuePair queryParam: queryParams) {
    +    // request query params
    +    List queryParams =
    +        URLEncodedUtils.parse(uri, requestBuilder.getCharset().name());
    +    for (NameValuePair queryParam : queryParams) {
           requestBuilder.addParameter(queryParam);
         }
     
    -    //request headers
    +    // request headers
         boolean hasAcceptHeader = false;
         for (Map.Entry> headerEntry : request.headers().entrySet()) {
           String headerName = headerEntry.getKey();
    @@ -126,12 +125,12 @@ HttpUriRequest toHttpUriRequest(Request request, Request.Options options) throws
             requestBuilder.addHeader(headerName, headerValue);
           }
         }
    -    //some servers choke on the default accept string, so we'll set it to anything
    +    // some servers choke on the default accept string, so we'll set it to anything
         if (!hasAcceptHeader) {
           requestBuilder.addHeader(ACCEPT_HEADER_NAME, "*/*");
         }
     
    -    //request body
    +    // request body
         if (request.body() != null) {
           HttpEntity entity = null;
           if (request.charset() != null) {
    @@ -186,11 +185,11 @@ Response toFeignResponse(HttpResponse httpResponse) throws IOException {
         }
     
         return Response.builder()
    -            .status(statusCode)
    -            .reason(reason)
    -            .headers(headers)
    -            .body(toFeignBody(httpResponse))
    -            .build();
    +        .status(statusCode)
    +        .reason(reason)
    +        .headers(headers)
    +        .body(toFeignBody(httpResponse))
    +        .build();
       }
     
       Response.Body toFeignBody(HttpResponse httpResponse) throws IOException {
    @@ -202,8 +201,9 @@ Response.Body toFeignBody(HttpResponse httpResponse) throws IOException {
     
           @Override
           public Integer length() {
    -        return entity.getContentLength() >= 0 && entity.getContentLength() <= Integer.MAX_VALUE ?
    -                (int) entity.getContentLength() : null;
    +        return entity.getContentLength() >= 0 && entity.getContentLength() <= Integer.MAX_VALUE
    +            ? (int) entity.getContentLength()
    +            : null;
           }
     
           @Override
    diff --git a/httpclient/src/test/java/feign/httpclient/ApacheHttpClientTest.java b/httpclient/src/test/java/feign/httpclient/ApacheHttpClientTest.java
    index b8c35fd9e8..bcd76bcd41 100644
    --- a/httpclient/src/test/java/feign/httpclient/ApacheHttpClientTest.java
    +++ b/httpclient/src/test/java/feign/httpclient/ApacheHttpClientTest.java
    @@ -22,14 +22,11 @@
     import org.apache.http.client.HttpClient;
     import org.apache.http.impl.client.HttpClientBuilder;
     import org.junit.Test;
    -
     import javax.ws.rs.GET;
     import javax.ws.rs.PUT;
     import javax.ws.rs.Path;
     import javax.ws.rs.QueryParam;
    -
     import java.nio.charset.StandardCharsets;
    -
     import static org.junit.Assert.assertEquals;
     import static org.junit.Assert.assertNull;
     
    @@ -38,41 +35,41 @@
      */
     public class ApacheHttpClientTest extends AbstractClientTest {
     
    -    @Override
    -    public Builder newBuilder() {
    -        return Feign.builder().client(new ApacheHttpClient());
    -    }
    +  @Override
    +  public Builder newBuilder() {
    +    return Feign.builder().client(new ApacheHttpClient());
    +  }
     
    -    @Test
    -    public void queryParamsAreRespectedWhenBodyIsEmpty() throws InterruptedException {
    -        final HttpClient httpClient = HttpClientBuilder.create().build();
    -        final JaxRsTestInterface testInterface = Feign.builder()
    -                .contract(new JAXRSContract())
    -                .client(new ApacheHttpClient(httpClient))
    -                .target(JaxRsTestInterface.class, "http://localhost:" + server.getPort());
    +  @Test
    +  public void queryParamsAreRespectedWhenBodyIsEmpty() throws InterruptedException {
    +    final HttpClient httpClient = HttpClientBuilder.create().build();
    +    final JaxRsTestInterface testInterface = Feign.builder()
    +        .contract(new JAXRSContract())
    +        .client(new ApacheHttpClient(httpClient))
    +        .target(JaxRsTestInterface.class, "http://localhost:" + server.getPort());
     
    -        server.enqueue(new MockResponse().setBody("foo"));
    -        server.enqueue(new MockResponse().setBody("foo"));
    +    server.enqueue(new MockResponse().setBody("foo"));
    +    server.enqueue(new MockResponse().setBody("foo"));
     
    -        assertEquals("foo", testInterface.withBody("foo", "bar"));
    -        final RecordedRequest request1 = server.takeRequest();
    -        assertEquals("/withBody?foo=foo", request1.getPath());
    -        assertEquals("bar", request1.getBody().readString(StandardCharsets.UTF_8));
    +    assertEquals("foo", testInterface.withBody("foo", "bar"));
    +    final RecordedRequest request1 = server.takeRequest();
    +    assertEquals("/withBody?foo=foo", request1.getPath());
    +    assertEquals("bar", request1.getBody().readString(StandardCharsets.UTF_8));
     
    -        assertEquals("foo", testInterface.withoutBody("foo"));
    -        final RecordedRequest request2 = server.takeRequest();
    -        assertEquals("/withoutBody?foo=foo", request2.getPath());
    -        assertEquals("", request2.getBody().readString(StandardCharsets.UTF_8));
    -    }
    +    assertEquals("foo", testInterface.withoutBody("foo"));
    +    final RecordedRequest request2 = server.takeRequest();
    +    assertEquals("/withoutBody?foo=foo", request2.getPath());
    +    assertEquals("", request2.getBody().readString(StandardCharsets.UTF_8));
    +  }
     
    -    @Path("/")
    -    public interface JaxRsTestInterface {
    -        @PUT
    -        @Path("/withBody")
    -        public String withBody(@QueryParam("foo") String foo, String bar);
    +  @Path("/")
    +  public interface JaxRsTestInterface {
    +    @PUT
    +    @Path("/withBody")
    +    public String withBody(@QueryParam("foo") String foo, String bar);
     
    -        @PUT
    -        @Path("/withoutBody")
    -        public String withoutBody(@QueryParam("foo") String foo);
    -    }
    +    @PUT
    +    @Path("/withoutBody")
    +    public String withoutBody(@QueryParam("foo") String foo);
    +  }
     }
    diff --git a/hystrix/src/main/java/feign/hystrix/FallbackFactory.java b/hystrix/src/main/java/feign/hystrix/FallbackFactory.java
    index 0535b754f0..b09196b24c 100644
    --- a/hystrix/src/main/java/feign/hystrix/FallbackFactory.java
    +++ b/hystrix/src/main/java/feign/hystrix/FallbackFactory.java
    @@ -16,14 +16,15 @@
     import feign.FeignException;
     import java.util.logging.Level;
     import java.util.logging.Logger;
    -
     import static feign.Util.checkNotNull;
     
     /**
      * Used to control the fallback given its cause.
      *
      * Ex.
    - * 
    {@code
    + * 
    + * 
    + * {@code
      * // This instance will be invoked if there are errors of any kind.
      * FallbackFactory fallbackFactory = cause -> (owner, repo) -> {
      *   if (cause instanceof FeignException && ((FeignException) cause).status() == 403) {
    @@ -47,7 +48,7 @@ public interface FallbackFactory {
        * Returns an instance of the fallback appropriate for the given cause
        *
        * @param cause corresponds to {@link com.netflix.hystrix.AbstractCommand#getExecutionException()}
    -   * often, but not always an instance of {@link FeignException}.
    +   *        often, but not always an instance of {@link FeignException}.
        */
       T create(Throwable cause);
     
    diff --git a/hystrix/src/main/java/feign/hystrix/HystrixDelegatingContract.java b/hystrix/src/main/java/feign/hystrix/HystrixDelegatingContract.java
    index 2680a489d0..233b99372a 100644
    --- a/hystrix/src/main/java/feign/hystrix/HystrixDelegatingContract.java
    +++ b/hystrix/src/main/java/feign/hystrix/HystrixDelegatingContract.java
    @@ -14,13 +14,10 @@
     package feign.hystrix;
     
     import static feign.Util.resolveLastTypeParameter;
    -
     import java.lang.reflect.ParameterizedType;
     import java.lang.reflect.Type;
     import java.util.List;
    -
     import com.netflix.hystrix.HystrixCommand;
    -
     import feign.Contract;
     import feign.MethodMetadata;
     import rx.Completable;
    @@ -28,10 +25,12 @@
     import rx.Single;
     
     /**
    - * This special cases methods that return {@link HystrixCommand}, {@link Observable}, or {@link Single} so that they
    - * are decoded properly.
    + * This special cases methods that return {@link HystrixCommand}, {@link Observable}, or
    + * {@link Single} so that they are decoded properly.
      * 
    - * 

    For example, {@literal HystrixCommand} and {@literal Observable} will decode {@code Foo}. + *

    + * For example, {@literal HystrixCommand} and {@literal Observable} will decode + * {@code Foo}. */ // Visible for use in custom Hystrix invocation handlers public final class HystrixDelegatingContract implements Contract { @@ -49,16 +48,20 @@ public List parseAndValidatateMetadata(Class targetType) { for (MethodMetadata metadata : metadatas) { Type type = metadata.returnType(); - if (type instanceof ParameterizedType && ((ParameterizedType) type).getRawType().equals(HystrixCommand.class)) { + if (type instanceof ParameterizedType + && ((ParameterizedType) type).getRawType().equals(HystrixCommand.class)) { Type actualType = resolveLastTypeParameter(type, HystrixCommand.class); metadata.returnType(actualType); - } else if (type instanceof ParameterizedType && ((ParameterizedType) type).getRawType().equals(Observable.class)) { + } else if (type instanceof ParameterizedType + && ((ParameterizedType) type).getRawType().equals(Observable.class)) { Type actualType = resolveLastTypeParameter(type, Observable.class); metadata.returnType(actualType); - } else if (type instanceof ParameterizedType && ((ParameterizedType) type).getRawType().equals(Single.class)) { + } else if (type instanceof ParameterizedType + && ((ParameterizedType) type).getRawType().equals(Single.class)) { Type actualType = resolveLastTypeParameter(type, Single.class); metadata.returnType(actualType); - } else if (type instanceof ParameterizedType && ((ParameterizedType) type).getRawType().equals(Completable.class)) { + } else if (type instanceof ParameterizedType + && ((ParameterizedType) type).getRawType().equals(Completable.class)) { metadata.returnType(void.class); } } diff --git a/hystrix/src/main/java/feign/hystrix/HystrixFeign.java b/hystrix/src/main/java/feign/hystrix/HystrixFeign.java index 62015d0f57..d8eeefe561 100644 --- a/hystrix/src/main/java/feign/hystrix/HystrixFeign.java +++ b/hystrix/src/main/java/feign/hystrix/HystrixFeign.java @@ -14,11 +14,9 @@ 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; @@ -76,18 +74,19 @@ public T target(Target target, FallbackFactory fallbackFacto * Like {@link Feign#newInstance(Target)}, except with {@link HystrixCommand#getFallback() * fallback} support. * - *

    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. + *

    + * 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: + * *

          * {@code
          *
          * // When dealing with fallbacks, it is less tedious to keep interfaces small.
          * interface GitHub {
    -     *   @RequestLine("GET /repos/{owner}/{repo}/contributors")
    +     *   @RequestLine("GET /repos/{owner}/{repo}/contributors")
          *   List contributors(@Param("owner") String owner, @Param("repo") String repo);
          * }
          *
    @@ -103,7 +102,8 @@ public  T target(Target target, FallbackFactory fallbackFacto
          * GitHub github = HystrixFeign.builder()
          *                             ...
          *                             .target(GitHub.class, "https://api.github.com", fallback);
    -     * }
    + * } + *
    * * @see #target(Target, Object) */ @@ -115,7 +115,9 @@ public T target(Class apiType, String url, T fallback) { * Same as {@link #target(Class, String, T)}, except you can inspect a source exception before * creating a fallback object. */ - public T target(Class apiType, String url, FallbackFactory fallbackFactory) { + public T target(Class apiType, + String url, + FallbackFactory fallbackFactory) { return target(new Target.HardCodedTarget(apiType, url), fallbackFactory); } @@ -138,9 +140,11 @@ public Feign build() { /** Configures components needed for hystrix integration. */ Feign build(final FallbackFactory nullableFallbackFactory) { super.invocationHandlerFactory(new InvocationHandlerFactory() { - @Override public InvocationHandler create(Target target, - Map dispatch) { - return new HystrixInvocationHandler(target, dispatch, setterFactory, nullableFallbackFactory); + @Override + public InvocationHandler create(Target target, + Map dispatch) { + return new HystrixInvocationHandler(target, dispatch, setterFactory, + nullableFallbackFactory); } }); super.contract(new HystrixDelegatingContract(contract)); diff --git a/hystrix/src/main/java/feign/hystrix/HystrixInvocationHandler.java b/hystrix/src/main/java/feign/hystrix/HystrixInvocationHandler.java index 16e41a8aa4..1217487714 100644 --- a/hystrix/src/main/java/feign/hystrix/HystrixInvocationHandler.java +++ b/hystrix/src/main/java/feign/hystrix/HystrixInvocationHandler.java @@ -15,7 +15,6 @@ import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommand.Setter; - import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -23,14 +22,12 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; - import feign.InvocationHandlerFactory.MethodHandler; import feign.Target; import feign.Util; import rx.Completable; import rx.Observable; import rx.Single; - import static feign.Util.checkNotNull; final class HystrixInvocationHandler implements InvocationHandler { @@ -42,7 +39,7 @@ final class HystrixInvocationHandler implements InvocationHandler { private final Map setterMethodMap; HystrixInvocationHandler(Target target, Map dispatch, - SetterFactory setterFactory, FallbackFactory fallbackFactory) { + SetterFactory setterFactory, FallbackFactory fallbackFactory) { this.target = checkNotNull(target, "target"); this.dispatch = checkNotNull(dispatch, "dispatch"); this.fallbackFactory = fallbackFactory; @@ -71,7 +68,8 @@ static Map toFallbackMethod(Map dispatch) /** * Process all methods in the target so that appropriate setters are created. */ - static Map toSetters(SetterFactory setterFactory, Target target, + static Map toSetters(SetterFactory setterFactory, + Target target, Set methods) { Map result = new LinkedHashMap(); for (Method method : methods) { @@ -100,49 +98,50 @@ public Object invoke(final Object proxy, final Method method, final Object[] arg return toString(); } - HystrixCommand hystrixCommand = new HystrixCommand(setterMethodMap.get(method)) { - @Override - protected Object run() throws Exception { - try { - return HystrixInvocationHandler.this.dispatch.get(method).invoke(args); - } catch (Exception e) { - throw e; - } catch (Throwable t) { - throw (Error) t; - } - } + HystrixCommand hystrixCommand = + new HystrixCommand(setterMethodMap.get(method)) { + @Override + protected Object run() throws Exception { + try { + return HystrixInvocationHandler.this.dispatch.get(method).invoke(args); + } catch (Exception e) { + throw e; + } catch (Throwable t) { + throw (Error) t; + } + } - @Override - protected Object getFallback() { - if (fallbackFactory == null) { - return super.getFallback(); - } - try { - Object fallback = fallbackFactory.create(getExecutionException()); - Object result = fallbackMethodMap.get(method).invoke(fallback, args); - if (isReturnsHystrixCommand(method)) { - return ((HystrixCommand) result).execute(); - } else if (isReturnsObservable(method)) { - // Create a cold Observable - return ((Observable) result).toBlocking().first(); - } else if (isReturnsSingle(method)) { - // Create a cold Observable as a Single - return ((Single) result).toObservable().toBlocking().first(); - } else if (isReturnsCompletable(method)) { - ((Completable) result).await(); - return null; - } else { - return result; + @Override + protected Object getFallback() { + if (fallbackFactory == null) { + return super.getFallback(); + } + try { + Object fallback = fallbackFactory.create(getExecutionException()); + Object result = fallbackMethodMap.get(method).invoke(fallback, args); + if (isReturnsHystrixCommand(method)) { + return ((HystrixCommand) result).execute(); + } else if (isReturnsObservable(method)) { + // Create a cold Observable + return ((Observable) result).toBlocking().first(); + } else if (isReturnsSingle(method)) { + // Create a cold Observable as a Single + return ((Single) result).toObservable().toBlocking().first(); + } else if (isReturnsCompletable(method)) { + ((Completable) result).await(); + return null; + } else { + return result; + } + } catch (IllegalAccessException e) { + // shouldn't happen as method is public due to being an interface + throw new AssertionError(e); + } catch (InvocationTargetException e) { + // Exceptions on fallback are tossed by Hystrix + throw new AssertionError(e.getCause()); + } } - } catch (IllegalAccessException e) { - // shouldn't happen as method is public due to being an interface - throw new AssertionError(e); - } catch (InvocationTargetException e) { - // Exceptions on fallback are tossed by Hystrix - throw new AssertionError(e.getCause()); - } - } - }; + }; if (Util.isDefault(method)) { return hystrixCommand.execute(); diff --git a/hystrix/src/main/java/feign/hystrix/SetterFactory.java b/hystrix/src/main/java/feign/hystrix/SetterFactory.java index ee115935fe..02a54957b1 100644 --- a/hystrix/src/main/java/feign/hystrix/SetterFactory.java +++ b/hystrix/src/main/java/feign/hystrix/SetterFactory.java @@ -16,9 +16,7 @@ import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.HystrixCommandKey; - import java.lang.reflect.Method; - import feign.Feign; import feign.Target; @@ -26,11 +24,14 @@ * Used to control properties of a hystrix command. Use cases include reading from static * configuration or custom annotations. * - *

    This is parsed up-front, like {@link feign.Contract}, so will not be invoked for each command + *

    + * This is parsed up-front, like {@link feign.Contract}, so will not be invoked for each command * invocation. * - *

    Note: when deciding the {@link com.netflix.hystrix.HystrixCommand.Setter#andCommandKey(HystrixCommandKey) - * command key}, recall it lives in a shared cache, so make sure it is unique. + *

    + * Note: when deciding the + * {@link com.netflix.hystrix.HystrixCommand.Setter#andCommandKey(HystrixCommandKey) command key}, + * recall it lives in a shared cache, so make sure it is unique. */ public interface SetterFactory { @@ -54,4 +55,4 @@ public HystrixCommand.Setter create(Target target, Method method) { .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey)); } } -} \ No newline at end of file +} diff --git a/hystrix/src/test/java/feign/hystrix/FallbackFactoryTest.java b/hystrix/src/test/java/feign/hystrix/FallbackFactoryTest.java index 650c5705bb..a9a98cce9c 100644 --- a/hystrix/src/test/java/feign/hystrix/FallbackFactoryTest.java +++ b/hystrix/src/test/java/feign/hystrix/FallbackFactoryTest.java @@ -23,13 +23,13 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; - import static feign.assertj.MockWebServerAssertions.assertThat; public class FallbackFactoryTest { interface TestInterface { - @RequestLine("POST /") String invoke(); + @RequestLine("POST /") + String invoke(); } @Rule @@ -58,7 +58,8 @@ static class FallbackApiWithCtor implements TestInterface { this.cause = cause; } - @Override public String invoke() { + @Override + public String invoke() { return "foo"; } } @@ -81,7 +82,8 @@ public void fallbackFactory_example_ctor() { // old school api = target(new FallbackFactory() { - @Override public TestInterface create(Throwable cause) { + @Override + public TestInterface create(Throwable cause) { return new FallbackApiWithCtor(cause); } }); @@ -92,7 +94,8 @@ public void fallbackFactory_example_ctor() { // retrofit so people don't have to track 2 classes static class FallbackApiRetro implements TestInterface, FallbackFactory { - @Override public FallbackApiRetro create(Throwable cause) { + @Override + public FallbackApiRetro create(Throwable cause) { return new FallbackApiRetro(cause); } @@ -106,7 +109,8 @@ public FallbackApiRetro() { this.cause = cause; } - @Override public String invoke() { + @Override + public String invoke() { return cause != null ? cause.getMessage() : "foo"; } } @@ -135,7 +139,8 @@ public void defaultFallbackFactory_doesntLogByDefault() { server.enqueue(new MockResponse().setResponseCode(500)); Logger logger = new Logger("", null) { - @Override public void log(Level level, String msg, Throwable thrown) { + @Override + public void log(Level level, String msg, Throwable thrown) { throw new AssertionError("logged eventhough not FINE level"); } }; @@ -149,7 +154,8 @@ public void defaultFallbackFactory_logsAtFineLevel() { AtomicBoolean logged = new AtomicBoolean(); Logger logger = new Logger("", null) { - @Override public void log(Level level, String msg, Throwable thrown) { + @Override + public void log(Level level, String msg, Throwable thrown) { logged.set(true); assertThat(msg).isEqualTo("fallback due to: status 500 reading TestInterface#invoke()"); diff --git a/hystrix/src/test/java/feign/hystrix/HystrixBuilderTest.java b/hystrix/src/test/java/feign/hystrix/HystrixBuilderTest.java index 7cbed19c99..f6b00995e3 100644 --- a/hystrix/src/test/java/feign/hystrix/HystrixBuilderTest.java +++ b/hystrix/src/test/java/feign/hystrix/HystrixBuilderTest.java @@ -18,17 +18,14 @@ import com.netflix.hystrix.exception.HystrixRuntimeException; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; - import org.assertj.core.api.Assertions; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; - import feign.FeignException; import feign.Headers; import feign.Param; @@ -40,7 +37,6 @@ import rx.Observable; import rx.Single; import rx.observers.TestSubscriber; - import static feign.assertj.MockWebServerAssertions.assertThat; import static org.hamcrest.core.Is.isA; diff --git a/hystrix/src/test/java/feign/hystrix/SetterFactoryTest.java b/hystrix/src/test/java/feign/hystrix/SetterFactoryTest.java index f1021d8cbb..2c610db354 100644 --- a/hystrix/src/test/java/feign/hystrix/SetterFactoryTest.java +++ b/hystrix/src/test/java/feign/hystrix/SetterFactoryTest.java @@ -17,11 +17,9 @@ import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.HystrixCommandKey; import com.netflix.hystrix.exception.HystrixRuntimeException; - import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; - import feign.RequestLine; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; @@ -45,13 +43,13 @@ public void customSetter() { server.enqueue(new MockResponse().setResponseCode(500)); -SetterFactory commandKeyIsRequestLine = (target, method) -> { - String groupKey = target.name(); - String commandKey = method.getAnnotation(RequestLine.class).value(); - return HystrixCommand.Setter - .withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey)) - .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey)); -}; + SetterFactory commandKeyIsRequestLine = (target, method) -> { + String groupKey = target.name(); + String commandKey = method.getAnnotation(RequestLine.class).value(); + return HystrixCommand.Setter + .withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey)) + .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey)); + }; TestInterface api = HystrixFeign.builder() .setterFactory(commandKeyIsRequestLine) diff --git a/jackson-jaxb/src/main/java/feign/jackson/jaxb/JacksonJaxbJsonDecoder.java b/jackson-jaxb/src/main/java/feign/jackson/jaxb/JacksonJaxbJsonDecoder.java index b13f694906..c4ca9d71b1 100644 --- a/jackson-jaxb/src/main/java/feign/jackson/jaxb/JacksonJaxbJsonDecoder.java +++ b/jackson-jaxb/src/main/java/feign/jackson/jaxb/JacksonJaxbJsonDecoder.java @@ -15,15 +15,12 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; - import java.io.IOException; import java.lang.reflect.Type; - import feign.FeignException; import feign.Response; import feign.Util; import feign.codec.Decoder; - import static com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider.DEFAULT_ANNOTATIONS; import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE; @@ -40,8 +37,11 @@ public JacksonJaxbJsonDecoder(ObjectMapper objectMapper) { @Override public Object decode(Response response, Type type) throws IOException, FeignException { - if (response.status() == 404) return Util.emptyValueOf(type); - if (response.body() == null) return null; - return jacksonJaxbJsonProvider.readFrom(Object.class, type, null, APPLICATION_JSON_TYPE, null, response.body().asInputStream()); + if (response.status() == 404) + return Util.emptyValueOf(type); + if (response.body() == null) + return null; + return jacksonJaxbJsonProvider.readFrom(Object.class, type, null, APPLICATION_JSON_TYPE, null, + response.body().asInputStream()); } } diff --git a/jackson-jaxb/src/main/java/feign/jackson/jaxb/JacksonJaxbJsonEncoder.java b/jackson-jaxb/src/main/java/feign/jackson/jaxb/JacksonJaxbJsonEncoder.java index f00e5b2069..e631df8b61 100644 --- a/jackson-jaxb/src/main/java/feign/jackson/jaxb/JacksonJaxbJsonEncoder.java +++ b/jackson-jaxb/src/main/java/feign/jackson/jaxb/JacksonJaxbJsonEncoder.java @@ -15,16 +15,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.lang.reflect.Type; import java.nio.charset.Charset; - import feign.RequestTemplate; import feign.codec.EncodeException; import feign.codec.Encoder; - import static com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider.DEFAULT_ANNOTATIONS; import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE; @@ -41,10 +38,12 @@ public JacksonJaxbJsonEncoder(ObjectMapper objectMapper) { @Override - public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException { + public void encode(Object object, Type bodyType, RequestTemplate template) + throws EncodeException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); try { - jacksonJaxbJsonProvider.writeTo(object, bodyType.getClass(), null, null, APPLICATION_JSON_TYPE, null, outputStream); + jacksonJaxbJsonProvider.writeTo(object, bodyType.getClass(), null, null, + APPLICATION_JSON_TYPE, null, outputStream); template.body(outputStream.toByteArray(), Charset.defaultCharset()); } catch (IOException e) { throw new EncodeException(e.getMessage(), e); diff --git a/jackson-jaxb/src/test/java/feign/jackson/jaxb/JacksonJaxbCodecTest.java b/jackson-jaxb/src/test/java/feign/jackson/jaxb/JacksonJaxbCodecTest.java index fb52be5cc2..ccd9533fd8 100644 --- a/jackson-jaxb/src/test/java/feign/jackson/jaxb/JacksonJaxbCodecTest.java +++ b/jackson-jaxb/src/test/java/feign/jackson/jaxb/JacksonJaxbCodecTest.java @@ -14,18 +14,14 @@ package feign.jackson.jaxb; import org.junit.Test; - import java.util.Collection; import java.util.Collections; - import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; - import feign.RequestTemplate; import feign.Response; - import static feign.Util.UTF_8; import static feign.assertj.FeignAssertions.assertThat; @@ -44,11 +40,11 @@ public void encodeTest() { @Test public void decodeTest() throws Exception { Response response = Response.builder() - .status(200) - .reason("OK") - .headers(Collections.>emptyMap()) - .body("{\"value\":\"Test\"}", UTF_8) - .build(); + .status(200) + .reason("OK") + .headers(Collections.>emptyMap()) + .body("{\"value\":\"Test\"}", UTF_8) + .build(); JacksonJaxbJsonDecoder decoder = new JacksonJaxbJsonDecoder(); assertThat(decoder.decode(response, MockObject.class)) @@ -59,10 +55,10 @@ public void decodeTest() throws Exception { @Test public void notFoundDecodesToEmpty() throws Exception { Response response = Response.builder() - .status(404) - .reason("NOT FOUND") - .headers(Collections.>emptyMap()) - .build(); + .status(404) + .reason("NOT FOUND") + .headers(Collections.>emptyMap()) + .build(); assertThat((byte[]) new JacksonJaxbJsonDecoder().decode(response, byte[].class)).isEmpty(); } @@ -73,8 +69,7 @@ static class MockObject { @XmlElement private String value; - MockObject() { - } + MockObject() {} MockObject(String value) { this.value = value; diff --git a/jackson/src/main/java/feign/jackson/JacksonDecoder.java b/jackson/src/main/java/feign/jackson/JacksonDecoder.java index 8e04080d44..a586544abc 100644 --- a/jackson/src/main/java/feign/jackson/JacksonDecoder.java +++ b/jackson/src/main/java/feign/jackson/JacksonDecoder.java @@ -17,13 +17,11 @@ import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.RuntimeJsonMappingException; - import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.lang.reflect.Type; import java.util.Collections; - import feign.Response; import feign.Util; import feign.codec.Decoder; @@ -38,7 +36,7 @@ public JacksonDecoder() { public JacksonDecoder(Iterable modules) { this(new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .registerModules(modules)); + .registerModules(modules)); } public JacksonDecoder(ObjectMapper mapper) { @@ -47,8 +45,10 @@ public JacksonDecoder(ObjectMapper mapper) { @Override public Object decode(Response response, Type type) throws IOException { - if (response.status() == 404) return Util.emptyValueOf(type); - if (response.body() == null) return null; + if (response.status() == 404) + return Util.emptyValueOf(type); + if (response.body() == null) + return null; Reader reader = response.body().asReader(); if (!reader.markSupported()) { reader = new BufferedReader(reader, 1); diff --git a/jackson/src/main/java/feign/jackson/JacksonEncoder.java b/jackson/src/main/java/feign/jackson/JacksonEncoder.java index 9028b34a23..b61b3e785f 100644 --- a/jackson/src/main/java/feign/jackson/JacksonEncoder.java +++ b/jackson/src/main/java/feign/jackson/JacksonEncoder.java @@ -19,10 +19,8 @@ import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; - import java.lang.reflect.Type; import java.util.Collections; - import feign.RequestTemplate; import feign.codec.EncodeException; import feign.codec.Encoder; @@ -37,9 +35,9 @@ public JacksonEncoder() { public JacksonEncoder(Iterable modules) { this(new ObjectMapper() - .setSerializationInclusion(JsonInclude.Include.NON_NULL) - .configure(SerializationFeature.INDENT_OUTPUT, true) - .registerModules(modules)); + .setSerializationInclusion(JsonInclude.Include.NON_NULL) + .configure(SerializationFeature.INDENT_OUTPUT, true) + .registerModules(modules)); } public JacksonEncoder(ObjectMapper mapper) { diff --git a/jackson/src/main/java/feign/jackson/JacksonIteratorDecoder.java b/jackson/src/main/java/feign/jackson/JacksonIteratorDecoder.java index 3ffb9200d6..c07b13e907 100644 --- a/jackson/src/main/java/feign/jackson/JacksonIteratorDecoder.java +++ b/jackson/src/main/java/feign/jackson/JacksonIteratorDecoder.java @@ -20,7 +20,6 @@ import feign.Util; import feign.codec.DecodeException; import feign.codec.Decoder; - import java.io.BufferedReader; import java.io.Closeable; import java.io.IOException; @@ -29,17 +28,20 @@ import java.lang.reflect.Type; import java.util.Collections; import java.util.Iterator; - import static feign.Util.ensureClosed; /** - * Jackson decoder which return a closeable iterator. - * Returned iterator auto-close the {@code Response} when it reached json array end or failed to parse stream. - * If this iterator is not fetched till the end, it has to be casted to {@code Closeable} and explicity {@code Closeable#close} by the consumer. + * Jackson decoder which return a closeable iterator. Returned iterator auto-close the + * {@code Response} when it reached json array end or failed to parse stream. If this iterator is + * not fetched till the end, it has to be casted to {@code Closeable} and explicity + * {@code Closeable#close} by the consumer. + *

    *

    *

    - *

    Example:
    - *

    
    + * Example: 
    + * + *
    + * 
      * Feign.builder()
      *   .decoder(JacksonIteratorDecoder.create())
      *   .doNotCloseAfterDecode() // Required to fetch the iterator after the response is processed, need to be close
    @@ -47,7 +49,8 @@
      * interface GitHub {
      *  {@literal @}RequestLine("GET /repos/{owner}/{repo}/contributors")
      *   Iterator contributors(@Param("owner") String owner, @Param("repo") String repo);
    - * }
    + * }
    + *
    */ public final class JacksonIteratorDecoder implements Decoder { @@ -59,8 +62,10 @@ public final class JacksonIteratorDecoder implements Decoder { @Override public Object decode(Response response, Type type) throws IOException { - if (response.status() == 404) return Util.emptyValueOf(type); - if (response.body() == null) return null; + if (response.status() == 404) + return Util.emptyValueOf(type); + if (response.body() == null) + return null; Reader reader = response.body().asReader(); if (!reader.markSupported()) { reader = new BufferedReader(reader, 1); @@ -72,7 +77,8 @@ public Object decode(Response response, Type type) throws IOException { return null; // Eagerly returning null avoids "No content to map due to end-of-input" } reader.reset(); - return new JacksonIterator(actualIteratorTypeArgument(type), mapper, response, reader); + return new JacksonIterator(actualIteratorTypeArgument(type), mapper, response, + reader); } catch (RuntimeJsonMappingException e) { if (e.getCause() != null && e.getCause() instanceof IOException) { throw IOException.class.cast(e.getCause()); @@ -87,7 +93,8 @@ private static Type actualIteratorTypeArgument(Type type) { } ParameterizedType parameterizedType = (ParameterizedType) type; if (!Iterator.class.equals(parameterizedType.getRawType())) { - throw new IllegalArgumentException("Not an iterator type " + parameterizedType.getRawType().toString()); + throw new IllegalArgumentException( + "Not an iterator type " + parameterizedType.getRawType().toString()); } return ((ParameterizedType) type).getActualTypeArguments()[0]; } diff --git a/jackson/src/test/java/feign/jackson/JacksonCodecTest.java b/jackson/src/test/java/feign/jackson/JacksonCodecTest.java index fe906ab26c..38091579dd 100644 --- a/jackson/src/test/java/feign/jackson/JacksonCodecTest.java +++ b/jackson/src/test/java/feign/jackson/JacksonCodecTest.java @@ -23,9 +23,7 @@ import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.StdSerializer; - import org.junit.Test; - import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; @@ -37,10 +35,8 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; - import feign.RequestTemplate; import feign.Response; - import static feign.Util.UTF_8; import static feign.assertj.FeignAssertions.assertThat; import static org.junit.Assert.assertEquals; @@ -50,15 +46,15 @@ public class JacksonCodecTest { private String zonesJson = ""// - + "[\n"// - + " {\n"// - + " \"name\": \"denominator.io.\"\n"// - + " },\n"// - + " {\n"// - + " \"name\": \"denominator.io.\",\n"// - + " \"id\": \"ABCD\"\n"// - + " }\n"// - + "]\n"; + + "[\n"// + + " {\n"// + + " \"name\": \"denominator.io.\"\n"// + + " },\n"// + + " {\n"// + + " \"name\": \"denominator.io.\",\n"// + + " \"id\": \"ABCD\"\n"// + + " }\n"// + + "]\n"; @Test public void encodesMapObjectNumericalValuesAsInteger() throws Exception { @@ -69,9 +65,9 @@ public void encodesMapObjectNumericalValuesAsInteger() throws Exception { new JacksonEncoder().encode(map, map.getClass(), template); assertThat(template).hasBody(""// - + "{\n" // - + " \"foo\" : 1\n" // - + "}"); + + "{\n" // + + " \"foo\" : 1\n" // + + "}"); } @Test @@ -81,14 +77,13 @@ public void encodesFormParams() throws Exception { form.put("bar", Arrays.asList(2, 3)); RequestTemplate template = new RequestTemplate(); - new JacksonEncoder().encode(form, new TypeReference>() { - }.getType(), template); + new JacksonEncoder().encode(form, new TypeReference>() {}.getType(), template); assertThat(template).hasBody(""// - + "{\n" // - + " \"foo\" : 1,\n" // - + " \"bar\" : [ 2, 3 ]\n" // - + "}"); + + "{\n" // + + " \"foo\" : 1,\n" // + + " \"bar\" : [ 2, 3 ]\n" // + + "}"); } @Test @@ -98,33 +93,33 @@ public void decodes() throws Exception { zones.add(new Zone("denominator.io.", "ABCD")); Response response = Response.builder() - .status(200) - .reason("OK") - .headers(Collections.>emptyMap()) - .body(zonesJson, UTF_8) - .build(); - assertEquals(zones, new JacksonDecoder().decode(response, new TypeReference>() { - }.getType())); + .status(200) + .reason("OK") + .headers(Collections.>emptyMap()) + .body(zonesJson, UTF_8) + .build(); + assertEquals(zones, + new JacksonDecoder().decode(response, new TypeReference>() {}.getType())); } @Test public void nullBodyDecodesToNull() throws Exception { Response response = Response.builder() - .status(204) - .reason("OK") - .headers(Collections.>emptyMap()) - .build(); + .status(204) + .reason("OK") + .headers(Collections.>emptyMap()) + .build(); assertNull(new JacksonDecoder().decode(response, String.class)); } @Test public void emptyBodyDecodesToNull() throws Exception { Response response = Response.builder() - .status(204) - .reason("OK") - .headers(Collections.>emptyMap()) - .body(new byte[0]) - .build(); + .status(204) + .reason("OK") + .headers(Collections.>emptyMap()) + .body(new byte[0]) + .build(); assertNull(new JacksonDecoder().decode(response, String.class)); } @@ -139,13 +134,12 @@ public void customDecoder() throws Exception { zones.add(new Zone("DENOMINATOR.IO.", "ABCD")); Response response = Response.builder() - .status(200) - .reason("OK") - .headers(Collections.>emptyMap()) - .body(zonesJson, UTF_8) - .build(); - assertEquals(zones, decoder.decode(response, new TypeReference>() { - }.getType())); + .status(200) + .reason("OK") + .headers(Collections.>emptyMap()) + .body(zonesJson, UTF_8) + .build(); + assertEquals(zones, decoder.decode(response, new TypeReference>() {}.getType())); } @Test @@ -158,16 +152,15 @@ public void customEncoder() throws Exception { zones.add(new Zone("denominator.io.", "abcd")); RequestTemplate template = new RequestTemplate(); - encoder.encode(zones, new TypeReference>() { - }.getType(), template); + encoder.encode(zones, new TypeReference>() {}.getType(), template); assertThat(template).hasBody("" // - + "[ {\n" - + " \"name\" : \"DENOMINATOR.IO.\"\n" - + "}, {\n" - + " \"name\" : \"DENOMINATOR.IO.\",\n" - + " \"id\" : \"ABCD\"\n" - + "} ]"); + + "[ {\n" + + " \"name\" : \"DENOMINATOR.IO.\"\n" + + "}, {\n" + + " \"name\" : \"DENOMINATOR.IO.\",\n" + + " \"id\" : \"ABCD\"\n" + + "} ]"); } @Test @@ -177,12 +170,13 @@ public void decodesIterator() throws Exception { zones.add(new Zone("denominator.io.", "ABCD")); Response response = Response.builder() - .status(200) - .reason("OK") - .headers(Collections.>emptyMap()) - .body(zonesJson, UTF_8) - .build(); - Object decoded = JacksonIteratorDecoder.create().decode(response, new TypeReference>() {}.getType()); + .status(200) + .reason("OK") + .headers(Collections.>emptyMap()) + .body(zonesJson, UTF_8) + .build(); + Object decoded = JacksonIteratorDecoder.create().decode(response, + new TypeReference>() {}.getType()); assertTrue(Iterator.class.isAssignableFrom(decoded.getClass())); assertTrue(Closeable.class.isAssignableFrom(decoded.getClass())); assertEquals(zones, asList((Iterator) decoded)); @@ -198,21 +192,21 @@ private List asList(Iterator iter) { @Test public void nullBodyDecodesToNullIterator() throws Exception { Response response = Response.builder() - .status(204) - .reason("OK") - .headers(Collections.>emptyMap()) - .build(); + .status(204) + .reason("OK") + .headers(Collections.>emptyMap()) + .build(); assertNull(JacksonIteratorDecoder.create().decode(response, Iterator.class)); } @Test public void emptyBodyDecodesToNullIterator() throws Exception { Response response = Response.builder() - .status(204) - .reason("OK") - .headers(Collections.>emptyMap()) - .body(new byte[0]) - .build(); + .status(204) + .reason("OK") + .headers(Collections.>emptyMap()) + .body(new byte[0]) + .build(); assertNull(JacksonIteratorDecoder.create().decode(response, Iterator.class)); } @@ -279,10 +273,10 @@ public void serialize(Zone value, JsonGenerator jgen, SerializerProvider provide @Test public void notFoundDecodesToEmpty() throws Exception { Response response = Response.builder() - .status(404) - .reason("NOT FOUND") - .headers(Collections.>emptyMap()) - .build(); + .status(404) + .reason("NOT FOUND") + .headers(Collections.>emptyMap()) + .build(); assertThat((byte[]) new JacksonDecoder().decode(response, byte[].class)).isEmpty(); } diff --git a/jackson/src/test/java/feign/jackson/JacksonIteratorTest.java b/jackson/src/test/java/feign/jackson/JacksonIteratorTest.java index 67aa0ef01c..7ba81aa210 100644 --- a/jackson/src/test/java/feign/jackson/JacksonIteratorTest.java +++ b/jackson/src/test/java/feign/jackson/JacksonIteratorTest.java @@ -20,7 +20,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -28,7 +27,6 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.concurrent.atomic.AtomicBoolean; - import static feign.Util.UTF_8; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.core.Is.isA; @@ -45,7 +43,8 @@ public void shouldDecodePrimitiveArrays() throws IOException { @Test public void shouldDecodeObjects() throws IOException { - assertThat(iterator(User.class, "[{\"login\":\"bob\"},{\"login\":\"joe\"}]")).containsExactly(new User("bob"), new User("joe")); + assertThat(iterator(User.class, "[{\"login\":\"bob\"},{\"login\":\"joe\"}]")) + .containsExactly(new User("bob"), new User("joe")); } @Test @@ -53,7 +52,8 @@ public void malformedObjectThrowsDecodeException() throws IOException { thrown.expect(DecodeException.class); thrown.expectCause(isA(IOException.class)); - assertThat(iterator(User.class, "[{\"login\":\"bob\"},{\"login\":\"joe...")).containsOnly(new User("bob")); + assertThat(iterator(User.class, "[{\"login\":\"bob\"},{\"login\":\"joe...")) + .containsOnly(new User("bob")); } @Test diff --git a/jackson/src/test/java/feign/jackson/examples/GitHubExample.java b/jackson/src/test/java/feign/jackson/examples/GitHubExample.java index b6811359c4..f9221ed7a0 100644 --- a/jackson/src/test/java/feign/jackson/examples/GitHubExample.java +++ b/jackson/src/test/java/feign/jackson/examples/GitHubExample.java @@ -14,7 +14,6 @@ package feign.jackson.examples; import java.util.List; - import feign.Feign; import feign.Param; import feign.RequestLine; diff --git a/jackson/src/test/java/feign/jackson/examples/GitHubIteratorExample.java b/jackson/src/test/java/feign/jackson/examples/GitHubIteratorExample.java index 5941af2a9c..9ab9da8e31 100644 --- a/jackson/src/test/java/feign/jackson/examples/GitHubIteratorExample.java +++ b/jackson/src/test/java/feign/jackson/examples/GitHubIteratorExample.java @@ -17,7 +17,6 @@ import feign.Param; import feign.RequestLine; import feign.jackson.JacksonIteratorDecoder; - import java.io.Closeable; import java.io.IOException; import java.util.Iterator; diff --git a/java8/src/main/java/feign/optionals/OptionalDecoder.java b/java8/src/main/java/feign/optionals/OptionalDecoder.java index e22e599395..06d9c76c3b 100644 --- a/java8/src/main/java/feign/optionals/OptionalDecoder.java +++ b/java8/src/main/java/feign/optionals/OptionalDecoder.java @@ -16,7 +16,6 @@ import feign.Response; import feign.Util; import feign.codec.Decoder; - import java.io.IOException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -24,29 +23,30 @@ import java.util.Optional; public final class OptionalDecoder implements Decoder { - final Decoder delegate; + final Decoder delegate; - public OptionalDecoder(Decoder delegate) { - Objects.requireNonNull(delegate, "Decoder must not be null. "); - this.delegate = delegate; - } + public OptionalDecoder(Decoder delegate) { + Objects.requireNonNull(delegate, "Decoder must not be null. "); + this.delegate = delegate; + } - @Override public Object decode(Response response, Type type) throws IOException { - if(!isOptional(type)) { - return delegate.decode(response, type); - } - if(response.status() == 404 || response.status() == 204) { - return Optional.empty(); - } - Type enclosedType = Util.resolveLastTypeParameter(type, Optional.class); - return Optional.of(delegate.decode(response, enclosedType)); + @Override + public Object decode(Response response, Type type) throws IOException { + if (!isOptional(type)) { + return delegate.decode(response, type); + } + if (response.status() == 404 || response.status() == 204) { + return Optional.empty(); } + Type enclosedType = Util.resolveLastTypeParameter(type, Optional.class); + return Optional.of(delegate.decode(response, enclosedType)); + } - static boolean isOptional(Type type) { - if(!(type instanceof ParameterizedType)) { - return false; - } - ParameterizedType parameterizedType = (ParameterizedType) type; - return parameterizedType.getRawType().equals(Optional.class); + static boolean isOptional(Type type) { + if (!(type instanceof ParameterizedType)) { + return false; } + ParameterizedType parameterizedType = (ParameterizedType) type; + return parameterizedType.getRawType().equals(Optional.class); + } } diff --git a/java8/src/main/java/feign/stream/StreamDecoder.java b/java8/src/main/java/feign/stream/StreamDecoder.java index 4c81987a37..058ec09b19 100644 --- a/java8/src/main/java/feign/stream/StreamDecoder.java +++ b/java8/src/main/java/feign/stream/StreamDecoder.java @@ -16,7 +16,6 @@ import feign.FeignException; import feign.Response; import feign.codec.Decoder; - import java.io.Closeable; import java.io.IOException; import java.lang.reflect.ParameterizedType; @@ -26,12 +25,16 @@ import java.util.Spliterators; import java.util.stream.Stream; import java.util.stream.StreamSupport; - import static feign.Util.ensureClosed; /** - * Iterator based decoder that support streaming.

    Example:
    - *

    
    + * Iterator based decoder that support streaming.
    + * 

    + *

    + * Example:
    + * + *

    + * 
      * Feign.builder()
      *   .decoder(StreamDecoder.create(JacksonIteratorDecoder.create()))
      *   .doNotCloseAfterDecode() // Required for streaming
    @@ -39,7 +42,8 @@
      * interface GitHub {
      *  {@literal @}RequestLine("GET /repos/{owner}/{repo}/contributors")
      *   Stream contributors(@Param("owner") String owner, @Param("repo") String repo);
    - * }
    + * }
    + *
    */ public final class StreamDecoder implements Decoder { @@ -100,4 +104,4 @@ public Type getOwnerType() { return null; } } -} \ No newline at end of file +} diff --git a/java8/src/test/java/feign/optionals/OptionalDecoderTests.java b/java8/src/test/java/feign/optionals/OptionalDecoderTests.java index ee57900ce2..a7b8d3836a 100644 --- a/java8/src/test/java/feign/optionals/OptionalDecoderTests.java +++ b/java8/src/test/java/feign/optionals/OptionalDecoderTests.java @@ -19,43 +19,41 @@ import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import org.junit.Test; - import java.io.IOException; import java.util.Optional; - import static org.assertj.core.api.Assertions.assertThat; public class OptionalDecoderTests { - interface OptionalInterface { - @RequestLine("GET /") - Optional get(); - } - - @Test - public void simple404OptionalTest() throws IOException, InterruptedException { - final MockWebServer server = new MockWebServer(); - server.enqueue(new MockResponse().setResponseCode(404)); - server.enqueue(new MockResponse().setBody("foo")); - - final OptionalInterface api = Feign.builder() - .decode404() - .decoder(new OptionalDecoder(new Decoder.Default())) - .target(OptionalInterface.class, server.url("/").toString()); - - assertThat(api.get().isPresent()).isFalse(); - assertThat(api.get().get()).isEqualTo("foo"); - } - - @Test - public void simple204OptionalTest() throws IOException, InterruptedException { - final MockWebServer server = new MockWebServer(); - server.enqueue(new MockResponse().setResponseCode(204)); - - final OptionalInterface api = Feign.builder() - .decoder(new OptionalDecoder(new Decoder.Default())) - .target(OptionalInterface.class, server.url("/").toString()); - - assertThat(api.get().isPresent()).isFalse(); - } + interface OptionalInterface { + @RequestLine("GET /") + Optional get(); + } + + @Test + public void simple404OptionalTest() throws IOException, InterruptedException { + final MockWebServer server = new MockWebServer(); + server.enqueue(new MockResponse().setResponseCode(404)); + server.enqueue(new MockResponse().setBody("foo")); + + final OptionalInterface api = Feign.builder() + .decode404() + .decoder(new OptionalDecoder(new Decoder.Default())) + .target(OptionalInterface.class, server.url("/").toString()); + + assertThat(api.get().isPresent()).isFalse(); + assertThat(api.get().get()).isEqualTo("foo"); + } + + @Test + public void simple204OptionalTest() throws IOException, InterruptedException { + final MockWebServer server = new MockWebServer(); + server.enqueue(new MockResponse().setResponseCode(204)); + + final OptionalInterface api = Feign.builder() + .decoder(new OptionalDecoder(new Decoder.Default())) + .target(OptionalInterface.class, server.url("/").toString()); + + assertThat(api.get().isPresent()).isFalse(); + } } diff --git a/java8/src/test/java/feign/stream/StreamDecoderTest.java b/java8/src/test/java/feign/stream/StreamDecoderTest.java index 100e9b1554..8ab0d1d200 100644 --- a/java8/src/test/java/feign/stream/StreamDecoderTest.java +++ b/java8/src/test/java/feign/stream/StreamDecoderTest.java @@ -30,7 +30,6 @@ import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import org.junit.Test; - import static feign.Util.UTF_8; import static org.assertj.core.api.Assertions.assertThat; @@ -67,7 +66,8 @@ public void simpleStreamTest() throws IOException, InterruptedException { server.enqueue(new MockResponse().setBody("foo\nbar")); StreamInterface api = Feign.builder() - .decoder(StreamDecoder.create((response, type) -> new BufferedReader(response.body().asReader()).lines().iterator())) + .decoder(StreamDecoder.create( + (response, type) -> new BufferedReader(response.body().asReader()).lines().iterator())) .doNotCloseAfterDecode() .target(StreamInterface.class, server.url("/").toString()); @@ -105,8 +105,8 @@ public void shouldCloseIteratorWhenStreamClosed() throws IOException { TestCloseableIterator it = new TestCloseableIterator(); StreamDecoder decoder = new StreamDecoder((r, t) -> it); - try (Stream stream = (Stream) decoder.decode(response, new TypeReference>() { - }.getType())) { + try (Stream stream = + (Stream) decoder.decode(response, new TypeReference>() {}.getType())) { assertThat(stream.collect(Collectors.toList())).hasSize(1); assertThat(it.called).isTrue(); } finally { @@ -118,15 +118,18 @@ static class TestCloseableIterator implements Iterator, Closeable { boolean called; boolean closed; - @Override public void close() throws IOException { + @Override + public void close() throws IOException { this.closed = true; } - @Override public boolean hasNext() { + @Override + public boolean hasNext() { return !called; } - @Override public String next() { + @Override + public String next() { called = true; return "feign"; } diff --git a/jaxb/src/main/java/feign/jaxb/JAXBContextFactory.java b/jaxb/src/main/java/feign/jaxb/JAXBContextFactory.java index c6c43e0f9f..25a6a089df 100644 --- a/jaxb/src/main/java/feign/jaxb/JAXBContextFactory.java +++ b/jaxb/src/main/java/feign/jaxb/JAXBContextFactory.java @@ -17,7 +17,6 @@ import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; - import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; @@ -30,8 +29,7 @@ */ public final class JAXBContextFactory { - private final ConcurrentHashMap - jaxbContexts = + private final ConcurrentHashMap jaxbContexts = new ConcurrentHashMap(64); private final Map properties; diff --git a/jaxb/src/main/java/feign/jaxb/JAXBDecoder.java b/jaxb/src/main/java/feign/jaxb/JAXBDecoder.java index c3838278f1..93ed316ad8 100644 --- a/jaxb/src/main/java/feign/jaxb/JAXBDecoder.java +++ b/jaxb/src/main/java/feign/jaxb/JAXBDecoder.java @@ -15,14 +15,12 @@ import java.io.IOException; import java.lang.reflect.Type; - import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; import javax.xml.transform.Source; import javax.xml.transform.sax.SAXSource; - import feign.Response; import feign.Util; import feign.codec.DecodeException; @@ -31,19 +29,24 @@ import org.xml.sax.SAXException; /** - * Decodes responses using JAXB.

    Basic example with with Feign.Builder:

    + * Decodes responses using JAXB.
    + *

    + * Basic example with with Feign.Builder: + *

    + * *
      * JAXBContextFactory jaxbFactory = new JAXBContextFactory.Builder()
    - *      .withMarshallerJAXBEncoding("UTF-8")
    - *      .withMarshallerSchemaLocation("http://apihost http://apihost/schema.xsd")
    - *      .build();
    + *     .withMarshallerJAXBEncoding("UTF-8")
    + *     .withMarshallerSchemaLocation("http://apihost http://apihost/schema.xsd")
    + *     .build();
      *
      * api = Feign.builder()
    - *            .decoder(new JAXBDecoder(jaxbFactory))
    - *            .target(MyApi.class, "http://api");
    + *     .decoder(new JAXBDecoder(jaxbFactory))
    + *     .target(MyApi.class, "http://api");
      * 
    - *

    The JAXBContextFactory should be reused across requests as it caches the created JAXB - * contexts.

    + *

    + * The JAXBContextFactory should be reused across requests as it caches the created JAXB contexts. + *

    */ public class JAXBDecoder implements Decoder { @@ -62,8 +65,10 @@ private JAXBDecoder(Builder builder) { @Override public Object decode(Response response, Type type) throws IOException { - if (response.status() == 404) return Util.emptyValueOf(type); - if (response.body() == null) return null; + if (response.status() == 404) + return Util.emptyValueOf(type); + if (response.body() == null) + return null; if (!(type instanceof Class)) { throw new UnsupportedOperationException( "JAXB only supports decoding raw types. Found " + type); @@ -76,10 +81,12 @@ public Object decode(Response response, Type type) throws IOException { saxParserFactory.setFeature("http://xml.org/sax/features/external-general-entities", false); saxParserFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); saxParserFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false); - saxParserFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + saxParserFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", + false); saxParserFactory.setNamespaceAware(namespaceAware); - Source source = new SAXSource(saxParserFactory.newSAXParser().getXMLReader(), new InputSource(response.body().asInputStream())); + Source source = new SAXSource(saxParserFactory.newSAXParser().getXMLReader(), + new InputSource(response.body().asInputStream())); Unmarshaller unmarshaller = jaxbContextFactory.createUnmarshaller((Class) type); return unmarshaller.unmarshal(source); } catch (JAXBException e) { @@ -100,8 +107,7 @@ public static class Builder { private JAXBContextFactory jaxbContextFactory; /** - * Controls whether the underlying XML parser is namespace aware. - * Default is true. + * Controls whether the underlying XML parser is namespace aware. Default is true. */ public Builder withNamespaceAware(boolean namespaceAware) { this.namespaceAware = namespaceAware; diff --git a/jaxb/src/main/java/feign/jaxb/JAXBEncoder.java b/jaxb/src/main/java/feign/jaxb/JAXBEncoder.java index 3fb728142a..5bb30eac19 100644 --- a/jaxb/src/main/java/feign/jaxb/JAXBEncoder.java +++ b/jaxb/src/main/java/feign/jaxb/JAXBEncoder.java @@ -15,28 +15,31 @@ import java.io.StringWriter; import java.lang.reflect.Type; - import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; - import feign.RequestTemplate; import feign.codec.EncodeException; import feign.codec.Encoder; /** - * Encodes requests using JAXB.

    Basic example with with Feign.Builder:

    + * Encodes requests using JAXB.
    + *

    + * Basic example with with Feign.Builder: + *

    + * *
      * JAXBContextFactory jaxbFactory = new JAXBContextFactory.Builder()
    - *      .withMarshallerJAXBEncoding("UTF-8")
    - *      .withMarshallerSchemaLocation("http://apihost http://apihost/schema.xsd")
    - *      .build();
    + *     .withMarshallerJAXBEncoding("UTF-8")
    + *     .withMarshallerSchemaLocation("http://apihost http://apihost/schema.xsd")
    + *     .build();
      *
      * api = Feign.builder()
    - *            .encoder(new JAXBEncoder(jaxbFactory))
    - *            .target(MyApi.class, "http://api");
    + *     .encoder(new JAXBEncoder(jaxbFactory))
    + *     .target(MyApi.class, "http://api");
      * 
    - *

    The JAXBContextFactory should be reused across requests as it caches the created JAXB - * contexts.

    + *

    + * The JAXBContextFactory should be reused across requests as it caches the created JAXB contexts. + *

    */ public class JAXBEncoder implements Encoder { diff --git a/jaxb/src/test/java/feign/jaxb/JAXBCodecTest.java b/jaxb/src/test/java/feign/jaxb/JAXBCodecTest.java index 8cb64e8e80..2139fba245 100644 --- a/jaxb/src/test/java/feign/jaxb/JAXBCodecTest.java +++ b/jaxb/src/test/java/feign/jaxb/JAXBCodecTest.java @@ -16,21 +16,17 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; - import java.lang.reflect.Type; import java.util.Collection; import java.util.Collections; import java.util.Map; - import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; - import feign.RequestTemplate; import feign.Response; import feign.codec.Encoder; - import static feign.Util.UTF_8; import static feign.assertj.FeignAssertions.assertThat; import static org.junit.Assert.assertEquals; @@ -84,7 +80,7 @@ public void encodesXmlWithCustomJAXBEncoding() throws Exception { encoder.encode(mock, MockObject.class, template); assertThat(template).hasBody("Test"); + + "standalone=\"yes\"?>Test"); } @Test @@ -103,11 +99,11 @@ public void encodesXmlWithCustomJAXBSchemaLocation() throws Exception { encoder.encode(mock, MockObject.class, template); assertThat(template).hasBody("" - + - "Test"); + "standalone=\"yes\"?>" + + + "Test"); } @Test @@ -125,10 +121,10 @@ public void encodesXmlWithCustomJAXBNoNamespaceSchemaLocation() throws Exception encoder.encode(mock, MockObject.class, template); assertThat(template).hasBody("" + - "Test"); + "standalone=\"yes\"?>" + + "Test"); } @Test @@ -164,14 +160,14 @@ public void decodesXml() throws Exception { mock.value = "Test"; String mockXml = "" - + "Test"; + + "Test"; Response response = Response.builder() - .status(200) - .reason("OK") - .headers(Collections.>emptyMap()) - .body(mockXml, UTF_8) - .build(); + .status(200) + .reason("OK") + .headers(Collections.>emptyMap()) + .body(mockXml, UTF_8) + .build(); JAXBDecoder decoder = new JAXBDecoder(new JAXBContextFactory.Builder().build()); @@ -191,11 +187,11 @@ class ParameterizedHolder { Type parameterized = ParameterizedHolder.class.getDeclaredField("field").getGenericType(); Response response = Response.builder() - .status(200) - .reason("OK") - .headers(Collections.>emptyMap()) - .body("", UTF_8) - .build(); + .status(200) + .reason("OK") + .headers(Collections.>emptyMap()) + .body("", UTF_8) + .build(); new JAXBDecoder(new JAXBContextFactory.Builder().build()).decode(response, parameterized); } @@ -204,10 +200,10 @@ class ParameterizedHolder { @Test public void notFoundDecodesToEmpty() throws Exception { Response response = Response.builder() - .status(404) - .reason("NOT FOUND") - .headers(Collections.>emptyMap()) - .build(); + .status(404) + .reason("NOT FOUND") + .headers(Collections.>emptyMap()) + .build(); assertThat((byte[]) new JAXBDecoder(new JAXBContextFactory.Builder().build()) .decode(response, byte[].class)).isEmpty(); } diff --git a/jaxb/src/test/java/feign/jaxb/JAXBContextFactoryTest.java b/jaxb/src/test/java/feign/jaxb/JAXBContextFactoryTest.java index 41d5826d54..8c3c64846b 100644 --- a/jaxb/src/test/java/feign/jaxb/JAXBContextFactoryTest.java +++ b/jaxb/src/test/java/feign/jaxb/JAXBContextFactoryTest.java @@ -14,9 +14,7 @@ package feign.jaxb; import org.junit.Test; - import javax.xml.bind.Marshaller; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -24,8 +22,7 @@ public class JAXBContextFactoryTest { @Test public void buildsMarshallerWithJAXBEncodingProperty() throws Exception { - JAXBContextFactory - factory = + JAXBContextFactory factory = new JAXBContextFactory.Builder().withMarshallerJAXBEncoding("UTF-16").build(); Marshaller marshaller = factory.createMarshaller(Object.class); @@ -41,7 +38,7 @@ public void buildsMarshallerWithSchemaLocationProperty() throws Exception { Marshaller marshaller = factory.createMarshaller(Object.class); assertEquals("http://apihost http://apihost/schema.xsd", - marshaller.getProperty(Marshaller.JAXB_SCHEMA_LOCATION)); + marshaller.getProperty(Marshaller.JAXB_SCHEMA_LOCATION)); } @Test @@ -52,13 +49,12 @@ public void buildsMarshallerWithNoNamespaceSchemaLocationProperty() throws Excep Marshaller marshaller = factory.createMarshaller(Object.class); assertEquals("http://apihost/schema.xsd", - marshaller.getProperty(Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION)); + marshaller.getProperty(Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION)); } @Test public void buildsMarshallerWithFormattedOutputProperty() throws Exception { - JAXBContextFactory - factory = + JAXBContextFactory factory = new JAXBContextFactory.Builder().withMarshallerFormattedOutput(true).build(); Marshaller marshaller = factory.createMarshaller(Object.class); @@ -67,8 +63,7 @@ public void buildsMarshallerWithFormattedOutputProperty() throws Exception { @Test public void buildsMarshallerWithFragmentProperty() throws Exception { - JAXBContextFactory - factory = + JAXBContextFactory factory = new JAXBContextFactory.Builder().withMarshallerFragment(true).build(); Marshaller marshaller = factory.createMarshaller(Object.class); diff --git a/jaxb/src/test/java/feign/jaxb/examples/AWSSignatureVersion4.java b/jaxb/src/test/java/feign/jaxb/examples/AWSSignatureVersion4.java index b1e44ba911..e5137dccb7 100644 --- a/jaxb/src/test/java/feign/jaxb/examples/AWSSignatureVersion4.java +++ b/jaxb/src/test/java/feign/jaxb/examples/AWSSignatureVersion4.java @@ -18,20 +18,16 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimeZone; - import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; - import feign.Request; import feign.RequestTemplate; - import static feign.Util.UTF_8; // http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html public class AWSSignatureVersion4 { - private static final String - EMPTY_STRING_HASH = + private static final String EMPTY_STRING_HASH = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; private static final SimpleDateFormat iso8601 = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"); static { @@ -81,7 +77,7 @@ private static String canonicalString(RequestTemplate input, String host) { // HexEncode(Hash(Payload)) String bodyText = input.charset() != null && input.body() != null ? new String(input.body(), input.charset()) - : null; + : null; if (bodyText != null) { canonicalRequest.append(hex(sha256(bodyText))); } else { @@ -135,8 +131,7 @@ public Request apply(RequestTemplate input) { timestamp = iso8601.format(new Date()); } - String - credentialScope = + String credentialScope = String.format("%s/%s/%s/%s", timestamp.substring(0, 8), region, service, "aws4_request"); input.query("X-Amz-Algorithm", "AWS4-HMAC-SHA256"); diff --git a/jaxb/src/test/java/feign/jaxb/examples/IAMExample.java b/jaxb/src/test/java/feign/jaxb/examples/IAMExample.java index 5d938fee56..ed44ef9d0f 100644 --- a/jaxb/src/test/java/feign/jaxb/examples/IAMExample.java +++ b/jaxb/src/test/java/feign/jaxb/examples/IAMExample.java @@ -18,7 +18,6 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; - import feign.Feign; import feign.Request; import feign.RequestLine; diff --git a/jaxb/src/test/java/feign/jaxb/examples/package-info.java b/jaxb/src/test/java/feign/jaxb/examples/package-info.java index 8a099a5f59..0acfdbb956 100644 --- a/jaxb/src/test/java/feign/jaxb/examples/package-info.java +++ b/jaxb/src/test/java/feign/jaxb/examples/package-info.java @@ -11,4 +11,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -@javax.xml.bind.annotation.XmlSchema(namespace = "https://iam.amazonaws.com/doc/2010-05-08/", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) package feign.jaxb.examples; +@javax.xml.bind.annotation.XmlSchema(namespace = "https://iam.amazonaws.com/doc/2010-05-08/", + elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) +package feign.jaxb.examples; diff --git a/jaxrs/src/main/java/feign/jaxrs/JAXRSContract.java b/jaxrs/src/main/java/feign/jaxrs/JAXRSContract.java index 93a63412e5..2ce7fb23b0 100644 --- a/jaxrs/src/main/java/feign/jaxrs/JAXRSContract.java +++ b/jaxrs/src/main/java/feign/jaxrs/JAXRSContract.java @@ -15,13 +15,11 @@ import feign.Contract; import feign.MethodMetadata; - import javax.ws.rs.*; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; - import static feign.Util.checkState; import static feign.Util.emptyToNull; @@ -35,7 +33,8 @@ public class JAXRSContract extends Contract.BaseContract { static final String CONTENT_TYPE = "Content-Type"; // Protected so unittest can call us - // XXX: Should parseAndValidateMetadata(Class, Method) be public instead? The old deprecated parseAndValidateMetadata(Method) was public.. + // XXX: Should parseAndValidateMetadata(Class, Method) be public instead? The old deprecated + // parseAndValidateMetadata(Method) was public.. @Override protected MethodMetadata parseAndValidateMetadata(Class targetType, Method method) { return super.parseAndValidateMetadata(targetType, method); @@ -50,7 +49,8 @@ protected void processAnnotationOnClass(MethodMetadata data, Class clz) { pathValue = "/" + pathValue; } if (pathValue.endsWith("/")) { - // Strip off any trailing slashes, since the template has already had slashes appropriately added + // Strip off any trailing slashes, since the template has already had slashes appropriately + // added pathValue = pathValue.substring(0, pathValue.length() - 1); } data.template().insert(0, pathValue); @@ -66,14 +66,15 @@ protected void processAnnotationOnClass(MethodMetadata data, Class clz) { } @Override - protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, + protected void processAnnotationOnMethod(MethodMetadata data, + Annotation methodAnnotation, Method method) { Class annotationType = methodAnnotation.annotationType(); HttpMethod http = annotationType.getAnnotation(HttpMethod.class); if (http != null) { checkState(data.template().method() == null, - "Method %s contains multiple HTTP methods. Found: %s and %s", method.getName(), - data.template().method(), http.value()); + "Method %s contains multiple HTTP methods. Found: %s and %s", method.getName(), + data.template().method(), http.value()); data.template().method(http.value()); } else if (annotationType == Path.class) { String pathValue = emptyToNull(Path.class.cast(methodAnnotation).value()); @@ -84,9 +85,11 @@ protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodA if (!methodAnnotationValue.startsWith("/") && !data.template().url().endsWith("/")) { methodAnnotationValue = "/" + methodAnnotationValue; } - // jax-rs allows whitespace around the param name, as well as an optional regex. The contract should + // jax-rs allows whitespace around the param name, as well as an optional regex. The contract + // should // strip these out appropriately. - methodAnnotationValue = methodAnnotationValue.replaceAll("\\{\\s*(.+?)\\s*(:.+?)?\\}", "\\{$1\\}"); + methodAnnotationValue = + methodAnnotationValue.replaceAll("\\{\\s*(.+?)\\s*(:.+?)?\\}", "\\{$1\\}"); data.template().append(methodAnnotationValue); } else if (annotationType == Produces.class) { handleProducesAnnotation(data, (Produces) methodAnnotation, "method " + method.getName()); @@ -112,34 +115,37 @@ private void handleConsumesAnnotation(MethodMetadata data, Consumes consumes, St } /** - * Allows derived contracts to specify unsupported jax-rs parameter annotations which should be ignored. - * Required for JAX-RS 2 compatibility. + * Allows derived contracts to specify unsupported jax-rs parameter annotations which should be + * ignored. Required for JAX-RS 2 compatibility. */ protected boolean isUnsupportedHttpParameterAnnotation(Annotation parameterAnnotation) { return false; } @Override - protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, + protected boolean processAnnotationsOnParameter(MethodMetadata data, + Annotation[] annotations, int paramIndex) { boolean isHttpParam = false; for (Annotation parameterAnnotation : annotations) { Class annotationType = parameterAnnotation.annotationType(); - // masc20180327. parameter with unsupported jax-rs annotations should not be passed as body params. - // this will prevent interfaces from becoming unusable entirely due to single (unsupported) endpoints. + // masc20180327. parameter with unsupported jax-rs annotations should not be passed as body + // params. + // this will prevent interfaces from becoming unusable entirely due to single (unsupported) + // endpoints. // https://github.com/OpenFeign/feign/issues/669 if (this.isUnsupportedHttpParameterAnnotation(parameterAnnotation)) { isHttpParam = true; } else if (annotationType == PathParam.class) { String name = PathParam.class.cast(parameterAnnotation).value(); checkState(emptyToNull(name) != null, "PathParam.value() was empty on parameter %s", - paramIndex); + paramIndex); nameParam(data, name, paramIndex); isHttpParam = true; } else if (annotationType == QueryParam.class) { String name = QueryParam.class.cast(parameterAnnotation).value(); checkState(emptyToNull(name) != null, "QueryParam.value() was empty on parameter %s", - paramIndex); + paramIndex); Collection query = addTemplatedParam(data.template().queries().get(name), name); data.template().query(name, query); nameParam(data, name, paramIndex); @@ -147,7 +153,7 @@ protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[ } else if (annotationType == HeaderParam.class) { String name = HeaderParam.class.cast(parameterAnnotation).value(); checkState(emptyToNull(name) != null, "HeaderParam.value() was empty on parameter %s", - paramIndex); + paramIndex); Collection header = addTemplatedParam(data.template().headers().get(name), name); data.template().header(name, header); nameParam(data, name, paramIndex); @@ -155,7 +161,7 @@ protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[ } else if (annotationType == FormParam.class) { String name = FormParam.class.cast(parameterAnnotation).value(); checkState(emptyToNull(name) != null, "FormParam.value() was empty on parameter %s", - paramIndex); + paramIndex); data.formParams().add(name); nameParam(data, name, paramIndex); isHttpParam = true; diff --git a/jaxrs/src/test/java/feign/jaxrs/JAXRSContractTest.java b/jaxrs/src/test/java/feign/jaxrs/JAXRSContractTest.java index e6839fa478..dd50cea3de 100644 --- a/jaxrs/src/test/java/feign/jaxrs/JAXRSContractTest.java +++ b/jaxrs/src/test/java/feign/jaxrs/JAXRSContractTest.java @@ -16,14 +16,12 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.net.URI; import java.util.List; - import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.FormParam; @@ -37,17 +35,15 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; - import feign.MethodMetadata; import feign.Response; - import static feign.assertj.FeignAssertions.assertThat; import static java.util.Arrays.asList; import static org.assertj.core.data.MapEntry.entry; /** - * Tests interfaces defined per {@link JAXRSContract} are interpreted into expected {@link feign - * .RequestTemplate template} instances. + * Tests interfaces defined per {@link JAXRSContract} are interpreted into expected + * {@link feign .RequestTemplate template} instances. */ public class JAXRSContractTest { @@ -91,31 +87,27 @@ public void queryParamsInPathExtract() throws Exception { assertThat(parseAndValidateMetadata(WithQueryParamsInPath.class, "one").template()) .hasUrl("/") .hasQueries( - entry("Action", asList("GetUser")) - ); + entry("Action", asList("GetUser"))); assertThat(parseAndValidateMetadata(WithQueryParamsInPath.class, "two").template()) .hasUrl("/") .hasQueries( entry("Action", asList("GetUser")), - entry("Version", asList("2010-05-08")) - ); + entry("Version", asList("2010-05-08"))); assertThat(parseAndValidateMetadata(WithQueryParamsInPath.class, "three").template()) .hasUrl("/") .hasQueries( entry("Action", asList("GetUser")), entry("Version", asList("2010-05-08")), - entry("limit", asList("1")) - ); + entry("limit", asList("1"))); assertThat(parseAndValidateMetadata(WithQueryParamsInPath.class, "empty").template()) .hasUrl("/") .hasQueries( - entry("flag", asList(new String[]{null})), + entry("flag", asList(new String[] {null})), entry("Action", asList("GetUser")), - entry("Version", asList("2010-05-08")) - ); + entry("Version", asList("2010-05-08"))); } @Test @@ -124,8 +116,8 @@ public void producesAddsAcceptHeader() throws Exception { assertThat(md.template()) .hasHeaders( - entry("Content-Type", asList("application/json")), - entry("Accept", asList("application/xml"))); + entry("Content-Type", asList("application/json")), + entry("Accept", asList("application/xml"))); } @Test @@ -149,7 +141,8 @@ public void consumesAddsContentTypeHeader() throws Exception { MethodMetadata md = parseAndValidateMetadata(ProducesAndConsumes.class, "consumes"); assertThat(md.template()) - .hasHeaders(entry("Accept", asList("text/html")), entry("Content-Type", asList("application/xml"))); + .hasHeaders(entry("Accept", asList("text/html")), + entry("Content-Type", asList("application/xml"))); } @Test @@ -173,7 +166,8 @@ public void producesAndConsumesOnClassAddsHeader() throws Exception { MethodMetadata md = parseAndValidateMetadata(ProducesAndConsumes.class, "producesAndConsumes"); assertThat(md.template()) - .hasHeaders(entry("Content-Type", asList("application/json")), entry("Accept", asList("text/html"))); + .hasHeaders(entry("Content-Type", asList("application/json")), + entry("Accept", asList("text/html"))); } @Test @@ -197,28 +191,28 @@ public void tooManyBodies() throws Exception { @Test public void emptyPathOnType() throws Exception { assertThat(parseAndValidateMetadata(EmptyPathOnType.class, "base").template()) - .hasUrl(""); + .hasUrl(""); } @Test public void emptyPathOnTypeSpecific() throws Exception { assertThat(parseAndValidateMetadata(EmptyPathOnType.class, "get").template()) - .hasUrl("/specific"); + .hasUrl("/specific"); } @Test public void parsePathMethod() throws Exception { - assertThat(parseAndValidateMetadata(PathOnType.class,"base").template()) + assertThat(parseAndValidateMetadata(PathOnType.class, "base").template()) .hasUrl("/base"); - assertThat(parseAndValidateMetadata(PathOnType.class,"get").template()) + assertThat(parseAndValidateMetadata(PathOnType.class, "get").template()) .hasUrl("/base/specific"); } @Test public void emptyPathOnMethod() throws Exception { - assertThat(parseAndValidateMetadata(PathOnType.class,"emptyPath").template()) - .hasUrl("/base"); + assertThat(parseAndValidateMetadata(PathOnType.class, "emptyPath").template()) + .hasUrl("/base"); } @Test @@ -231,26 +225,26 @@ public void emptyPathParam() throws Exception { @Test public void pathParamWithSpaces() throws Exception { - assertThat(parseAndValidateMetadata( - PathOnType.class, "pathParamWithSpaces", String.class).template()) - .hasUrl("/base/{param}"); + assertThat(parseAndValidateMetadata( + PathOnType.class, "pathParamWithSpaces", String.class).template()) + .hasUrl("/base/{param}"); } @Test public void regexPathOnMethod() throws Exception { - assertThat(parseAndValidateMetadata( - PathOnType.class, "pathParamWithRegex", String.class).template()) - .hasUrl("/base/regex/{param}"); + assertThat(parseAndValidateMetadata( + PathOnType.class, "pathParamWithRegex", String.class).template()) + .hasUrl("/base/regex/{param}"); - assertThat(parseAndValidateMetadata( - PathOnType.class, "pathParamWithMultipleRegex", String.class, String.class).template()) - .hasUrl("/base/regex/{param1}/{param2}"); + assertThat(parseAndValidateMetadata( + PathOnType.class, "pathParamWithMultipleRegex", String.class, String.class).template()) + .hasUrl("/base/regex/{param1}/{param2}"); } @Test public void withPathAndURIParams() throws Exception { MethodMetadata md = parseAndValidateMetadata(WithURIParam.class, - "uriParam", String.class, URI.class, String.class); + "uriParam", String.class, URI.class, String.class); assertThat(md.indexToName()).containsExactly( entry(0, asList("1")), @@ -264,14 +258,14 @@ public void withPathAndURIParams() throws Exception { public void pathAndQueryParams() throws Exception { MethodMetadata md = parseAndValidateMetadata(WithPathAndQueryParams.class, - "recordsByNameAndType", int.class, String.class, String.class); + "recordsByNameAndType", int.class, String.class, String.class); assertThat(md.template()) .hasQueries(entry("name", asList("{name}")), entry("type", asList("{type}"))); assertThat(md.indexToName()).containsExactly(entry(0, asList("domainId")), - entry(1, asList("name")), - entry(2, asList("type"))); + entry(1, asList("name")), + entry(2, asList("type"))); } @Test @@ -285,7 +279,7 @@ public void emptyQueryParam() throws Exception { @Test public void formParamsParseIntoIndexToName() throws Exception { MethodMetadata md = parseAndValidateMetadata(FormParams.class, - "login", String.class, String.class, String.class); + "login", String.class, String.class, String.class); assertThat(md.formParams()) .containsExactly("customer_name", "user_name", "password"); @@ -293,8 +287,7 @@ public void formParamsParseIntoIndexToName() throws Exception { assertThat(md.indexToName()).containsExactly( entry(0, asList("customer_name")), entry(1, asList("user_name")), - entry(2, asList("password")) - ); + entry(2, asList("password"))); } /** @@ -303,7 +296,7 @@ public void formParamsParseIntoIndexToName() throws Exception { @Test public void formParamsDoesNotSetBodyType() throws Exception { MethodMetadata md = parseAndValidateMetadata(FormParams.class, - "login", String.class, String.class, String.class); + "login", String.class, String.class, String.class); assertThat(md.bodyType()).isNull(); } @@ -355,20 +348,21 @@ public void pathsWithSomeOtherSlashesParseCorrectly() throws Exception { @Test public void classWithRootPathParsesCorrectly() throws Exception { - assertThat(parseAndValidateMetadata(ClassRootPath.class, "get").template()) - .hasUrl("/specific"); + assertThat(parseAndValidateMetadata(ClassRootPath.class, "get").template()) + .hasUrl("/specific"); } @Test public void classPathWithTrailingSlashParsesCorrectly() throws Exception { - assertThat(parseAndValidateMetadata(ClassPathWithTrailingSlash.class, "get").template()) - .hasUrl("/base/specific"); + assertThat(parseAndValidateMetadata(ClassPathWithTrailingSlash.class, "get").template()) + .hasUrl("/base/specific"); } @Test public void methodPathWithoutLeadingSlashParsesCorrectly() throws Exception { - assertThat(parseAndValidateMetadata(MethodWithFirstPathThenGetWithoutLeadingSlash.class, "get").template()) - .hasUrl("/base/specific"); + assertThat(parseAndValidateMetadata(MethodWithFirstPathThenGetWithoutLeadingSlash.class, "get") + .template()) + .hasUrl("/base/specific"); } interface Methods { @@ -502,7 +496,8 @@ interface PathOnType { @GET @Path("regex/{param1:[0-9]*}/{ param2 : .+}") - Response pathParamWithMultipleRegex(@PathParam("param1") String param1, @PathParam("param2") String param2); + Response pathParamWithMultipleRegex(@PathParam("param1") String param1, + @PathParam("param2") String param2); } interface WithURIParam { @@ -528,8 +523,9 @@ interface FormParams { @POST void login( - @FormParam("customer_name") String customer, - @FormParam("user_name") String user, @FormParam("password") String password); + @FormParam("customer_name") String customer, + @FormParam("user_name") String user, + @FormParam("password") String password); @GET Response emptyFormParam(@FormParam("") String empty); @@ -570,29 +566,30 @@ interface PathsWithSomeOtherSlashes { @Path("/") interface ClassRootPath { - @GET - @Path("/specific") - Response get(); + @GET + @Path("/specific") + Response get(); } @Path("/base/") interface ClassPathWithTrailingSlash { - @GET - @Path("/specific") - Response get(); + @GET + @Path("/specific") + Response get(); } @Path("/base/") interface MethodWithFirstPathThenGetWithoutLeadingSlash { - @Path("specific") - @GET - Response get(); + @Path("specific") + @GET + Response get(); } - private MethodMetadata parseAndValidateMetadata(Class targetType, String method, + private MethodMetadata parseAndValidateMetadata(Class targetType, + String method, Class... parameterTypes) throws NoSuchMethodException { return contract.parseAndValidateMetadata(targetType, - targetType.getMethod(method, parameterTypes)); + targetType.getMethod(method, parameterTypes)); } } diff --git a/jaxrs/src/test/java/feign/jaxrs/examples/GitHubExample.java b/jaxrs/src/test/java/feign/jaxrs/examples/GitHubExample.java index 5a227190b0..8ca834c192 100644 --- a/jaxrs/src/test/java/feign/jaxrs/examples/GitHubExample.java +++ b/jaxrs/src/test/java/feign/jaxrs/examples/GitHubExample.java @@ -14,11 +14,9 @@ package feign.jaxrs.examples; import java.util.List; - import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; - import feign.Feign; import feign.jaxrs.JAXRSContract; diff --git a/jaxrs2/src/main/java/feign/jaxrs2/JAXRS2Contract.java b/jaxrs2/src/main/java/feign/jaxrs2/JAXRS2Contract.java index 3341d29f9e..4af5f39544 100644 --- a/jaxrs2/src/main/java/feign/jaxrs2/JAXRS2Contract.java +++ b/jaxrs2/src/main/java/feign/jaxrs2/JAXRS2Contract.java @@ -15,9 +15,7 @@ import javax.ws.rs.container.Suspended; import javax.ws.rs.core.Context; - import feign.jaxrs.JAXRSContract; - import java.lang.annotation.Annotation; /** @@ -29,10 +27,12 @@ public final class JAXRS2Contract extends JAXRSContract { protected boolean isUnsupportedHttpParameterAnnotation(Annotation parameterAnnotation) { Class annotationType = parameterAnnotation.annotationType(); - // masc20180327. parameter with unsupported jax-rs annotations should not be passed as body params. - // this will prevent interfaces from becoming unusable entirely due to single (unsupported) endpoints. + // masc20180327. parameter with unsupported jax-rs annotations should not be passed as body + // params. + // this will prevent interfaces from becoming unusable entirely due to single (unsupported) + // endpoints. // https://github.com/OpenFeign/feign/issues/669 return (annotationType == Suspended.class || - annotationType == Context.class); + annotationType == Context.class); } } diff --git a/jaxrs2/src/test/java/feign/jaxrs2/JAXRS2ContractTest.java b/jaxrs2/src/test/java/feign/jaxrs2/JAXRS2ContractTest.java index cbb842409c..473ff72fbc 100644 --- a/jaxrs2/src/test/java/feign/jaxrs2/JAXRS2ContractTest.java +++ b/jaxrs2/src/test/java/feign/jaxrs2/JAXRS2ContractTest.java @@ -17,16 +17,14 @@ import feign.jaxrs.JAXRSContractTest; /** - * Tests interfaces defined per {@link JAXRS2Contract} are interpreted into expected {@link feign - * .RequestTemplate template} instances. + * Tests interfaces defined per {@link JAXRS2Contract} are interpreted into expected + * {@link feign .RequestTemplate template} instances. */ -public class JAXRS2ContractTest extends JAXRSContractTest -{ +public class JAXRS2ContractTest extends JAXRSContractTest { - @Override - protected JAXRSContract createContract() - { - return new JAXRS2Contract(); - } + @Override + protected JAXRSContract createContract() { + return new JAXRS2Contract(); + } } diff --git a/mock/src/main/java/feign/mock/HttpMethod.java b/mock/src/main/java/feign/mock/HttpMethod.java index 03d65c43f8..13c9484a31 100644 --- a/mock/src/main/java/feign/mock/HttpMethod.java +++ b/mock/src/main/java/feign/mock/HttpMethod.java @@ -15,6 +15,6 @@ public enum HttpMethod { - GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH + GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH } diff --git a/mock/src/main/java/feign/mock/MockClient.java b/mock/src/main/java/feign/mock/MockClient.java index 68feec1bd2..cefebbd3d5 100644 --- a/mock/src/main/java/feign/mock/MockClient.java +++ b/mock/src/main/java/feign/mock/MockClient.java @@ -14,7 +14,6 @@ package feign.mock; import static feign.Util.UTF_8; - import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; @@ -26,7 +25,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; - import feign.Client; import feign.Request; import feign.Response; @@ -34,243 +32,248 @@ public class MockClient implements Client { - class RequestResponse { - - private final RequestKey requestKey; - - private final Response.Builder responseBuilder; - - public RequestResponse(RequestKey requestKey, Response.Builder responseBuilder) { - this.requestKey = requestKey; - this.responseBuilder = responseBuilder; - } - - } - - public static final Map> EMPTY_HEADERS = Collections.emptyMap(); - - private final List responses = new ArrayList(); - - private final Map> requests = new HashMap>(); - - private boolean sequential; - - private Iterator responseIterator; - - public MockClient() { - } - - public MockClient(boolean sequential) { - this.sequential = sequential; - } - - @Override - public synchronized Response execute(Request request, Request.Options options) throws IOException { - RequestKey requestKey = RequestKey.create(request); - Response.Builder responseBuilder; - if (sequential) { - responseBuilder = executeSequential(requestKey); - } else { - responseBuilder = executeAny(request, requestKey); - } - - return responseBuilder.request(request).build(); - } - - private Response.Builder executeSequential(RequestKey requestKey) { - Response.Builder responseBuilder; - if (responseIterator == null) { - responseIterator = responses.iterator(); - } - if (!responseIterator.hasNext()) { - throw new VerificationAssertionError("Received excessive request %s", requestKey); - } - - RequestResponse expectedRequestResponse = responseIterator.next(); - if (!expectedRequestResponse.requestKey.equalsExtended(requestKey)) { - throw new VerificationAssertionError("Expected %s, but was %s", expectedRequestResponse.requestKey, - requestKey); - } - - responseBuilder = expectedRequestResponse.responseBuilder; - return responseBuilder; - } - - private Response.Builder executeAny(Request request, RequestKey requestKey) { - Response.Builder responseBuilder; - if (requests.containsKey(requestKey)) { - requests.get(requestKey).add(request); - } else { - requests.put(requestKey, new ArrayList(Arrays.asList(request))); - } - - responseBuilder = getResponseBuilder(request, requestKey); - return responseBuilder; - } - - private Response.Builder getResponseBuilder(Request request, RequestKey requestKey) { - Response.Builder responseBuilder = null; - for (RequestResponse requestResponse : responses) { - if (requestResponse.requestKey.equalsExtended(requestKey)) { - responseBuilder = requestResponse.responseBuilder; - // Don't break here, last one should win to be compatible with - // previous - // releases of this library! - } - } - if (responseBuilder == null) { - responseBuilder = Response.builder().status(HttpURLConnection.HTTP_NOT_FOUND).reason("Not mocker") - .headers(request.headers()); - } - return responseBuilder; - } - - public MockClient ok(HttpMethod method, String url, InputStream responseBody) throws IOException { - return ok(RequestKey.builder(method, url).build(), responseBody); - } - - public MockClient ok(HttpMethod method, String url, String responseBody) { - return ok(RequestKey.builder(method, url).build(), responseBody); - } - - public MockClient ok(HttpMethod method, String url, byte[] responseBody) { - return ok(RequestKey.builder(method, url).build(), responseBody); - } - - public MockClient ok(HttpMethod method, String url) { - return ok(RequestKey.builder(method, url).build()); - } - - public MockClient ok(RequestKey requestKey, InputStream responseBody) throws IOException { - return ok(requestKey, Util.toByteArray(responseBody)); - } - - public MockClient ok(RequestKey requestKey, String responseBody) { - return ok(requestKey, responseBody.getBytes(UTF_8)); - } - - public MockClient ok(RequestKey requestKey, byte[] responseBody) { - return add(requestKey, HttpURLConnection.HTTP_OK, responseBody); - } - - public MockClient ok(RequestKey requestKey) { - return ok(requestKey, (byte[]) null); - } - - public MockClient add(HttpMethod method, String url, int status, InputStream responseBody) throws IOException { - return add(RequestKey.builder(method, url).build(), status, responseBody); - } - - public MockClient add(HttpMethod method, String url, int status, String responseBody) { - return add(RequestKey.builder(method, url).build(), status, responseBody); - } - - public MockClient add(HttpMethod method, String url, int status, byte[] responseBody) { - return add(RequestKey.builder(method, url).build(), status, responseBody); - } - - public MockClient add(HttpMethod method, String url, int status) { - return add(RequestKey.builder(method, url).build(), status); - } - - /** - * @param response - *
      - *
    • the status defaults to 0, not 200!
    • - *
    • the internal feign-code requires the headers to be - * set
    • - *
    - */ - public MockClient add(HttpMethod method, String url, Response.Builder response) { - return add(RequestKey.builder(method, url).build(), response); - } - - public MockClient add(RequestKey requestKey, int status, InputStream responseBody) throws IOException { - return add(requestKey, status, Util.toByteArray(responseBody)); - } - - public MockClient add(RequestKey requestKey, int status, String responseBody) { - return add(requestKey, status, responseBody.getBytes(UTF_8)); - } - - public MockClient add(RequestKey requestKey, int status, byte[] responseBody) { - return add(requestKey, - Response.builder().status(status).reason("Mocked").headers(EMPTY_HEADERS).body(responseBody)); - } - - public MockClient add(RequestKey requestKey, int status) { - return add(requestKey, status, (byte[]) null); - } - - public MockClient add(RequestKey requestKey, Response.Builder response) { - responses.add(new RequestResponse(requestKey, response)); - return this; - } - - public MockClient add(HttpMethod method, String url, Response response) { - return this.add(method, url, response.toBuilder()); - } - - public MockClient noContent(HttpMethod method, String url) { - return add(method, url, HttpURLConnection.HTTP_NO_CONTENT); - } - - public Request verifyOne(HttpMethod method, String url) { - return verifyTimes(method, url, 1).get(0); - } - - public List verifyTimes(final HttpMethod method, final String url, final int times) { - if (times < 0) { - throw new IllegalArgumentException("times must be a non negative number"); - } - - if (times == 0) { - verifyNever(method, url); - return Collections.emptyList(); - } - - RequestKey requestKey = RequestKey.builder(method, url).build(); - if (!requests.containsKey(requestKey)) { - throw new VerificationAssertionError("Wanted: '%s' but never invoked!", requestKey); - } - - List result = requests.get(requestKey); - if (result.size() != times) { - throw new VerificationAssertionError("Wanted: '%s' to be invoked: '%s' times but got: '%s'!", requestKey, - times, result.size()); - } - - return result; - } - - public void verifyNever(HttpMethod method, String url) { - RequestKey requestKey = RequestKey.builder(method, url).build(); - if (requests.containsKey(requestKey)) { - throw new VerificationAssertionError("Do not wanted: '%s' but was invoked!", requestKey); - } - } - - /** - * To be called in an @After method: - * - *
    -	 * @After
    -	 * public void tearDown() {
    -	 *     mockClient.verifyStatus();
    -	 * }
    -	 * 
    - */ - public void verifyStatus() { - if (sequential) { - boolean unopenedIterator = responseIterator == null && !responses.isEmpty(); - if (unopenedIterator || responseIterator.hasNext()) { - throw new VerificationAssertionError("More executions were expected"); - } - } - } - - public void resetRequests() { - requests.clear(); - } + class RequestResponse { + + private final RequestKey requestKey; + + private final Response.Builder responseBuilder; + + public RequestResponse(RequestKey requestKey, Response.Builder responseBuilder) { + this.requestKey = requestKey; + this.responseBuilder = responseBuilder; + } + + } + + public static final Map> EMPTY_HEADERS = Collections.emptyMap(); + + private final List responses = new ArrayList(); + + private final Map> requests = new HashMap>(); + + private boolean sequential; + + private Iterator responseIterator; + + public MockClient() {} + + public MockClient(boolean sequential) { + this.sequential = sequential; + } + + @Override + public synchronized Response execute(Request request, Request.Options options) + throws IOException { + RequestKey requestKey = RequestKey.create(request); + Response.Builder responseBuilder; + if (sequential) { + responseBuilder = executeSequential(requestKey); + } else { + responseBuilder = executeAny(request, requestKey); + } + + return responseBuilder.request(request).build(); + } + + private Response.Builder executeSequential(RequestKey requestKey) { + Response.Builder responseBuilder; + if (responseIterator == null) { + responseIterator = responses.iterator(); + } + if (!responseIterator.hasNext()) { + throw new VerificationAssertionError("Received excessive request %s", requestKey); + } + + RequestResponse expectedRequestResponse = responseIterator.next(); + if (!expectedRequestResponse.requestKey.equalsExtended(requestKey)) { + throw new VerificationAssertionError("Expected %s, but was %s", + expectedRequestResponse.requestKey, + requestKey); + } + + responseBuilder = expectedRequestResponse.responseBuilder; + return responseBuilder; + } + + private Response.Builder executeAny(Request request, RequestKey requestKey) { + Response.Builder responseBuilder; + if (requests.containsKey(requestKey)) { + requests.get(requestKey).add(request); + } else { + requests.put(requestKey, new ArrayList(Arrays.asList(request))); + } + + responseBuilder = getResponseBuilder(request, requestKey); + return responseBuilder; + } + + private Response.Builder getResponseBuilder(Request request, RequestKey requestKey) { + Response.Builder responseBuilder = null; + for (RequestResponse requestResponse : responses) { + if (requestResponse.requestKey.equalsExtended(requestKey)) { + responseBuilder = requestResponse.responseBuilder; + // Don't break here, last one should win to be compatible with + // previous + // releases of this library! + } + } + if (responseBuilder == null) { + responseBuilder = + Response.builder().status(HttpURLConnection.HTTP_NOT_FOUND).reason("Not mocker") + .headers(request.headers()); + } + return responseBuilder; + } + + public MockClient ok(HttpMethod method, String url, InputStream responseBody) throws IOException { + return ok(RequestKey.builder(method, url).build(), responseBody); + } + + public MockClient ok(HttpMethod method, String url, String responseBody) { + return ok(RequestKey.builder(method, url).build(), responseBody); + } + + public MockClient ok(HttpMethod method, String url, byte[] responseBody) { + return ok(RequestKey.builder(method, url).build(), responseBody); + } + + public MockClient ok(HttpMethod method, String url) { + return ok(RequestKey.builder(method, url).build()); + } + + public MockClient ok(RequestKey requestKey, InputStream responseBody) throws IOException { + return ok(requestKey, Util.toByteArray(responseBody)); + } + + public MockClient ok(RequestKey requestKey, String responseBody) { + return ok(requestKey, responseBody.getBytes(UTF_8)); + } + + public MockClient ok(RequestKey requestKey, byte[] responseBody) { + return add(requestKey, HttpURLConnection.HTTP_OK, responseBody); + } + + public MockClient ok(RequestKey requestKey) { + return ok(requestKey, (byte[]) null); + } + + public MockClient add(HttpMethod method, String url, int status, InputStream responseBody) + throws IOException { + return add(RequestKey.builder(method, url).build(), status, responseBody); + } + + public MockClient add(HttpMethod method, String url, int status, String responseBody) { + return add(RequestKey.builder(method, url).build(), status, responseBody); + } + + public MockClient add(HttpMethod method, String url, int status, byte[] responseBody) { + return add(RequestKey.builder(method, url).build(), status, responseBody); + } + + public MockClient add(HttpMethod method, String url, int status) { + return add(RequestKey.builder(method, url).build(), status); + } + + /** + * @param response + *
      + *
    • the status defaults to 0, not 200!
    • + *
    • the internal feign-code requires the headers to be set
    • + *
    + */ + public MockClient add(HttpMethod method, String url, Response.Builder response) { + return add(RequestKey.builder(method, url).build(), response); + } + + public MockClient add(RequestKey requestKey, int status, InputStream responseBody) + throws IOException { + return add(requestKey, status, Util.toByteArray(responseBody)); + } + + public MockClient add(RequestKey requestKey, int status, String responseBody) { + return add(requestKey, status, responseBody.getBytes(UTF_8)); + } + + public MockClient add(RequestKey requestKey, int status, byte[] responseBody) { + return add(requestKey, + Response.builder().status(status).reason("Mocked").headers(EMPTY_HEADERS) + .body(responseBody)); + } + + public MockClient add(RequestKey requestKey, int status) { + return add(requestKey, status, (byte[]) null); + } + + public MockClient add(RequestKey requestKey, Response.Builder response) { + responses.add(new RequestResponse(requestKey, response)); + return this; + } + + public MockClient add(HttpMethod method, String url, Response response) { + return this.add(method, url, response.toBuilder()); + } + + public MockClient noContent(HttpMethod method, String url) { + return add(method, url, HttpURLConnection.HTTP_NO_CONTENT); + } + + public Request verifyOne(HttpMethod method, String url) { + return verifyTimes(method, url, 1).get(0); + } + + public List verifyTimes(final HttpMethod method, final String url, final int times) { + if (times < 0) { + throw new IllegalArgumentException("times must be a non negative number"); + } + + if (times == 0) { + verifyNever(method, url); + return Collections.emptyList(); + } + + RequestKey requestKey = RequestKey.builder(method, url).build(); + if (!requests.containsKey(requestKey)) { + throw new VerificationAssertionError("Wanted: '%s' but never invoked!", requestKey); + } + + List result = requests.get(requestKey); + if (result.size() != times) { + throw new VerificationAssertionError("Wanted: '%s' to be invoked: '%s' times but got: '%s'!", + requestKey, + times, result.size()); + } + + return result; + } + + public void verifyNever(HttpMethod method, String url) { + RequestKey requestKey = RequestKey.builder(method, url).build(); + if (requests.containsKey(requestKey)) { + throw new VerificationAssertionError("Do not wanted: '%s' but was invoked!", requestKey); + } + } + + /** + * To be called in an @After method: + * + *
    +   * @After
    +   * public void tearDown() {
    +   *   mockClient.verifyStatus();
    +   * }
    +   * 
    + */ + public void verifyStatus() { + if (sequential) { + boolean unopenedIterator = responseIterator == null && !responses.isEmpty(); + if (unopenedIterator || responseIterator.hasNext()) { + throw new VerificationAssertionError("More executions were expected"); + } + } + } + + public void resetRequests() { + requests.clear(); + } } diff --git a/mock/src/main/java/feign/mock/MockTarget.java b/mock/src/main/java/feign/mock/MockTarget.java index 391e3964a2..4aa6e27cdd 100644 --- a/mock/src/main/java/feign/mock/MockTarget.java +++ b/mock/src/main/java/feign/mock/MockTarget.java @@ -19,31 +19,31 @@ public class MockTarget implements Target { - private final Class type; - - public MockTarget(Class type) { - this.type = type; - } - - @Override - public Class type() { - return type; - } - - @Override - public String name() { - return type.getSimpleName(); - } - - @Override - public String url() { - return ""; - } - - @Override - public Request apply(RequestTemplate input) { - input.insert(0, url()); - return input.request(); - } + private final Class type; + + public MockTarget(Class type) { + this.type = type; + } + + @Override + public Class type() { + return type; + } + + @Override + public String name() { + return type.getSimpleName(); + } + + @Override + public String url() { + return ""; + } + + @Override + public Request apply(RequestTemplate input) { + input.insert(0, url()); + return input.request(); + } } diff --git a/mock/src/main/java/feign/mock/RequestKey.java b/mock/src/main/java/feign/mock/RequestKey.java index 4ce92764e1..3414b5122a 100644 --- a/mock/src/main/java/feign/mock/RequestKey.java +++ b/mock/src/main/java/feign/mock/RequestKey.java @@ -14,7 +14,6 @@ package feign.mock; import static feign.Util.UTF_8; - import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.nio.charset.Charset; @@ -26,148 +25,156 @@ public class RequestKey { - public static class Builder { - - private final HttpMethod method; - - private final String url; - - private Map> headers; - - private Charset charset; - - private byte[] body; - - private Builder(HttpMethod method, String url) { - this.method = method; - this.url = url; - } - - public Builder headers(Map> headers) { - this.headers = headers; - return this; - } - - public Builder charset(Charset charset) { - this.charset = charset; - return this; - } - - public Builder body(String body) { - return body(body.getBytes(UTF_8)); - } - - public Builder body(byte[] body) { - this.body = body; - return this; - } - - public RequestKey build() { - return new RequestKey(this); - } - - } - - public static Builder builder(HttpMethod method, String url) { - return new Builder(method, url); - } - - public static RequestKey create(Request request) { - return new RequestKey(request); - } - - private static String buildUrl(Request request) { - try { - return URLDecoder.decode(request.url(), Util.UTF_8.name()); - } catch (final UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } + public static class Builder { private final HttpMethod method; private final String url; - private final Map> headers; + private Map> headers; - private final Charset charset; + private Charset charset; - private final byte[] body; + private byte[] body; - private RequestKey(Builder builder) { - this.method = builder.method; - this.url = builder.url; - this.headers = builder.headers; - this.charset = builder.charset; - this.body = builder.body; + private Builder(HttpMethod method, String url) { + this.method = method; + this.url = url; } - private RequestKey(Request request) { - this.method = HttpMethod.valueOf(request.method()); - this.url = buildUrl(request); - this.headers = request.headers(); - this.charset = request.charset(); - this.body = request.body(); + public Builder headers(Map> headers) { + this.headers = headers; + return this; } - public HttpMethod getMethod() { - return method; + public Builder charset(Charset charset) { + this.charset = charset; + return this; } - public String getUrl() { - return url; + public Builder body(String body) { + return body(body.getBytes(UTF_8)); } - public Map> getHeaders() { - return headers; + public Builder body(byte[] body) { + this.body = body; + return this; } - public Charset getCharset() { - return charset; + public RequestKey build() { + return new RequestKey(this); } - public byte[] getBody() { - return body; - } + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((method == null) ? 0 : method.hashCode()); - result = prime * result + ((url == null) ? 0 : url.hashCode()); - return result; - } + public static Builder builder(HttpMethod method, String url) { + return new Builder(method, url); + } - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - final RequestKey other = (RequestKey) obj; - if (method != other.method) return false; - if (url == null) { - if (other.url != null) return false; - } else if (!url.equals(other.url)) return false; - return true; - } + public static RequestKey create(Request request) { + return new RequestKey(request); + } - public boolean equalsExtended(Object obj) { - if (equals(obj)) { - RequestKey other = (RequestKey) obj; - boolean headersEqual = other.headers == null || headers == null || headers.equals(other.headers); - boolean charsetEqual = other.charset == null || charset == null || charset.equals(other.charset); - boolean bodyEqual = other.body == null || body == null || Arrays.equals(other.body, body); - return headersEqual && charsetEqual && bodyEqual; - } - return false; + private static String buildUrl(Request request) { + try { + return URLDecoder.decode(request.url(), Util.UTF_8.name()); + } catch (final UnsupportedEncodingException e) { + throw new RuntimeException(e); } - - @Override - public String toString() { - return String.format("Request [%s %s: %s headers and %s]", method, url, - headers == null ? "without" : "with " + headers.size(), - charset == null ? "no charset" : "charset " + charset); + } + + private final HttpMethod method; + + private final String url; + + private final Map> headers; + + private final Charset charset; + + private final byte[] body; + + private RequestKey(Builder builder) { + this.method = builder.method; + this.url = builder.url; + this.headers = builder.headers; + this.charset = builder.charset; + this.body = builder.body; + } + + private RequestKey(Request request) { + this.method = HttpMethod.valueOf(request.method()); + this.url = buildUrl(request); + this.headers = request.headers(); + this.charset = request.charset(); + this.body = request.body(); + } + + public HttpMethod getMethod() { + return method; + } + + public String getUrl() { + return url; + } + + public Map> getHeaders() { + return headers; + } + + public Charset getCharset() { + return charset; + } + + public byte[] getBody() { + return body; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((method == null) ? 0 : method.hashCode()); + result = prime * result + ((url == null) ? 0 : url.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final RequestKey other = (RequestKey) obj; + if (method != other.method) + return false; + if (url == null) { + if (other.url != null) + return false; + } else if (!url.equals(other.url)) + return false; + return true; + } + + public boolean equalsExtended(Object obj) { + if (equals(obj)) { + RequestKey other = (RequestKey) obj; + boolean headersEqual = + other.headers == null || headers == null || headers.equals(other.headers); + boolean charsetEqual = + other.charset == null || charset == null || charset.equals(other.charset); + boolean bodyEqual = other.body == null || body == null || Arrays.equals(other.body, body); + return headersEqual && charsetEqual && bodyEqual; } + return false; + } + + @Override + public String toString() { + return String.format("Request [%s %s: %s headers and %s]", method, url, + headers == null ? "without" : "with " + headers.size(), + charset == null ? "no charset" : "charset " + charset); + } } diff --git a/mock/src/main/java/feign/mock/VerificationAssertionError.java b/mock/src/main/java/feign/mock/VerificationAssertionError.java index 421611f67e..146fb39a0a 100644 --- a/mock/src/main/java/feign/mock/VerificationAssertionError.java +++ b/mock/src/main/java/feign/mock/VerificationAssertionError.java @@ -15,10 +15,10 @@ public class VerificationAssertionError extends AssertionError { - private static final long serialVersionUID = -3302777023656958993L; + private static final long serialVersionUID = -3302777023656958993L; - public VerificationAssertionError(String message, Object... arguments) { - super(String.format(message, arguments)); - } + public VerificationAssertionError(String message, Object... arguments) { + super(String.format(message, arguments)); + } } diff --git a/mock/src/test/java/feign/mock/MockClientSequentialTest.java b/mock/src/test/java/feign/mock/MockClientSequentialTest.java index acd310af7f..f8c0b243b4 100644 --- a/mock/src/test/java/feign/mock/MockClientSequentialTest.java +++ b/mock/src/test/java/feign/mock/MockClientSequentialTest.java @@ -20,18 +20,14 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.fail; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Type; import java.util.List; - import javax.net.ssl.HttpsURLConnection; - import org.junit.Before; import org.junit.Test; - import feign.Body; import feign.Feign; import feign.FeignException; @@ -44,125 +40,130 @@ public class MockClientSequentialTest { - interface GitHub { - - @RequestLine("GET /repos/{owner}/{repo}/contributors") - List contributors(@Param("owner") String owner, @Param("repo") String repo); - - @RequestLine("GET /repos/{owner}/{repo}/contributors?client_id={client_id}") - List contributors(@Param("client_id") String clientId, @Param("owner") String owner, - @Param("repo") String repo); - - @RequestLine("PATCH /repos/{owner}/{repo}/contributors") - List patchContributors(@Param("owner") String owner, @Param("repo") String repo); - - @RequestLine("POST /repos/{owner}/{repo}/contributors") - @Body("%7B\"login\":\"{login}\",\"type\":\"{type}\"%7D") - Contributor create(@Param("owner") String owner, @Param("repo") String repo, @Param("login") String login, - @Param("type") String type); - - } - - static class Contributor { - - String login; - - int contributions; - - } - - class AssertionDecoder implements Decoder { - - private final Decoder delegate; - - public AssertionDecoder(Decoder delegate) { - this.delegate = delegate; - } - - @Override - public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException { - assertThat(response.request(), notNullValue()); - - return delegate.decode(response, type); - } - - } - - private GitHub githubSequential; - - private MockClient mockClientSequential; - - @Before - public void setup() throws IOException { - try (InputStream input = getClass().getResourceAsStream("/fixtures/contributors.json")) { - byte[] data = toByteArray(input); - - mockClientSequential = new MockClient(true); - githubSequential = Feign.builder().decoder(new AssertionDecoder(new GsonDecoder())) - .client(mockClientSequential - .add(HttpMethod.GET, "/repos/netflix/feign/contributors", HttpsURLConnection.HTTP_OK, data) - .add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=55", - HttpsURLConnection.HTTP_NOT_FOUND) - .add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=7 7", - HttpsURLConnection.HTTP_INTERNAL_ERROR, new ByteArrayInputStream(data)) - .add(HttpMethod.GET, "/repos/netflix/feign/contributors", - Response.builder().status(HttpsURLConnection.HTTP_OK) - .headers(MockClient.EMPTY_HEADERS).body(data))) - .target(new MockTarget<>(GitHub.class)); - } - } - - @Test - public void sequentialRequests() throws Exception { - githubSequential.contributors("netflix", "feign"); - try { - githubSequential.contributors("55", "netflix", "feign"); - fail(); - } catch (FeignException e) { - assertThat(e.status(), equalTo(HttpsURLConnection.HTTP_NOT_FOUND)); - } - try { - githubSequential.contributors("7 7", "netflix", "feign"); - fail(); - } catch (FeignException e) { - assertThat(e.status(), equalTo(HttpsURLConnection.HTTP_INTERNAL_ERROR)); - } - githubSequential.contributors("netflix", "feign"); - - mockClientSequential.verifyStatus(); - } - - @Test - public void sequentialRequestsCalledTooLess() throws Exception { - githubSequential.contributors("netflix", "feign"); - try { - mockClientSequential.verifyStatus(); - fail(); - } catch (VerificationAssertionError e) { - assertThat(e.getMessage(), startsWith("More executions")); - } - } - - @Test - public void sequentialRequestsCalledTooMany() throws Exception { - sequentialRequests(); - - try { - githubSequential.contributors("netflix", "feign"); - fail(); - } catch (VerificationAssertionError e) { - assertThat(e.getMessage(), containsString("excessive")); - } - } - - @Test - public void sequentialRequestsInWrongOrder() throws Exception { - try { - githubSequential.contributors("7 7", "netflix", "feign"); - fail(); - } catch (VerificationAssertionError e) { - assertThat(e.getMessage(), startsWith("Expected Request [")); - } - } + interface GitHub { + + @RequestLine("GET /repos/{owner}/{repo}/contributors") + List contributors(@Param("owner") String owner, @Param("repo") String repo); + + @RequestLine("GET /repos/{owner}/{repo}/contributors?client_id={client_id}") + List contributors(@Param("client_id") String clientId, + @Param("owner") String owner, + @Param("repo") String repo); + + @RequestLine("PATCH /repos/{owner}/{repo}/contributors") + List patchContributors(@Param("owner") String owner, @Param("repo") String repo); + + @RequestLine("POST /repos/{owner}/{repo}/contributors") + @Body("%7B\"login\":\"{login}\",\"type\":\"{type}\"%7D") + Contributor create(@Param("owner") String owner, + @Param("repo") String repo, + @Param("login") String login, + @Param("type") String type); + + } + + static class Contributor { + + String login; + + int contributions; + + } + + class AssertionDecoder implements Decoder { + + private final Decoder delegate; + + public AssertionDecoder(Decoder delegate) { + this.delegate = delegate; + } + + @Override + public Object decode(Response response, Type type) + throws IOException, DecodeException, FeignException { + assertThat(response.request(), notNullValue()); + + return delegate.decode(response, type); + } + + } + + private GitHub githubSequential; + + private MockClient mockClientSequential; + + @Before + public void setup() throws IOException { + try (InputStream input = getClass().getResourceAsStream("/fixtures/contributors.json")) { + byte[] data = toByteArray(input); + + mockClientSequential = new MockClient(true); + githubSequential = Feign.builder().decoder(new AssertionDecoder(new GsonDecoder())) + .client(mockClientSequential + .add(HttpMethod.GET, "/repos/netflix/feign/contributors", HttpsURLConnection.HTTP_OK, + data) + .add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=55", + HttpsURLConnection.HTTP_NOT_FOUND) + .add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=7 7", + HttpsURLConnection.HTTP_INTERNAL_ERROR, new ByteArrayInputStream(data)) + .add(HttpMethod.GET, "/repos/netflix/feign/contributors", + Response.builder().status(HttpsURLConnection.HTTP_OK) + .headers(MockClient.EMPTY_HEADERS).body(data))) + .target(new MockTarget<>(GitHub.class)); + } + } + + @Test + public void sequentialRequests() throws Exception { + githubSequential.contributors("netflix", "feign"); + try { + githubSequential.contributors("55", "netflix", "feign"); + fail(); + } catch (FeignException e) { + assertThat(e.status(), equalTo(HttpsURLConnection.HTTP_NOT_FOUND)); + } + try { + githubSequential.contributors("7 7", "netflix", "feign"); + fail(); + } catch (FeignException e) { + assertThat(e.status(), equalTo(HttpsURLConnection.HTTP_INTERNAL_ERROR)); + } + githubSequential.contributors("netflix", "feign"); + + mockClientSequential.verifyStatus(); + } + + @Test + public void sequentialRequestsCalledTooLess() throws Exception { + githubSequential.contributors("netflix", "feign"); + try { + mockClientSequential.verifyStatus(); + fail(); + } catch (VerificationAssertionError e) { + assertThat(e.getMessage(), startsWith("More executions")); + } + } + + @Test + public void sequentialRequestsCalledTooMany() throws Exception { + sequentialRequests(); + + try { + githubSequential.contributors("netflix", "feign"); + fail(); + } catch (VerificationAssertionError e) { + assertThat(e.getMessage(), containsString("excessive")); + } + } + + @Test + public void sequentialRequestsInWrongOrder() throws Exception { + try { + githubSequential.contributors("7 7", "netflix", "feign"); + fail(); + } catch (VerificationAssertionError e) { + assertThat(e.getMessage(), startsWith("Expected Request [")); + } + } } diff --git a/mock/src/test/java/feign/mock/MockClientTest.java b/mock/src/test/java/feign/mock/MockClientTest.java index 27eb949b24..511df69467 100644 --- a/mock/src/test/java/feign/mock/MockClientTest.java +++ b/mock/src/test/java/feign/mock/MockClientTest.java @@ -20,19 +20,15 @@ import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.fail; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Type; import java.util.List; - import javax.net.ssl.HttpsURLConnection; - import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; - import feign.Body; import feign.Feign; import feign.FeignException; @@ -46,216 +42,224 @@ public class MockClientTest { - interface GitHub { - - @RequestLine("GET /repos/{owner}/{repo}/contributors") - List contributors(@Param("owner") String owner, @Param("repo") String repo); - - @RequestLine("GET /repos/{owner}/{repo}/contributors?client_id={client_id}") - List contributors(@Param("client_id") String clientId, @Param("owner") String owner, - @Param("repo") String repo); - - @RequestLine("PATCH /repos/{owner}/{repo}/contributors") - List patchContributors(@Param("owner") String owner, @Param("repo") String repo); - - @RequestLine("POST /repos/{owner}/{repo}/contributors") - @Body("%7B\"login\":\"{login}\",\"type\":\"{type}\"%7D") - Contributor create(@Param("owner") String owner, @Param("repo") String repo, @Param("login") String login, - @Param("type") String type); - - } - - static class Contributor { - - String login; - - int contributions; - - } - - class AssertionDecoder implements Decoder { - - private final Decoder delegate; - - public AssertionDecoder(Decoder delegate) { - this.delegate = delegate; - } - - @Override - public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException { - assertThat(response.request(), notNullValue()); - - return delegate.decode(response, type); - } - - } - - private GitHub github; - - private MockClient mockClient; - - @Before - public void setup() throws IOException { - try (InputStream input = getClass().getResourceAsStream("/fixtures/contributors.json")) { - byte[] data = toByteArray(input); - mockClient = new MockClient(); - github = Feign.builder().decoder(new AssertionDecoder(new GsonDecoder())) - .client(mockClient.ok(HttpMethod.GET, "/repos/netflix/feign/contributors", data) - .ok(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=55") - .ok(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=7 7", - new ByteArrayInputStream(data)) - .ok(HttpMethod.POST, "/repos/netflix/feign/contributors", - "{\"login\":\"velo\",\"contributions\":0}") - .noContent(HttpMethod.PATCH, "/repos/velo/feign-mock/contributors") - .add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=1234567890", - HttpsURLConnection.HTTP_NOT_FOUND) - .add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=123456789", - HttpsURLConnection.HTTP_INTERNAL_ERROR, new ByteArrayInputStream(data)) - .add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=123456789", - HttpsURLConnection.HTTP_INTERNAL_ERROR, "") - .add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=123456789", - HttpsURLConnection.HTTP_INTERNAL_ERROR, data)) - .target(new MockTarget<>(GitHub.class)); - } - } - - @Test - public void hitMock() { - List contributors = github.contributors("netflix", "feign"); - assertThat(contributors, hasSize(30)); - mockClient.verifyStatus(); - } - - @Test - public void missMock() { - try { - github.contributors("velo", "feign-mock"); - fail(); - } catch (FeignException e) { - assertThat(e.getMessage(), Matchers.containsString("404")); - } - } - - @Test - public void missHttpMethod() { - try { - github.patchContributors("netflix", "feign"); - fail(); - } catch (FeignException e) { - assertThat(e.getMessage(), Matchers.containsString("404")); - } - } - - @Test - public void paramsEncoding() { - List contributors = github.contributors("7 7", "netflix", "feign"); - assertThat(contributors, hasSize(30)); - mockClient.verifyStatus(); - } - - @Test - public void verifyInvocation() { - Contributor contribution = github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); - // making sure it received a proper response - assertThat(contribution, notNullValue()); - assertThat(contribution.login, equalTo("velo")); - assertThat(contribution.contributions, equalTo(0)); - - List results = mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 1); - assertThat(results, hasSize(1)); - - byte[] body = mockClient.verifyOne(HttpMethod.POST, "/repos/netflix/feign/contributors").body(); - assertThat(body, notNullValue()); - - String message = new String(body); - assertThat(message, containsString("velo_at_github")); - assertThat(message, containsString("preposterous hacker")); - - mockClient.verifyStatus(); - } - - @Test - public void verifyNone() { - github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); - mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 1); - - try { - mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 0); - fail(); - } catch (VerificationAssertionError e) { - assertThat(e.getMessage(), containsString("Do not wanted")); - assertThat(e.getMessage(), containsString("POST")); - assertThat(e.getMessage(), containsString("/repos/netflix/feign/contributors")); - } - - try { - mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 3); - fail(); - } catch (VerificationAssertionError e) { - assertThat(e.getMessage(), containsString("Wanted")); - assertThat(e.getMessage(), containsString("POST")); - assertThat(e.getMessage(), containsString("/repos/netflix/feign/contributors")); - assertThat(e.getMessage(), containsString("'3'")); - assertThat(e.getMessage(), containsString("'1'")); - } - } - - @Test - public void verifyNotInvoked() { - mockClient.verifyNever(HttpMethod.POST, "/repos/netflix/feign/contributors"); - List results = mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 0); - assertThat(results, hasSize(0)); - try { - mockClient.verifyOne(HttpMethod.POST, "/repos/netflix/feign/contributors"); - fail(); - } catch (VerificationAssertionError e) { - assertThat(e.getMessage(), containsString("Wanted")); - assertThat(e.getMessage(), containsString("POST")); - assertThat(e.getMessage(), containsString("/repos/netflix/feign/contributors")); - assertThat(e.getMessage(), containsString("never invoked")); - } - } - - @Test - public void verifyNegative() { - try { - mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", -1); - fail(); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage(), containsString("non negative")); - } - } - - @Test - public void verifyMultipleRequests() { - mockClient.verifyNever(HttpMethod.POST, "/repos/netflix/feign/contributors"); - - github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); - Request result = mockClient.verifyOne(HttpMethod.POST, "/repos/netflix/feign/contributors"); - assertThat(result, notNullValue()); - - github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); - List results = mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 2); - assertThat(results, hasSize(2)); - - github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); - results = mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 3); - assertThat(results, hasSize(3)); - - mockClient.verifyStatus(); - } - - @Test - public void resetRequests() { - mockClient.verifyNever(HttpMethod.POST, "/repos/netflix/feign/contributors"); - - github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); - Request result = mockClient.verifyOne(HttpMethod.POST, "/repos/netflix/feign/contributors"); - assertThat(result, notNullValue()); - - mockClient.resetRequests(); - - mockClient.verifyNever(HttpMethod.POST, "/repos/netflix/feign/contributors"); - } + interface GitHub { + + @RequestLine("GET /repos/{owner}/{repo}/contributors") + List contributors(@Param("owner") String owner, @Param("repo") String repo); + + @RequestLine("GET /repos/{owner}/{repo}/contributors?client_id={client_id}") + List contributors(@Param("client_id") String clientId, + @Param("owner") String owner, + @Param("repo") String repo); + + @RequestLine("PATCH /repos/{owner}/{repo}/contributors") + List patchContributors(@Param("owner") String owner, @Param("repo") String repo); + + @RequestLine("POST /repos/{owner}/{repo}/contributors") + @Body("%7B\"login\":\"{login}\",\"type\":\"{type}\"%7D") + Contributor create(@Param("owner") String owner, + @Param("repo") String repo, + @Param("login") String login, + @Param("type") String type); + + } + + static class Contributor { + + String login; + + int contributions; + + } + + class AssertionDecoder implements Decoder { + + private final Decoder delegate; + + public AssertionDecoder(Decoder delegate) { + this.delegate = delegate; + } + + @Override + public Object decode(Response response, Type type) + throws IOException, DecodeException, FeignException { + assertThat(response.request(), notNullValue()); + + return delegate.decode(response, type); + } + + } + + private GitHub github; + + private MockClient mockClient; + + @Before + public void setup() throws IOException { + try (InputStream input = getClass().getResourceAsStream("/fixtures/contributors.json")) { + byte[] data = toByteArray(input); + mockClient = new MockClient(); + github = Feign.builder().decoder(new AssertionDecoder(new GsonDecoder())) + .client(mockClient.ok(HttpMethod.GET, "/repos/netflix/feign/contributors", data) + .ok(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=55") + .ok(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=7 7", + new ByteArrayInputStream(data)) + .ok(HttpMethod.POST, "/repos/netflix/feign/contributors", + "{\"login\":\"velo\",\"contributions\":0}") + .noContent(HttpMethod.PATCH, "/repos/velo/feign-mock/contributors") + .add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=1234567890", + HttpsURLConnection.HTTP_NOT_FOUND) + .add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=123456789", + HttpsURLConnection.HTTP_INTERNAL_ERROR, new ByteArrayInputStream(data)) + .add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=123456789", + HttpsURLConnection.HTTP_INTERNAL_ERROR, "") + .add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=123456789", + HttpsURLConnection.HTTP_INTERNAL_ERROR, data)) + .target(new MockTarget<>(GitHub.class)); + } + } + + @Test + public void hitMock() { + List contributors = github.contributors("netflix", "feign"); + assertThat(contributors, hasSize(30)); + mockClient.verifyStatus(); + } + + @Test + public void missMock() { + try { + github.contributors("velo", "feign-mock"); + fail(); + } catch (FeignException e) { + assertThat(e.getMessage(), Matchers.containsString("404")); + } + } + + @Test + public void missHttpMethod() { + try { + github.patchContributors("netflix", "feign"); + fail(); + } catch (FeignException e) { + assertThat(e.getMessage(), Matchers.containsString("404")); + } + } + + @Test + public void paramsEncoding() { + List contributors = github.contributors("7 7", "netflix", "feign"); + assertThat(contributors, hasSize(30)); + mockClient.verifyStatus(); + } + + @Test + public void verifyInvocation() { + Contributor contribution = + github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); + // making sure it received a proper response + assertThat(contribution, notNullValue()); + assertThat(contribution.login, equalTo("velo")); + assertThat(contribution.contributions, equalTo(0)); + + List results = + mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 1); + assertThat(results, hasSize(1)); + + byte[] body = mockClient.verifyOne(HttpMethod.POST, "/repos/netflix/feign/contributors").body(); + assertThat(body, notNullValue()); + + String message = new String(body); + assertThat(message, containsString("velo_at_github")); + assertThat(message, containsString("preposterous hacker")); + + mockClient.verifyStatus(); + } + + @Test + public void verifyNone() { + github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); + mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 1); + + try { + mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 0); + fail(); + } catch (VerificationAssertionError e) { + assertThat(e.getMessage(), containsString("Do not wanted")); + assertThat(e.getMessage(), containsString("POST")); + assertThat(e.getMessage(), containsString("/repos/netflix/feign/contributors")); + } + + try { + mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 3); + fail(); + } catch (VerificationAssertionError e) { + assertThat(e.getMessage(), containsString("Wanted")); + assertThat(e.getMessage(), containsString("POST")); + assertThat(e.getMessage(), containsString("/repos/netflix/feign/contributors")); + assertThat(e.getMessage(), containsString("'3'")); + assertThat(e.getMessage(), containsString("'1'")); + } + } + + @Test + public void verifyNotInvoked() { + mockClient.verifyNever(HttpMethod.POST, "/repos/netflix/feign/contributors"); + List results = + mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 0); + assertThat(results, hasSize(0)); + try { + mockClient.verifyOne(HttpMethod.POST, "/repos/netflix/feign/contributors"); + fail(); + } catch (VerificationAssertionError e) { + assertThat(e.getMessage(), containsString("Wanted")); + assertThat(e.getMessage(), containsString("POST")); + assertThat(e.getMessage(), containsString("/repos/netflix/feign/contributors")); + assertThat(e.getMessage(), containsString("never invoked")); + } + } + + @Test + public void verifyNegative() { + try { + mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", -1); + fail(); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), containsString("non negative")); + } + } + + @Test + public void verifyMultipleRequests() { + mockClient.verifyNever(HttpMethod.POST, "/repos/netflix/feign/contributors"); + + github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); + Request result = mockClient.verifyOne(HttpMethod.POST, "/repos/netflix/feign/contributors"); + assertThat(result, notNullValue()); + + github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); + List results = + mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 2); + assertThat(results, hasSize(2)); + + github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); + results = mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 3); + assertThat(results, hasSize(3)); + + mockClient.verifyStatus(); + } + + @Test + public void resetRequests() { + mockClient.verifyNever(HttpMethod.POST, "/repos/netflix/feign/contributors"); + + github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); + Request result = mockClient.verifyOne(HttpMethod.POST, "/repos/netflix/feign/contributors"); + assertThat(result, notNullValue()); + + mockClient.resetRequests(); + + mockClient.verifyNever(HttpMethod.POST, "/repos/netflix/feign/contributors"); + } } diff --git a/mock/src/test/java/feign/mock/MockTargetTest.java b/mock/src/test/java/feign/mock/MockTargetTest.java index e1f39dda48..72dfe0869a 100644 --- a/mock/src/test/java/feign/mock/MockTargetTest.java +++ b/mock/src/test/java/feign/mock/MockTargetTest.java @@ -15,22 +15,21 @@ import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.*; - import org.junit.Before; import org.junit.Test; public class MockTargetTest { - private MockTarget target; + private MockTarget target; - @Before - public void setup() { - target = new MockTarget<>(MockTargetTest.class); - } + @Before + public void setup() { + target = new MockTarget<>(MockTargetTest.class); + } - @Test - public void test() { - assertThat(target.name(), equalTo("MockTargetTest")); - } + @Test + public void test() { + assertThat(target.name(), equalTo("MockTargetTest")); + } } diff --git a/mock/src/test/java/feign/mock/RequestKeyTest.java b/mock/src/test/java/feign/mock/RequestKeyTest.java index a9dadff1f6..00ea7e7edf 100644 --- a/mock/src/test/java/feign/mock/RequestKeyTest.java +++ b/mock/src/test/java/feign/mock/RequestKeyTest.java @@ -20,140 +20,142 @@ import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertThat; - import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Map; - import org.junit.Before; import org.junit.Test; - import feign.Request; public class RequestKeyTest { - private RequestKey requestKey; - - @Before - public void setUp() { - Map> map = new HashMap<>(); - map.put("my-header", Arrays.asList("val")); - requestKey = RequestKey.builder(HttpMethod.GET, "a").headers(map).charset(StandardCharsets.UTF_16) - .body("content").build(); - } - - @Test - public void builder() throws Exception { - assertThat(requestKey.getMethod(), equalTo(HttpMethod.GET)); - assertThat(requestKey.getUrl(), equalTo("a")); - assertThat(requestKey.getHeaders().entrySet(), hasSize(1)); - assertThat(requestKey.getHeaders().get("my-header"), equalTo((Collection) Arrays.asList("val"))); - assertThat(requestKey.getCharset(), equalTo(StandardCharsets.UTF_16)); - } - - @Test - public void create() throws Exception { - Map> map = new HashMap<>(); - map.put("my-header", Arrays.asList("val")); - Request request = Request.create("GET", "a", map, "content".getBytes(StandardCharsets.UTF_8), - StandardCharsets.UTF_16); - requestKey = RequestKey.create(request); - - assertThat(requestKey.getMethod(), equalTo(HttpMethod.GET)); - assertThat(requestKey.getUrl(), equalTo("a")); - assertThat(requestKey.getHeaders().entrySet(), hasSize(1)); - assertThat(requestKey.getHeaders().get("my-header"), equalTo((Collection) Arrays.asList("val"))); - assertThat(requestKey.getCharset(), equalTo(StandardCharsets.UTF_16)); - assertThat(requestKey.getBody(), equalTo("content".getBytes(StandardCharsets.UTF_8))); - } - - @Test - public void checkHashes() { - RequestKey requestKey1 = RequestKey.builder(HttpMethod.GET, "a").build(); - RequestKey requestKey2 = RequestKey.builder(HttpMethod.GET, "b").build(); - - assertThat(requestKey1.hashCode(), not(equalTo(requestKey2.hashCode()))); - assertThat(requestKey1, not(equalTo(requestKey2))); - } - - @Test - public void equalObject() { - assertThat(requestKey, not(equalTo(new Object()))); - } - - @Test - public void equalNull() { - assertThat(requestKey, not(equalTo(null))); - } - - @Test - public void equalPost() { - RequestKey requestKey1 = RequestKey.builder(HttpMethod.GET, "a").build(); - RequestKey requestKey2 = RequestKey.builder(HttpMethod.POST, "a").build(); - - assertThat(requestKey1.hashCode(), not(equalTo(requestKey2.hashCode()))); - assertThat(requestKey1, not(equalTo(requestKey2))); - } - - @Test - public void equalSelf() { - assertThat(requestKey.hashCode(), equalTo(requestKey.hashCode())); - assertThat(requestKey, equalTo(requestKey)); - } - - @Test - public void equalMinimum() { - RequestKey requestKey2 = RequestKey.builder(HttpMethod.GET, "a").build(); - - assertThat(requestKey.hashCode(), equalTo(requestKey2.hashCode())); - assertThat(requestKey, equalTo(requestKey2)); - } - - @Test - public void equalExtra() { - Map> map = new HashMap<>(); - map.put("my-other-header", Arrays.asList("other value")); - RequestKey requestKey2 = RequestKey.builder(HttpMethod.GET, "a").headers(map) - .charset(StandardCharsets.ISO_8859_1).build(); - - assertThat(requestKey.hashCode(), equalTo(requestKey2.hashCode())); - assertThat(requestKey, equalTo(requestKey2)); - } - - @Test - public void equalsExtended() { - RequestKey requestKey2 = RequestKey.builder(HttpMethod.GET, "a").build(); - - assertThat(requestKey.hashCode(), equalTo(requestKey2.hashCode())); - assertThat(requestKey.equalsExtended(requestKey2), equalTo(true)); - } - - @Test - public void equalsExtendedExtra() { - Map> map = new HashMap<>(); - map.put("my-other-header", Arrays.asList("other value")); - RequestKey requestKey2 = RequestKey.builder(HttpMethod.GET, "a").headers(map) - .charset(StandardCharsets.ISO_8859_1).build(); - - assertThat(requestKey.hashCode(), equalTo(requestKey2.hashCode())); - assertThat(requestKey.equalsExtended(requestKey2), equalTo(false)); - } - - @Test - public void testToString() throws Exception { - assertThat(requestKey.toString(), startsWith("Request [GET a: ")); - assertThat(requestKey.toString(), both(containsString(" with 1 ")).and(containsString(" UTF-16]"))); - } - - @Test - public void testToStringSimple() throws Exception { - requestKey = RequestKey.builder(HttpMethod.GET, "a").build(); - - assertThat(requestKey.toString(), startsWith("Request [GET a: ")); - assertThat(requestKey.toString(), both(containsString(" without ")).and(containsString(" no charset"))); - } + private RequestKey requestKey; + + @Before + public void setUp() { + Map> map = new HashMap<>(); + map.put("my-header", Arrays.asList("val")); + requestKey = + RequestKey.builder(HttpMethod.GET, "a").headers(map).charset(StandardCharsets.UTF_16) + .body("content").build(); + } + + @Test + public void builder() throws Exception { + assertThat(requestKey.getMethod(), equalTo(HttpMethod.GET)); + assertThat(requestKey.getUrl(), equalTo("a")); + assertThat(requestKey.getHeaders().entrySet(), hasSize(1)); + assertThat(requestKey.getHeaders().get("my-header"), + equalTo((Collection) Arrays.asList("val"))); + assertThat(requestKey.getCharset(), equalTo(StandardCharsets.UTF_16)); + } + + @Test + public void create() throws Exception { + Map> map = new HashMap<>(); + map.put("my-header", Arrays.asList("val")); + Request request = Request.create("GET", "a", map, "content".getBytes(StandardCharsets.UTF_8), + StandardCharsets.UTF_16); + requestKey = RequestKey.create(request); + + assertThat(requestKey.getMethod(), equalTo(HttpMethod.GET)); + assertThat(requestKey.getUrl(), equalTo("a")); + assertThat(requestKey.getHeaders().entrySet(), hasSize(1)); + assertThat(requestKey.getHeaders().get("my-header"), + equalTo((Collection) Arrays.asList("val"))); + assertThat(requestKey.getCharset(), equalTo(StandardCharsets.UTF_16)); + assertThat(requestKey.getBody(), equalTo("content".getBytes(StandardCharsets.UTF_8))); + } + + @Test + public void checkHashes() { + RequestKey requestKey1 = RequestKey.builder(HttpMethod.GET, "a").build(); + RequestKey requestKey2 = RequestKey.builder(HttpMethod.GET, "b").build(); + + assertThat(requestKey1.hashCode(), not(equalTo(requestKey2.hashCode()))); + assertThat(requestKey1, not(equalTo(requestKey2))); + } + + @Test + public void equalObject() { + assertThat(requestKey, not(equalTo(new Object()))); + } + + @Test + public void equalNull() { + assertThat(requestKey, not(equalTo(null))); + } + + @Test + public void equalPost() { + RequestKey requestKey1 = RequestKey.builder(HttpMethod.GET, "a").build(); + RequestKey requestKey2 = RequestKey.builder(HttpMethod.POST, "a").build(); + + assertThat(requestKey1.hashCode(), not(equalTo(requestKey2.hashCode()))); + assertThat(requestKey1, not(equalTo(requestKey2))); + } + + @Test + public void equalSelf() { + assertThat(requestKey.hashCode(), equalTo(requestKey.hashCode())); + assertThat(requestKey, equalTo(requestKey)); + } + + @Test + public void equalMinimum() { + RequestKey requestKey2 = RequestKey.builder(HttpMethod.GET, "a").build(); + + assertThat(requestKey.hashCode(), equalTo(requestKey2.hashCode())); + assertThat(requestKey, equalTo(requestKey2)); + } + + @Test + public void equalExtra() { + Map> map = new HashMap<>(); + map.put("my-other-header", Arrays.asList("other value")); + RequestKey requestKey2 = RequestKey.builder(HttpMethod.GET, "a").headers(map) + .charset(StandardCharsets.ISO_8859_1).build(); + + assertThat(requestKey.hashCode(), equalTo(requestKey2.hashCode())); + assertThat(requestKey, equalTo(requestKey2)); + } + + @Test + public void equalsExtended() { + RequestKey requestKey2 = RequestKey.builder(HttpMethod.GET, "a").build(); + + assertThat(requestKey.hashCode(), equalTo(requestKey2.hashCode())); + assertThat(requestKey.equalsExtended(requestKey2), equalTo(true)); + } + + @Test + public void equalsExtendedExtra() { + Map> map = new HashMap<>(); + map.put("my-other-header", Arrays.asList("other value")); + RequestKey requestKey2 = RequestKey.builder(HttpMethod.GET, "a").headers(map) + .charset(StandardCharsets.ISO_8859_1).build(); + + assertThat(requestKey.hashCode(), equalTo(requestKey2.hashCode())); + assertThat(requestKey.equalsExtended(requestKey2), equalTo(false)); + } + + @Test + public void testToString() throws Exception { + assertThat(requestKey.toString(), startsWith("Request [GET a: ")); + assertThat(requestKey.toString(), + both(containsString(" with 1 ")).and(containsString(" UTF-16]"))); + } + + @Test + public void testToStringSimple() throws Exception { + requestKey = RequestKey.builder(HttpMethod.GET, "a").build(); + + assertThat(requestKey.toString(), startsWith("Request [GET a: ")); + assertThat(requestKey.toString(), + both(containsString(" without ")).and(containsString(" no charset"))); + } } // diff --git a/okhttp/src/main/java/feign/okhttp/OkHttpClient.java b/okhttp/src/main/java/feign/okhttp/OkHttpClient.java index ea7d9268fd..7eae32e2d5 100644 --- a/okhttp/src/main/java/feign/okhttp/OkHttpClient.java +++ b/okhttp/src/main/java/feign/okhttp/OkHttpClient.java @@ -19,19 +19,19 @@ import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.ResponseBody; - import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.util.Collection; import java.util.Map; import java.util.concurrent.TimeUnit; - import feign.Client; /** - * This module directs Feign's http requests to OkHttp, - * which enables SPDY and better network control. Ex. + * This module directs Feign's http requests to + * OkHttp, which enables SPDY and better network + * control. Ex. + * *
      * GitHub github = Feign.builder().client(new OkHttpClient()).target(GitHub.class,
      * "https://api.github.com");
    @@ -92,11 +92,11 @@ static Request toOkHttpRequest(feign.Request input) {
     
       private static feign.Response toFeignResponse(Response input) throws IOException {
         return feign.Response.builder()
    -            .status(input.code())
    -            .reason(input.message())
    -            .headers(toMap(input.headers()))
    -            .body(toBody(input.body()))
    -            .build();
    +        .status(input.code())
    +        .reason(input.message())
    +        .headers(toMap(input.headers()))
    +        .body(toBody(input.body()))
    +        .build();
       }
     
       private static Map> toMap(Headers headers) {
    @@ -110,8 +110,9 @@ private static feign.Response.Body toBody(final ResponseBody input) throws IOExc
           }
           return null;
         }
    -    final Integer length = input.contentLength() >= 0 && input.contentLength() <= Integer.MAX_VALUE ?
    -            (int) input.contentLength() : null;
    +    final Integer length = input.contentLength() >= 0 && input.contentLength() <= Integer.MAX_VALUE
    +        ? (int) input.contentLength()
    +        : null;
     
         return new feign.Response.Body() {
     
    @@ -148,11 +149,11 @@ public feign.Response execute(feign.Request input, feign.Request.Options options
         okhttp3.OkHttpClient requestScoped;
         if (delegate.connectTimeoutMillis() != options.connectTimeoutMillis()
             || delegate.readTimeoutMillis() != options.readTimeoutMillis()) {
    -       requestScoped = delegate.newBuilder()
    -               .connectTimeout(options.connectTimeoutMillis(), TimeUnit.MILLISECONDS)
    -               .readTimeout(options.readTimeoutMillis(), TimeUnit.MILLISECONDS)
    -               .followRedirects(options.isFollowRedirects())
    -               .build();
    +      requestScoped = delegate.newBuilder()
    +          .connectTimeout(options.connectTimeoutMillis(), TimeUnit.MILLISECONDS)
    +          .readTimeout(options.readTimeoutMillis(), TimeUnit.MILLISECONDS)
    +          .followRedirects(options.isFollowRedirects())
    +          .build();
         } else {
           requestScoped = delegate;
         }
    diff --git a/okhttp/src/test/java/feign/okhttp/OkHttpClientTest.java b/okhttp/src/test/java/feign/okhttp/OkHttpClientTest.java
    index 803c86d41b..aaeefe4057 100644
    --- a/okhttp/src/test/java/feign/okhttp/OkHttpClientTest.java
    +++ b/okhttp/src/test/java/feign/okhttp/OkHttpClientTest.java
    @@ -21,12 +21,9 @@
     import feign.Util;
     import feign.assertj.MockWebServerAssertions;
     import feign.client.AbstractClientTest;
    -
     import feign.Feign;
     import okhttp3.mockwebserver.MockResponse;
     import org.junit.Test;
    -
    -
     import static org.junit.Assert.assertEquals;
     
     /** Tests client-specific behavior, such as ensuring Content-Length is sent when specified. */
    @@ -41,32 +38,35 @@ public Builder newBuilder() {
       @Test
       public void testContentTypeWithoutCharset() throws Exception {
         server.enqueue(new MockResponse()
    -            .setBody("AAAAAAAA"));
    +        .setBody("AAAAAAAA"));
         OkHttpClientTestInterface api = newBuilder()
    -            .target(OkHttpClientTestInterface.class, "http://localhost:" + server.getPort());
    +        .target(OkHttpClientTestInterface.class, "http://localhost:" + server.getPort());
     
         Response response = api.getWithContentType();
         // Response length should not be null
         assertEquals("AAAAAAAA", Util.toString(response.body().asReader()));
     
         MockWebServerAssertions.assertThat(server.takeRequest())
    -            .hasHeaders("Accept: text/plain", "Content-Type: text/plain") // Note: OkHttp adds content length.
    -            .hasMethod("GET");
    +        .hasHeaders("Accept: text/plain", "Content-Type: text/plain") // Note: OkHttp adds content
    +                                                                      // length.
    +        .hasMethod("GET");
       }
     
     
       @Test
       public void testNoFollowRedirect() throws Exception {
    -    server.enqueue(new MockResponse().setResponseCode(302).addHeader("Location", server.url("redirect")));
    +    server.enqueue(
    +        new MockResponse().setResponseCode(302).addHeader("Location", server.url("redirect")));
     
         OkHttpClientTestInterface api = newBuilder()
    -            .options(new Request.Options(1000, 1000, false))
    -            .target(OkHttpClientTestInterface.class, "http://localhost:" + server.getPort());
    +        .options(new Request.Options(1000, 1000, false))
    +        .target(OkHttpClientTestInterface.class, "http://localhost:" + server.getPort());
     
         Response response = api.get();
         // Response length should not be null
         assertEquals(302, response.status());
    -    assertEquals(server.url("redirect").toString(), response.headers().get("Location").iterator().next());
    +    assertEquals(server.url("redirect").toString(),
    +        response.headers().get("Location").iterator().next());
     
       }
     
    @@ -75,12 +75,13 @@ public void testNoFollowRedirect() throws Exception {
       public void testFollowRedirect() throws Exception {
         String expectedBody = "Hello";
     
    -    server.enqueue(new MockResponse().setResponseCode(302).addHeader("Location", server.url("redirect")));
    +    server.enqueue(
    +        new MockResponse().setResponseCode(302).addHeader("Location", server.url("redirect")));
         server.enqueue(new MockResponse().setBody(expectedBody));
     
         OkHttpClientTestInterface api = newBuilder()
    -            .options(new Request.Options(1000, 1000, true))
    -            .target(OkHttpClientTestInterface.class, "http://localhost:" + server.getPort());
    +        .options(new Request.Options(1000, 1000, true))
    +        .target(OkHttpClientTestInterface.class, "http://localhost:" + server.getPort());
     
         Response response = api.get();
         // Response length should not be null
    diff --git a/pom.xml b/pom.xml
    index faf5542493..948b1d6777 100644
    --- a/pom.xml
    +++ b/pom.xml
    @@ -442,6 +442,23 @@
               
             
           
    +      
    +        com.marvinformatics.formatter
    +        formatter-maven-plugin
    +        2.2.0
    +        
    +          LF
    +          ${main.basedir}/src/config/eclipse-java-google-style.xml
    +        
    +        
    +          
    +            
    +              format
    +            
    +            verify
    +          
    +        
    +      
         
       
     
    diff --git a/ribbon/src/main/java/feign/ribbon/LBClient.java b/ribbon/src/main/java/feign/ribbon/LBClient.java
    index 63aa3b3f1d..0c87b6516e 100644
    --- a/ribbon/src/main/java/feign/ribbon/LBClient.java
    +++ b/ribbon/src/main/java/feign/ribbon/LBClient.java
    @@ -21,7 +21,6 @@
     import com.netflix.client.config.CommonClientConfigKey;
     import com.netflix.client.config.IClientConfig;
     import com.netflix.loadbalancer.ILoadBalancer;
    -
     import java.io.IOException;
     import java.net.URI;
     import java.util.Arrays;
    @@ -31,7 +30,6 @@
     import java.util.LinkedHashSet;
     import java.util.Map;
     import java.util.Set;
    -
     import feign.Client;
     import feign.Request;
     import feign.Response;
    @@ -55,7 +53,7 @@ static Set parseStatusCodes(String statusCodesString) {
           return Collections.emptySet();
         }
         Set codes = new LinkedHashSet();
    -    for (String codeString: statusCodesString.split(",")) {
    +    for (String codeString : statusCodesString.split(",")) {
           codes.add(Integer.parseInt(codeString));
         }
         return codes;
    @@ -72,14 +70,14 @@ static Set parseStatusCodes(String statusCodesString) {
     
       @Override
       public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride)
    -          throws IOException, ClientException {
    +      throws IOException, ClientException {
         Request.Options options;
         if (configOverride != null) {
           options =
               new Request.Options(
                   configOverride.get(CommonClientConfigKey.ConnectTimeout, connectTimeout),
                   (configOverride.get(CommonClientConfigKey.ReadTimeout, readTimeout)),
    -              configOverride.get(CommonClientConfigKey.FollowRedirects,followRedirects));
    +              configOverride.get(CommonClientConfigKey.FollowRedirects, followRedirects));
         } else {
           options = new Request.Options(connectTimeout, readTimeout);
         }
    @@ -93,7 +91,8 @@ public RibbonResponse execute(RibbonRequest request, IClientConfig configOverrid
     
       @Override
       public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
    -      RibbonRequest request, IClientConfig requestConfig) {
    +                                                                    RibbonRequest request,
    +                                                                    IClientConfig requestConfig) {
         if (clientConfig.get(CommonClientConfigKey.OkToRetryOnAllOperations, false)) {
           return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(), requestConfig);
         }
    @@ -123,7 +122,8 @@ Request toRequest() {
           Map> headers = new LinkedHashMap>();
           headers.putAll(request.headers());
           headers.put(Util.CONTENT_LENGTH, Arrays.asList(String.valueOf(bodyLength)));
    -      return Request.create(request.method(), getUri().toASCIIString(), headers, body, request.charset());
    +      return Request.create(request.method(), getUri().toASCIIString(), headers, body,
    +          request.charset());
         }
     
         Client client() {
    diff --git a/ribbon/src/main/java/feign/ribbon/LBClientFactory.java b/ribbon/src/main/java/feign/ribbon/LBClientFactory.java
    index 3ceb905e0f..38b9339afe 100644
    --- a/ribbon/src/main/java/feign/ribbon/LBClientFactory.java
    +++ b/ribbon/src/main/java/feign/ribbon/LBClientFactory.java
    @@ -30,13 +30,15 @@ public interface LBClientFactory {
       public static final class Default implements LBClientFactory {
         @Override
         public LBClient create(String clientName) {
    -      IClientConfig config = ClientFactory.getNamedConfig(clientName, DisableAutoRetriesByDefaultClientConfig.class);
    +      IClientConfig config =
    +          ClientFactory.getNamedConfig(clientName, DisableAutoRetriesByDefaultClientConfig.class);
           ILoadBalancer lb = ClientFactory.getNamedLoadBalancer(clientName);
           return LBClient.create(lb, config);
         }
       }
     
    -  IClientConfigKey RetryableStatusCodes = new CommonClientConfigKey("RetryableStatusCodes") {};
    +  IClientConfigKey RetryableStatusCodes =
    +      new CommonClientConfigKey("RetryableStatusCodes") {};
     
       final class DisableAutoRetriesByDefaultClientConfig extends DefaultClientConfigImpl {
         @Override
    diff --git a/ribbon/src/main/java/feign/ribbon/LoadBalancingTarget.java b/ribbon/src/main/java/feign/ribbon/LoadBalancingTarget.java
    index 51c8752d25..c7e238047f 100644
    --- a/ribbon/src/main/java/feign/ribbon/LoadBalancingTarget.java
    +++ b/ribbon/src/main/java/feign/ribbon/LoadBalancingTarget.java
    @@ -15,13 +15,10 @@
     
     import com.netflix.loadbalancer.AbstractLoadBalancer;
     import com.netflix.loadbalancer.Server;
    -
     import java.net.URI;
    -
     import feign.Request;
     import feign.RequestTemplate;
     import feign.Target;
    -
     import static com.netflix.client.ClientFactory.getNamedLoadBalancer;
     import static feign.Util.checkNotNull;
     import static java.lang.String.format;
    @@ -29,11 +26,14 @@
     /**
      * Basic integration for {@link com.netflix.loadbalancer.ILoadBalancer loadbalancer-aware} targets.
      * Using this will enable dynamic url discovery via ribbon including incrementing server request
    - * counts. 
    Ex. + * counts.
    + * Ex. + * *
      * MyService api = Feign.builder().target(LoadBalancingTarget.create(MyService.class,
      * "http://myAppProd"))
      * 
    + * * Where {@code myAppProd} is the ribbon loadbalancer name and {@code * myAppProd.ribbon.listOfServers} configuration is set. * @@ -46,7 +46,7 @@ public class LoadBalancingTarget implements Target { private final String path; private final Class type; private final AbstractLoadBalancer lb; - + /** * @Deprecated will be removed in Feign 10 */ @@ -58,7 +58,7 @@ protected LoadBalancingTarget(Class type, String scheme, String name) { this.path = ""; this.lb = AbstractLoadBalancer.class.cast(getNamedLoadBalancer(name())); } - + protected LoadBalancingTarget(Class type, String scheme, String name, String path) { this.type = checkNotNull(type, "type"); this.scheme = checkNotNull(scheme, "scheme"); @@ -68,12 +68,12 @@ protected LoadBalancingTarget(Class type, String scheme, String name, String } /** - * Creates a target which dynamically derives urls from a {@link com.netflix.loadbalancer.ILoadBalancer - * loadbalancer}. + * Creates a target which dynamically derives urls from a + * {@link com.netflix.loadbalancer.ILoadBalancer loadbalancer}. * - * @param type corresponds to {@link feign.Target#type()} - * @param url naming convention is {@code https://name} or {@code http://name/api/v2} where name - * corresponds to {@link com.netflix.client.ClientFactory#getNamedLoadBalancer(String)} + * @param type corresponds to {@link feign.Target#type()} + * @param url naming convention is {@code https://name} or {@code http://name/api/v2} where name + * corresponds to {@link com.netflix.client.ClientFactory#getNamedLoadBalancer(String)} */ public static LoadBalancingTarget create(Class type, String url) { URI asUri = URI.create(url); @@ -119,7 +119,7 @@ public boolean equals(Object obj) { if (obj instanceof LoadBalancingTarget) { LoadBalancingTarget other = (LoadBalancingTarget) obj; return type.equals(other.type) - && name.equals(other.name); + && name.equals(other.name); } return false; } @@ -134,6 +134,7 @@ public int hashCode() { @Override public String toString() { - return "LoadBalancingTarget(type=" + type.getSimpleName() + ", name=" + name + ", path=" + path + ")"; + return "LoadBalancingTarget(type=" + type.getSimpleName() + ", name=" + name + ", path=" + path + + ")"; } } diff --git a/ribbon/src/main/java/feign/ribbon/RibbonClient.java b/ribbon/src/main/java/feign/ribbon/RibbonClient.java index 33b90bf369..43eda34685 100644 --- a/ribbon/src/main/java/feign/ribbon/RibbonClient.java +++ b/ribbon/src/main/java/feign/ribbon/RibbonClient.java @@ -101,7 +101,7 @@ static URI cleanUrl(String originalUrl, String host) { private LBClient lbClient(String clientName) { return lbClientFactory.create(clientName); } - + static class FeignOptionsClientConfig extends DefaultClientConfigImpl { public FeignOptionsClientConfig(Request.Options options) { @@ -121,11 +121,10 @@ public void loadDefaultValues() { } } - + public static final class Builder { - Builder() { - } + Builder() {} private Client delegate; private LBClientFactory lbClientFactory; @@ -143,8 +142,7 @@ public Builder lbClientFactory(LBClientFactory lbClientFactory) { public RibbonClient build() { return new RibbonClient( delegate != null ? delegate : new Client.Default(null, null), - lbClientFactory != null ? lbClientFactory : new LBClientFactory.Default() - ); + lbClientFactory != null ? lbClientFactory : new LBClientFactory.Default()); } } } diff --git a/ribbon/src/test/java/feign/ribbon/LBClientFactoryTest.java b/ribbon/src/test/java/feign/ribbon/LBClientFactoryTest.java index 064b333b36..6f337e5345 100644 --- a/ribbon/src/test/java/feign/ribbon/LBClientFactoryTest.java +++ b/ribbon/src/test/java/feign/ribbon/LBClientFactoryTest.java @@ -14,9 +14,7 @@ package feign.ribbon; import static org.junit.Assert.assertEquals; - import org.junit.Test; - import com.netflix.client.ClientFactory; public class LBClientFactoryTest { diff --git a/ribbon/src/test/java/feign/ribbon/LBClientTest.java b/ribbon/src/test/java/feign/ribbon/LBClientTest.java index 1efacc9cba..d4a57e4a7d 100644 --- a/ribbon/src/test/java/feign/ribbon/LBClientTest.java +++ b/ribbon/src/test/java/feign/ribbon/LBClientTest.java @@ -19,12 +19,9 @@ import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; - import org.junit.Test; - import feign.Request; import feign.ribbon.LBClient.RibbonRequest; - import static org.assertj.core.api.Assertions.assertThat; public class LBClientTest { @@ -46,7 +43,8 @@ public void testRibbonRequest() throws URISyntaxException { URI uri = new URI(urlWithEncodedJson); Map> headers = new LinkedHashMap>(); // create a Request for recreating another Request by toRequest() - Request requestOrigin = Request.create(method, uri.toASCIIString(), headers, null, Charset.forName("utf-8")); + Request requestOrigin = + Request.create(method, uri.toASCIIString(), headers, null, Charset.forName("utf-8")); RibbonRequest ribbonRequest = new RibbonRequest(null, requestOrigin, uri); // use toRequest() recreate a Request @@ -54,7 +52,9 @@ public void testRibbonRequest() throws URISyntaxException { // test that requestOrigin and requestRecreate are same except the header 'Content-Length' // ps, requestOrigin and requestRecreate won't be null - assertThat(requestOrigin.toString()).isEqualTo(String.format("%s %s HTTP/1.1\n", method, urlWithEncodedJson)); - assertThat(requestRecreate.toString()).isEqualTo(String.format("%s %s HTTP/1.1\nContent-Length: 0\n", method, urlWithEncodedJson)); + assertThat(requestOrigin.toString()) + .isEqualTo(String.format("%s %s HTTP/1.1\n", method, urlWithEncodedJson)); + assertThat(requestRecreate.toString()).isEqualTo( + String.format("%s %s HTTP/1.1\nContent-Length: 0\n", method, urlWithEncodedJson)); } } diff --git a/ribbon/src/test/java/feign/ribbon/LoadBalancingTargetTest.java b/ribbon/src/test/java/feign/ribbon/LoadBalancingTargetTest.java index dacb552e8a..9f3499d469 100644 --- a/ribbon/src/test/java/feign/ribbon/LoadBalancingTargetTest.java +++ b/ribbon/src/test/java/feign/ribbon/LoadBalancingTargetTest.java @@ -15,16 +15,12 @@ import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; - import org.junit.Rule; import org.junit.Test; - import java.io.IOException; import java.net.URL; - import feign.Feign; import feign.RequestLine; - import static com.netflix.config.ConfigurationManager.getConfigInstance; import static org.junit.Assert.assertEquals; @@ -49,12 +45,11 @@ public void loadBalancingDefaultPolicyRoundRobin() throws IOException, Interrupt server2.enqueue(new MockResponse().setBody("success!")); getConfigInstance().setProperty(serverListKey, - hostAndPort(server1.url("").url()) + "," + hostAndPort( - server2.url("").url())); + hostAndPort(server1.url("").url()) + "," + hostAndPort( + server2.url("").url())); try { - LoadBalancingTarget - target = + LoadBalancingTarget target = LoadBalancingTarget.create(TestInterface.class, "http://" + name); TestInterface api = Feign.builder().target(target); @@ -69,25 +64,24 @@ public void loadBalancingDefaultPolicyRoundRobin() throws IOException, Interrupt getConfigInstance().clearProperty(serverListKey); } } - + @Test public void loadBalancingTargetPath() throws InterruptedException { String name = "LoadBalancingTargetTest-loadBalancingDefaultPolicyRoundRobin"; String serverListKey = name + ".ribbon.listOfServers"; - + server1.enqueue(new MockResponse().setBody("success!")); - + getConfigInstance().setProperty(serverListKey, - hostAndPort(server1.url("").url())); - + hostAndPort(server1.url("").url())); + try { - LoadBalancingTarget - target = - LoadBalancingTarget.create(TestInterface.class, "http://" + name + "/context-path"); + LoadBalancingTarget target = + LoadBalancingTarget.create(TestInterface.class, "http://" + name + "/context-path"); TestInterface api = Feign.builder().target(target); - + api.get(); - + assertEquals("http:///context-path", target.url()); assertEquals("/context-path/servers", server1.takeRequest().getPath()); } finally { @@ -99,7 +93,7 @@ interface TestInterface { @RequestLine("POST /") void post(); - + @RequestLine("GET /servers") void get(); } diff --git a/ribbon/src/test/java/feign/ribbon/PropagateFirstIOExceptionTest.java b/ribbon/src/test/java/feign/ribbon/PropagateFirstIOExceptionTest.java index c708eb3240..09572180f9 100644 --- a/ribbon/src/test/java/feign/ribbon/PropagateFirstIOExceptionTest.java +++ b/ribbon/src/test/java/feign/ribbon/PropagateFirstIOExceptionTest.java @@ -19,7 +19,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; - import static org.hamcrest.CoreMatchers.isA; public class PropagateFirstIOExceptionTest { diff --git a/ribbon/src/test/java/feign/ribbon/RibbonClientTest.java b/ribbon/src/test/java/feign/ribbon/RibbonClientTest.java index 23e4847910..ac92892800 100644 --- a/ribbon/src/test/java/feign/ribbon/RibbonClientTest.java +++ b/ribbon/src/test/java/feign/ribbon/RibbonClientTest.java @@ -22,25 +22,21 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; - import java.io.IOException; import java.net.URI; import java.net.URL; import java.util.Collection; - import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; - import com.netflix.client.config.CommonClientConfigKey; import com.netflix.client.config.IClientConfig; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.SocketPolicy; import okhttp3.mockwebserver.MockWebServer; - import feign.Client; import feign.Feign; import feign.Param; @@ -66,7 +62,8 @@ public class RibbonClientTest { @BeforeClass public static void disableSunRetry() throws Exception { - // The Sun HTTP Client retries all requests once on an IOException, which makes testing retry code harder than would + // The Sun HTTP Client retries all requests once on an IOException, which makes testing retry + // code harder than would // be ideal. We can only disable it for post, so lets at least do that. oldRetryConfig = System.setProperty(SUN_RETRY_PROPERTY, "false"); } @@ -91,11 +88,11 @@ public void loadBalancingDefaultPolicyRoundRobin() throws IOException, Interrupt server2.enqueue(new MockResponse().setBody("success!")); getConfigInstance().setProperty(serverListKey(), - hostAndPort(server1.url("").url()) + "," + hostAndPort( - server2.url("").url())); + hostAndPort(server1.url("").url()) + "," + hostAndPort( + server2.url("").url())); TestInterface api = Feign.builder().client(RibbonClient.create()) - .target(TestInterface.class, "http://" + client()); + .target(TestInterface.class, "http://" + client()); api.post(); api.post(); @@ -114,7 +111,7 @@ public void ioExceptionRetry() throws IOException, InterruptedException { getConfigInstance().setProperty(serverListKey(), hostAndPort(server1.url("").url())); TestInterface api = Feign.builder().client(RibbonClient.create()) - .target(TestInterface.class, "http://" + client()); + .target(TestInterface.class, "http://" + client()); api.post(); @@ -129,10 +126,9 @@ public void ioExceptionFailsAfterTooManyFailures() throws IOException, Interrupt getConfigInstance().setProperty(serverListKey(), hostAndPort(server1.url("").url())); - TestInterface - api = - Feign.builder().client(RibbonClient.create()).retryer(Retryer.NEVER_RETRY) - .target(TestInterface.class, "http://" + client()); + TestInterface api = + Feign.builder().client(RibbonClient.create()).retryer(Retryer.NEVER_RETRY) + .target(TestInterface.class, "http://" + client()); try { api.post(); @@ -140,7 +136,7 @@ public void ioExceptionFailsAfterTooManyFailures() throws IOException, Interrupt } catch (RetryableException ignored) { } - //TODO: why are these retrying? + // TODO: why are these retrying? assertThat(server1.getRequestCount()).isGreaterThanOrEqualTo(1); // TODO: verify ribbon stats match // assertEquals(target.lb().getLoadBalancerStats().getSingleServerStat()) @@ -153,11 +149,12 @@ public void ribbonRetryConfigurationOnSameServer() throws IOException, Interrupt server2.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START)); server2.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START)); - getConfigInstance().setProperty(serverListKey(), hostAndPort(server1.url("").url()) + "," + hostAndPort(server2.url("").url())); + getConfigInstance().setProperty(serverListKey(), + hostAndPort(server1.url("").url()) + "," + hostAndPort(server2.url("").url())); getConfigInstance().setProperty(client() + ".ribbon.MaxAutoRetries", 1); TestInterface api = Feign.builder().client(RibbonClient.create()).retryer(Retryer.NEVER_RETRY) - .target(TestInterface.class, "http://" + client()); + .target(TestInterface.class, "http://" + client()); try { api.post(); @@ -178,11 +175,12 @@ public void ribbonRetryConfigurationOnMultipleServers() throws IOException, Inte server2.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START)); server2.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START)); - getConfigInstance().setProperty(serverListKey(), hostAndPort(server1.url("").url()) + "," + hostAndPort(server2.url("").url())); + getConfigInstance().setProperty(serverListKey(), + hostAndPort(server1.url("").url()) + "," + hostAndPort(server2.url("").url())); getConfigInstance().setProperty(client() + ".ribbon.MaxAutoRetriesNextServer", 1); TestInterface api = Feign.builder().client(RibbonClient.create()).retryer(Retryer.NEVER_RETRY) - .target(TestInterface.class, "http://" + client()); + .target(TestInterface.class, "http://" + client()); try { api.post(); @@ -197,10 +195,10 @@ public void ribbonRetryConfigurationOnMultipleServers() throws IOException, Inte } /* - This test-case replicates a bug that occurs when using RibbonRequest with a query string. - - The querystrings would not be URL-encoded, leading to invalid HTTP-requests if the query string contained - invalid characters (ex. space). + * This test-case replicates a bug that occurs when using RibbonRequest with a query string. + * + * The querystrings would not be URL-encoded, leading to invalid HTTP-requests if the query string + * contained invalid characters (ex. space). */ @Test public void urlEncodeQueryStringParameters() throws IOException, InterruptedException { @@ -213,7 +211,7 @@ public void urlEncodeQueryStringParameters() throws IOException, InterruptedExce getConfigInstance().setProperty(serverListKey(), hostAndPort(server1.url("").url())); TestInterface api = Feign.builder().client(RibbonClient.create()) - .target(TestInterface.class, "http://" + client()); + .target(TestInterface.class, "http://" + client()); api.getWithQueryParameters(queryStringValue); @@ -233,7 +231,8 @@ public void testHTTPSViaRibbon() { getConfigInstance().setProperty(serverListKey(), hostAndPort(server1.url("").url())); - TestInterface api = Feign.builder().client(RibbonClient.builder().delegate(trustSSLSockets).build()) + TestInterface api = + Feign.builder().client(RibbonClient.builder().delegate(trustSSLSockets).build()) .target(TestInterface.class, "https://" + client()); api.post(); assertEquals(1, server1.getRequestCount()); @@ -263,14 +262,14 @@ public void ribbonRetryOnStatusCodes() throws IOException, InterruptedException server1.enqueue(new MockResponse().setResponseCode(502)); server2.enqueue(new MockResponse().setResponseCode(503)); - getConfigInstance().setProperty(serverListKey(), hostAndPort(server1.url("").url()) + "," + hostAndPort(server2.url("").url())); + getConfigInstance().setProperty(serverListKey(), + hostAndPort(server1.url("").url()) + "," + hostAndPort(server2.url("").url())); getConfigInstance().setProperty(client() + ".ribbon.MaxAutoRetriesNextServer", 1); getConfigInstance().setProperty(client() + ".ribbon.RetryableStatusCodes", "503,502"); - TestInterface - api = - Feign.builder().client(RibbonClient.create()).retryer(Retryer.NEVER_RETRY) - .target(TestInterface.class, "http://" + client()); + TestInterface api = + Feign.builder().client(RibbonClient.create()).retryer(Retryer.NEVER_RETRY) + .target(TestInterface.class, "http://" + client()); try { api.post(); @@ -286,16 +285,17 @@ public void ribbonRetryOnStatusCodes() throws IOException, InterruptedException @Test public void testFeignOptionsFollowRedirect() { String expectedLocation = server2.url("").url().toString(); - server1.enqueue(new MockResponse().setResponseCode(302).setHeader("Location", expectedLocation)); + server1 + .enqueue(new MockResponse().setResponseCode(302).setHeader("Location", expectedLocation)); getConfigInstance().setProperty(serverListKey(), hostAndPort(server1.url("").url())); Request.Options options = new Request.Options(1000, 1000, false); TestInterface api = Feign.builder() - .options(options) - .client(RibbonClient.create()) - .retryer(Retryer.NEVER_RETRY) - .target(TestInterface.class, "http://" + client()); + .options(options) + .client(RibbonClient.create()) + .retryer(Retryer.NEVER_RETRY) + .target(TestInterface.class, "http://" + client()); try { Response response = api.get(); @@ -314,18 +314,20 @@ public void testFeignOptionsFollowRedirect() { @Test public void testFeignOptionsNoFollowRedirect() { // 302 will say go to server 2 - server1.enqueue(new MockResponse().setResponseCode(302).setHeader("Location", server2.url("").url().toString())); + server1.enqueue(new MockResponse().setResponseCode(302).setHeader("Location", + server2.url("").url().toString())); // server 2 will send back 200 with "Hello" as body server2.enqueue(new MockResponse().setResponseCode(200).setBody("Hello")); - getConfigInstance().setProperty(serverListKey(), hostAndPort(server1.url("").url()) + "," + hostAndPort(server2.url("").url())); + getConfigInstance().setProperty(serverListKey(), + hostAndPort(server1.url("").url()) + "," + hostAndPort(server2.url("").url())); Request.Options options = new Request.Options(1000, 1000, true); TestInterface api = Feign.builder() - .options(options) - .client(RibbonClient.create()) - .retryer(Retryer.NEVER_RETRY) - .target(TestInterface.class, "http://" + client()); + .options(options) + .client(RibbonClient.create()) + .retryer(Retryer.NEVER_RETRY) + .target(TestInterface.class, "http://" + client()); try { Response response = api.get(); @@ -337,7 +339,7 @@ public void testFeignOptionsNoFollowRedirect() { } } - + @Test public void testFeignOptionsClientConfig() { Request.Options options = new Request.Options(1111, 22222); @@ -345,7 +347,8 @@ public void testFeignOptionsClientConfig() { assertThat(config.get(CommonClientConfigKey.ConnectTimeout), equalTo(options.connectTimeoutMillis())); assertThat(config.get(CommonClientConfigKey.ReadTimeout), equalTo(options.readTimeoutMillis())); - assertThat(config.get(CommonClientConfigKey.FollowRedirects), equalTo(options.isFollowRedirects())); + assertThat(config.get(CommonClientConfigKey.FollowRedirects), + equalTo(options.isFollowRedirects())); assertEquals(3, config.getProperties().size()); } diff --git a/sax/src/main/java/feign/sax/SAXDecoder.java b/sax/src/main/java/feign/sax/SAXDecoder.java index 84feba39d9..65acc60e28 100644 --- a/sax/src/main/java/feign/sax/SAXDecoder.java +++ b/sax/src/main/java/feign/sax/SAXDecoder.java @@ -18,19 +18,16 @@ import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLReaderFactory; - import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Type; import java.util.LinkedHashMap; import java.util.Map; - import feign.Response; import feign.Util; import feign.codec.DecodeException; import feign.codec.Decoder; - import static feign.Util.checkNotNull; import static feign.Util.checkState; import static feign.Util.ensureClosed; @@ -38,14 +35,16 @@ /** * Decodes responses using SAX, which is supported both in normal JVM environments, as well Android. - *

    Basic example with with Feign.Builder


    + *
    + *

    Basic example with with Feign.Builder


    + * *
      * api = Feign.builder()
    - *            .decoder(SAXDecoder.builder()
    - *                               .registerContentHandler(ContentHandlerForFoo.class)
    - *                               .registerContentHandler(ContentHandlerForBar.class)
    - *                               .build())
    - *            .target(MyApi.class, "http://api");
    + *     .decoder(SAXDecoder.builder()
    + *         .registerContentHandler(ContentHandlerForFoo.class)
    + *         .registerContentHandler(ContentHandlerForBar.class)
    + *         .build())
    + *     .target(MyApi.class, "http://api");
      * 
    */ public class SAXDecoder implements Decoder { @@ -62,11 +61,13 @@ public static Builder builder() { @Override public Object decode(Response response, Type type) throws IOException, DecodeException { - if (response.status() == 404) return Util.emptyValueOf(type); - if (response.body() == null) return null; + if (response.status() == 404) + return Util.emptyValueOf(type); + if (response.body() == null) + return null; ContentHandlerWithResult.Factory handlerFactory = handlerFactories.get(type); checkState(handlerFactory != null, "type %s not in configured handlers %s", type, - handlerFactories.keySet()); + handlerFactories.keySet()); ContentHandlerWithResult handler = handlerFactory.create(); try { XMLReader xmlReader = XMLReaderFactory.createXMLReader(); @@ -113,19 +114,21 @@ public static class Builder { /** * Will call {@link Constructor#newInstance(Object...)} on {@code handlerClass} for each content - * stream.

    Note


    While this is costly vs {@code new}, it may not affect real - * performance due to the high cost of reading streams. + * stream. + *

    + *

    Note


    + * While this is costly vs {@code new}, it may not affect real performance due to the high cost + * of reading streams. * * @throws IllegalArgumentException if there's no no-arg constructor on {@code handlerClass}. */ public > Builder registerContentHandler( - Class handlerClass) { - Type - type = + Class handlerClass) { + Type type = resolveLastTypeParameter(checkNotNull(handlerClass, "handlerClass"), - ContentHandlerWithResult.class); + ContentHandlerWithResult.class); return registerContentHandler(type, - new NewInstanceContentHandlerWithResultFactory(handlerClass)); + new NewInstanceContentHandlerWithResultFactory(handlerClass)); } /** diff --git a/sax/src/test/java/feign/sax/SAXDecoderTest.java b/sax/src/test/java/feign/sax/SAXDecoderTest.java index 44edce4e42..c57f88028d 100644 --- a/sax/src/test/java/feign/sax/SAXDecoderTest.java +++ b/sax/src/test/java/feign/sax/SAXDecoderTest.java @@ -17,15 +17,12 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.xml.sax.helpers.DefaultHandler; - import java.io.IOException; import java.text.ParseException; import java.util.Collection; import java.util.Collections; - import feign.Response; import feign.codec.Decoder; - import static feign.Util.UTF_8; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; @@ -34,26 +31,26 @@ public class SAXDecoderTest { static String statusFailed = ""// - + "\n" -// - + " \n"// - + " \n" -// - + " Failed\n" -// - + " \n"// - + " \n"// - + ""; + + "\n" + // + + " \n"// + + " \n" + // + + " Failed\n" + // + + " \n"// + + " \n"// + + ""; @Rule public final ExpectedException thrown = ExpectedException.none(); Decoder decoder = SAXDecoder.builder() // .registerContentHandler(NetworkStatus.class, - new SAXDecoder.ContentHandlerWithResult.Factory() { - @Override - public SAXDecoder.ContentHandlerWithResult create() { - return new NetworkStatusHandler(); - } - }) // + new SAXDecoder.ContentHandlerWithResult.Factory() { + @Override + public SAXDecoder.ContentHandlerWithResult create() { + return new NetworkStatusHandler(); + } + }) // .registerContentHandler(NetworkStatusStringHandler.class) // .build(); @@ -73,20 +70,20 @@ public void niceErrorOnUnconfiguredType() throws ParseException, IOException { private Response statusFailedResponse() { return Response.builder() - .status(200) - .reason("OK") - .headers(Collections.>emptyMap()) - .body(statusFailed, UTF_8) - .build(); + .status(200) + .reason("OK") + .headers(Collections.>emptyMap()) + .body(statusFailed, UTF_8) + .build(); } @Test public void nullBodyDecodesToNull() throws Exception { Response response = Response.builder() - .status(204) - .reason("OK") - .headers(Collections.>emptyMap()) - .build(); + .status(204) + .reason("OK") + .headers(Collections.>emptyMap()) + .build(); assertNull(decoder.decode(response, String.class)); } @@ -94,10 +91,10 @@ public void nullBodyDecodesToNull() throws Exception { @Test public void notFoundDecodesToEmpty() throws Exception { Response response = Response.builder() - .status(404) - .reason("NOT FOUND") - .headers(Collections.>emptyMap()) - .build(); + .status(404) + .reason("NOT FOUND") + .headers(Collections.>emptyMap()) + .build(); assertThat((byte[]) decoder.decode(response, byte[].class)).isEmpty(); } @@ -106,7 +103,7 @@ static enum NetworkStatus { } static class NetworkStatusStringHandler extends DefaultHandler implements - SAXDecoder.ContentHandlerWithResult { + SAXDecoder.ContentHandlerWithResult { private StringBuilder currentText = new StringBuilder(); @@ -132,7 +129,7 @@ public void characters(char ch[], int start, int length) { } static class NetworkStatusHandler extends DefaultHandler implements - SAXDecoder.ContentHandlerWithResult { + SAXDecoder.ContentHandlerWithResult { private StringBuilder currentText = new StringBuilder(); diff --git a/sax/src/test/java/feign/sax/examples/AWSSignatureVersion4.java b/sax/src/test/java/feign/sax/examples/AWSSignatureVersion4.java index f453d352eb..53f00521b6 100644 --- a/sax/src/test/java/feign/sax/examples/AWSSignatureVersion4.java +++ b/sax/src/test/java/feign/sax/examples/AWSSignatureVersion4.java @@ -18,20 +18,16 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimeZone; - import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; - import feign.Request; import feign.RequestTemplate; - import static feign.Util.UTF_8; // http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html public class AWSSignatureVersion4 { - private static final String - EMPTY_STRING_HASH = + private static final String EMPTY_STRING_HASH = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; private static final SimpleDateFormat iso8601 = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"); static { @@ -81,7 +77,7 @@ private static String canonicalString(RequestTemplate input, String host) { // HexEncode(Hash(Payload)) String bodyText = input.charset() != null && input.body() != null ? new String(input.body(), input.charset()) - : null; + : null; if (bodyText != null) { canonicalRequest.append(hex(sha256(bodyText))); } else { @@ -136,8 +132,7 @@ public Request apply(RequestTemplate input) { timestamp = iso8601.format(new Date()); } - String - credentialScope = + String credentialScope = String.format("%s/%s/%s/%s", timestamp.substring(0, 8), region, service, "aws4_request"); input.query("X-Amz-Algorithm", "AWS4-HMAC-SHA256"); diff --git a/sax/src/test/java/feign/sax/examples/IAMExample.java b/sax/src/test/java/feign/sax/examples/IAMExample.java index 2335239e18..2d7157a84c 100644 --- a/sax/src/test/java/feign/sax/examples/IAMExample.java +++ b/sax/src/test/java/feign/sax/examples/IAMExample.java @@ -14,7 +14,6 @@ package feign.sax.examples; import org.xml.sax.helpers.DefaultHandler; - import feign.Feign; import feign.Request; import feign.RequestLine; diff --git a/slf4j/src/main/java/feign/slf4j/Slf4jLogger.java b/slf4j/src/main/java/feign/slf4j/Slf4jLogger.java index 6b3d6462ed..847c14827e 100644 --- a/slf4j/src/main/java/feign/slf4j/Slf4jLogger.java +++ b/slf4j/src/main/java/feign/slf4j/Slf4jLogger.java @@ -15,16 +15,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import java.io.IOException; - import feign.Request; import feign.Response; /** - * Logs to SLF4J at the debug level, if the underlying logger has debug logging enabled. The - * underlying logger can be specified at construction-time, defaulting to the logger for {@link - * feign.Logger}. + * Logs to SLF4J at the debug level, if the underlying logger has debug logging enabled. The + * underlying logger can be specified at construction-time, defaulting to the logger for + * {@link feign.Logger}. */ public class Slf4jLogger extends feign.Logger { @@ -54,8 +52,11 @@ protected void logRequest(String configKey, Level logLevel, Request request) { } @Override - protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response, - long elapsedTime) throws IOException { + protected Response logAndRebufferResponse(String configKey, + Level logLevel, + Response response, + long elapsedTime) + throws IOException { if (logger.isDebugEnabled()) { return super.logAndRebufferResponse(configKey, logLevel, response, elapsedTime); } @@ -64,7 +65,8 @@ protected Response logAndRebufferResponse(String configKey, Level logLevel, Resp @Override protected void log(String configKey, String format, Object... args) { - // Not using SLF4J's support for parameterized messages (even though it would be more efficient) because it would + // Not using SLF4J's support for parameterized messages (even though it would be more efficient) + // because it would // require the incoming message formats to be SLF4J-specific. if (logger.isDebugEnabled()) { logger.debug(String.format(methodTag(configKey) + format, args)); diff --git a/slf4j/src/test/java/feign/slf4j/RecordingSimpleLogger.java b/slf4j/src/test/java/feign/slf4j/RecordingSimpleLogger.java index 03874f4064..5530344222 100644 --- a/slf4j/src/test/java/feign/slf4j/RecordingSimpleLogger.java +++ b/slf4j/src/test/java/feign/slf4j/RecordingSimpleLogger.java @@ -19,12 +19,10 @@ import org.slf4j.LoggerFactory; import org.slf4j.impl.SimpleLogger; import org.slf4j.impl.SimpleLoggerFactory; - import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.lang.reflect.Field; import java.lang.reflect.Method; - import static org.junit.Assert.assertEquals; import static org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY; import static org.slf4j.impl.SimpleLogger.SHOW_THREAD_NAME_KEY; diff --git a/slf4j/src/test/java/feign/slf4j/Slf4jLoggerTest.java b/slf4j/src/test/java/feign/slf4j/Slf4jLoggerTest.java index 7ce707c571..976ecacf3a 100644 --- a/slf4j/src/test/java/feign/slf4j/Slf4jLoggerTest.java +++ b/slf4j/src/test/java/feign/slf4j/Slf4jLoggerTest.java @@ -16,10 +16,8 @@ import org.junit.Rule; import org.junit.Test; import org.slf4j.LoggerFactory; - import java.util.Collection; import java.util.Collections; - import feign.Feign; import feign.Logger; import feign.Request; @@ -32,12 +30,12 @@ public class Slf4jLoggerTest { private static final Request REQUEST = new RequestTemplate().method("GET").append("http://api.example.com").request(); private static final Response RESPONSE = - Response.builder() - .status(200) - .reason("OK") - .headers(Collections.>emptyMap()) - .body(new byte[0]) - .build(); + Response.builder() + .status(200) + .reason("OK") + .headers(Collections.>emptyMap()) + .body(new byte[0]) + .build(); @Rule public final RecordingSimpleLogger slf4j = new RecordingSimpleLogger(); private Slf4jLogger logger; @@ -92,9 +90,9 @@ public void logOnlyIfDebugEnabled() throws Exception { public void logRequestsAndResponses() throws Exception { slf4j.logLevel("debug"); slf4j.expectMessages("DEBUG feign.Logger - [someMethod] A message with 2 formatting tokens.\n" + - "DEBUG feign.Logger - [someMethod] ---> GET http://api.example.com HTTP/1.1\n" - + - "DEBUG feign.Logger - [someMethod] <--- HTTP/1.1 200 OK (273ms)\n"); + "DEBUG feign.Logger - [someMethod] ---> GET http://api.example.com HTTP/1.1\n" + + + "DEBUG feign.Logger - [someMethod] <--- HTTP/1.1 200 OK (273ms)\n"); logger = new Slf4jLogger(); logger.log(CONFIG_KEY, "A message with %d formatting %s.", 2, "tokens"); diff --git a/src/config/eclipse-java-google-style.xml b/src/config/eclipse-java-google-style.xml new file mode 100644 index 0000000000..0457366248 --- /dev/null +++ b/src/config/eclipse-java-google-style.xml @@ -0,0 +1,332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +