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 extends BeforeRetryHandler> 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())