diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 5be83808ed7a3..642f153fc802d 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -56,7 +56,7 @@ 4.0.0 3.10.0 2.9.0 - 6.3.0 + 6.4.0 4.5.3 2.1.2 1.0.13 diff --git a/extensions/smallrye-fault-tolerance/deployment/src/main/java/io/quarkus/smallrye/faulttolerance/deployment/DotNames.java b/extensions/smallrye-fault-tolerance/deployment/src/main/java/io/quarkus/smallrye/faulttolerance/deployment/DotNames.java index c18bc940a2535..556d0cd9c474a 100644 --- a/extensions/smallrye-fault-tolerance/deployment/src/main/java/io/quarkus/smallrye/faulttolerance/deployment/DotNames.java +++ b/extensions/smallrye-fault-tolerance/deployment/src/main/java/io/quarkus/smallrye/faulttolerance/deployment/DotNames.java @@ -16,6 +16,8 @@ import io.smallrye.faulttolerance.FaultToleranceInterceptor; import io.smallrye.faulttolerance.api.ApplyFaultTolerance; import io.smallrye.faulttolerance.api.AsynchronousNonBlocking; +import io.smallrye.faulttolerance.api.BeforeRetry; +import io.smallrye.faulttolerance.api.BeforeRetryHandler; import io.smallrye.faulttolerance.api.CircuitBreakerName; import io.smallrye.faulttolerance.api.CustomBackoff; import io.smallrye.faulttolerance.api.CustomBackoffStrategy; @@ -28,6 +30,7 @@ public final class DotNames { public static final DotName OBJECT = DotName.createSimple(Object.class); public static final DotName FALLBACK_HANDLER = DotName.createSimple(FallbackHandler.class); + public static final DotName BEFORE_RETRY_HANDLER = DotName.createSimple(BeforeRetryHandler.class); public static final DotName FAULT_TOLERANCE_INTERCEPTOR = DotName.createSimple(FaultToleranceInterceptor.class); @@ -54,9 +57,11 @@ public final class DotNames { public static final DotName CUSTOM_BACKOFF = DotName.createSimple(CustomBackoff.class); public static final DotName CUSTOM_BACKOFF_STRATEGY = DotName.createSimple(CustomBackoffStrategy.class); public static final DotName RETRY_WHEN = DotName.createSimple(RetryWhen.class); + public static final DotName BEFORE_RETRY = DotName.createSimple(BeforeRetry.class); - // certain SmallRye annotations (@CircuitBreakerName, @[Non]Blocking, @*Backoff, @RetryWhen) alone do _not_ trigger - // the fault tolerance interceptor, only in combination with other fault tolerance annotations + // certain SmallRye annotations (@CircuitBreakerName, @[Non]Blocking, @*Backoff, @RetryWhen, @BeforeRetry) + // do _not_ trigger the fault tolerance interceptor alone, only in combination + // with other fault tolerance annotations public static final Set FT_ANNOTATIONS = Set.of(APPLY_FAULT_TOLERANCE, ASYNCHRONOUS, ASYNCHRONOUS_NON_BLOCKING, BULKHEAD, CIRCUIT_BREAKER, FALLBACK, RATE_LIMIT, RETRY, TIMEOUT); diff --git a/extensions/smallrye-fault-tolerance/deployment/src/main/java/io/quarkus/smallrye/faulttolerance/deployment/FaultToleranceScanner.java b/extensions/smallrye-fault-tolerance/deployment/src/main/java/io/quarkus/smallrye/faulttolerance/deployment/FaultToleranceScanner.java index f4105a2f2d516..96d96c90d853b 100644 --- a/extensions/smallrye-fault-tolerance/deployment/src/main/java/io/quarkus/smallrye/faulttolerance/deployment/FaultToleranceScanner.java +++ b/extensions/smallrye-fault-tolerance/deployment/src/main/java/io/quarkus/smallrye/faulttolerance/deployment/FaultToleranceScanner.java @@ -26,6 +26,7 @@ import io.smallrye.common.annotation.NonBlocking; import io.smallrye.faulttolerance.api.ApplyFaultTolerance; import io.smallrye.faulttolerance.api.AsynchronousNonBlocking; +import io.smallrye.faulttolerance.api.BeforeRetry; import io.smallrye.faulttolerance.api.CircuitBreakerName; import io.smallrye.faulttolerance.api.CustomBackoff; import io.smallrye.faulttolerance.api.ExponentialBackoff; @@ -136,6 +137,7 @@ FaultToleranceMethod createFaultToleranceMethod(ClassInfo beanClass, MethodInfo result.exponentialBackoff = getAnnotation(ExponentialBackoff.class, method, beanClass, annotationsPresentDirectly); result.fibonacciBackoff = getAnnotation(FibonacciBackoff.class, method, beanClass, annotationsPresentDirectly); result.retryWhen = getAnnotation(RetryWhen.class, method, beanClass, annotationsPresentDirectly); + result.beforeRetry = getAnnotation(BeforeRetry.class, method, beanClass, annotationsPresentDirectly); result.annotationsPresentDirectly = annotationsPresentDirectly; diff --git a/extensions/smallrye-fault-tolerance/deployment/src/main/java/io/quarkus/smallrye/faulttolerance/deployment/SmallRyeFaultToleranceProcessor.java b/extensions/smallrye-fault-tolerance/deployment/src/main/java/io/quarkus/smallrye/faulttolerance/deployment/SmallRyeFaultToleranceProcessor.java index 6bd18bf866aff..f6c4778e6f508 100644 --- a/extensions/smallrye-fault-tolerance/deployment/src/main/java/io/quarkus/smallrye/faulttolerance/deployment/SmallRyeFaultToleranceProcessor.java +++ b/extensions/smallrye-fault-tolerance/deployment/src/main/java/io/quarkus/smallrye/faulttolerance/deployment/SmallRyeFaultToleranceProcessor.java @@ -58,6 +58,7 @@ import io.quarkus.runtime.metrics.MetricsFactory; import io.quarkus.smallrye.faulttolerance.deployment.devui.FaultToleranceInfoBuildItem; import io.quarkus.smallrye.faulttolerance.runtime.QuarkusAsyncExecutorProvider; +import io.quarkus.smallrye.faulttolerance.runtime.QuarkusBeforeRetryHandlerProvider; import io.quarkus.smallrye.faulttolerance.runtime.QuarkusExistingCircuitBreakerNames; import io.quarkus.smallrye.faulttolerance.runtime.QuarkusFallbackHandlerProvider; import io.quarkus.smallrye.faulttolerance.runtime.QuarkusFaultToleranceOperationProvider; @@ -102,19 +103,22 @@ public void build(BuildProducer annotationsTran IndexView index = combinedIndexBuildItem.getIndex(); - // Add reflective access to fallback handlers - Set fallbackHandlers = new HashSet<>(); + // Add reflective access to fallback handlers and before retry handlers + Set handlers = new HashSet<>(); for (ClassInfo implementor : index.getAllKnownImplementors(DotNames.FALLBACK_HANDLER)) { - fallbackHandlers.add(implementor.name().toString()); + handlers.add(implementor.name().toString()); } - if (!fallbackHandlers.isEmpty()) { - AdditionalBeanBuildItem.Builder fallbackHandlersBeans = AdditionalBeanBuildItem.builder() + for (ClassInfo implementor : index.getAllKnownImplementors(DotNames.BEFORE_RETRY_HANDLER)) { + handlers.add(implementor.name().toString()); + } + if (!handlers.isEmpty()) { + AdditionalBeanBuildItem.Builder handlerBeans = AdditionalBeanBuildItem.builder() .setDefaultScope(BuiltinScope.DEPENDENT.getName()); - for (String fallbackHandler : fallbackHandlers) { - reflectiveClass.produce(ReflectiveClassBuildItem.builder(fallbackHandler).methods().build()); - fallbackHandlersBeans.addBeanClass(fallbackHandler); + for (String handler : handlers) { + reflectiveClass.produce(ReflectiveClassBuildItem.builder(handler).methods().build()); + handlerBeans.addBeanClass(handler); } - beans.produce(fallbackHandlersBeans.build()); + beans.produce(handlerBeans.build()); } // Add reflective access to fallback methods for (AnnotationInstance annotation : index.getAnnotations(DotNames.FALLBACK)) { @@ -200,6 +204,7 @@ public void transform(TransformationContext context) { ExecutorHolder.class, StrategyCache.class, QuarkusFallbackHandlerProvider.class, + QuarkusBeforeRetryHandlerProvider.class, QuarkusAsyncExecutorProvider.class, CircuitBreakerMaintenanceImpl.class, RequestContextIntegration.class, @@ -376,6 +381,11 @@ void processFaultToleranceAnnotations(SmallRyeFaultToleranceRecorder recorder, exceptions.add(new DefinitionException("@RetryWhen present on '" + it.target() + "', but @Retry is missing")); } } + for (AnnotationInstance it : index.getAnnotations(DotNames.BEFORE_RETRY)) { + if (!annotationStore.hasAnnotation(it.target(), DotNames.RETRY)) { + exceptions.add(new DefinitionException("@BeforeRetry present on '" + it.target() + "', but @Retry is missing")); + } + } if (!exceptions.isEmpty()) { errors.produce(new ValidationPhaseBuildItem.ValidationErrorBuildItem(exceptions)); diff --git a/extensions/smallrye-fault-tolerance/deployment/src/main/resources/dev-ui/qwc-fault-tolerance-methods.js b/extensions/smallrye-fault-tolerance/deployment/src/main/resources/dev-ui/qwc-fault-tolerance-methods.js index f45e180b7259b..d05d2dc0b123c 100644 --- a/extensions/smallrye-fault-tolerance/deployment/src/main/resources/dev-ui/qwc-fault-tolerance-methods.js +++ b/extensions/smallrye-fault-tolerance/deployment/src/main/resources/dev-ui/qwc-fault-tolerance-methods.js @@ -86,6 +86,7 @@ export class QwcFaultToleranceMethods extends LitElement { ${guardedMethod.FibonacciBackoff ? this._renderFibonacciBackoff(guardedMethod.FibonacciBackoff) : html``} ${guardedMethod.CustomBackoff ? this._renderCustomBackoff(guardedMethod.CustomBackoff) : html``} ${guardedMethod.RetryWhen ? this._renderRetryWhen(guardedMethod.RetryWhen) : html``} + ${guardedMethod.BeforeRetry ? this._renderBeforeRetry(guardedMethod.BeforeRetry) : html``} ${guardedMethod.Timeout ? this._renderTimeout(guardedMethod.Timeout) : html``} `; @@ -189,6 +190,15 @@ export class QwcFaultToleranceMethods extends LitElement { `; } + _renderBeforeRetry(beforeRetry) { + return html` + + ↪ + @BeforeRetry(value = ${beforeRetry.value}, methodName = ${beforeRetry.methodName}) + + `; + } + _renderTimeout(timeout) { return html` @Timeout(${timeout.value} ${timeout.valueUnit}) diff --git a/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/BeforeRetryHandlerService.java b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/BeforeRetryHandlerService.java new file mode 100644 index 0000000000000..a73c3732889f3 --- /dev/null +++ b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/BeforeRetryHandlerService.java @@ -0,0 +1,34 @@ +package io.quarkus.smallrye.faulttolerance.test.retry.beforeretry; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import jakarta.enterprise.context.Dependent; +import jakarta.inject.Inject; + +import org.eclipse.microprofile.faulttolerance.ExecutionContext; +import org.eclipse.microprofile.faulttolerance.Retry; + +import io.smallrye.faulttolerance.api.BeforeRetry; +import io.smallrye.faulttolerance.api.BeforeRetryHandler; + +@Dependent +public class BeforeRetryHandlerService { + static final Set ids = ConcurrentHashMap.newKeySet(); + + @Retry + @BeforeRetry(MyBeforeRetryHandler.class) + public void hello() { + throw new IllegalArgumentException(); + } + + static class MyBeforeRetryHandler implements BeforeRetryHandler { + @Inject + MyDependency dep; + + @Override + public void handle(ExecutionContext context) { + ids.add(dep.id); + } + } +} diff --git a/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/BeforeRetryHandlerTest.java b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/BeforeRetryHandlerTest.java new file mode 100644 index 0000000000000..d77613377bd4e --- /dev/null +++ b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/BeforeRetryHandlerTest.java @@ -0,0 +1,29 @@ +package io.quarkus.smallrye.faulttolerance.test.retry.beforeretry; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import jakarta.inject.Inject; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class BeforeRetryHandlerTest { + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClasses(BeforeRetryHandlerService.class, MyDependency.class)); + + @Inject + BeforeRetryHandlerService service; + + @Test + public void test() { + assertThrows(IllegalArgumentException.class, service::hello); + assertThat(BeforeRetryHandlerService.ids) + .hasSize(3) + .containsExactly(1, 2, 3); + } +} diff --git a/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/BeforeRetryMethodService.java b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/BeforeRetryMethodService.java new file mode 100644 index 0000000000000..c5f9392b1c7c5 --- /dev/null +++ b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/BeforeRetryMethodService.java @@ -0,0 +1,27 @@ +package io.quarkus.smallrye.faulttolerance.test.retry.beforeretry; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +import jakarta.enterprise.context.Dependent; + +import org.eclipse.microprofile.faulttolerance.Retry; + +import io.smallrye.faulttolerance.api.BeforeRetry; + +@Dependent +public class BeforeRetryMethodService { + static final Set ids = ConcurrentHashMap.newKeySet(); + private static final AtomicInteger counter = new AtomicInteger(); + + @Retry + @BeforeRetry(methodName = "beforeRetry") + public void hello() { + throw new IllegalArgumentException(); + } + + void beforeRetry() { + ids.add(counter.incrementAndGet()); + } +} diff --git a/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/BeforeRetryMethodTest.java b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/BeforeRetryMethodTest.java new file mode 100644 index 0000000000000..6debe857fccf6 --- /dev/null +++ b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/BeforeRetryMethodTest.java @@ -0,0 +1,29 @@ +package io.quarkus.smallrye.faulttolerance.test.retry.beforeretry; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import jakarta.inject.Inject; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class BeforeRetryMethodTest { + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClasses(BeforeRetryMethodService.class)); + + @Inject + BeforeRetryMethodService service; + + @Test + public void test() { + assertThrows(IllegalArgumentException.class, service::hello); + assertThat(BeforeRetryMethodService.ids) + .hasSize(3) + .containsExactly(1, 2, 3); + } +} diff --git a/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/BothValueAndMethodNameSetService.java b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/BothValueAndMethodNameSetService.java new file mode 100644 index 0000000000000..3edb1f08c3dfd --- /dev/null +++ b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/BothValueAndMethodNameSetService.java @@ -0,0 +1,27 @@ +package io.quarkus.smallrye.faulttolerance.test.retry.beforeretry; + +import jakarta.enterprise.context.Dependent; + +import org.eclipse.microprofile.faulttolerance.ExecutionContext; +import org.eclipse.microprofile.faulttolerance.Retry; + +import io.smallrye.faulttolerance.api.BeforeRetry; +import io.smallrye.faulttolerance.api.BeforeRetryHandler; + +@Dependent +public class BothValueAndMethodNameSetService { + @Retry + @BeforeRetry(value = MyHandler.class, methodName = "beforeRetry") + public void hello() { + throw new IllegalArgumentException(); + } + + void beforeRetry() { + } + + static class MyHandler implements BeforeRetryHandler { + @Override + public void handle(ExecutionContext context) { + } + } +} diff --git a/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/BothValueAndMethodNameSetTest.java b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/BothValueAndMethodNameSetTest.java new file mode 100644 index 0000000000000..bc013d921083b --- /dev/null +++ b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/BothValueAndMethodNameSetTest.java @@ -0,0 +1,28 @@ +package io.quarkus.smallrye.faulttolerance.test.retry.beforeretry; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import jakarta.enterprise.inject.spi.DeploymentException; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class BothValueAndMethodNameSetTest { + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClasses(BothValueAndMethodNameSetService.class)) + .assertException(e -> { + assertEquals(DeploymentException.class, e.getClass()); + assertTrue(e.getMessage().contains("Invalid @BeforeRetry")); + assertTrue(e.getMessage().contains( + "before retry handler class and before retry method can't be specified both at the same time")); + }); + + @Test + public void test() { + } +} diff --git a/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/MyDependency.java b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/MyDependency.java new file mode 100644 index 0000000000000..ec2f45563b9a6 --- /dev/null +++ b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/MyDependency.java @@ -0,0 +1,12 @@ +package io.quarkus.smallrye.faulttolerance.test.retry.beforeretry; + +import java.util.concurrent.atomic.AtomicInteger; + +import jakarta.enterprise.context.Dependent; + +@Dependent +public class MyDependency { + private static final AtomicInteger counter = new AtomicInteger(); + + public final int id = counter.incrementAndGet(); +} diff --git a/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/NoBeforeRetryMethodFoundService.java b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/NoBeforeRetryMethodFoundService.java new file mode 100644 index 0000000000000..64bb25845aa58 --- /dev/null +++ b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/NoBeforeRetryMethodFoundService.java @@ -0,0 +1,20 @@ +package io.quarkus.smallrye.faulttolerance.test.retry.beforeretry; + +import jakarta.enterprise.context.Dependent; + +import org.eclipse.microprofile.faulttolerance.Retry; + +import io.smallrye.faulttolerance.api.BeforeRetry; + +@Dependent +public class NoBeforeRetryMethodFoundService { + @Retry + @BeforeRetry(methodName = "beforeRetry") + public void hello() { + throw new IllegalArgumentException(); + } + + public int beforeRetry(int param) { + return 0; + } +} diff --git a/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/NoBeforeRetryMethodFoundTest.java b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/NoBeforeRetryMethodFoundTest.java new file mode 100644 index 0000000000000..864476f85cc43 --- /dev/null +++ b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/NoBeforeRetryMethodFoundTest.java @@ -0,0 +1,27 @@ +package io.quarkus.smallrye.faulttolerance.test.retry.beforeretry; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import jakarta.enterprise.inject.spi.DeploymentException; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class NoBeforeRetryMethodFoundTest { + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClasses(NoBeforeRetryMethodFoundService.class)) + .assertException(e -> { + assertEquals(DeploymentException.class, e.getClass()); + assertTrue(e.getMessage().contains("Invalid @BeforeRetry")); + assertTrue(e.getMessage().contains("can't find before retry method")); + }); + + @Test + public void test() { + } +} diff --git a/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/RetryOnClassBeforeRetryOnMethodService.java b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/RetryOnClassBeforeRetryOnMethodService.java new file mode 100644 index 0000000000000..dfb1653b315fb --- /dev/null +++ b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/RetryOnClassBeforeRetryOnMethodService.java @@ -0,0 +1,19 @@ +package io.quarkus.smallrye.faulttolerance.test.retry.beforeretry; + +import jakarta.enterprise.context.Dependent; + +import org.eclipse.microprofile.faulttolerance.Retry; + +import io.smallrye.faulttolerance.api.BeforeRetry; + +@Dependent +@Retry +public class RetryOnClassBeforeRetryOnMethodService { + @BeforeRetry(methodName = "beforeRetry") + public void hello() { + throw new IllegalArgumentException(); + } + + void beforeRetry() { + } +} diff --git a/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/RetryOnClassBeforeRetryOnMethodTest.java b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/RetryOnClassBeforeRetryOnMethodTest.java new file mode 100644 index 0000000000000..6298e8fa18df0 --- /dev/null +++ b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/RetryOnClassBeforeRetryOnMethodTest.java @@ -0,0 +1,27 @@ +package io.quarkus.smallrye.faulttolerance.test.retry.beforeretry; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import jakarta.enterprise.inject.spi.DefinitionException; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class RetryOnClassBeforeRetryOnMethodTest { + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClasses(RetryOnClassBeforeRetryOnMethodService.class)) + .assertException(e -> { + assertEquals(DefinitionException.class, e.getClass()); + assertTrue(e.getMessage().contains("@BeforeRetry present")); + assertTrue(e.getMessage().contains("@Retry is missing")); + }); + + @Test + public void test() { + } +} diff --git a/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/RetryOnMethodBeforeRetryOnClassService.java b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/RetryOnMethodBeforeRetryOnClassService.java new file mode 100644 index 0000000000000..93b4736e12469 --- /dev/null +++ b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/RetryOnMethodBeforeRetryOnClassService.java @@ -0,0 +1,19 @@ +package io.quarkus.smallrye.faulttolerance.test.retry.beforeretry; + +import jakarta.enterprise.context.Dependent; + +import org.eclipse.microprofile.faulttolerance.Retry; + +import io.smallrye.faulttolerance.api.BeforeRetry; + +@Dependent +@BeforeRetry(methodName = "beforeRetry") +public class RetryOnMethodBeforeRetryOnClassService { + @Retry + public void hello() { + throw new IllegalArgumentException(); + } + + void beforeRetry() { + } +} diff --git a/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/RetryOnMethodBeforeRetryOnClassTest.java b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/RetryOnMethodBeforeRetryOnClassTest.java new file mode 100644 index 0000000000000..b63ddefa7817a --- /dev/null +++ b/extensions/smallrye-fault-tolerance/deployment/src/test/java/io/quarkus/smallrye/faulttolerance/test/retry/beforeretry/RetryOnMethodBeforeRetryOnClassTest.java @@ -0,0 +1,27 @@ +package io.quarkus.smallrye.faulttolerance.test.retry.beforeretry; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import jakarta.enterprise.inject.spi.DefinitionException; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class RetryOnMethodBeforeRetryOnClassTest { + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClasses(RetryOnMethodBeforeRetryOnClassService.class)) + .assertException(e -> { + assertEquals(DefinitionException.class, e.getClass()); + assertTrue(e.getMessage().contains("@BeforeRetry present")); + assertTrue(e.getMessage().contains("@Retry is missing")); + }); + + @Test + public void test() { + } +} diff --git a/extensions/smallrye-fault-tolerance/runtime/src/main/java/io/quarkus/smallrye/faulttolerance/runtime/QuarkusBeforeRetryHandlerProvider.java b/extensions/smallrye-fault-tolerance/runtime/src/main/java/io/quarkus/smallrye/faulttolerance/runtime/QuarkusBeforeRetryHandlerProvider.java new file mode 100644 index 0000000000000..77adb00f64da0 --- /dev/null +++ b/extensions/smallrye-fault-tolerance/runtime/src/main/java/io/quarkus/smallrye/faulttolerance/runtime/QuarkusBeforeRetryHandlerProvider.java @@ -0,0 +1,45 @@ +package io.quarkus.smallrye.faulttolerance.runtime; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Alternative; +import jakarta.enterprise.inject.Any; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; + +import org.eclipse.microprofile.faulttolerance.ExecutionContext; + +import io.smallrye.faulttolerance.BeforeRetryHandlerProvider; +import io.smallrye.faulttolerance.api.BeforeRetryHandler; +import io.smallrye.faulttolerance.config.FaultToleranceOperation; + +@Dependent +@Alternative +@Priority(1) +public class QuarkusBeforeRetryHandlerProvider implements BeforeRetryHandlerProvider { + + @Inject + @Any + Instance instance; + + @Override + public BeforeRetryHandler get(FaultToleranceOperation operation) { + if (operation.hasBeforeRetry()) { + return new BeforeRetryHandler() { + @Override + public void handle(ExecutionContext context) { + Class clazz = operation.getBeforeRetry().value(); + BeforeRetryHandler handler = instance.select(clazz).get(); + try { + handler.handle(context); + } finally { + // The instance exists to service a single invocation only + instance.destroy(handler); + } + } + }; + } + return null; + } + +} diff --git a/extensions/smallrye-fault-tolerance/runtime/src/main/java/io/quarkus/smallrye/faulttolerance/runtime/devui/FaultToleranceJsonRpcService.java b/extensions/smallrye-fault-tolerance/runtime/src/main/java/io/quarkus/smallrye/faulttolerance/runtime/devui/FaultToleranceJsonRpcService.java index 4362f1509f2e2..0068e9b1a2d49 100644 --- a/extensions/smallrye-fault-tolerance/runtime/src/main/java/io/quarkus/smallrye/faulttolerance/runtime/devui/FaultToleranceJsonRpcService.java +++ b/extensions/smallrye-fault-tolerance/runtime/src/main/java/io/quarkus/smallrye/faulttolerance/runtime/devui/FaultToleranceJsonRpcService.java @@ -17,6 +17,7 @@ import io.smallrye.common.annotation.NonBlocking; import io.smallrye.faulttolerance.api.ApplyFaultTolerance; import io.smallrye.faulttolerance.api.AsynchronousNonBlocking; +import io.smallrye.faulttolerance.api.BeforeRetry; import io.smallrye.faulttolerance.api.CircuitBreakerName; import io.smallrye.faulttolerance.api.CustomBackoff; import io.smallrye.faulttolerance.api.ExponentialBackoff; @@ -133,6 +134,11 @@ private JsonObject convert(FaultToleranceOperation operation) { .put("result", operation.getRetryWhen().result().getName()) .put("exception", operation.getRetryWhen().exception().getName())); } + if (operation.hasBeforeRetry()) { + result.put(BeforeRetry.class.getSimpleName(), new JsonObject() + .put("value", operation.getBeforeRetry().value().getName()) + .put("methodName", operation.getBeforeRetry().methodName())); + } if (operation.hasTimeout()) { result.put(Timeout.class.getSimpleName(), new JsonObject() .put("value", operation.getTimeout().value())