Browse Source

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
pull/2173/head
Vitalij Berdinskih 1 year ago committed by GitHub
parent
commit
be25759e93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      core/src/main/java/feign/FeignException.java
  2. 31
      core/src/main/java/feign/RetryableException.java
  3. 8
      core/src/main/java/feign/Retryer.java
  4. 39
      core/src/main/java/feign/codec/ErrorDecoder.java
  5. 76
      core/src/test/java/feign/AsyncFeignTest.java
  6. 40
      core/src/test/java/feign/DefaultContractTest.java
  7. 69
      core/src/test/java/feign/FeignTest.java
  8. 57
      core/src/test/java/feign/FeignUnderAsyncTest.java
  9. 4
      core/src/test/java/feign/RetryableExceptionTest.java
  10. 13
      core/src/test/java/feign/RetryerTest.java
  11. 4
      core/src/test/java/feign/UtilTest.java
  12. 4
      core/src/test/java/feign/codec/DefaultEncoderTest.java
  13. 35
      core/src/test/java/feign/codec/RetryAfterDecoderTest.java
  14. 64
      hc5/src/test/java/feign/hc5/AsyncApacheHttp5ClientTest.java
  15. 12
      hystrix/src/test/java/feign/hystrix/HystrixBuilderTest.java
  16. 63
      java11/src/test/java/feign/http2client/test/Http2ClientAsyncTest.java
  17. 13
      jaxb-jakarta/src/test/java/feign/jaxb/examples/AWSSignatureVersion4.java
  18. 13
      jaxb/src/test/java/feign/jaxb/examples/AWSSignatureVersion4.java
  19. 2
      jaxrs2/src/main/java/feign/jaxrs2/JAXRSClient.java
  20. 6
      json/src/test/java/feign/json/JsonDecoderTest.java
  21. 6
      json/src/test/java/feign/json/JsonEncoderTest.java
  22. 62
      okhttp/src/test/java/feign/okhttp/OkHttpClientAsyncTest.java
  23. 2
      pom.xml
  24. 13
      sax/src/test/java/feign/sax/examples/AWSSignatureVersion4.java

3
core/src/main/java/feign/FeignException.java

