From a95db4fe73200e9722df00dc49e23503a64ac1ab Mon Sep 17 00:00:00 2001 From: mdabrowski-eu Date: Sat, 15 Jan 2022 03:27:58 +0100 Subject: [PATCH 1/3] BAEL-5301 Retrying Feign Calls --- .../feign/retry/Custom5xxErrorDecoder.java | 27 ++++++++++ .../baeldung/feign/retry/NaiveRetryer.java | 21 ++++++++ .../retry/ResilientFeignClientBuilder.java | 28 ++++++++++ .../retry/Custom5xxErrorDecoderUnitTest.java | 51 +++++++++++++++++++ 4 files changed, 127 insertions(+) create mode 100644 feign/src/main/java/com/baeldung/feign/retry/Custom5xxErrorDecoder.java create mode 100644 feign/src/main/java/com/baeldung/feign/retry/NaiveRetryer.java create mode 100644 feign/src/main/java/com/baeldung/feign/retry/ResilientFeignClientBuilder.java create mode 100644 feign/src/test/java/com/baeldung/feign/retry/Custom5xxErrorDecoderUnitTest.java diff --git a/feign/src/main/java/com/baeldung/feign/retry/Custom5xxErrorDecoder.java b/feign/src/main/java/com/baeldung/feign/retry/Custom5xxErrorDecoder.java new file mode 100644 index 0000000000..c4edfe219f --- /dev/null +++ b/feign/src/main/java/com/baeldung/feign/retry/Custom5xxErrorDecoder.java @@ -0,0 +1,27 @@ +package com.baeldung.feign.retry; + + +import feign.FeignException; +import feign.Response; +import feign.RetryableException; +import feign.codec.ErrorDecoder; + +import static feign.FeignException.errorStatus; + +public class Custom5xxErrorDecoder implements ErrorDecoder { + @Override + public Exception decode(String methodKey, Response response) { + FeignException exception = errorStatus(methodKey, response); + int status = response.status(); + if (status >= 500) { + return new RetryableException( + response.status(), + exception.getMessage(), + response.request().httpMethod(), + exception, + null, + response.request()); + } + return exception; + } +} diff --git a/feign/src/main/java/com/baeldung/feign/retry/NaiveRetryer.java b/feign/src/main/java/com/baeldung/feign/retry/NaiveRetryer.java new file mode 100644 index 0000000000..5a3c13fadd --- /dev/null +++ b/feign/src/main/java/com/baeldung/feign/retry/NaiveRetryer.java @@ -0,0 +1,21 @@ +package com.baeldung.feign.retry; + +import feign.RetryableException; +import feign.Retryer; + +public class NaiveRetryer implements feign.Retryer { + @Override + public void continueOrPropagate(RetryableException e) { + try { + Thread.sleep(1000L); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + throw e; + } + } + + @Override + public Retryer clone() { + return new NaiveRetryer(); + } +} diff --git a/feign/src/main/java/com/baeldung/feign/retry/ResilientFeignClientBuilder.java b/feign/src/main/java/com/baeldung/feign/retry/ResilientFeignClientBuilder.java new file mode 100644 index 0000000000..edcaf13242 --- /dev/null +++ b/feign/src/main/java/com/baeldung/feign/retry/ResilientFeignClientBuilder.java @@ -0,0 +1,28 @@ +package com.baeldung.feign.retry; + +import com.baeldung.feign.clients.BookClient; +import feign.Feign; +import feign.Logger; +import feign.Retryer; +import feign.gson.GsonDecoder; +import feign.gson.GsonEncoder; +import feign.okhttp.OkHttpClient; +import feign.slf4j.Slf4jLogger; +import lombok.Getter; + +import java.util.concurrent.TimeUnit; + +@Getter +public class ResilientFeignClientBuilder { + public BookClient bookClient = createClient(BookClient.class, "http://localhost:8081/api/books"); + + public static T createClient(Class type, String uri) { + return Feign.builder() + .client(new OkHttpClient()) + .encoder(new GsonEncoder()) + .decoder(new GsonDecoder()) + .retryer(new Retryer.Default(100L, TimeUnit.SECONDS.toMillis(3L), 5)) + .errorDecoder(new Custom5xxErrorDecoder()) + .target(type, uri); + } +} diff --git a/feign/src/test/java/com/baeldung/feign/retry/Custom5xxErrorDecoderUnitTest.java b/feign/src/test/java/com/baeldung/feign/retry/Custom5xxErrorDecoderUnitTest.java new file mode 100644 index 0000000000..0b67090268 --- /dev/null +++ b/feign/src/test/java/com/baeldung/feign/retry/Custom5xxErrorDecoderUnitTest.java @@ -0,0 +1,51 @@ +package com.baeldung.feign.retry; + +import feign.*; +import feign.codec.ErrorDecoder; +import org.jetbrains.annotations.NotNull; +import org.junit.Test; + +import java.nio.charset.Charset; +import java.util.Collection; +import java.util.HashMap; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class Custom5xxErrorDecoderUnitTest { + @Test + public void given5xxResponse_whenDecode_thenReturnRetryableException() { + // given + ErrorDecoder decoder = new Custom5xxErrorDecoder(); + Response response = responseStub(500); + + // when + Exception exception = decoder.decode("GET", response); + + // then + assertTrue(exception instanceof RetryableException); + } + + @Test + public void given4xxResponse_whenDecode_thenReturnFeignException() { + // given + ErrorDecoder decoder = new Custom5xxErrorDecoder(); + Response response = responseStub(400); + + // when + Exception exception = decoder.decode("GET", response); + + // then + assertTrue(exception instanceof FeignException); + assertFalse(exception instanceof RetryableException); + } + + @NotNull + private Response responseStub(int status) { + return Response.builder() + .request(Request.create( + Request.HttpMethod.GET, "url", new HashMap>(), new byte[0], Charset.defaultCharset(), new RequestTemplate())) + .status(status) + .build(); + } +} From e677f7a3ea880349d66e1c5813c9bc80f8ba9a72 Mon Sep 17 00:00:00 2001 From: mdabrowski-eu Date: Mon, 24 Jan 2022 23:27:48 +0100 Subject: [PATCH 2/3] BAEL-5301 Fix indentation --- .../baeldung/feign/retry/Custom5xxErrorDecoder.java | 12 ++++++------ .../feign/retry/ResilientFeignClientBuilder.java | 12 ++++++------ .../feign/retry/Custom5xxErrorDecoderUnitTest.java | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/feign/src/main/java/com/baeldung/feign/retry/Custom5xxErrorDecoder.java b/feign/src/main/java/com/baeldung/feign/retry/Custom5xxErrorDecoder.java index c4edfe219f..6ff3c42ba1 100644 --- a/feign/src/main/java/com/baeldung/feign/retry/Custom5xxErrorDecoder.java +++ b/feign/src/main/java/com/baeldung/feign/retry/Custom5xxErrorDecoder.java @@ -15,12 +15,12 @@ public class Custom5xxErrorDecoder implements ErrorDecoder { int status = response.status(); if (status >= 500) { return new RetryableException( - response.status(), - exception.getMessage(), - response.request().httpMethod(), - exception, - null, - response.request()); + response.status(), + exception.getMessage(), + response.request().httpMethod(), + exception, + null, + response.request()); } return exception; } diff --git a/feign/src/main/java/com/baeldung/feign/retry/ResilientFeignClientBuilder.java b/feign/src/main/java/com/baeldung/feign/retry/ResilientFeignClientBuilder.java index edcaf13242..d8285d362a 100644 --- a/feign/src/main/java/com/baeldung/feign/retry/ResilientFeignClientBuilder.java +++ b/feign/src/main/java/com/baeldung/feign/retry/ResilientFeignClientBuilder.java @@ -18,11 +18,11 @@ public class ResilientFeignClientBuilder { public static T createClient(Class type, String uri) { return Feign.builder() - .client(new OkHttpClient()) - .encoder(new GsonEncoder()) - .decoder(new GsonDecoder()) - .retryer(new Retryer.Default(100L, TimeUnit.SECONDS.toMillis(3L), 5)) - .errorDecoder(new Custom5xxErrorDecoder()) - .target(type, uri); + .client(new OkHttpClient()) + .encoder(new GsonEncoder()) + .decoder(new GsonDecoder()) + .retryer(new Retryer.Default(100L, TimeUnit.SECONDS.toMillis(3L), 5)) + .errorDecoder(new Custom5xxErrorDecoder()) + .target(type, uri); } } diff --git a/feign/src/test/java/com/baeldung/feign/retry/Custom5xxErrorDecoderUnitTest.java b/feign/src/test/java/com/baeldung/feign/retry/Custom5xxErrorDecoderUnitTest.java index 0b67090268..b51a1a65b3 100644 --- a/feign/src/test/java/com/baeldung/feign/retry/Custom5xxErrorDecoderUnitTest.java +++ b/feign/src/test/java/com/baeldung/feign/retry/Custom5xxErrorDecoderUnitTest.java @@ -44,7 +44,7 @@ public class Custom5xxErrorDecoderUnitTest { private Response responseStub(int status) { return Response.builder() .request(Request.create( - Request.HttpMethod.GET, "url", new HashMap>(), new byte[0], Charset.defaultCharset(), new RequestTemplate())) + Request.HttpMethod.GET, "url", new HashMap>(), new byte[0], Charset.defaultCharset(), new RequestTemplate())) .status(status) .build(); } From e046bc734b03023f959d008e143e2d8337cc0e6b Mon Sep 17 00:00:00 2001 From: mdabrowski-eu Date: Sat, 29 Jan 2022 23:23:37 +0100 Subject: [PATCH 3/3] BAEL-5301 Fix indentation --- .../feign/retry/Custom5xxErrorDecoderUnitTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/feign/src/test/java/com/baeldung/feign/retry/Custom5xxErrorDecoderUnitTest.java b/feign/src/test/java/com/baeldung/feign/retry/Custom5xxErrorDecoderUnitTest.java index b51a1a65b3..8c43b42b76 100644 --- a/feign/src/test/java/com/baeldung/feign/retry/Custom5xxErrorDecoderUnitTest.java +++ b/feign/src/test/java/com/baeldung/feign/retry/Custom5xxErrorDecoderUnitTest.java @@ -43,9 +43,9 @@ public class Custom5xxErrorDecoderUnitTest { @NotNull private Response responseStub(int status) { return Response.builder() - .request(Request.create( - Request.HttpMethod.GET, "url", new HashMap>(), new byte[0], Charset.defaultCharset(), new RequestTemplate())) - .status(status) - .build(); + .request(Request.create( + Request.HttpMethod.GET, "url", new HashMap<>(), new byte[0], Charset.defaultCharset(), new RequestTemplate())) + .status(status) + .build(); } }