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(); + } +}