@ -271,12 +271,13 @@ public class FeignException extends RuntimeException { @@ -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 {

31
core/src/main/java/feign/RetryableException.java

@ -24,7 +24,7 @@ import java.util.Map; @@ -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 { @@ -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 { @@ -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 { @@ -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<String, Collection<String>> 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<String, Collection<String>> responseHeaders) {
super(status, message, request, responseBody, responseHeaders);
@ -63,8 +88,8 @@ public class RetryableException extends FeignException { @@ -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() {

8
core/src/main/java/feign/Retryer.java

@ -22,7 +22,7 @@ import static java.util.concurrent.TimeUnit.SECONDS; @@ -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 { @@ -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 { @@ -79,7 +79,7 @@ public interface Retryer extends Cloneable {
}
/**
* Calculates the time interval to a retry attempt. <br>
* Calculates the time interval to a retry attempt.<br>
* 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 { @@ -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

39
core/src/main/java/feign/codec/ErrorDecoder.java

@ -16,16 +16,15 @@ package feign.codec; @@ -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 { @@ -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 { @@ -125,21 +124,19 @@ public interface ErrorDecoder {
}
/**
* Decodes a {@link feign.Util#RETRY_AFTER} header into an absolute date, if possible. <br>
* Decodes a {@link feign.Util#RETRY_AFTER} header into an epoch millisecond, if possible.<br>
* See <a href="https://tools.ietf.org/html/rfc2616#section-14.37">Retry-After format</a>
*/
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 { @@ -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
* <a href="https://tools.ietf.org/html/rfc2616#section-14.37" >Retry-After format</a>
*/
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;
}
}
}

76
core/src/test/java/feign/AsyncFeignTest.java

@ -16,7 +16,6 @@ package feign; @@ -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; @@ -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; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -1022,16 +1024,17 @@ public class AsyncFeignTest {
CompletableFuture<Response> queryParams(@Param("1") String one,
@Param("2") Iterable<String> twos);
@RequestLine("POST /?date={date}")
CompletableFuture<Void> expand(@Param(value = "date", expander = DateToMillis.class) Date date);
@RequestLine("POST /?clock={clock}")
CompletableFuture<Void> expand(@Param(value = "clock",
expander = ClockToMillis.class) Clock clock);
@RequestLine("GET /?date={date}")
CompletableFuture<Void> expandList(@Param(value = "date",
expander = DateToMillis.class) List<Date> dates);
@RequestLine("GET /?clock={clock}")
CompletableFuture<Void> expandList(@Param(value = "clock",
expander = ClockToMillis.class) List<Clock> clocks);
@RequestLine("GET /?date={date}")
CompletableFuture<Void> expandArray(@Param(value = "date",
expander = DateToMillis.class) Date[] dates);
@RequestLine("GET /?clock={clock}")
CompletableFuture<Void> expandArray(@Param(value = "clock",
expander = ClockToMillis.class) Clock[] clocks);
@RequestLine("GET /")
CompletableFuture<Void> headerMap(@HeaderMap Map<String, Object> headerMap);
@ -1062,11 +1065,11 @@ public class AsyncFeignTest { @@ -1062,11 +1065,11 @@ public class AsyncFeignTest {
@RequestLine("GET /")
CompletableFuture<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());
}
}
}
@ -1249,5 +1252,28 @@ public class AsyncFeignTest { @@ -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);
}
}
}

40
core/src/test/java/feign/DefaultContractTest.java

@ -20,6 +20,9 @@ import org.junit.Test; @@ -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 { @@ -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 { @@ -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 { @@ -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);
}
}
}

69
core/src/test/java/feign/FeignTest.java

@ -39,6 +39,9 @@ import org.mockito.ArgumentMatchers; @@ -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.*; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -1171,14 +1175,14 @@ public class FeignTest {
@RequestLine("GET /")
Response queryMapWithArrayValues(@QueryMap Map<String, String[]> 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<Date> dates);
@RequestLine("GET /?clock={clock}")
void expandList(@Param(value = "clock", expander = ClockToMillis.class) List<Clock> 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<String, Object> headerMap);
@ -1219,11 +1223,11 @@ public class FeignTest { @@ -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 { @@ -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);
}
}
}

57
core/src/test/java/feign/FeignUnderAsyncTest.java

@ -36,11 +36,13 @@ import org.junit.rules.ExpectedException; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -857,14 +859,14 @@ public class FeignUnderAsyncTest {
@RequestLine("GET /?1={1}&2={2}")
Response queryParams(@Param("1") String one, @Param("2") Iterable<String> 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<Date> dates);
@RequestLine("GET /?clock={clock}")
void expandList(@Param(value = "clock", expander = ClockToMillis.class) List<Clock> 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<String, Object> headerMap);
@ -895,11 +897,11 @@ public class FeignUnderAsyncTest { @@ -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 { @@ -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);
}
}
}

4
core/src/test/java/feign/RetryableExceptionTest.java

@ -24,6 +24,7 @@ public class RetryableExceptionTest { @@ -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 { @@ -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"));

13
core/src/test/java/feign/RetryerTest.java

@ -18,7 +18,6 @@ import org.junit.Rule; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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

4
core/src/test/java/feign/UtilTest.java

@ -51,7 +51,7 @@ public class UtilTest { @@ -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 { @@ -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

4
core/src/test/java/feign/codec/DefaultEncoderTest.java

@ -16,8 +16,8 @@ package feign.codec; @@ -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 { @@ -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());
}
}

35
core/src/test/java/feign/codec/RetryAfterDecoderTest.java

@ -13,43 +13,50 @@ @@ -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);
}
}
}

64
hc5/src/test/java/feign/hc5/AsyncApacheHttp5ClientTest.java

