From be25759e93d2b94408b44270013d8379ec9b47bd Mon Sep 17 00:00:00 2001 From: Vitalij Berdinskih Date: Fri, 15 Sep 2023 10:45:35 +0300 Subject: [PATCH] Retryer: replace an instance of Date with an epoch millisecond (#2170) * Retryer: replace an instance of Date with an epoch millisecond * Style issue: unnecessary explicit casting * Add another check to RetryableException's test * Update serialization ID. Resolve some deprecation issues of Integer. * Remove obsolete Date * Remove obsolete Date 2 * Resolve issue with overrided method of a mock class --- core/src/main/java/feign/FeignException.java | 3 +- .../main/java/feign/RetryableException.java | 31 +++++++- core/src/main/java/feign/Retryer.java | 8 +- .../main/java/feign/codec/ErrorDecoder.java | 39 +++++----- core/src/test/java/feign/AsyncFeignTest.java | 76 +++++++++++++------ .../test/java/feign/DefaultContractTest.java | 40 ++++++++-- core/src/test/java/feign/FeignTest.java | 69 ++++++++++++----- .../test/java/feign/FeignUnderAsyncTest.java | 57 ++++++++++---- .../java/feign/RetryableExceptionTest.java | 4 +- core/src/test/java/feign/RetryerTest.java | 13 ++-- core/src/test/java/feign/UtilTest.java | 4 +- .../java/feign/codec/DefaultEncoderTest.java | 4 +- .../feign/codec/RetryAfterDecoderTest.java | 35 +++++---- .../feign/hc5/AsyncApacheHttp5ClientTest.java | 64 +++++++++++----- .../feign/hystrix/HystrixBuilderTest.java | 12 +-- .../test/Http2ClientAsyncTest.java | 63 ++++++++++----- .../jaxb/examples/AWSSignatureVersion4.java | 13 +--- .../jaxb/examples/AWSSignatureVersion4.java | 13 +--- .../main/java/feign/jaxrs2/JAXRSClient.java | 2 +- .../test/java/feign/json/JsonDecoderTest.java | 6 +- .../test/java/feign/json/JsonEncoderTest.java | 6 +- .../feign/okhttp/OkHttpClientAsyncTest.java | 62 ++++++++++----- pom.xml | 2 + .../sax/examples/AWSSignatureVersion4.java | 13 +--- 24 files changed, 415 insertions(+), 224 deletions(-) diff --git a/core/src/main/java/feign/FeignException.java b/core/src/main/java/feign/FeignException.java index d167c9e1..1582a3f3 100644 --- a/core/src/main/java/feign/FeignException.java +++ b/core/src/main/java/feign/FeignException.java @@ -271,12 +271,13 @@ public class FeignException extends RuntimeException { } static FeignException errorExecuting(Request request, IOException cause) { + final Long nonRetryable = null; return new RetryableException( -1, format("%s executing %s %s", cause.getMessage(), request.httpMethod(), request.url()), request.httpMethod(), cause, - null, request); + nonRetryable, request); } public static class FeignClientException extends FeignException { diff --git a/core/src/main/java/feign/RetryableException.java b/core/src/main/java/feign/RetryableException.java index b80daf44..e304725a 100644 --- a/core/src/main/java/feign/RetryableException.java +++ b/core/src/main/java/feign/RetryableException.java @@ -24,7 +24,7 @@ import java.util.Map; */ public class RetryableException extends FeignException { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 2L; private final Long retryAfter; private final HttpMethod httpMethod; @@ -32,6 +32,14 @@ public class RetryableException extends FeignException { /** * @param retryAfter usually corresponds to the {@link feign.Util#RETRY_AFTER} header. */ + public RetryableException(int status, String message, HttpMethod httpMethod, Throwable cause, + Long retryAfter, Request request) { + super(status, message, request, cause); + this.httpMethod = httpMethod; + this.retryAfter = retryAfter; + } + + @Deprecated public RetryableException(int status, String message, HttpMethod httpMethod, Throwable cause, Date retryAfter, Request request) { super(status, message, request, cause); @@ -42,6 +50,14 @@ public class RetryableException extends FeignException { /** * @param retryAfter usually corresponds to the {@link feign.Util#RETRY_AFTER} header. */ + public RetryableException(int status, String message, HttpMethod httpMethod, Long retryAfter, + Request request) { + super(status, message, request); + this.httpMethod = httpMethod; + this.retryAfter = retryAfter; + } + + @Deprecated public RetryableException(int status, String message, HttpMethod httpMethod, Date retryAfter, Request request) { super(status, message, request); @@ -52,6 +68,15 @@ public class RetryableException extends FeignException { /** * @param retryAfter usually corresponds to the {@link feign.Util#RETRY_AFTER} header. */ + public RetryableException(int status, String message, HttpMethod httpMethod, Long retryAfter, + Request request, byte[] responseBody, + Map> responseHeaders) { + super(status, message, request, responseBody, responseHeaders); + this.httpMethod = httpMethod; + this.retryAfter = retryAfter; + } + + @Deprecated public RetryableException(int status, String message, HttpMethod httpMethod, Date retryAfter, Request request, byte[] responseBody, Map> responseHeaders) { super(status, message, request, responseBody, responseHeaders); @@ -63,8 +88,8 @@ public class RetryableException extends FeignException { * 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. */ - public Date retryAfter() { - return retryAfter != null ? new Date(retryAfter) : null; + public Long retryAfter() { + return retryAfter; } public HttpMethod method() { diff --git a/core/src/main/java/feign/Retryer.java b/core/src/main/java/feign/Retryer.java index 89e50172..43dac357 100644 --- a/core/src/main/java/feign/Retryer.java +++ b/core/src/main/java/feign/Retryer.java @@ -22,7 +22,7 @@ import static java.util.concurrent.TimeUnit.SECONDS; public interface Retryer extends Cloneable { /** - * if retry is permitted, return (possibly after sleeping). Otherwise propagate the exception. + * if retry is permitted, return (possibly after sleeping). Otherwise, propagate the exception. */ void continueOrPropagate(RetryableException e); @@ -59,7 +59,7 @@ public interface Retryer extends Cloneable { long interval; if (e.retryAfter() != null) { - interval = e.retryAfter().getTime() - currentTimeMillis(); + interval = e.retryAfter() - currentTimeMillis(); if (interval > maxPeriod) { interval = maxPeriod; } @@ -79,7 +79,7 @@ public interface Retryer extends Cloneable { } /** - * Calculates the time interval to a retry attempt.
+ * 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. * @@ -87,7 +87,7 @@ public interface Retryer extends Cloneable { */ long nextMaxInterval() { long interval = (long) (period * Math.pow(1.5, attempt - 1)); - return interval > maxPeriod ? maxPeriod : interval; + return Math.min(interval, maxPeriod); } @Override diff --git a/core/src/main/java/feign/codec/ErrorDecoder.java b/core/src/main/java/feign/codec/ErrorDecoder.java index 280bdf28..b1a39354 100644 --- a/core/src/main/java/feign/codec/ErrorDecoder.java +++ b/core/src/main/java/feign/codec/ErrorDecoder.java @@ -16,16 +16,15 @@ package feign.codec; import static feign.FeignException.errorStatus; import static feign.Util.RETRY_AFTER; import static feign.Util.checkNotNull; -import static java.util.Locale.US; +import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME; import static java.util.concurrent.TimeUnit.SECONDS; import feign.FeignException; import feign.Response; import feign.RetryableException; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.Collection; -import java.util.Date; import java.util.Map; /** @@ -103,7 +102,7 @@ public interface ErrorDecoder { public Exception decode(String methodKey, Response response) { FeignException exception = errorStatus(methodKey, response, maxBodyBytesLength, maxBodyCharsLength); - Date retryAfter = retryAfterDecoder.apply(firstOrNull(response.headers(), RETRY_AFTER)); + Long retryAfter = retryAfterDecoder.apply(firstOrNull(response.headers(), RETRY_AFTER)); if (retryAfter != null) { return new RetryableException( response.status(), @@ -125,21 +124,19 @@ public interface ErrorDecoder { } /** - * Decodes a {@link feign.Util#RETRY_AFTER} header into an absolute date, if possible.
+ * Decodes a {@link feign.Util#RETRY_AFTER} header into an epoch millisecond, if possible.
* See Retry-After format */ static class RetryAfterDecoder { - static final DateFormat RFC822_FORMAT = - new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", US); - private final DateFormat rfc822Format; + private final DateTimeFormatter dateTimeFormatter; RetryAfterDecoder() { - this(RFC822_FORMAT); + this(RFC_1123_DATE_TIME); } - RetryAfterDecoder(DateFormat rfc822Format) { - this.rfc822Format = checkNotNull(rfc822Format, "rfc822Format"); + RetryAfterDecoder(DateTimeFormatter dateTimeFormatter) { + this.dateTimeFormatter = checkNotNull(dateTimeFormatter, "dateTimeFormatter"); } protected long currentTimeMillis() { @@ -147,26 +144,24 @@ public interface ErrorDecoder { } /** - * returns a date that corresponds to the first time a request can be retried. + * returns an epoch millisecond that corresponds to the first time a request can be retried. * * @param retryAfter String in * Retry-After format */ - public Date apply(String retryAfter) { + public Long apply(String retryAfter) { if (retryAfter == null) { return null; } if (retryAfter.matches("^[0-9]+\\.?0*$")) { retryAfter = retryAfter.replaceAll("\\.0*$", ""); long deltaMillis = SECONDS.toMillis(Long.parseLong(retryAfter)); - return new Date(currentTimeMillis() + deltaMillis); + return currentTimeMillis() + deltaMillis; } - synchronized (rfc822Format) { - try { - return rfc822Format.parse(retryAfter); - } catch (ParseException ignored) { - return null; - } + try { + return ZonedDateTime.parse(retryAfter, dateTimeFormatter).toInstant().toEpochMilli(); + } catch (NullPointerException | DateTimeParseException ignored) { + return null; } } } diff --git a/core/src/test/java/feign/AsyncFeignTest.java b/core/src/test/java/feign/AsyncFeignTest.java index b731ab3e..93266c9c 100644 --- a/core/src/test/java/feign/AsyncFeignTest.java +++ b/core/src/test/java/feign/AsyncFeignTest.java @@ -16,7 +16,6 @@ package feign; import static feign.ExceptionPropagationPolicy.UNWRAP; import static feign.Util.UTF_8; import static feign.assertj.MockWebServerAssertions.assertThat; -import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.data.MapEntry.entry; import static org.hamcrest.CoreMatchers.isA; import static org.junit.Assert.assertEquals; @@ -37,11 +36,13 @@ import feign.querymap.FieldQueryMapEncoder; import java.io.IOException; import java.lang.reflect.Type; import java.net.URI; +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -62,12 +63,12 @@ import okio.Buffer; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; -import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.junit.rules.ExpectedException; public class AsyncFeignTest { + private static final Long NON_RETRYABLE = null; @Rule public final ExpectedException thrown = ExpectedException.none(); @Rule @@ -226,9 +227,9 @@ public class AsyncFeignTest { TestInterfaceAsync api = new TestInterfaceAsyncBuilder().target("http://localhost:" + server.getPort()); - CompletableFuture cf = api.expand(new Date(1234l)); + CompletableFuture cf = api.expand(new TestClock(1234l) {}); - assertThat(server.takeRequest()).hasPath("/?date=1234"); + assertThat(server.takeRequest()).hasPath("/?clock=1234"); checkCFCompletedSoon(cf); } @@ -240,9 +241,10 @@ public class AsyncFeignTest { TestInterfaceAsync api = new TestInterfaceAsyncBuilder().target("http://localhost:" + server.getPort()); - CompletableFuture cf = api.expandList(Arrays.asList(new Date(1234l), new Date(12345l))); + CompletableFuture cf = + api.expandList(Arrays.asList(new TestClock(1234l), new TestClock(12345l))); - assertThat(server.takeRequest()).hasPath("/?date=1234&date=12345"); + assertThat(server.takeRequest()).hasPath("/?clock=1234&clock=12345"); checkCFCompletedSoon(cf); } @@ -254,9 +256,9 @@ public class AsyncFeignTest { TestInterfaceAsync api = new TestInterfaceAsyncBuilder().target("http://localhost:" + server.getPort()); - CompletableFuture cf = api.expandList(Arrays.asList(new Date(1234l), null)); + CompletableFuture cf = api.expandList(Arrays.asList(new TestClock(1234l), null)); - assertThat(server.takeRequest()).hasPath("/?date=1234"); + assertThat(server.takeRequest()).hasPath("/?clock=1234"); checkCFCompletedSoon(cf); } @@ -505,8 +507,8 @@ public class AsyncFeignTest { public Object decode(Response response, Type type) throws IOException { String string = super.decode(response, type).toString(); if ("retry!".equals(string)) { - throw new RetryableException(response.status(), string, HttpMethod.POST, null, - response.request()); + throw new RetryableException(response.status(), string, HttpMethod.POST, + NON_RETRYABLE, response.request()); } return string; } @@ -584,7 +586,7 @@ public class AsyncFeignTest { @Override public Exception decode(String methodKey, Response response) { return new RetryableException(response.status(), "play it again sam!", HttpMethod.POST, - null, response.request()); + NON_RETRYABLE, response.request()); } }).target(TestInterfaceAsync.class, "http://localhost:" + server.getPort()); @@ -609,7 +611,7 @@ public class AsyncFeignTest { @Override public Exception decode(String methodKey, Response response) { return new RetryableException(response.status(), "play it again sam!", HttpMethod.POST, - new TestInterfaceException(message), null, response.request()); + new TestInterfaceException(message), NON_RETRYABLE, response.request()); } }).target(TestInterfaceAsync.class, "http://localhost:" + server.getPort()); @@ -631,8 +633,8 @@ public class AsyncFeignTest { .errorDecoder(new ErrorDecoder() { @Override public Exception decode(String methodKey, Response response) { - return new RetryableException(response.status(), message, HttpMethod.POST, null, - response.request()); + return new RetryableException(response.status(), message, HttpMethod.POST, + NON_RETRYABLE, response.request()); } }).target(TestInterfaceAsync.class, "http://localhost:" + server.getPort()); @@ -1022,16 +1024,17 @@ public class AsyncFeignTest { CompletableFuture queryParams(@Param("1") String one, @Param("2") Iterable twos); - @RequestLine("POST /?date={date}") - CompletableFuture expand(@Param(value = "date", expander = DateToMillis.class) Date date); + @RequestLine("POST /?clock={clock}") + CompletableFuture expand(@Param(value = "clock", + expander = ClockToMillis.class) Clock clock); - @RequestLine("GET /?date={date}") - CompletableFuture expandList(@Param(value = "date", - expander = DateToMillis.class) List dates); + @RequestLine("GET /?clock={clock}") + CompletableFuture expandList(@Param(value = "clock", + expander = ClockToMillis.class) List clocks); - @RequestLine("GET /?date={date}") - CompletableFuture expandArray(@Param(value = "date", - expander = DateToMillis.class) Date[] dates); + @RequestLine("GET /?clock={clock}") + CompletableFuture expandArray(@Param(value = "clock", + expander = ClockToMillis.class) Clock[] clocks); @RequestLine("GET /") CompletableFuture headerMap(@HeaderMap Map headerMap); @@ -1062,11 +1065,11 @@ public class AsyncFeignTest { @RequestLine("GET /") CompletableFuture queryMapPropertyInheritence(@QueryMap ChildPojo object); - class DateToMillis implements Param.Expander { + class ClockToMillis implements Param.Expander { @Override public String expand(Object value) { - return String.valueOf(((Date) value).getTime()); + return String.valueOf(((Clock) value).millis()); } } } @@ -1249,5 +1252,28 @@ public class AsyncFeignTest { CompletableFuture x(); } + class TestClock extends Clock { + + private long millis; + + public TestClock(long millis) { + this.millis = millis; + } + + @Override + public ZoneId getZone() { + throw new UnsupportedOperationException("This operation is not supported."); + } + + @Override + public Clock withZone(ZoneId zone) { + return this; + } + + @Override + public Instant instant() { + return Instant.ofEpochMilli(millis); + } + } } diff --git a/core/src/test/java/feign/DefaultContractTest.java b/core/src/test/java/feign/DefaultContractTest.java index 1fe3cadf..ff3480ab 100644 --- a/core/src/test/java/feign/DefaultContractTest.java +++ b/core/src/test/java/feign/DefaultContractTest.java @@ -20,6 +20,9 @@ import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.rules.ExpectedException; import java.net.URI; +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; import java.util.*; import static feign.assertj.FeignAssertions.assertThat; import static java.util.Arrays.asList; @@ -312,10 +315,10 @@ public class DefaultContractTest { @Test public void customExpander() throws Exception { - final MethodMetadata md = parseAndValidateMetadata(CustomExpander.class, "date", Date.class); + final MethodMetadata md = parseAndValidateMetadata(CustomExpander.class, "clock", Clock.class); assertThat(md.indexToExpanderClass()) - .containsExactly(entry(0, DateToMillis.class)); + .containsExactly(entry(0, ClockToMillis.class)); } @Test @@ -583,15 +586,15 @@ public class DefaultContractTest { interface CustomExpander { - @RequestLine("POST /?date={date}") - void date(@Param(value = "date", expander = DateToMillis.class) Date date); + @RequestLine("POST /?clock={clock}") + void clock(@Param(value = "clock", expander = ClockToMillis.class) Clock clock); } - class DateToMillis implements Param.Expander { + class ClockToMillis implements Param.Expander { @Override public String expand(Object value) { - return String.valueOf(((Date) value).getTime()); + return String.valueOf(((Clock) value).millis()); } } @@ -876,4 +879,29 @@ public class DefaultContractTest { Integer limit, @SuppressWarnings({"a"}) Integer offset); } + + class TestClock extends Clock { + + private long millis; + + public TestClock(long millis) { + this.millis = millis; + } + + @Override + public ZoneId getZone() { + throw new UnsupportedOperationException("This operation is not supported."); + } + + @Override + public Clock withZone(ZoneId zone) { + return this; + } + + @Override + public Instant instant() { + return Instant.ofEpochMilli(millis); + } + } + } diff --git a/core/src/test/java/feign/FeignTest.java b/core/src/test/java/feign/FeignTest.java index b6bea3c0..af6724f4 100755 --- a/core/src/test/java/feign/FeignTest.java +++ b/core/src/test/java/feign/FeignTest.java @@ -39,6 +39,9 @@ import org.mockito.ArgumentMatchers; import java.io.IOException; import java.lang.reflect.Type; import java.net.URI; +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; import java.util.*; import java.util.concurrent.atomic.AtomicReference; import static feign.ExceptionPropagationPolicy.UNWRAP; @@ -54,6 +57,7 @@ import static org.mockito.Mockito.*; @SuppressWarnings("deprecation") public class FeignTest { + private static final Long NON_RETRYABLE = null; @Rule public final ExpectedException thrown = ExpectedException.none(); @Rule @@ -209,10 +213,10 @@ public class FeignTest { TestInterface api = new TestInterfaceBuilder().target("http://localhost:" + server.getPort()); - api.expand(new Date(1234L)); + api.expand(new TestClock(1234L)); assertThat(server.takeRequest()) - .hasPath("/?date=1234"); + .hasPath("/?clock=1234"); } @Test @@ -221,10 +225,10 @@ public class FeignTest { TestInterface api = new TestInterfaceBuilder().target("http://localhost:" + server.getPort()); - api.expandList(Arrays.asList(new Date(1234L), new Date(12345L))); + api.expandList(Arrays.asList(new TestClock(1234L), new TestClock(12345L))); assertThat(server.takeRequest()) - .hasPath("/?date=1234&date=12345"); + .hasPath("/?clock=1234&clock=12345"); } @Test @@ -233,10 +237,10 @@ public class FeignTest { TestInterface api = new TestInterfaceBuilder().target("http://localhost:" + server.getPort()); - api.expandList(Arrays.asList(new Date(1234l), null)); + api.expandList(Arrays.asList(new TestClock(1234l), null)); assertThat(server.takeRequest()) - .hasPath("/?date=1234"); + .hasPath("/?clock=1234"); } @Test @@ -535,8 +539,8 @@ public class FeignTest { public Object decode(Response response, Type type) throws IOException { String string = super.decode(response, type).toString(); if ("retry!".equals(string)) { - throw new RetryableException(response.status(), string, HttpMethod.POST, null, - response.request()); + throw new RetryableException(response.status(), string, HttpMethod.POST, + NON_RETRYABLE, response.request()); } return string; } @@ -616,7 +620,7 @@ public class FeignTest { @Override public Exception decode(String methodKey, Response response) { return new RetryableException(response.status(), "play it again sam!", HttpMethod.POST, - null, response.request()); + NON_RETRYABLE, response.request()); } }).target(TestInterface.class, "http://localhost:" + server.getPort()); @@ -641,7 +645,7 @@ public class FeignTest { @Override public Exception decode(String methodKey, Response response) { return new RetryableException(response.status(), "play it again sam!", HttpMethod.POST, - new TestInterfaceException(message), null, response.request()); + new TestInterfaceException(message), NON_RETRYABLE, response.request()); } }).target(TestInterface.class, "http://localhost:" + server.getPort()); @@ -663,8 +667,8 @@ public class FeignTest { .errorDecoder(new ErrorDecoder() { @Override public Exception decode(String methodKey, Response response) { - return new RetryableException(response.status(), message, HttpMethod.POST, null, - response.request()); + return new RetryableException(response.status(), message, HttpMethod.POST, + NON_RETRYABLE, response.request()); } }).target(TestInterface.class, "http://localhost:" + server.getPort()); @@ -1171,14 +1175,14 @@ public class FeignTest { @RequestLine("GET /") Response queryMapWithArrayValues(@QueryMap Map twos); - @RequestLine("POST /?date={date}") - void expand(@Param(value = "date", expander = DateToMillis.class) Date date); + @RequestLine("POST /?clock={clock}") + void expand(@Param(value = "clock", expander = ClockToMillis.class) Clock clock); - @RequestLine("GET /?date={date}") - void expandList(@Param(value = "date", expander = DateToMillis.class) List dates); + @RequestLine("GET /?clock={clock}") + void expandList(@Param(value = "clock", expander = ClockToMillis.class) List clocks); - @RequestLine("GET /?date={date}") - void expandArray(@Param(value = "date", expander = DateToMillis.class) Date[] dates); + @RequestLine("GET /?clock={clock}") + void expandArray(@Param(value = "clock", expander = ClockToMillis.class) Clock[] clocks); @RequestLine("GET /") void headerMap(@HeaderMap Map headerMap); @@ -1219,11 +1223,11 @@ public class FeignTest { @Headers("Custom: {complex}") void supportComplexHttpHeaders(@Param("complex") String complex); - class DateToMillis implements Param.Expander { + class ClockToMillis implements Param.Expander { @Override public String expand(Object value) { - return String.valueOf(((Date) value).getTime()); + return String.valueOf(((Clock) value).millis()); } } } @@ -1392,4 +1396,29 @@ public class FeignTest { return chain.next(invocationContext); } } + + class TestClock extends Clock { + + private long millis; + + public TestClock(long millis) { + this.millis = millis; + } + + @Override + public ZoneId getZone() { + throw new UnsupportedOperationException("This operation is not supported."); + } + + @Override + public Clock withZone(ZoneId zone) { + return this; + } + + @Override + public Instant instant() { + return Instant.ofEpochMilli(millis); + } + } + } diff --git a/core/src/test/java/feign/FeignUnderAsyncTest.java b/core/src/test/java/feign/FeignUnderAsyncTest.java index 8a4d3c49..a02bcdb6 100644 --- a/core/src/test/java/feign/FeignUnderAsyncTest.java +++ b/core/src/test/java/feign/FeignUnderAsyncTest.java @@ -36,11 +36,13 @@ import org.junit.rules.ExpectedException; import java.io.IOException; import java.lang.reflect.Type; import java.net.URI; +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -201,10 +203,10 @@ public class FeignUnderAsyncTest { TestInterface api = new TestInterfaceBuilder().target("http://localhost:" + server.getPort()); - api.expand(new Date(1234L)); + api.expand(new TestClock(1234L)); assertThat(server.takeRequest()) - .hasPath("/?date=1234"); + .hasPath("/?clock=1234"); } @Test @@ -213,10 +215,10 @@ public class FeignUnderAsyncTest { TestInterface api = new TestInterfaceBuilder().target("http://localhost:" + server.getPort()); - api.expandList(Arrays.asList(new Date(1234L), new Date(12345L))); + api.expandList(Arrays.asList(new TestClock(1234L), new TestClock(12345L))); assertThat(server.takeRequest()) - .hasPath("/?date=1234&date=12345"); + .hasPath("/?clock=1234&clock=12345"); } @Test @@ -225,10 +227,10 @@ public class FeignUnderAsyncTest { TestInterface api = new TestInterfaceBuilder().target("http://localhost:" + server.getPort()); - api.expandList(Arrays.asList(new Date(1234l), null)); + api.expandList(Arrays.asList(new TestClock(1234l), null)); assertThat(server.takeRequest()) - .hasPath("/?date=1234"); + .hasPath("/?clock=1234"); } @Test @@ -857,14 +859,14 @@ public class FeignUnderAsyncTest { @RequestLine("GET /?1={1}&2={2}") Response queryParams(@Param("1") String one, @Param("2") Iterable twos); - @RequestLine("POST /?date={date}") - void expand(@Param(value = "date", expander = DateToMillis.class) Date date); + @RequestLine("POST /?clock={clock}") + void expand(@Param(value = "clock", expander = ClockToMillis.class) Clock clock); - @RequestLine("GET /?date={date}") - void expandList(@Param(value = "date", expander = DateToMillis.class) List dates); + @RequestLine("GET /?clock={clock}") + void expandList(@Param(value = "clock", expander = ClockToMillis.class) List clocks); - @RequestLine("GET /?date={date}") - void expandArray(@Param(value = "date", expander = DateToMillis.class) Date[] dates); + @RequestLine("GET /?clock={clock}") + void expandArray(@Param(value = "clock", expander = ClockToMillis.class) Clock[] clocks); @RequestLine("GET /") void headerMap(@HeaderMap Map headerMap); @@ -895,11 +897,11 @@ public class FeignUnderAsyncTest { @RequestLine("GET /") void queryMapPropertyInheritence(@QueryMap ChildPojo object); - class DateToMillis implements Param.Expander { + class ClockToMillis implements Param.Expander { @Override public String expand(Object value) { - return String.valueOf(((Date) value).getTime()); + return String.valueOf(((Clock) value).millis()); } } } @@ -1014,4 +1016,29 @@ public class FeignUnderAsyncTest { return delegate.target(TestInterface.class, url); } } + + class TestClock extends Clock { + + private long millis; + + public TestClock(long millis) { + this.millis = millis; + } + + @Override + public ZoneId getZone() { + throw new UnsupportedOperationException("This operation is not supported."); + } + + @Override + public Clock withZone(ZoneId zone) { + return this; + } + + @Override + public Instant instant() { + return Instant.ofEpochMilli(millis); + } + } + } diff --git a/core/src/test/java/feign/RetryableExceptionTest.java b/core/src/test/java/feign/RetryableExceptionTest.java index a94fdb29..db9780cb 100644 --- a/core/src/test/java/feign/RetryableExceptionTest.java +++ b/core/src/test/java/feign/RetryableExceptionTest.java @@ -24,6 +24,7 @@ public class RetryableExceptionTest { @Test public void createRetryableExceptionWithResponseAndResponseHeader() { // given + Long retryAfter = 5000L; Request request = Request.create(Request.HttpMethod.GET, "/", Collections.emptyMap(), null, Util.UTF_8); byte[] response = "response".getBytes(StandardCharsets.UTF_8); @@ -32,10 +33,11 @@ public class RetryableExceptionTest { // when RetryableException retryableException = - new RetryableException(-1, null, null, new Date(5000), request, response, responseHeader); + new RetryableException(-1, null, null, retryAfter, request, response, responseHeader); // then assertNotNull(retryableException); + assertEquals(retryAfter, retryableException.retryAfter()); assertEquals(new String(response, UTF_8), retryableException.contentUTF8()); assertTrue(retryableException.responseHeaders().containsKey("TEST_HEADER")); assertTrue(retryableException.responseHeaders().get("TEST_HEADER").contains("TEST_CONTENT")); diff --git a/core/src/test/java/feign/RetryerTest.java b/core/src/test/java/feign/RetryerTest.java index 2f41861e..9fd197b4 100644 --- a/core/src/test/java/feign/RetryerTest.java +++ b/core/src/test/java/feign/RetryerTest.java @@ -18,7 +18,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import java.util.Collections; -import java.util.Date; import feign.Retryer.Default; import static org.junit.Assert.assertEquals; @@ -32,8 +31,9 @@ public class RetryerTest { .create(Request.HttpMethod.GET, "/", Collections.emptyMap(), null, Util.UTF_8); @Test - public void only5TriesAllowedAndExponentialBackoff() throws Exception { - RetryableException e = new RetryableException(-1, null, null, null, REQUEST); + public void only5TriesAllowedAndExponentialBackoff() { + final Long nonRetryable = null; + RetryableException e = new RetryableException(-1, null, null, nonRetryable, REQUEST); Default retryer = new Retryer.Default(); assertEquals(1, retryer.attempt); assertEquals(0, retryer.sleptForMillis); @@ -66,7 +66,7 @@ public class RetryerTest { } }; - retryer.continueOrPropagate(new RetryableException(-1, null, null, new Date(5000), REQUEST)); + retryer.continueOrPropagate(new RetryableException(-1, null, null, 5000L, REQUEST)); assertEquals(2, retryer.attempt); assertEquals(1000, retryer.sleptForMillis); } @@ -74,7 +74,7 @@ public class RetryerTest { @Test(expected = RetryableException.class) public void neverRetryAlwaysPropagates() { Retryer.NEVER_RETRY - .continueOrPropagate(new RetryableException(-1, null, null, new Date(5000), REQUEST)); + .continueOrPropagate(new RetryableException(-1, null, null, 5000L, REQUEST)); } @Test @@ -83,8 +83,7 @@ public class RetryerTest { Thread.currentThread().interrupt(); RetryableException expected = - new RetryableException(-1, null, null, new Date(System.currentTimeMillis() + 5000), - REQUEST); + new RetryableException(-1, null, null, System.currentTimeMillis() + 5000, REQUEST); try { retryer.continueOrPropagate(expected); Thread.interrupted(); // reset interrupted flag in case it wasn't diff --git a/core/src/test/java/feign/UtilTest.java b/core/src/test/java/feign/UtilTest.java index 85a570df..006dca51 100644 --- a/core/src/test/java/feign/UtilTest.java +++ b/core/src/test/java/feign/UtilTest.java @@ -51,7 +51,7 @@ public class UtilTest { @Test public void removesEvenNumbers() { - Integer[] values = new Integer[] {22, 23}; + Integer[] values = {22, 23}; assertThat(removeValues(values, number -> number % 2 == 0, Integer.class)) .containsExactly(23); } @@ -167,7 +167,7 @@ public class UtilTest { // Act final Object retval = Util.checkNotNull(reference, errorMessageTemplate, errorMessageArgs); // Assert result - assertEquals(new Integer(0), retval); + assertEquals(0, retval); } @Test diff --git a/core/src/test/java/feign/codec/DefaultEncoderTest.java b/core/src/test/java/feign/codec/DefaultEncoderTest.java index 8e0aaac3..12982c0c 100644 --- a/core/src/test/java/feign/codec/DefaultEncoderTest.java +++ b/core/src/test/java/feign/codec/DefaultEncoderTest.java @@ -16,8 +16,8 @@ package feign.codec; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import java.time.Clock; import java.util.Arrays; -import java.util.Date; import feign.RequestTemplate; import static feign.Util.UTF_8; import static org.junit.Assert.assertEquals; @@ -51,6 +51,6 @@ public class DefaultEncoderTest { thrown.expect(EncodeException.class); thrown.expectMessage("is not a type supported by this encoder."); - encoder.encode(new Date(), Date.class, new RequestTemplate()); + encoder.encode(Clock.systemUTC(), Clock.class, new RequestTemplate()); } } diff --git a/core/src/test/java/feign/codec/RetryAfterDecoderTest.java b/core/src/test/java/feign/codec/RetryAfterDecoderTest.java index f1d971cd..724265e5 100644 --- a/core/src/test/java/feign/codec/RetryAfterDecoderTest.java +++ b/core/src/test/java/feign/codec/RetryAfterDecoderTest.java @@ -13,43 +13,50 @@ */ package feign.codec; -import static feign.codec.ErrorDecoder.RetryAfterDecoder.RFC822_FORMAT; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME; +import static org.junit.Assert.*; import feign.codec.ErrorDecoder.RetryAfterDecoder; import java.text.ParseException; +import java.time.ZonedDateTime; +import java.time.format.DateTimeParseException; import org.junit.Test; public class RetryAfterDecoderTest { - private RetryAfterDecoder decoder = new RetryAfterDecoder(RFC822_FORMAT) { + private final RetryAfterDecoder decoder = new RetryAfterDecoder(RFC_1123_DATE_TIME) { protected long currentTimeMillis() { - try { - return RFC822_FORMAT.parse("Sat, 1 Jan 2000 00:00:00 GMT").getTime(); - } catch (ParseException e) { - throw new RuntimeException(e); - } + return parseDateTime("Sat, 1 Jan 2000 00:00:00 GMT"); } }; @Test - public void malformDateFailsGracefully() { - assertFalse(decoder.apply("Fri, 31 Dec 1999 23:59:59 ZBW") != null); + public void malformedDateFailsGracefully() { + assertNull(decoder.apply("Fri, 31 Dec 1999 23:59:59 ZBW")); } @Test public void rfc822Parses() throws ParseException { - assertEquals(RFC822_FORMAT.parse("Fri, 31 Dec 1999 23:59:59 GMT"), + assertEquals(parseDateTime("Fri, 31 Dec 1999 23:59:59 GMT"), decoder.apply("Fri, 31 Dec 1999 23:59:59 GMT")); } @Test public void relativeSecondsParses() throws ParseException { - assertEquals(RFC822_FORMAT.parse("Sun, 2 Jan 2000 00:00:00 GMT"), decoder.apply("86400")); + assertEquals(parseDateTime("Sun, 2 Jan 2000 00:00:00 GMT"), + decoder.apply("86400")); } @Test public void relativeSecondsParseDecimalIntegers() throws ParseException { - assertEquals(RFC822_FORMAT.parse("Sun, 2 Jan 2000 00:00:00 GMT"), decoder.apply("86400.0")); + assertEquals(parseDateTime("Sun, 2 Jan 2000 00:00:00 GMT"), + decoder.apply("86400.0")); + } + + private Long parseDateTime(String text) { + try { + return ZonedDateTime.parse(text, RFC_1123_DATE_TIME).toInstant().toEpochMilli(); + } catch (NullPointerException | DateTimeParseException exception) { + throw new RuntimeException(exception); + } } } diff --git a/hc5/src/test/java/feign/hc5/AsyncApacheHttp5ClientTest.java b/hc5/src/test/java/feign/hc5/AsyncApacheHttp5ClientTest.java index ca9f89e5..02661aff 100644 --- a/hc5/src/test/java/feign/hc5/AsyncApacheHttp5ClientTest.java +++ b/hc5/src/test/java/feign/hc5/AsyncApacheHttp5ClientTest.java @@ -15,17 +15,14 @@ package feign.hc5; import static feign.assertj.MockWebServerAssertions.assertThat; import static java.util.concurrent.TimeUnit.SECONDS; -import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.data.MapEntry.entry; import static org.hamcrest.CoreMatchers.isA; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; -import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.junit.Rule; import org.junit.Test; @@ -33,6 +30,9 @@ import org.junit.rules.ExpectedException; import java.io.IOException; import java.lang.reflect.Type; import java.net.URI; +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicReference; @@ -41,10 +41,8 @@ import feign.Feign.ResponseMappingDecoder; import feign.Request.HttpMethod; import feign.Target.HardCodedTarget; import feign.codec.*; -import feign.jaxrs.JAXRSContract; import feign.querymap.BeanQueryMapEncoder; import feign.querymap.FieldQueryMapEncoder; -import kotlin.text.Charsets; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okio.Buffer; @@ -179,9 +177,9 @@ public class AsyncApacheHttp5ClientTest { final TestInterfaceAsync api = new TestInterfaceAsyncBuilder().target("http://localhost:" + server.getPort()); - final CompletableFuture cf = api.expand(new Date(1234l)); + final CompletableFuture cf = api.expand(new TestClock(1234l)); - assertThat(server.takeRequest()).hasPath("/?date=1234"); + assertThat(server.takeRequest()).hasPath("/?clock=1234"); checkCFCompletedSoon(cf); } @@ -194,9 +192,9 @@ public class AsyncApacheHttp5ClientTest { new TestInterfaceAsyncBuilder().target("http://localhost:" + server.getPort()); final CompletableFuture cf = - api.expandList(Arrays.asList(new Date(1234l), new Date(12345l))); + api.expandList(Arrays.asList(new TestClock(1234l), new TestClock(12345l))); - assertThat(server.takeRequest()).hasPath("/?date=1234&date=12345"); + assertThat(server.takeRequest()).hasPath("/?clock=1234&clock=12345"); checkCFCompletedSoon(cf); } @@ -208,9 +206,9 @@ public class AsyncApacheHttp5ClientTest { final TestInterfaceAsync api = new TestInterfaceAsyncBuilder().target("http://localhost:" + server.getPort()); - final CompletableFuture cf = api.expandList(Arrays.asList(new Date(1234l), null)); + final CompletableFuture cf = api.expandList(Arrays.asList(new TestClock(1234l), null)); - assertThat(server.takeRequest()).hasPath("/?date=1234"); + assertThat(server.takeRequest()).hasPath("/?clock=1234"); checkCFCompletedSoon(cf); } @@ -866,16 +864,17 @@ public class AsyncApacheHttp5ClientTest { CompletableFuture queryParams(@Param("1") String one, @Param("2") Iterable twos); - @RequestLine("POST /?date={date}") - CompletableFuture expand(@Param(value = "date", expander = DateToMillis.class) Date date); + @RequestLine("POST /?clock={clock}") + CompletableFuture expand(@Param(value = "clock", + expander = ClockToMillis.class) Clock clock); - @RequestLine("GET /?date={date}") - CompletableFuture expandList(@Param(value = "date", - expander = DateToMillis.class) List dates); + @RequestLine("GET /?clock={clock}") + CompletableFuture expandList(@Param(value = "clock", + expander = ClockToMillis.class) List clocks); - @RequestLine("GET /?date={date}") - CompletableFuture expandArray(@Param(value = "date", - expander = DateToMillis.class) Date[] dates); + @RequestLine("GET /?clock={clock}") + CompletableFuture expandArray(@Param(value = "clock", + expander = ClockToMillis.class) Clock[] clocks); @RequestLine("GET /") CompletableFuture headerMap(@HeaderMap Map headerMap); @@ -906,11 +905,11 @@ public class AsyncApacheHttp5ClientTest { @RequestLine("GET /") CompletableFuture queryMapPropertyInheritence(@QueryMap ChildPojo object); - class DateToMillis implements Param.Expander { + class ClockToMillis implements Param.Expander { @Override public String expand(Object value) { - return String.valueOf(((Date) value).getTime()); + return String.valueOf(((Clock) value).millis()); } } } @@ -1065,5 +1064,28 @@ public class AsyncApacheHttp5ClientTest { CompletableFuture x(); } + class TestClock extends Clock { + + private long millis; + + public TestClock(long millis) { + this.millis = millis; + } + + @Override + public ZoneId getZone() { + throw new UnsupportedOperationException("This operation is not supported."); + } + + @Override + public Clock withZone(ZoneId zone) { + return this; + } + + @Override + public Instant instant() { + return Instant.ofEpochMilli(millis); + } + } } diff --git a/hystrix/src/test/java/feign/hystrix/HystrixBuilderTest.java b/hystrix/src/test/java/feign/hystrix/HystrixBuilderTest.java index 692c3af0..e4f7a09f 100644 --- a/hystrix/src/test/java/feign/hystrix/HystrixBuilderTest.java +++ b/hystrix/src/test/java/feign/hystrix/HystrixBuilderTest.java @@ -86,7 +86,7 @@ public class HystrixBuilderTest { final HystrixCommand command = api.intCommand(); assertThat(command).isNotNull(); - assertThat(command.execute()).isEqualTo(new Integer(1)); + assertThat(command.execute()).isEqualTo(Integer.valueOf(1)); } @Test @@ -98,7 +98,7 @@ public class HystrixBuilderTest { final HystrixCommand command = api.intCommand(); assertThat(command).isNotNull(); - assertThat(command.execute()).isEqualTo(new Integer(0)); + assertThat(command.execute()).isEqualTo(Integer.valueOf(0)); } @Test @@ -250,7 +250,7 @@ public class HystrixBuilderTest { final TestSubscriber testSubscriber = new TestSubscriber(); observable.subscribe(testSubscriber); testSubscriber.awaitTerminalEvent(); - Assertions.assertThat(testSubscriber.getOnNextEvents().get(0)).isEqualTo(new Integer(1)); + Assertions.assertThat(testSubscriber.getOnNextEvents().get(0)).isEqualTo(Integer.valueOf(1)); } @Test @@ -267,7 +267,7 @@ public class HystrixBuilderTest { final TestSubscriber testSubscriber = new TestSubscriber(); observable.subscribe(testSubscriber); testSubscriber.awaitTerminalEvent(); - Assertions.assertThat(testSubscriber.getOnNextEvents().get(0)).isEqualTo(new Integer(0)); + Assertions.assertThat(testSubscriber.getOnNextEvents().get(0)).isEqualTo(Integer.valueOf(0)); } @Test @@ -373,7 +373,7 @@ public class HystrixBuilderTest { final TestSubscriber testSubscriber = new TestSubscriber(); single.subscribe(testSubscriber); testSubscriber.awaitTerminalEvent(); - Assertions.assertThat(testSubscriber.getOnNextEvents().get(0)).isEqualTo(new Integer(1)); + Assertions.assertThat(testSubscriber.getOnNextEvents().get(0)).isEqualTo(Integer.valueOf(1)); } @Test @@ -390,7 +390,7 @@ public class HystrixBuilderTest { final TestSubscriber testSubscriber = new TestSubscriber(); single.subscribe(testSubscriber); testSubscriber.awaitTerminalEvent(); - Assertions.assertThat(testSubscriber.getOnNextEvents().get(0)).isEqualTo(new Integer(0)); + Assertions.assertThat(testSubscriber.getOnNextEvents().get(0)).isEqualTo(Integer.valueOf(0)); } @Test diff --git a/java11/src/test/java/feign/http2client/test/Http2ClientAsyncTest.java b/java11/src/test/java/feign/http2client/test/Http2ClientAsyncTest.java index 787a7adc..f6235bc5 100644 --- a/java11/src/test/java/feign/http2client/test/Http2ClientAsyncTest.java +++ b/java11/src/test/java/feign/http2client/test/Http2ClientAsyncTest.java @@ -14,7 +14,6 @@ package feign.http2client.test; import static feign.assertj.MockWebServerAssertions.assertThat; -import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.data.MapEntry.entry; import static org.hamcrest.CoreMatchers.isA; import static org.junit.Assert.assertEquals; @@ -24,11 +23,13 @@ import java.io.IOException; import java.lang.reflect.Type; import java.net.URI; import java.nio.charset.StandardCharsets; +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -223,9 +224,9 @@ public class Http2ClientAsyncTest { final TestInterfaceAsync api = newAsyncBuilder().target("http://localhost:" + server.getPort()); - final CompletableFuture cf = api.expand(new Date(1234l)); + final CompletableFuture cf = api.expand(new TestClock(1234l)); - assertThat(server.takeRequest()).hasPath("/?date=1234"); + assertThat(server.takeRequest()).hasPath("/?clock=1234"); checkCFCompletedSoon(cf); } @@ -238,9 +239,9 @@ public class Http2ClientAsyncTest { newAsyncBuilder().target("http://localhost:" + server.getPort()); final CompletableFuture cf = - api.expandList(Arrays.asList(new Date(1234l), new Date(12345l))); + api.expandList(Arrays.asList(new TestClock(1234l), new TestClock(12345l))); - assertThat(server.takeRequest()).hasPath("/?date=1234&date=12345"); + assertThat(server.takeRequest()).hasPath("/?clock=1234&clock=12345"); checkCFCompletedSoon(cf); } @@ -252,9 +253,9 @@ public class Http2ClientAsyncTest { final TestInterfaceAsync api = newAsyncBuilder().target("http://localhost:" + server.getPort()); - final CompletableFuture cf = api.expandList(Arrays.asList(new Date(1234l), null)); + final CompletableFuture cf = api.expandList(Arrays.asList(new TestClock(1234l), null)); - assertThat(server.takeRequest()).hasPath("/?date=1234"); + assertThat(server.takeRequest()).hasPath("/?clock=1234"); checkCFCompletedSoon(cf); } @@ -865,16 +866,17 @@ public class Http2ClientAsyncTest { CompletableFuture queryParams(@Param("1") String one, @Param("2") Iterable twos); - @RequestLine("POST /?date={date}") - CompletableFuture expand(@Param(value = "date", expander = DateToMillis.class) Date date); + @RequestLine("POST /?clock={clock}") + CompletableFuture expand(@Param(value = "clock", + expander = ClockToMillis.class) Clock clock); - @RequestLine("GET /?date={date}") - CompletableFuture expandList(@Param(value = "date", - expander = DateToMillis.class) List dates); + @RequestLine("GET /?clock={clock}") + CompletableFuture expandList(@Param(value = "clock", + expander = ClockToMillis.class) List clocks); - @RequestLine("GET /?date={date}") - CompletableFuture expandArray(@Param(value = "date", - expander = DateToMillis.class) Date[] dates); + @RequestLine("GET /?clock={clock}") + CompletableFuture expandArray(@Param(value = "clock", + expander = ClockToMillis.class) Clock[] clocks); @RequestLine("GET /") CompletableFuture headerMap(@HeaderMap Map headerMap); @@ -905,11 +907,11 @@ public class Http2ClientAsyncTest { @RequestLine("GET /") CompletableFuture queryMapPropertyInheritence(@QueryMap ChildPojo object); - class DateToMillis implements Param.Expander { + class ClockToMillis implements Param.Expander { @Override public String expand(Object value) { - return String.valueOf(((Date) value).getTime()); + return String.valueOf(((Clock) value).millis()); } } } @@ -1062,4 +1064,29 @@ public class Http2ClientAsyncTest { @RequestLine("GET /") CompletableFuture x(); } + + class TestClock extends Clock { + + private long millis; + + public TestClock(long millis) { + this.millis = millis; + } + + @Override + public ZoneId getZone() { + throw new UnsupportedOperationException("This operation is not supported."); + } + + @Override + public Clock withZone(ZoneId zone) { + throw new UnsupportedOperationException("This operation is not supported."); + } + + @Override + public Instant instant() { + return Instant.ofEpochMilli(millis); + } + } + } diff --git a/jaxb-jakarta/src/test/java/feign/jaxb/examples/AWSSignatureVersion4.java b/jaxb-jakarta/src/test/java/feign/jaxb/examples/AWSSignatureVersion4.java index 574c72ca..76c0f1f6 100644 --- a/jaxb-jakarta/src/test/java/feign/jaxb/examples/AWSSignatureVersion4.java +++ b/jaxb-jakarta/src/test/java/feign/jaxb/examples/AWSSignatureVersion4.java @@ -19,9 +19,7 @@ import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.net.URI; import java.security.MessageDigest; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; +import java.time.Clock; import static feign.Util.UTF_8; // http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html @@ -29,10 +27,6 @@ public class AWSSignatureVersion4 { private static final String EMPTY_STRING_HASH = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; - private static final SimpleDateFormat iso8601 = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"); - static { - iso8601.setTimeZone(TimeZone.getTimeZone("GMT")); - } String region = "us-east-1"; String service = "iam"; String accessKey; @@ -125,10 +119,7 @@ public class AWSSignatureVersion4 { String host = URI.create(input.url()).getHost(); - String timestamp; - synchronized (iso8601) { - timestamp = iso8601.format(new Date()); - } + String timestamp = Clock.systemUTC().instant().toString(); String credentialScope = String.format("%s/%s/%s/%s", timestamp.substring(0, 8), region, service, "aws4_request"); diff --git a/jaxb/src/test/java/feign/jaxb/examples/AWSSignatureVersion4.java b/jaxb/src/test/java/feign/jaxb/examples/AWSSignatureVersion4.java index 797acd7c..1425c1eb 100644 --- a/jaxb/src/test/java/feign/jaxb/examples/AWSSignatureVersion4.java +++ b/jaxb/src/test/java/feign/jaxb/examples/AWSSignatureVersion4.java @@ -15,9 +15,7 @@ package feign.jaxb.examples; import java.net.URI; import java.security.MessageDigest; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; +import java.time.Clock; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import feign.Request; @@ -29,10 +27,6 @@ public class AWSSignatureVersion4 { private static final String EMPTY_STRING_HASH = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; - private static final SimpleDateFormat iso8601 = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"); - static { - iso8601.setTimeZone(TimeZone.getTimeZone("GMT")); - } String region = "us-east-1"; String service = "iam"; String accessKey; @@ -125,10 +119,7 @@ public class AWSSignatureVersion4 { String host = URI.create(input.url()).getHost(); - String timestamp; - synchronized (iso8601) { - timestamp = iso8601.format(new Date()); - } + String timestamp = Clock.systemUTC().instant().toString(); String credentialScope = String.format("%s/%s/%s/%s", timestamp.substring(0, 8), region, service, "aws4_request"); diff --git a/jaxrs2/src/main/java/feign/jaxrs2/JAXRSClient.java b/jaxrs2/src/main/java/feign/jaxrs2/JAXRSClient.java index a223c7e4..17af624b 100644 --- a/jaxrs2/src/main/java/feign/jaxrs2/JAXRSClient.java +++ b/jaxrs2/src/main/java/feign/jaxrs2/JAXRSClient.java @@ -86,7 +86,7 @@ public class JAXRSClient implements Client { } try { - return new Integer(headers.getFirst(header)); + return Integer.valueOf(headers.getFirst(header)); } catch (final NumberFormatException e) { // not a number or too big to fit Integer return null; diff --git a/json/src/test/java/feign/json/JsonDecoderTest.java b/json/src/test/java/feign/json/JsonDecoderTest.java index 0d9c5877..797b965b 100644 --- a/json/src/test/java/feign/json/JsonDecoderTest.java +++ b/json/src/test/java/feign/json/JsonDecoderTest.java @@ -26,8 +26,8 @@ import feign.Response; import feign.codec.DecodeException; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.time.Clock; import java.util.Collections; -import java.util.Date; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -147,8 +147,8 @@ public class JsonDecoderTest { .request(request) .build(); Exception exception = assertThrows(DecodeException.class, - () -> new JsonDecoder().decode(response, Date.class)); - assertEquals("class java.util.Date is not a type supported by this decoder.", + () -> new JsonDecoder().decode(response, Clock.class)); + assertEquals("class java.time.Clock is not a type supported by this decoder.", exception.getMessage()); } diff --git a/json/src/test/java/feign/json/JsonEncoderTest.java b/json/src/test/java/feign/json/JsonEncoderTest.java index 0ffc1882..2d995ddb 100644 --- a/json/src/test/java/feign/json/JsonEncoderTest.java +++ b/json/src/test/java/feign/json/JsonEncoderTest.java @@ -20,7 +20,7 @@ import org.json.JSONObject; import org.junit.Before; import org.junit.Test; import org.skyscreamer.jsonassert.JSONAssert; -import java.util.Date; +import java.time.Clock; import static feign.Util.UTF_8; import static org.junit.Assert.*; @@ -64,8 +64,8 @@ public class JsonEncoderTest { @Test public void unknownTypeThrowsEncodeException() { Exception exception = assertThrows(EncodeException.class, - () -> new JsonEncoder().encode("qwerty", Date.class, new RequestTemplate())); - assertEquals("class java.util.Date is not a type supported by this encoder.", + () -> new JsonEncoder().encode("qwerty", Clock.class, new RequestTemplate())); + assertEquals("class java.time.Clock is not a type supported by this encoder.", exception.getMessage()); } diff --git a/okhttp/src/test/java/feign/okhttp/OkHttpClientAsyncTest.java b/okhttp/src/test/java/feign/okhttp/OkHttpClientAsyncTest.java index 6b48dbf2..2f0f6864 100644 --- a/okhttp/src/test/java/feign/okhttp/OkHttpClientAsyncTest.java +++ b/okhttp/src/test/java/feign/okhttp/OkHttpClientAsyncTest.java @@ -23,11 +23,13 @@ import java.io.IOException; import java.lang.reflect.Type; import java.net.URI; import java.nio.charset.StandardCharsets; +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -221,9 +223,9 @@ public class OkHttpClientAsyncTest { final TestInterfaceAsync api = newAsyncBuilder().target("http://localhost:" + server.getPort()); - final CompletableFuture cf = api.expand(new Date(1234l)); + final CompletableFuture cf = api.expand(new TestClock(1234l)); - assertThat(server.takeRequest()).hasPath("/?date=1234"); + assertThat(server.takeRequest()).hasPath("/?clock=1234"); checkCFCompletedSoon(cf); } @@ -236,9 +238,9 @@ public class OkHttpClientAsyncTest { newAsyncBuilder().target("http://localhost:" + server.getPort()); final CompletableFuture cf = - api.expandList(Arrays.asList(new Date(1234l), new Date(12345l))); + api.expandList(Arrays.asList(new TestClock(1234l), new TestClock(12345l))); - assertThat(server.takeRequest()).hasPath("/?date=1234&date=12345"); + assertThat(server.takeRequest()).hasPath("/?clock=1234&clock=12345"); checkCFCompletedSoon(cf); } @@ -250,9 +252,9 @@ public class OkHttpClientAsyncTest { final TestInterfaceAsync api = newAsyncBuilder().target("http://localhost:" + server.getPort()); - final CompletableFuture cf = api.expandList(Arrays.asList(new Date(1234l), null)); + final CompletableFuture cf = api.expandList(Arrays.asList(new TestClock(1234l), null)); - assertThat(server.takeRequest()).hasPath("/?date=1234"); + assertThat(server.takeRequest()).hasPath("/?clock=1234"); checkCFCompletedSoon(cf); } @@ -863,16 +865,17 @@ public class OkHttpClientAsyncTest { CompletableFuture queryParams(@Param("1") String one, @Param("2") Iterable twos); - @RequestLine("POST /?date={date}") - CompletableFuture expand(@Param(value = "date", expander = DateToMillis.class) Date date); + @RequestLine("POST /?clock={clock}") + CompletableFuture expand(@Param(value = "clock", + expander = ClockToMillis.class) Clock clock); - @RequestLine("GET /?date={date}") - CompletableFuture expandList(@Param(value = "date", - expander = DateToMillis.class) List dates); + @RequestLine("GET /?clock={clock}") + CompletableFuture expandList(@Param(value = "clock", + expander = ClockToMillis.class) List clocks); - @RequestLine("GET /?date={date}") - CompletableFuture expandArray(@Param(value = "date", - expander = DateToMillis.class) Date[] dates); + @RequestLine("GET /?clock={clock}") + CompletableFuture expandArray(@Param(value = "clock", + expander = ClockToMillis.class) Clock[] clocks); @RequestLine("GET /") CompletableFuture headerMap(@HeaderMap Map headerMap); @@ -903,11 +906,11 @@ public class OkHttpClientAsyncTest { @RequestLine("GET /") CompletableFuture queryMapPropertyInheritence(@QueryMap ChildPojo object); - class DateToMillis implements Param.Expander { + class ClockToMillis implements Param.Expander { @Override public String expand(Object value) { - return String.valueOf(((Date) value).getTime()); + return String.valueOf(((Clock) value).millis()); } } } @@ -1025,4 +1028,29 @@ public class OkHttpClientAsyncTest { static final class ExtendedCF extends CompletableFuture { } + + class TestClock extends Clock { + + private long millis; + + public TestClock(long millis) { + this.millis = millis; + } + + @Override + public ZoneId getZone() { + throw new UnsupportedOperationException("This operation is not supported."); + } + + @Override + public Clock withZone(ZoneId zone) { + return this; + } + + @Override + public Instant instant() { + return Instant.ofEpochMilli(millis); + } + } + } diff --git a/pom.xml b/pom.xml index f8b8461b..45d4700a 100644 --- a/pom.xml +++ b/pom.xml @@ -630,6 +630,8 @@ NOTICE OSSMETADATA **/*.md + **/*.asciidoc + **/*.iuml bnd.bnd travis/** src/test/resources/** diff --git a/sax/src/test/java/feign/sax/examples/AWSSignatureVersion4.java b/sax/src/test/java/feign/sax/examples/AWSSignatureVersion4.java index 0b02a514..7b622886 100644 --- a/sax/src/test/java/feign/sax/examples/AWSSignatureVersion4.java +++ b/sax/src/test/java/feign/sax/examples/AWSSignatureVersion4.java @@ -15,9 +15,7 @@ package feign.sax.examples; import java.net.URI; import java.security.MessageDigest; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; +import java.time.Clock; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import feign.Request; @@ -29,10 +27,6 @@ public class AWSSignatureVersion4 { private static final String EMPTY_STRING_HASH = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; - private static final SimpleDateFormat iso8601 = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"); - static { - iso8601.setTimeZone(TimeZone.getTimeZone("GMT")); - } String region = "us-east-1"; String service = "iam"; String accessKey; @@ -126,10 +120,7 @@ public class AWSSignatureVersion4 { String host = URI.create(input.url()).getHost(); - String timestamp; - synchronized (iso8601) { - timestamp = iso8601.format(new Date()); - } + String timestamp = Clock.systemUTC().instant().toString(); String credentialScope = String.format("%s/%s/%s/%s", timestamp.substring(0, 8), region, service, "aws4_request");