GERONIMO-6586 - Adding support for retry behavior.
diff --git a/pom.xml b/pom.xml
index 3c7f9a4..33c3da1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -54,14 +54,105 @@
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<microprofile-fault-tolerance.version>1.1-SNAPSHOT</microprofile-fault-tolerance.version>
- <owb.version>1.7.4</owb.version>
- <owb2.version>2.0.1</owb2.version>
+ <owb.version>2.0.1</owb.version>
<arquillian.version>1.1.13.Final</arquillian.version>
<arquillian-weld-embedded.version>2.0.0.Beta5</arquillian-weld-embedded.version>
<cdi2-api.version>2.0</cdi2-api.version>
<weld.version>3.0.1.Final</weld.version>
</properties>
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.geronimo.safeguard</groupId>
+ <artifactId>safeguard-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>net.jodah</groupId>
+ <artifactId>failsafe</artifactId>
+ <version>1.0.4</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-jcdi_2.0_spec</artifactId>
+ <version>1.0.1</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-annotation_1.3_spec</artifactId>
+ <version>1.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-atinject_1.0_spec</artifactId>
+ <version>1.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.microprofile.fault-tolerance</groupId>
+ <artifactId>microprofile-fault-tolerance-api</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-interceptor_1.2_spec</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-el_2.2_spec</artifactId>
+ <version>1.0.2</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.openwebbeans</groupId>
+ <artifactId>openwebbeans-spi</artifactId>
+ <version>${owb.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.openwebbeans</groupId>
+ <artifactId>openwebbeans-impl</artifactId>
+ <version>${owb.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.openwebbeans.arquillian</groupId>
+ <artifactId>owb-arquillian-standalone</artifactId>
+ <version>${owb.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.testng</groupId>
+ <artifactId>testng</artifactId>
+ <version>6.9.9</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>3.8.0</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.weld.se</groupId>
+ <artifactId>weld-se-shaded</artifactId>
+ <version>${weld.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.container</groupId>
+ <artifactId>arquillian-weld-embedded</artifactId>
+ <version>${arquillian-weld-embedded.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
<profiles>
<profile>
<id>eclipse-repo</id>
diff --git a/safeguard-api/pom.xml b/safeguard-api/pom.xml
index 7676c35..2f240f6 100644
--- a/safeguard-api/pom.xml
+++ b/safeguard-api/pom.xml
@@ -30,5 +30,18 @@
<artifactId>safeguard-api</artifactId>
-
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-jcdi_2.0_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-interceptor_1.2_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.microprofile.fault-tolerance</groupId>
+ <artifactId>microprofile-fault-tolerance-api</artifactId>
+ </dependency>
+ </dependencies>
</project>
\ No newline at end of file
diff --git a/safeguard-api/src/main/java/org/apache/safeguard/api/SafeguardEnabled.java b/safeguard-api/src/main/java/org/apache/safeguard/api/SafeguardEnabled.java
new file mode 100644
index 0000000..4396a9b
--- /dev/null
+++ b/safeguard-api/src/main/java/org/apache/safeguard/api/SafeguardEnabled.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.safeguard.api;
+
+import javax.enterprise.util.AnnotationLiteral;
+import javax.interceptor.InterceptorBinding;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@InterceptorBinding
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface SafeguardEnabled {
+
+ SafeguardEnabled INSTANCE = new Literal();
+ class Literal extends AnnotationLiteral<SafeguardEnabled> implements SafeguardEnabled {}
+}
diff --git a/safeguard-api/src/main/java/org/apache/safeguard/api/retry/RetryBuilder.java b/safeguard-api/src/main/java/org/apache/safeguard/api/retry/RetryBuilder.java
new file mode 100644
index 0000000..10228db
--- /dev/null
+++ b/safeguard-api/src/main/java/org/apache/safeguard/api/retry/RetryBuilder.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.safeguard.api.retry;
+
+import java.time.Duration;
+
+public interface RetryBuilder {
+
+ RetryBuilder withMaxRetries(int maxRetries);
+ RetryBuilder withDelay(Duration delay);
+ RetryBuilder withMaxDuration(Duration maxDuration);
+ RetryBuilder withJitter(Duration jitter);
+ RetryBuilder withAbortOn(Class<? extends Throwable>... abortOn);
+ RetryBuilder withRetryOn(Class<? extends Throwable>... retryOn);
+
+ RetryDefinition build();
+}
diff --git a/safeguard-api/src/main/java/org/apache/safeguard/api/retry/RetryDefinition.java b/safeguard-api/src/main/java/org/apache/safeguard/api/retry/RetryDefinition.java
new file mode 100644
index 0000000..1383e17
--- /dev/null
+++ b/safeguard-api/src/main/java/org/apache/safeguard/api/retry/RetryDefinition.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.safeguard.api.retry;
+
+import java.time.Duration;
+import java.util.Collection;
+
+public interface RetryDefinition {
+
+ int getMaxRetries();
+
+ Duration getDelay();
+
+ Duration getMaxDuration();
+
+ Duration getJitter();
+
+ Collection<Class<? extends Throwable>> getRetryExceptions();
+
+ Collection<Class<? extends Throwable>> getAbortExceptions();
+
+}
diff --git a/safeguard-api/src/main/java/org/apache/safeguard/api/retry/RetryManager.java b/safeguard-api/src/main/java/org/apache/safeguard/api/retry/RetryManager.java
new file mode 100644
index 0000000..054d8d8
--- /dev/null
+++ b/safeguard-api/src/main/java/org/apache/safeguard/api/retry/RetryManager.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.safeguard.api.retry;
+
+public interface RetryManager {
+ RetryBuilder newRetryDefinition(String name);
+ RetryDefinition getRetryDefinition(String name);
+}
diff --git a/safeguard-impl/pom.xml b/safeguard-impl/pom.xml
index dbc12fa..a94eb64 100644
--- a/safeguard-impl/pom.xml
+++ b/safeguard-impl/pom.xml
@@ -30,5 +30,83 @@
<artifactId>safeguard-impl</artifactId>
-
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.geronimo.safeguard</groupId>
+ <artifactId>safeguard-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>net.jodah</groupId>
+ <artifactId>failsafe</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.testng</groupId>
+ <artifactId>arquillian-testng-container</artifactId>
+ <version>${arquillian.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-jcdi_2.0_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-atinject_1.0_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-annotation_1.3_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-interceptor_1.2_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.testng</groupId>
+ <artifactId>testng</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ </dependency>
+ </dependencies>
+ <profiles>
+ <profile>
+ <id>OWB2</id>
+ <activation>
+ <activeByDefault>true</activeByDefault>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-el_2.2_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.openwebbeans</groupId>
+ <artifactId>openwebbeans-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.openwebbeans</groupId>
+ <artifactId>openwebbeans-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.openwebbeans.arquillian</groupId>
+ <artifactId>owb-arquillian-standalone</artifactId>
+ </dependency>
+ </dependencies>
+ </profile>
+ <profile>
+ <id>Weld3</id>
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.weld.se</groupId>
+ <artifactId>weld-se-shaded</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.container</groupId>
+ <artifactId>arquillian-weld-embedded</artifactId>
+ </dependency>
+ </dependencies>
+ </profile>
+ </profiles>
</project>
\ No newline at end of file
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/GuardedExecutions.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/GuardedExecutions.java
new file mode 100644
index 0000000..a1e9a40
--- /dev/null
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/GuardedExecutions.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.safeguard.impl;
+
+import net.jodah.failsafe.Failsafe;
+import org.apache.safeguard.impl.retry.FailsafeRetryBuilder;
+import org.apache.safeguard.impl.retry.FailsafeRetryDefinition;
+import org.apache.safeguard.impl.retry.FailsafeRetryManager;
+import org.apache.safeguard.impl.util.AnnotationUtil;
+import org.apache.safeguard.impl.util.NamingUtil;
+import org.eclipse.microprofile.faulttolerance.Retry;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.interceptor.InvocationContext;
+import java.lang.reflect.Method;
+import java.time.Duration;
+import java.util.concurrent.Callable;
+
+@ApplicationScoped
+public class GuardedExecutions {
+ private FailsafeRetryManager retryManager;
+
+ GuardedExecutions() {
+
+ }
+
+ @Inject
+ public GuardedExecutions(FailsafeRetryManager retryManager) {
+ this.retryManager = retryManager;
+ }
+
+ public Object execute(InvocationContext invocationContext) {
+ Method method = invocationContext.getMethod();
+ String name = NamingUtil.createName(method);
+ FailsafeRetryDefinition failsafeRetryDefinition = retryManager.getRetryDefinition(name);
+ if (failsafeRetryDefinition == null) {
+ failsafeRetryDefinition = createDefinition(name, method);
+ }
+ return Failsafe.with(failsafeRetryDefinition.getRetryPolicy()).get(invocationContext::proceed);
+ }
+
+ public <T> T execute(String name, Callable<T> callable) {
+ FailsafeRetryDefinition failsafeRetryDefinition = retryManager.getRetryDefinition(name);
+ return Failsafe.with(failsafeRetryDefinition.getRetryPolicy()).get(callable);
+ }
+
+ private FailsafeRetryDefinition createDefinition(String name, Method method) {
+ Retry retry = AnnotationUtil.getAnnotation(method, Retry.class);
+ FailsafeRetryBuilder retryBuilder = retryManager.newRetryDefinition(name)
+ .withMaxRetries(retry.maxRetries())
+ .withRetryOn(retry.retryOn())
+ .withAbortOn(retry.abortOn());
+ if (retry.delay() > 0L) {
+ retryBuilder.withDelay(Duration.of(retry.delay(), retry.delayUnit()));
+ }
+ if (retry.jitter() > 0L) {
+ retryBuilder.withJitter(Duration.of(retry.jitter(), retry.jitterDelayUnit()));
+ }
+ if(retry.maxDuration() > 0L) {
+ retryBuilder.withMaxDuration(Duration.of(retry.maxDuration(), retry.durationUnit()));
+ }
+ return retryBuilder.build();
+ }
+}
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/cdi/SafeguardExtension.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/cdi/SafeguardExtension.java
new file mode 100644
index 0000000..318a6e6
--- /dev/null
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/cdi/SafeguardExtension.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.safeguard.impl.cdi;
+
+import org.apache.safeguard.api.SafeguardEnabled;
+import org.eclipse.microprofile.faulttolerance.Retry;
+
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.spi.AnnotatedConstructor;
+import javax.enterprise.inject.spi.AnnotatedField;
+import javax.enterprise.inject.spi.AnnotatedMethod;
+import javax.enterprise.inject.spi.AnnotatedType;
+import javax.enterprise.inject.spi.Extension;
+import javax.enterprise.inject.spi.ProcessAnnotatedType;
+import javax.enterprise.inject.spi.WithAnnotations;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import static org.apache.safeguard.api.SafeguardEnabled.INSTANCE;
+
+public class SafeguardExtension implements Extension {
+ public void findFaultTolerantBeans(@Observes @WithAnnotations({Retry.class})
+ ProcessAnnotatedType<?> pat) {
+ if (!pat.getAnnotatedType().isAnnotationPresent(SafeguardEnabled.class)) {
+ pat.setAnnotatedType(new SafeguardAnnotatedTypeWrapper(pat.getAnnotatedType()));
+ }
+ }
+
+ private static class SafeguardAnnotatedTypeWrapper<X> implements AnnotatedType<X> {
+
+ private final AnnotatedType<X> delegate;
+ private final Set<Annotation> annotations;
+
+ private SafeguardAnnotatedTypeWrapper(AnnotatedType<X> delegate) {
+ this.delegate = delegate;
+ Set<Annotation> annotations = delegate.getAnnotations();
+ Set<Annotation> allAnotations = new LinkedHashSet<>();
+ allAnotations.add(INSTANCE);
+ allAnotations.addAll(annotations);
+ this.annotations = allAnotations;
+ }
+
+ @Override
+ public Class<X> getJavaClass() {
+ return delegate.getJavaClass();
+ }
+
+ @Override
+ public Set<AnnotatedConstructor<X>> getConstructors() {
+ return delegate.getConstructors();
+ }
+
+ @Override
+ public Set<AnnotatedMethod<? super X>> getMethods() {
+ return delegate.getMethods();
+ }
+
+ @Override
+ public Set<AnnotatedField<? super X>> getFields() {
+ return delegate.getFields();
+ }
+
+ @Override
+ public Type getBaseType() {
+ return delegate.getBaseType();
+ }
+
+ @Override
+ public Set<Type> getTypeClosure() {
+ return delegate.getTypeClosure();
+ }
+
+ @Override
+ public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
+ return SafeguardEnabled.class.equals(annotationType) ? (T) INSTANCE : delegate.getAnnotation(annotationType);
+ }
+
+ @Override
+ public Set<Annotation> getAnnotations() {
+ return Collections.unmodifiableSet(annotations);
+ }
+
+ @Override
+ public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+ return SafeguardEnabled.class.equals(annotationType) || delegate.isAnnotationPresent(annotationType);
+ }
+ }
+}
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/cdi/SafeguardInterceptor.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/cdi/SafeguardInterceptor.java
new file mode 100644
index 0000000..156f44d
--- /dev/null
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/cdi/SafeguardInterceptor.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.safeguard.impl.cdi;
+
+import org.apache.safeguard.api.SafeguardEnabled;
+import org.apache.safeguard.impl.GuardedExecutions;
+import org.apache.safeguard.impl.util.AnnotationUtil;
+import org.eclipse.microprofile.faulttolerance.Retry;
+
+import javax.annotation.Priority;
+import javax.inject.Inject;
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InvocationContext;
+import java.lang.reflect.Method;
+
+@Interceptor
+@SafeguardEnabled
+@Priority(400)
+public class SafeguardInterceptor {
+ @Inject
+ private GuardedExecutions guardedExecutions;
+
+ @AroundInvoke
+ public Object runSafeguards(InvocationContext invocationContext) throws Exception{
+ if(isMethodSafeguarded(invocationContext.getMethod())) {
+ return guardedExecutions.execute(invocationContext);
+ }
+ else {
+ return invocationContext.proceed();
+ }
+ }
+
+ private boolean isMethodSafeguarded(Method method) {
+ return AnnotationUtil.getAnnotation(method, Retry.class) != null;
+ }
+}
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/retry/FailsafeRetryBuilder.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/retry/FailsafeRetryBuilder.java
new file mode 100644
index 0000000..2cf7f07
--- /dev/null
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/retry/FailsafeRetryBuilder.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.safeguard.impl.retry;
+
+import net.jodah.failsafe.RetryPolicy;
+import org.apache.safeguard.api.retry.RetryBuilder;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+public class FailsafeRetryBuilder implements RetryBuilder{
+ private final List<Class<? extends Throwable>> retryOn;
+ private final List<Class<? extends Throwable>> abortOn;
+ private final RetryPolicy retryPolicy;
+ private final String name;
+ private final FailsafeRetryManager failsafeRetryManager;
+
+ FailsafeRetryBuilder(String name, FailsafeRetryManager failsafeRetryManager) {
+ this.name = name;
+ this.failsafeRetryManager = failsafeRetryManager;
+ this.retryOn = new ArrayList<>();
+ this.abortOn = new ArrayList<>();
+ this.retryPolicy = new RetryPolicy();
+ }
+ @Override
+ public FailsafeRetryBuilder withMaxRetries(int maxRetries) {
+ retryPolicy.withMaxRetries(maxRetries);
+ return this;
+ }
+
+ @Override
+ public FailsafeRetryBuilder withDelay(Duration delay) {
+ retryPolicy.withDelay(delay.toMillis(), TimeUnit.MILLISECONDS);
+ return this;
+ }
+
+ @Override
+ public FailsafeRetryBuilder withMaxDuration(Duration maxDuration) {
+ retryPolicy.withMaxDuration(maxDuration.toMillis(), TimeUnit.MILLISECONDS);
+ return this;
+ }
+
+ @Override
+ public FailsafeRetryBuilder withJitter(Duration jitter) {
+ retryPolicy.withJitter(jitter.toMillis(), TimeUnit.MILLISECONDS);
+ return this;
+ }
+
+ @Override
+ public FailsafeRetryBuilder withAbortOn(Class<? extends Throwable>... abortOn) {
+ this.abortOn.addAll(Arrays.asList(abortOn));
+ return this;
+ }
+
+ @Override
+ public FailsafeRetryBuilder withRetryOn(Class<? extends Throwable>... retryOn) {
+ this.retryOn.addAll(Arrays.asList(retryOn));
+ return this;
+ }
+
+ @Override
+ public FailsafeRetryDefinition build() {
+ if(!this.abortOn.isEmpty()) {
+ retryPolicy.abortOn(this.abortOn);
+ }
+ if(!this.retryOn.isEmpty()) {
+ retryPolicy.retryOn(this.retryOn);
+ }
+ FailsafeRetryDefinition definition = new FailsafeRetryDefinition(retryPolicy, retryOn, abortOn);
+ failsafeRetryManager.register(name, definition);
+ return definition;
+ }
+}
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/retry/FailsafeRetryDefinition.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/retry/FailsafeRetryDefinition.java
new file mode 100644
index 0000000..916f204
--- /dev/null
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/retry/FailsafeRetryDefinition.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.safeguard.impl.retry;
+
+import net.jodah.failsafe.RetryPolicy;
+import org.apache.safeguard.api.retry.RetryDefinition;
+
+import java.time.Duration;
+import java.util.Collection;
+
+public class FailsafeRetryDefinition implements RetryDefinition{
+ private final RetryPolicy retryPolicy;
+ private final Collection<Class<? extends Throwable>> retryExceptions;
+ private final Collection<Class<? extends Throwable>> abortExceptions;
+
+ FailsafeRetryDefinition(RetryPolicy retryPolicy, Collection<Class<? extends Throwable>> retryExceptions, Collection<Class<? extends Throwable>> abortExceptions) {
+ this.retryPolicy = retryPolicy;
+ this.retryExceptions = retryExceptions;
+ this.abortExceptions = abortExceptions;
+ }
+
+ @Override
+ public int getMaxRetries() {
+ return retryPolicy.getMaxRetries();
+ }
+
+ @Override
+ public Duration getDelay() {
+ net.jodah.failsafe.util.Duration delay = retryPolicy.getDelay();
+ return Duration.ofMillis(delay.toMillis());
+ }
+
+ @Override
+ public Duration getMaxDuration() {
+ net.jodah.failsafe.util.Duration maxDuration = retryPolicy.getMaxDuration();
+ return Duration.ofMillis(maxDuration.toMillis());
+ }
+
+ @Override
+ public Duration getJitter() {
+ net.jodah.failsafe.util.Duration jitter = retryPolicy.getJitter();
+ return Duration.ofMillis(jitter.toMillis());
+ }
+
+ @Override
+ public Collection<Class<? extends Throwable>> getRetryExceptions() {
+ return this.retryExceptions;
+ }
+
+ @Override
+ public Collection<Class<? extends Throwable>> getAbortExceptions() {
+ return abortExceptions;
+ }
+
+ public RetryPolicy getRetryPolicy() {
+ return retryPolicy;
+ }
+}
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/retry/FailsafeRetryManager.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/retry/FailsafeRetryManager.java
new file mode 100644
index 0000000..6d1e71f
--- /dev/null
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/retry/FailsafeRetryManager.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.safeguard.impl.retry;
+
+import org.apache.safeguard.api.retry.RetryManager;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.ApplicationScoped;
+import java.util.HashMap;
+import java.util.Map;
+
+@ApplicationScoped
+public class FailsafeRetryManager implements RetryManager{
+ private Map<String, FailsafeRetryDefinition> retries;
+
+ @PostConstruct
+ public void init() {
+ retries = new HashMap<>();
+ }
+
+ @Override
+ public FailsafeRetryBuilder newRetryDefinition(String name) {
+ return new FailsafeRetryBuilder(name, this);
+ }
+
+ @Override
+ public FailsafeRetryDefinition getRetryDefinition(String name) {
+ return retries.get(name);
+ }
+
+ void register(String name, FailsafeRetryDefinition failsafeRetryDefinition) {
+ this.retries.put(name, failsafeRetryDefinition);
+ }
+}
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/util/AnnotationUtil.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/util/AnnotationUtil.java
new file mode 100644
index 0000000..18bacf6
--- /dev/null
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/util/AnnotationUtil.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.safeguard.impl.util;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+public final class AnnotationUtil {
+ private AnnotationUtil() {
+
+ }
+ public static <T extends Annotation> T getAnnotation(Method method, Class<T> clazz) {
+ T annotation = method.getAnnotation(clazz);
+ if (annotation != null) {
+ return annotation;
+ }
+ else {
+ return method.getDeclaringClass().getAnnotation(clazz);
+ }
+ }
+}
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/util/NamingUtil.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/util/NamingUtil.java
new file mode 100644
index 0000000..a48a6d7
--- /dev/null
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/util/NamingUtil.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.safeguard.impl.util;
+
+import java.lang.reflect.Method;
+
+public final class NamingUtil {
+ private NamingUtil() {
+
+ }
+
+ public static String createName(Method method) {
+ return method.getDeclaringClass().getCanonicalName()+"."+method.getName()+"."+method.getParameters();
+ }
+}
diff --git a/safeguard-impl/src/main/resources/META-INF/beans.xml b/safeguard-impl/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000..a130381
--- /dev/null
+++ b/safeguard-impl/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,19 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ -->
+<beans version="1.1" bean-discovery-mode="annotated"/>
diff --git a/safeguard-impl/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/safeguard-impl/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
new file mode 100644
index 0000000..0eae66c
--- /dev/null
+++ b/safeguard-impl/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
@@ -0,0 +1 @@
+org.apache.safeguard.impl.cdi.SafeguardExtension
\ No newline at end of file
diff --git a/safeguard-impl/src/test/java/org/apache/safeguard/retry/test/CDIRetryBean.java b/safeguard-impl/src/test/java/org/apache/safeguard/retry/test/CDIRetryBean.java
new file mode 100644
index 0000000..3d69ab0
--- /dev/null
+++ b/safeguard-impl/src/test/java/org/apache/safeguard/retry/test/CDIRetryBean.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.safeguard.retry.test;
+
+import org.eclipse.microprofile.faulttolerance.Retry;
+
+import javax.enterprise.context.ApplicationScoped;
+
+@ApplicationScoped
+public class CDIRetryBean {
+ private int calls = 0;
+ @Retry
+ public String doCall() {
+ calls++;
+ if(calls <= 2) {
+ throw new RuntimeException();
+ }
+ return "retried";
+ }
+
+ public int getCalls() {
+ return calls;
+ }
+}
diff --git a/safeguard-impl/src/test/java/org/apache/safeguard/retry/test/CDIRetryTest.java b/safeguard-impl/src/test/java/org/apache/safeguard/retry/test/CDIRetryTest.java
new file mode 100644
index 0000000..35586ce
--- /dev/null
+++ b/safeguard-impl/src/test/java/org/apache/safeguard/retry/test/CDIRetryTest.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.safeguard.retry.test;
+
+import org.apache.safeguard.impl.GuardedExecutions;
+import org.apache.safeguard.impl.cdi.SafeguardExtension;
+import org.apache.safeguard.impl.cdi.SafeguardInterceptor;
+import org.apache.safeguard.impl.retry.FailsafeRetryManager;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.testng.Arquillian;
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.EmptyAsset;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.testng.annotations.Test;
+
+import javax.enterprise.inject.spi.Extension;
+import javax.inject.Inject;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CDIRetryTest extends Arquillian{
+ @Deployment
+ public static Archive<?> create() {
+ return ShrinkWrap.create(JavaArchive.class)
+ .addClasses(GuardedExecutions.class, FailsafeRetryManager.class,
+ SafeguardInterceptor.class, CDIRetryBean.class)
+ .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")
+ .addAsServiceProviderAndClasses(Extension.class, SafeguardExtension.class);
+ }
+
+ @Inject
+ private CDIRetryBean cdiRetryBean;
+
+ @Test
+ public void shouldExecuteThreeTimesWithAnnotations() {
+ cdiRetryBean.doCall();
+
+ assertThat(cdiRetryBean.getCalls()).isEqualTo(3);
+ }
+}
diff --git a/safeguard-impl/src/test/java/org/apache/safeguard/retry/test/RetryBean.java b/safeguard-impl/src/test/java/org/apache/safeguard/retry/test/RetryBean.java
new file mode 100644
index 0000000..fcadf03
--- /dev/null
+++ b/safeguard-impl/src/test/java/org/apache/safeguard/retry/test/RetryBean.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.safeguard.retry.test;
+
+import java.util.concurrent.Callable;
+
+public class RetryBean implements Callable<String> {
+ private int calls = 0;
+ @Override
+ public String call() throws Exception {
+ calls++;
+ if(calls <= 2) {
+ throw new RuntimeException();
+ }
+ return "called";
+ }
+
+ public int getCalls() {
+ return calls;
+ }
+}
diff --git a/safeguard-impl/src/test/java/org/apache/safeguard/retry/test/RetryTest.java b/safeguard-impl/src/test/java/org/apache/safeguard/retry/test/RetryTest.java
new file mode 100644
index 0000000..7696534
--- /dev/null
+++ b/safeguard-impl/src/test/java/org/apache/safeguard/retry/test/RetryTest.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.safeguard.retry.test;
+
+import org.apache.safeguard.impl.GuardedExecutions;
+import org.apache.safeguard.impl.retry.FailsafeRetryManager;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RetryTest {
+ private GuardedExecutions guardedExecutions;
+ private static final String name = "GUARDED_RETRIES";
+ private FailsafeRetryManager retryManager;
+
+ @BeforeTest
+ public void setupForTest() {
+ this.retryManager = new FailsafeRetryManager();
+ retryManager.init();
+ guardedExecutions = new GuardedExecutions(retryManager);
+ }
+
+ @Test
+ public void testRetryWithManualBuild() {
+ int expectedCalls = 3;
+ retryManager.newRetryDefinition(name)
+ .withMaxRetries(expectedCalls)
+ .build();
+ RetryBean retryBean = new RetryBean();
+
+ guardedExecutions.execute(name, retryBean);
+
+ assertThat(retryBean.getCalls()).isEqualTo(expectedCalls);
+ }
+}