@ -15,17 +15,14 @@ package feign.hc5; @@ -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; @@ -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; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -866,16 +864,17 @@ public class AsyncApacheHttp5ClientTest {
CompletableFuture<Response> queryParams(@Param("1") String one,
@Param("2") Iterable<String> twos);
@RequestLine("POST /?date={date}")
CompletableFuture<Void> expand(@Param(value = "date", expander = DateToMillis.class) Date date);
@RequestLine("POST /?clock={clock}")
CompletableFuture<Void> expand(@Param(value = "clock",
expander = ClockToMillis.class) Clock clock);
@RequestLine("GET /?date={date}")
CompletableFuture<Void> expandList(@Param(value = "date",
expander = DateToMillis.class) List<Date> dates);
@RequestLine("GET /?clock={clock}")
CompletableFuture<Void> expandList(@Param(value = "clock",
expander = ClockToMillis.class) List<Clock> clocks);
@RequestLine("GET /?date={date}")
CompletableFuture<Void> expandArray(@Param(value = "date",
expander = DateToMillis.class) Date[] dates);
@RequestLine("GET /?clock={clock}")
CompletableFuture<Void> expandArray(@Param(value = "clock",
expander = ClockToMillis.class) Clock[] clocks);
@RequestLine("GET /")
CompletableFuture<Void> headerMap(@HeaderMap Map<String, Object> headerMap);
@ -906,11 +905,11 @@ public class AsyncApacheHttp5ClientTest { @@ -906,11 +905,11 @@ public class AsyncApacheHttp5ClientTest {
@RequestLine("GET /")
CompletableFuture<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());
}
}
}
@ -1065,5 +1064,28 @@ public class AsyncApacheHttp5ClientTest { @@ -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);
}
}
}

12
hystrix/src/test/java/feign/hystrix/HystrixBuilderTest.java

@ -86,7 +86,7 @@ public class HystrixBuilderTest { @@ -86,7 +86,7 @@ public class HystrixBuilderTest {
final HystrixCommand<Integer> 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 { @@ -98,7 +98,7 @@ public class HystrixBuilderTest {
final HystrixCommand<Integer> 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 { @@ -250,7 +250,7 @@ public class HystrixBuilderTest {
final TestSubscriber<Integer> testSubscriber = new TestSubscriber<Integer>();
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 { @@ -267,7 +267,7 @@ public class HystrixBuilderTest {
final TestSubscriber<Integer> testSubscriber = new TestSubscriber<Integer>();
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 { @@ -373,7 +373,7 @@ public class HystrixBuilderTest {
final TestSubscriber<Integer> testSubscriber = new TestSubscriber<Integer>();
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 { @@ -390,7 +390,7 @@ public class HystrixBuilderTest {
final TestSubscriber<Integer> testSubscriber = new TestSubscriber<Integer>();
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

63
java11/src/test/java/feign/http2client/test/Http2ClientAsyncTest.java

@ -14,7 +14,6 @@ @@ -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; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -865,16 +866,17 @@ public class Http2ClientAsyncTest {
CompletableFuture<Response> queryParams(@Param("1") String one,
@Param("2") Iterable<String> twos);
@RequestLine("POST /?date={date}")
CompletableFuture<Void> expand(@Param(value = "date", expander = DateToMillis.class) Date date);
@RequestLine("POST /?clock={clock}")
CompletableFuture<Void> expand(@Param(value = "clock",
expander = ClockToMillis.class) Clock clock);
@RequestLine("GET /?date={date}")
CompletableFuture<Void> expandList(@Param(value = "date",
expander = DateToMillis.class) List<Date> dates);
@RequestLine("GET /?clock={clock}")
CompletableFuture<Void> expandList(@Param(value = "clock",
expander = ClockToMillis.class) List<Clock> clocks);
@RequestLine("GET /?date={date}")
CompletableFuture<Void> expandArray(@Param(value = "date",
expander = DateToMillis.class) Date[] dates);
@RequestLine("GET /?clock={clock}")
CompletableFuture<Void> expandArray(@Param(value = "clock",
expander = ClockToMillis.class) Clock[] clocks);
@RequestLine("GET /")
CompletableFuture<Void> headerMap(@HeaderMap Map<String, Object> headerMap);
@ -905,11 +907,11 @@ public class Http2ClientAsyncTest { @@ -905,11 +907,11 @@ public class Http2ClientAsyncTest {
@RequestLine("GET /")
CompletableFuture<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());
}
}
}
@ -1062,4 +1064,29 @@ public class Http2ClientAsyncTest { @@ -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);
}
}
}

13
jaxb-jakarta/src/test/java/feign/jaxb/examples/AWSSignatureVersion4.java

@ -19,9 +19,7 @@ import javax.crypto.Mac; @@ -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 { @@ -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 { @@ -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");

13
jaxb/src/test/java/feign/jaxb/examples/AWSSignatureVersion4.java

@ -15,9 +15,7 @@ package feign.jaxb.examples; @@ -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 { @@ -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 { @@ -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");

2
jaxrs2/src/main/java/feign/jaxrs2/JAXRSClient.java

@ -86,7 +86,7 @@ public class JAXRSClient implements Client { @@ -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;

6
json/src/test/java/feign/json/JsonDecoderTest.java

@ -26,8 +26,8 @@ import feign.Response; @@ -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 { @@ -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());
}

6
json/src/test/java/feign/json/JsonEncoderTest.java

@ -20,7 +20,7 @@ import org.json.JSONObject; @@ -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 { @@ -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());
}

62
okhttp/src/test/java/feign/okhttp/OkHttpClientAsyncTest.java

@ -23,11 +23,13 @@ import java.io.IOException; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -863,16 +865,17 @@ public class OkHttpClientAsyncTest {
CompletableFuture<Response> queryParams(@Param("1") String one,
@Param("2") Iterable<String> twos);
@RequestLine("POST /?date={date}")
CompletableFuture<Void> expand(@Param(value = "date", expander = DateToMillis.class) Date date);
@RequestLine("POST /?clock={clock}")
CompletableFuture<Void> expand(@Param(value = "clock",
expander = ClockToMillis.class) Clock clock);
@RequestLine("GET /?date={date}")
CompletableFuture<Void> expandList(@Param(value = "date",
expander = DateToMillis.class) List<Date> dates);
@RequestLine("GET /?clock={clock}")
CompletableFuture<Void> expandList(@Param(value = "clock",
expander = ClockToMillis.class) List<Clock> clocks);
@RequestLine("GET /?date={date}")
CompletableFuture<Void> expandArray(@Param(value = "date",
expander = DateToMillis.class) Date[] dates);
@RequestLine("GET /?clock={clock}")
CompletableFuture<Void> expandArray(@Param(value = "clock",
expander = ClockToMillis.class) Clock[] clocks);
@RequestLine("GET /")
CompletableFuture<Void> headerMap(@HeaderMap Map<String, Object> headerMap);
@ -903,11 +906,11 @@ public class OkHttpClientAsyncTest { @@ -903,11 +906,11 @@ public class OkHttpClientAsyncTest {
@RequestLine("GET /")
CompletableFuture<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());
}
}
}
@ -1025,4 +1028,29 @@ public class OkHttpClientAsyncTest { @@ -1025,4 +1028,29 @@ public class OkHttpClientAsyncTest {
static final class ExtendedCF<T> extends CompletableFuture<T> {
}
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);
}
}
}

2
pom.xml

@ -630,6 +630,8 @@ @@ -630,6 +630,8 @@
<exclude>NOTICE</exclude>
<exclude>OSSMETADATA</exclude>
<exclude>**/*.md</exclude>
<exclude>**/*.asciidoc</exclude>
<exclude>**/*.iuml</exclude>
<exclude>bnd.bnd</exclude>
<exclude>travis/**</exclude>
<exclude>src/test/resources/**</exclude>

13
sax/src/test/java/feign/sax/examples/AWSSignatureVersion4.java

@ -15,9 +15,7 @@ package feign.sax.examples; @@ -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 { @@ -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 { @@ -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");

Loading…
Cancel
Save