starting to wire some metrics
diff --git a/pom.xml b/pom.xml
index f85ac1f..e5889f2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,213 +18,214 @@
~ under the License.
-->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.apache</groupId>
- <artifactId>apache</artifactId>
- <version>18</version>
- </parent>
- <groupId>org.apache.geronimo.safeguard</groupId>
- <artifactId>safeguard-parent</artifactId>
- <version>1.1-SNAPSHOT</version>
- <packaging>pom</packaging>
- <name>Apache Safeguard</name>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache</groupId>
+ <artifactId>apache</artifactId>
+ <version>18</version>
+ </parent>
+ <groupId>org.apache.geronimo.safeguard</groupId>
+ <artifactId>safeguard-parent</artifactId>
+ <version>1.1-SNAPSHOT</version>
+ <packaging>pom</packaging>
+ <name>Apache Safeguard</name>
- <modules>
- <module>safeguard-impl</module>
- </modules>
+ <modules>
+ <module>safeguard-impl</module>
+ </modules>
- <licenses>
- <license>
- <name>Apache License, Version 2.0</name>
- <url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
- <distribution>repo</distribution>
- </license>
- </licenses>
+ <licenses>
+ <license>
+ <name>Apache License, Version 2.0</name>
+ <url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
- <scm>
- <connection>scm:git:https://git-wip-us.apache.org/repos/asf/geronimo-safeguard.git</connection>
- <developerConnection>scm:git:https://git-wip-us.apache.org/repos/asf/geronimo-safeguard.git</developerConnection>
- <url>https://git-wip-us.apache.org/repos/asf/geronimo-safeguard.git</url>
- <tag>HEAD</tag>
- </scm>
+ <scm>
+ <connection>scm:git:https://git-wip-us.apache.org/repos/asf/geronimo-safeguard.git</connection>
+ <developerConnection>scm:git:https://git-wip-us.apache.org/repos/asf/geronimo-safeguard.git</developerConnection>
+ <url>https://git-wip-us.apache.org/repos/asf/geronimo-safeguard.git</url>
+ <tag>HEAD</tag>
+ </scm>
- <organization>
- <name>The Apache Software Foundation</name>
- <url>http://www.apache.org/</url>
- </organization>
+ <organization>
+ <name>The Apache Software Foundation</name>
+ <url>http://www.apache.org/</url>
+ </organization>
- <inceptionYear>2017</inceptionYear>
+ <inceptionYear>2017</inceptionYear>
- <developers>
- <developer>
- <name>Apache Geronimo Community</name>
- <url>https://geronimo.apache.org</url>
- <organization>Apache</organization>
- </developer>
- </developers>
+ <developers>
+ <developer>
+ <name>Apache Geronimo Community</name>
+ <url>https://geronimo.apache.org</url>
+ <organization>Apache</organization>
+ </developer>
+ </developers>
- <issueManagement>
- <system>ASF JIRA</system>
- <url>https://issues.apache.org/jira/browse/GERONIMO</url>
- </issueManagement>
+ <issueManagement>
+ <system>ASF JIRA</system>
+ <url>https://issues.apache.org/jira/browse/GERONIMO</url>
+ </issueManagement>
- <properties>
- <maven.compiler.source>1.8</maven.compiler.source>
- <maven.compiler.target>1.8</maven.compiler.target>
- <microprofile-fault-tolerance.version>1.1.3</microprofile-fault-tolerance.version>
- <owb.version>2.0.1</owb.version>
- <arquillian.version>1.1.14.Final</arquillian.version>
- <arquillian-weld-embedded.version>2.0.0.Final</arquillian-weld-embedded.version>
- <cdi2-api.version>2.0</cdi2-api.version>
- <weld.version>3.0.1.Final</weld.version>
- <geronimo-config-impl.version>1.2</geronimo-config-impl.version>
- <microprofile-config-api.version>1.2</microprofile-config-api.version>
- </properties>
+ <properties>
+ <maven.compiler.source>1.8</maven.compiler.source>
+ <maven.compiler.target>1.8</maven.compiler.target>
+ <microprofile-fault-tolerance.version>1.1.3</microprofile-fault-tolerance.version>
+ <owb.version>2.0.1</owb.version>
+ <arquillian.version>1.1.14.Final</arquillian.version>
+ <arquillian-weld-embedded.version>2.0.0.Final</arquillian-weld-embedded.version>
+ <cdi2-api.version>2.0</cdi2-api.version>
+ <weld.version>3.0.1.Final</weld.version>
+ <geronimo-config-impl.version>1.2</geronimo-config-impl.version>
+ <microprofile-config-api.version>1.2</microprofile-config-api.version>
+ </properties>
- <dependencyManagement>
- <dependencies>
- <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.config</groupId>
- <artifactId>microprofile-config-api</artifactId>
- <version>${microprofile-config-api.version}</version>
- <optional>true</optional>
- </dependency>
- <dependency>
- <groupId>org.apache.geronimo.config</groupId>
- <artifactId>geronimo-config-impl</artifactId>
- <version>${geronimo-config-impl.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.eclipse.microprofile.fault-tolerance</groupId>
- <artifactId>microprofile-fault-tolerance-api</artifactId>
- <version>${microprofile-fault-tolerance.version}</version>
- </dependency>
- <dependency>
- <groupId>org.eclipse.microprofile.fault-tolerance</groupId>
- <artifactId>microprofile-fault-tolerance-tck</artifactId>
- <version>${microprofile-fault-tolerance.version}</version>
- <scope>test</scope>
- </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>
- <dependency>
- <groupId>org.jboss.arquillian.testng</groupId>
- <artifactId>arquillian-testng-container</artifactId>
- <version>${arquillian.version}</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
- </dependencyManagement>
+ <dependencyManagement>
+ <dependencies>
+ <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.config</groupId>
+ <artifactId>microprofile-config-api</artifactId>
+ <version>${microprofile-config-api.version}</version>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.config</groupId>
+ <artifactId>geronimo-config-impl</artifactId>
+ <version>${geronimo-config-impl.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.microprofile.fault-tolerance</groupId>
+ <artifactId>microprofile-fault-tolerance-api</artifactId>
+ <version>${microprofile-fault-tolerance.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.microprofile.fault-tolerance</groupId>
+ <artifactId>microprofile-fault-tolerance-tck</artifactId>
+ <version>${microprofile-fault-tolerance.version}</version>
+ <scope>test</scope>
+ </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>
+ <dependency>
+ <groupId>org.jboss.arquillian.testng</groupId>
+ <artifactId>arquillian-testng-container</artifactId>
+ <version>${arquillian.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
- <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>
+ <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/pom.xml b/safeguard-impl/pom.xml
index 9022b62..7d4cf7b 100644
--- a/safeguard-impl/pom.xml
+++ b/safeguard-impl/pom.xml
@@ -18,96 +18,103 @@
~ under the License.
-->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <parent>
- <artifactId>safeguard-parent</artifactId>
- <groupId>org.apache.geronimo.safeguard</groupId>
- <version>1.1-SNAPSHOT</version>
- </parent>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>safeguard-parent</artifactId>
+ <groupId>org.apache.geronimo.safeguard</groupId>
+ <version>1.1-SNAPSHOT</version>
+ </parent>
- <modelVersion>4.0.0</modelVersion>
+ <modelVersion>4.0.0</modelVersion>
- <artifactId>safeguard-impl</artifactId>
- <name>Apache Safeguard :: Implementation</name>
+ <artifactId>safeguard-impl</artifactId>
+ <name>Apache Safeguard :: Implementation</name>
- <dependencies>
- <dependency>
- <groupId>org.eclipse.microprofile.fault-tolerance</groupId>
- <artifactId>microprofile-fault-tolerance-api</artifactId>
- <version>${microprofile-fault-tolerance.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.eclipse.microprofile.metrics</groupId>
- <artifactId>microprofile-metrics-api</artifactId>
- <version>1.1.1</version>
- <scope>provided</scope>
- </dependency>
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.microprofile.fault-tolerance</groupId>
+ <artifactId>microprofile-fault-tolerance-api</artifactId>
+ <version>${microprofile-fault-tolerance.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.microprofile.metrics</groupId>
+ <artifactId>microprofile-metrics-api</artifactId>
+ <version>1.1.1</version>
+ <scope>provided</scope>
+ </dependency>
- <dependency>
- <groupId>org.jboss.arquillian.testng</groupId>
- <artifactId>arquillian-testng-container</artifactId>
- </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.eclipse.microprofile.config</groupId>
- <artifactId>microprofile-config-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.testng</groupId>
- <artifactId>testng</artifactId>
- </dependency>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.geronimo.config</groupId>
- <artifactId>geronimo-config-impl</artifactId>
- </dependency>
- <dependency>
- <groupId>org.eclipse.microprofile.fault-tolerance</groupId>
- <artifactId>microprofile-fault-tolerance-tck</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.geronimo</groupId>
- <artifactId>geronimo-metrics</artifactId>
- <version>1.0.0</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
+ <dependency>
+ <groupId>org.jboss.arquillian.testng</groupId>
+ <artifactId>arquillian-testng-container</artifactId>
+ </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.eclipse.microprofile.config</groupId>
+ <artifactId>microprofile-config-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-all</artifactId>
+ <version>1.3</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.testng</groupId>
+ <artifactId>testng</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.config</groupId>
+ <artifactId>geronimo-config-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.microprofile.fault-tolerance</groupId>
+ <artifactId>microprofile-fault-tolerance-tck</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo</groupId>
+ <artifactId>geronimo-metrics</artifactId>
+ <version>1.0.0</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <version>3.0.0-M1</version>
- <configuration>
- <dependenciesToScan>
- <dependency>org.eclipse.microprofile.fault-tolerance:microprofile-fault-tolerance-tck</dependency>
- </dependenciesToScan>
- <excludes>
- <exclude>org.eclipse.microprofile.fault.tolerance.tck.ConfigTest</exclude>
- </excludes>
- <trimStackTrace>false</trimStackTrace>
- </configuration>
- </plugin>
- </plugins>
- </build>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>3.0.0-M1</version>
+ <configuration>
+ <dependenciesToScan>
+ <dependency>org.eclipse.microprofile.fault-tolerance:microprofile-fault-tolerance-tck</dependency>
+ </dependenciesToScan>
+ <excludes>
+ <exclude>org.eclipse.microprofile.fault.tolerance.tck.ConfigTest</exclude>
+ </excludes>
+ <trimStackTrace>false</trimStackTrace>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
</project>
\ No newline at end of file
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/asynchronous/BaseAsynchronousInterceptor.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/asynchronous/BaseAsynchronousInterceptor.java
index db68832..e85871e 100644
--- a/safeguard-impl/src/main/java/org/apache/safeguard/impl/asynchronous/BaseAsynchronousInterceptor.java
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/asynchronous/BaseAsynchronousInterceptor.java
@@ -32,6 +32,7 @@
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
+import org.apache.safeguard.impl.interceptor.IdGeneratorInterceptor;
import org.eclipse.microprofile.faulttolerance.Asynchronous;
import org.eclipse.microprofile.faulttolerance.exceptions.FaultToleranceDefinitionException;
@@ -39,11 +40,13 @@
protected abstract Executor getExecutor(InvocationContext context);
protected Object around(final InvocationContext context) throws Exception {
- if (context.getContextData().get(Asynchronous.class.getName()) != null) { // bulkhead or so handling threading
+ final String key = Asynchronous.class.getName() +
+ context.getContextData().get(IdGeneratorInterceptor.class.getName());
+ if (context.getContextData().get(key) != null) { // bulkhead or so handling threading
return context.proceed();
}
- context.getContextData().put(Asynchronous.class.getName(), "true");
+ context.getContextData().put(key, "true");
final Class<?> returnType = context.getMethod().getReturnType();
if (CompletionStage.class.isAssignableFrom(returnType)) {
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/bulkhead/BulkheadInterceptor.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/bulkhead/BulkheadInterceptor.java
index 6bdb6f7..b7f5799 100644
--- a/safeguard-impl/src/main/java/org/apache/safeguard/impl/bulkhead/BulkheadInterceptor.java
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/bulkhead/BulkheadInterceptor.java
@@ -25,10 +25,10 @@
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadPoolExecutor;
+import javax.annotation.PreDestroy;
import javax.annotation.Priority;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
@@ -38,11 +38,13 @@
import org.apache.safeguard.impl.annotation.AnnotationFinder;
import org.apache.safeguard.impl.asynchronous.BaseAsynchronousInterceptor;
+import org.apache.safeguard.impl.interceptor.IdGeneratorInterceptor;
import org.eclipse.microprofile.faulttolerance.Asynchronous;
import org.eclipse.microprofile.faulttolerance.Bulkhead;
import org.eclipse.microprofile.faulttolerance.exceptions.BulkheadException;
import org.eclipse.microprofile.faulttolerance.exceptions.FaultToleranceDefinitionException;
+// todo: metrics
@Bulkhead
@Interceptor
@Priority(Interceptor.Priority.PLATFORM_AFTER + 5)
@@ -64,7 +66,8 @@
}
}
if (model.useThreads) {
- context.getContextData().put(EXECUTOR_KEY, model.pool);
+ context.getContextData().put(
+ EXECUTOR_KEY + context.getContextData().get(IdGeneratorInterceptor.class.getName()), model.pool);
return around(context);
} else {
if (!model.semaphore.tryAcquire()) {
@@ -80,14 +83,15 @@
@Override
protected Executor getExecutor(final InvocationContext context) {
- return Executor.class.cast(context.getContextData().get(EXECUTOR_KEY));
+ return Executor.class.cast(context.getContextData()
+ .get(EXECUTOR_KEY + context.getContextData().get(IdGeneratorInterceptor.class.getName())));
}
static class Model {
private final int value;
private final int waitingQueue;
private final boolean useThreads;
- private final ExecutorService pool;
+ private final ThreadPoolExecutor pool;
private final Semaphore semaphore;
private Model(final Bulkhead bulkhead, final boolean useThreads) {
@@ -104,6 +108,9 @@
this.useThreads = useThreads;
if (this.useThreads) {
this.pool = new ThreadPoolExecutor(value, value, 0L, MILLISECONDS, new ArrayBlockingQueue<>(waitingQueue));
+ this.pool.setRejectedExecutionHandler((r, executor) -> {
+ throw new BulkheadException("Can't accept task " + r);
+ });
this.semaphore = null;
} else {
this.pool = null;
@@ -119,6 +126,11 @@
@Inject
private AnnotationFinder finder;
+ @PreDestroy
+ private void destroy() {
+ models.values().stream().filter(m -> m.pool != null).forEach(m -> m.pool.shutdownNow());
+ }
+
public Map<Method, Model> getModels() {
return models;
}
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/cdi/SafeguardEnabled.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/cdi/SafeguardEnabled.java
new file mode 100644
index 0000000..9755681
--- /dev/null
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/cdi/SafeguardEnabled.java
@@ -0,0 +1,40 @@
+/*
+ * 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 static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.enterprise.util.AnnotationLiteral;
+import javax.interceptor.InterceptorBinding;
+
+@InterceptorBinding
+@Target({TYPE, METHOD})
+@Retention(RUNTIME)
+public @interface SafeguardEnabled {
+ class Literal extends AnnotationLiteral<SafeguardEnabled> implements SafeguardEnabled {
+ public static final SafeguardEnabled.Literal INSTANCE = new SafeguardEnabled.Literal();
+
+ private static final long serialVersionUID = 1L;
+ }
+}
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
index e16294b..38e0fea 100644
--- 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
@@ -1,22 +1,38 @@
package org.apache.safeguard.impl.cdi;
+import static java.util.stream.Collectors.toList;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
+import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.stream.Stream;
+import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Default;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
+import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import javax.enterprise.inject.spi.ProcessBean;
+import javax.enterprise.inject.spi.WithAnnotations;
import org.apache.safeguard.impl.config.GeronimoFaultToleranceConfig;
import org.apache.safeguard.impl.customizable.Safeguard;
import org.apache.safeguard.impl.fallback.FallbackInterceptor;
+import org.apache.safeguard.impl.metrics.FaultToleranceMetrics;
+import org.eclipse.microprofile.faulttolerance.Asynchronous;
+import org.eclipse.microprofile.faulttolerance.Bulkhead;
+import org.eclipse.microprofile.faulttolerance.CircuitBreaker;
+import org.eclipse.microprofile.faulttolerance.Fallback;
+import org.eclipse.microprofile.faulttolerance.Retry;
+import org.eclipse.microprofile.faulttolerance.Timeout;
// todo: mp.fault.tolerance.interceptor.priority handling
public class SafeguardExtension implements Extension {
@@ -26,6 +42,26 @@
processAnnotatedType.configureAnnotatedType().add(new FallbackBinding());
}
+ void activateSafeguard(@Observes @WithAnnotations({
+ /*Asynchronous.class, */
+ Bulkhead.class, CircuitBreaker.class,
+ Fallback.class, Retry.class, Timeout.class
+ }) final ProcessAnnotatedType<?> processAnnotatedType) {
+ if (processAnnotatedType.getAnnotatedType().getJavaClass().getName().startsWith("org.apache.safeguard.impl")) {
+ return;
+ }
+ if (faultToleranceAnnotations().anyMatch(it -> processAnnotatedType.getAnnotatedType().isAnnotationPresent(it))) {
+ processAnnotatedType.configureAnnotatedType().add(SafeguardEnabled.Literal.INSTANCE);
+ } else {
+ final List<Method> methods = processAnnotatedType.getAnnotatedType().getMethods().stream()
+ .filter(it -> faultToleranceAnnotations().anyMatch(it::isAnnotationPresent))
+ .map(AnnotatedMethod::getJavaMember).collect(toList());
+ processAnnotatedType.configureAnnotatedType()
+ .filterMethods(it -> methods.contains(it.getJavaMember()))
+ .forEach(m -> m.add(SafeguardEnabled.Literal.INSTANCE));
+ }
+ }
+
void onBean(@Observes final ProcessBean<?> bean) {
if (bean.getBean().getQualifiers().stream().anyMatch(it -> it.annotationType() == Safeguard.class)
&& bean.getBean().getTypes().stream().anyMatch(it -> Executor.class.isAssignableFrom(toClass(it)))) {
@@ -40,8 +76,17 @@
.types(GeronimoFaultToleranceConfig.class, Object.class)
.beanClass(GeronimoFaultToleranceConfig.class)
.qualifiers(Default.Literal.INSTANCE, Any.Literal.INSTANCE)
+ .scope(ApplicationScoped.class)
.createWith(c -> config);
+ afterBeanDiscovery.addBean()
+ .id("geronimo_safeguard#metrics")
+ .types(FaultToleranceMetrics.class, Object.class)
+ .beanClass(FaultToleranceMetrics.class)
+ .qualifiers(Default.Literal.INSTANCE, Any.Literal.INSTANCE)
+ .scope(ApplicationScoped.class)
+ .createWith(c -> FaultToleranceMetrics.create());
+
if (!foundExecutor) {
afterBeanDiscovery.addBean()
.id("geronimo_safeguard#executor")
@@ -49,6 +94,7 @@
.beanClass(Executor.class)
.qualifiers(Safeguard.Literal.INSTANCE, Any.Literal.INSTANCE)
.createWith(c -> Executors.newCachedThreadPool())
+ .scope(ApplicationScoped.class)
.destroyWith((e, c) -> ExecutorService.class.cast(e).shutdownNow());
}
}
@@ -69,4 +115,9 @@
}
return Object.class; // will not match any of our test
}
+
+ private Stream<Class<? extends Annotation>> faultToleranceAnnotations() {
+ return Stream.of(Asynchronous.class, Bulkhead.class, CircuitBreaker.class,
+ Fallback.class, Retry.class, Timeout.class);
+ }
}
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/circuitbreaker/CircuitBreakerInterceptor.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/circuitbreaker/CircuitBreakerInterceptor.java
index eecb8e3..f7c54a8 100644
--- a/safeguard-impl/src/main/java/org/apache/safeguard/impl/circuitbreaker/CircuitBreakerInterceptor.java
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/circuitbreaker/CircuitBreakerInterceptor.java
@@ -23,6 +23,7 @@
import java.util.EnumMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream;
@@ -35,6 +36,7 @@
import org.apache.safeguard.impl.annotation.AnnotationFinder;
import org.apache.safeguard.impl.config.ConfigurationMapper;
+import org.apache.safeguard.impl.metrics.FaultToleranceMetrics;
import org.eclipse.microprofile.faulttolerance.CircuitBreaker;
import org.eclipse.microprofile.faulttolerance.exceptions.CircuitBreakerOpenException;
import org.eclipse.microprofile.faulttolerance.exceptions.FaultToleranceDefinitionException;
@@ -58,14 +60,20 @@
}
}
if (!circuitBreaker.checkState()) {
+ circuitBreaker.callsPrevented.inc();
throw new CircuitBreakerOpenException(context.getMethod() + " circuit breaker is open");
}
try {
- return context.proceed();
+ final Object result = context.proceed();
+ circuitBreaker.callsSucceeded.inc();
+ return result;
} catch (final Exception e) {
if (circuitBreaker.failOn.length > 0 &&
Stream.of(circuitBreaker.failOn).anyMatch(it -> it.isInstance(e) || it.isInstance(e.getCause()))) {
+ circuitBreaker.callsFailed.inc();
circuitBreaker.incrementAndCheckState(1);
+ } else {
+ circuitBreaker.callsSucceeded.inc();
}
throw e;
}
@@ -105,6 +113,9 @@
@Inject
private ConfigurationMapper mapper;
+ @Inject
+ private FaultToleranceMetrics metrics;
+
public Map<Method, CircuitBreakerImpl> getCircuitBreakers() {
return circuitBreakers;
}
@@ -133,7 +144,26 @@
if (successThreshold < 0) {
throw new FaultToleranceDefinitionException("CircuitBreaker success threshold can't be < 0");
}
- return new CircuitBreakerImpl(volumeThreshold, delay, successThreshold, delay, failOn, failureRatio);
+
+ final String metricsNameBase = "ft." + context.getMethod().getDeclaringClass().getCanonicalName() + "."
+ + context.getMethod().getName() + ".circuitbreaker.";
+
+ final CircuitBreakerImpl circuitBreaker = new CircuitBreakerImpl(volumeThreshold, delay, successThreshold,
+ delay, failOn, failureRatio, metrics.counter(metricsNameBase + "callsSucceeded.total",
+ "Number of calls allowed to run by the circuit breaker that returned successfully"),
+ metrics.counter(metricsNameBase + "callsFailed.total",
+ "Number of calls allowed to run by the circuit breaker that then failed"),
+ metrics.counter(metricsNameBase + "callsPrevented.total",
+ "Number of calls prevented from running by an open circuit breaker"),
+ metrics.counter(metricsNameBase + "opened.total",
+ "Number of times the circuit breaker has moved from closed state to open state"));
+ metrics.gauge(metricsNameBase + "open.total", "Amount of time the circuit breaker has spent in open state", "nanoseconds",
+ circuitBreaker.openDuration::get);
+ metrics.gauge(metricsNameBase + "halfOpen.total", "Amount of time the circuit breaker has spent in half-open state", "nanoseconds",
+ circuitBreaker.halfOpenDuration::get);
+ metrics.gauge(metricsNameBase + "closed.total", "Amount of time the circuit breaker has spent in closed state", "nanoseconds",
+ circuitBreaker.closedDuration::get);
+ return circuitBreaker;
}
}
@@ -150,10 +180,21 @@
private final double failureRatio;
private final Class<? extends Throwable>[] failOn;
- private CircuitBreakerImpl(final int openingThreshold, final long openingInterval,
- final int closingThreshold, final long closingInterval,
- final Class<? extends Throwable>[] failOn,
- final double failureRatio) {
+ private final AtomicLong openDuration = new AtomicLong();
+ private final FaultToleranceMetrics.Counter callsSucceeded;
+ private final FaultToleranceMetrics.Counter callsFailed;
+ private final FaultToleranceMetrics.Counter callsPrevented;
+ private final AtomicLong halfOpenDuration = new AtomicLong();
+ private final AtomicLong closedDuration = new AtomicLong();
+ private final FaultToleranceMetrics.Counter opened;
+
+ CircuitBreakerImpl(final int openingThreshold, final long openingInterval, final int closingThreshold,
+ final long closingInterval, final Class<? extends Throwable>[] failOn,
+ final double failureRatio,
+ final FaultToleranceMetrics.Counter callsSucceeded,
+ final FaultToleranceMetrics.Counter callsFailed,
+ final FaultToleranceMetrics.Counter callsPrevented,
+ final FaultToleranceMetrics.Counter opened) {
this.checkIntervalData = new AtomicReference<>(new CheckIntervalData(0, 0));
this.openingThreshold = openingThreshold;
this.openingInterval = openingInterval;
@@ -161,6 +202,10 @@
this.closingInterval = closingInterval;
this.failOn = failOn;
this.failureRatio = failureRatio;
+ this.callsSucceeded = callsSucceeded;
+ this.callsFailed = callsFailed;
+ this.callsPrevented = callsPrevented;
+ this.opened = opened;
}
protected static boolean isOpen(final State state) {
@@ -191,6 +236,9 @@
} while (!updateCheckIntervalData(currentData, nextData));
if (stateStrategy(currentState).isStateTransition(this, currentData, nextData)) {
currentState = currentState.oppositeState();
+ if (currentState == State.OPEN) {
+ opened.inc();
+ }
changeStateAndStartNewCheckInterval(currentState);
}
return !isOpen(currentState);
@@ -264,8 +312,12 @@
public boolean isStateTransition(final CircuitBreakerImpl breaker,
final CheckIntervalData currentData, final CheckIntervalData nextData) {
final long now = now();
- return nextData.eventCount > breaker.openingThreshold ||
- (now != currentData.checkIntervalStart && (currentData.eventCount / (now - currentData.checkIntervalStart)) > breaker.failureRatio);
+ final boolean result =
+ nextData.eventCount >= breaker.openingThreshold || (now != currentData.checkIntervalStart && (currentData.eventCount / (now - currentData.checkIntervalStart)) > breaker.failureRatio);
+ if (!result) {
+ breaker.closedDuration.set(now - currentData.checkIntervalStart);
+ }
+ return result;
}
@Override
@@ -278,8 +330,12 @@
@Override
public boolean isStateTransition(final CircuitBreakerImpl breaker,
final CheckIntervalData currentData, final CheckIntervalData nextData) {
- return nextData.checkIntervalStart != currentData.checkIntervalStart
- && currentData.eventCount < breaker.closingThreshold;
+ final boolean result =
+ nextData.checkIntervalStart != currentData.checkIntervalStart && currentData.eventCount <= breaker.closingThreshold;
+ if (!result) {
+ breaker.openDuration.set(now() - currentData.checkIntervalStart);
+ }
+ return result;
}
@Override
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/config/ConfigurationMapper.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/config/ConfigurationMapper.java
index bd9ee46..77f0d2d 100644
--- a/safeguard-impl/src/main/java/org/apache/safeguard/impl/config/ConfigurationMapper.java
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/config/ConfigurationMapper.java
@@ -49,8 +49,8 @@
private <T extends Annotation> Object findConfiguredValue(final T instance, final Class<T> api,
final Method sourceMethod,
final Method proxyMethod, final Object[] args) {
- return ofNullable(ofNullable(findDefaultConfiguration(proxyMethod))
- .orElseGet(() -> ofNullable(findMethodConfiguration(api, sourceMethod, proxyMethod))
+ return ofNullable(ofNullable(findMethodConfiguration(api, sourceMethod, proxyMethod))
+ .orElseGet(() -> ofNullable(findDefaultConfiguration(proxyMethod))
.orElseGet(() -> ofNullable(findClassConfiguration(api, sourceMethod, proxyMethod)).orElse(null))))
.map(v -> coerce(v, proxyMethod.getReturnType()))
.orElseGet(() -> {
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/fallback/FallbackInterceptor.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/fallback/FallbackInterceptor.java
index e3e8a61..c939059 100644
--- a/safeguard-impl/src/main/java/org/apache/safeguard/impl/fallback/FallbackInterceptor.java
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/fallback/FallbackInterceptor.java
@@ -43,6 +43,7 @@
import org.apache.safeguard.impl.annotation.AnnotationFinder;
import org.apache.safeguard.impl.cdi.SafeguardExtension;
+import org.apache.safeguard.impl.metrics.FaultToleranceMetrics;
import org.eclipse.microprofile.faulttolerance.ExecutionContext;
import org.eclipse.microprofile.faulttolerance.Fallback;
import org.eclipse.microprofile.faulttolerance.FallbackHandler;
@@ -103,6 +104,9 @@
@Inject
private BeanManager beanManager;
+ @Inject
+ private FaultToleranceMetrics metrics;
+
private final Collection<CreationalContext<?>> contexts = new ArrayList<>();
@PreDestroy
@@ -121,6 +125,8 @@
if (!method.isEmpty() && value != Fallback.DEFAULT.class) {
throw new FaultToleranceDefinitionException("You can't set a method and handler as fallback on " + context.getMethod());
}
+
+ FallbackHandler<?> handler;
if (value != Fallback.DEFAULT.class) {
Stream.of(value.getInterfaces()).filter(it -> it == FallbackHandler.class)
.findFirst()
@@ -134,29 +140,48 @@
if (!beanManager.isNormalScope(handlerBean.getScope())) {
contexts.add(creationalContext);
}
- return FallbackHandler.class.cast(beanManager.getReference(handlerBean, FallbackHandler.class, creationalContext));
- }
- try {
- final Method fallbackMethod = context.getTarget().getClass().getMethod(method);
- if (!extension.toClass(context.getMethod().getReturnType()).isAssignableFrom(extension.toClass(fallbackMethod.getReturnType())) ||
- !Arrays.equals(context.getMethod().getParameterTypes(), fallbackMethod.getParameterTypes())) {
- throw new FaultToleranceDefinitionException("handler method does not match method: " + context.getMethod());
- }
- if (!fallbackMethod.isAccessible()) {
- fallbackMethod.setAccessible(true);
- }
- return (FallbackHandler<Object>) context1 -> {
- try {
- return fallbackMethod.invoke(EnrichedExecutionContext.class.cast(context1).getTarget(), context1.getParameters());
- } catch (final IllegalAccessException e) {
- throw new IllegalStateException(e);
- } catch (final InvocationTargetException e) {
- throw new IllegalStateException(e.getTargetException());
+ final FallbackHandler fallbackHandler = FallbackHandler.class.cast(
+ beanManager.getReference(handlerBean, FallbackHandler.class, creationalContext));
+ handler = fallbackHandler;
+ } else {
+ try {
+ final Method fallbackMethod = context.getTarget()
+ .getClass()
+ .getMethod(method);
+ if (!extension.toClass(context.getMethod()
+ .getReturnType())
+ .isAssignableFrom(extension.toClass(fallbackMethod.getReturnType())) || !Arrays.equals(
+ context.getMethod()
+ .getParameterTypes(), fallbackMethod.getParameterTypes())) {
+ throw new FaultToleranceDefinitionException("handler method does not match method: " + context.getMethod());
}
- };
- } catch (final NoSuchMethodException e) {
- throw new FaultToleranceDefinitionException("No method " + method + " in " + context.getTarget());
+ if (!fallbackMethod.isAccessible()) {
+ fallbackMethod.setAccessible(true);
+ }
+ handler = (FallbackHandler<Object>) context1 -> {
+ try {
+ return fallbackMethod.invoke(EnrichedExecutionContext.class.cast(context1)
+ .getTarget(),
+ context1.getParameters());
+ } catch (final IllegalAccessException e) {
+ throw new IllegalStateException(e);
+ } catch (final InvocationTargetException e) {
+ throw new IllegalStateException(e.getTargetException());
+ }
+ };
+ } catch (final NoSuchMethodException e) {
+ throw new FaultToleranceDefinitionException("No method " + method + " in " + context.getTarget());
+ }
}
+
+ final String metricsName = "ft." + context.getMethod().getDeclaringClass().getCanonicalName() + "."
+ + context.getMethod().getName() + ".fallback.calls.total";
+ final FaultToleranceMetrics.Counter counter = metrics.counter(metricsName,
+ "Number of times the fallback handler or method was called");
+ return (FallbackHandler<Object>) context12 -> {
+ counter.inc();
+ return handler.handle(context12);
+ };
}
}
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/interceptor/IdGeneratorInterceptor.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/interceptor/IdGeneratorInterceptor.java
new file mode 100644
index 0000000..58dd561
--- /dev/null
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/interceptor/IdGeneratorInterceptor.java
@@ -0,0 +1,111 @@
+/*
+ * 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.interceptor;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.annotation.Priority;
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InvocationContext;
+
+import org.apache.safeguard.impl.cdi.SafeguardEnabled;
+import org.apache.safeguard.impl.metrics.FaultToleranceMetrics;
+
+// simple way to ensure we use a single key in the interceptor context by call
+// and avoids to manage a stack
+@Interceptor
+@SafeguardEnabled
+@Priority(Interceptor.Priority.PLATFORM_BEFORE)
+public class IdGeneratorInterceptor implements Serializable {
+ private static final String KEY = IdGeneratorInterceptor.class.getName();
+
+ private final AtomicLong idGenerator = new AtomicLong();
+
+ @Inject
+ private Cache cache;
+
+ @AroundInvoke
+ public Object generateId(final InvocationContext context) throws Exception {
+ final Object old = context.getContextData().get(KEY);
+ context.getContextData().put(IdGeneratorInterceptor.class.getName(), idGenerator.incrementAndGet());
+
+ final Map<Method, Counters> counters = cache.getCounters();
+ Counters methodCounters = counters.get(context.getMethod());
+ if (methodCounters == null) {
+ methodCounters = cache.create(context.getMethod());
+ final Counters existing = counters.putIfAbsent(context.getMethod(), methodCounters);
+ if (existing != null) {
+ methodCounters = existing;
+ }
+ }
+
+ methodCounters.total.inc();
+ try {
+ return context.proceed();
+ } catch (final Exception | Error e) {
+ methodCounters.failed.inc();
+ throw e;
+ } finally {
+ if (old != null) {
+ context.getContextData().put(IdGeneratorInterceptor.class.getName(), old);
+ } else {
+ context.getContextData().remove(IdGeneratorInterceptor.class.getName());
+ }
+ }
+ }
+
+ private static class Counters {
+ private final FaultToleranceMetrics.Counter total;
+ private final FaultToleranceMetrics.Counter failed;
+
+ private Counters(final FaultToleranceMetrics.Counter total, final FaultToleranceMetrics.Counter failed) {
+ this.total = total;
+ this.failed = failed;
+ }
+ }
+
+ @ApplicationScoped
+ public static class Cache {
+ private final Map<Method, Counters> counters = new ConcurrentHashMap<>();
+
+ @Inject
+ private FaultToleranceMetrics metrics;
+
+ public Map<Method, Counters> getCounters() {
+ return counters;
+ }
+
+ public Counters create(final Method method) {
+ //ft.org.eclipse.microprofile.fault.tolerance.tck.metrics.RetryMetricBean.failSeveralTimes.invocations.total
+ final String metricsNameBase = "ft." + method.getDeclaringClass().getCanonicalName() + "." + method.getName() + ".invocations.";
+ return new Counters(
+ metrics.counter(metricsNameBase + "total",
+ "The number of times the method was called"),
+ metrics.counter(metricsNameBase + "failed.total",
+ "The number of times the method was called and, after all Fault Tolerance actions had been processed, threw a Throwable"));
+ }
+ }
+}
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/metrics/FaultToleranceMetrics.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/metrics/FaultToleranceMetrics.java
index ec888d3..7edb886 100644
--- a/safeguard-impl/src/main/java/org/apache/safeguard/impl/metrics/FaultToleranceMetrics.java
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/metrics/FaultToleranceMetrics.java
@@ -23,16 +23,27 @@
import java.util.Optional;
import java.util.ServiceLoader;
+import java.util.function.Supplier;
import java.util.stream.StreamSupport;
import javax.annotation.Priority;
import javax.enterprise.inject.spi.CDI;
-// todo
public interface FaultToleranceMetrics {
+ Counter counter(String name, String description);
+ void gauge(String name, String description, String unit, Supplier<Long> supplier);
+ Histogram histogram(String name, String description);
- void inc(String value);
- void dec(String value);
+
+ interface Histogram {
+ void update(long duration);
+ }
+ interface Counter {
+ void inc();
+ void dec();
+ }
+ interface Gauge extends Supplier<Long> {
+ }
static FaultToleranceMetrics create() {
try {
@@ -46,17 +57,7 @@
} catch (final Exception e) {
// no-op
}
- return new FaultToleranceMetrics() {
- @Override
- public void inc(final String value) {
- // no-op
- }
-
- @Override
- public void dec(final String value) {
- // no-op
- }
- };
+ return new NoMetrics();
}
}
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/metrics/MicroprofileMetricsImpl.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/metrics/MicroprofileMetricsImpl.java
index 9bdf74d..0a2fa9f 100644
--- a/safeguard-impl/src/main/java/org/apache/safeguard/impl/metrics/MicroprofileMetricsImpl.java
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/metrics/MicroprofileMetricsImpl.java
@@ -18,8 +18,15 @@
*/
package org.apache.safeguard.impl.metrics;
+import static org.eclipse.microprofile.metrics.MetricType.COUNTER;
+import static org.eclipse.microprofile.metrics.MetricType.GAUGE;
+import static org.eclipse.microprofile.metrics.MetricType.HISTOGRAM;
+
+import java.util.function.Supplier;
+
import javax.enterprise.inject.Vetoed;
+import org.eclipse.microprofile.metrics.Metadata;
import org.eclipse.microprofile.metrics.MetricRegistry;
@Vetoed
@@ -31,12 +38,33 @@
}
@Override
- public void inc(final String value) {
- registry.counter(value).inc();
+ public Counter counter(final String name, final String description) {
+ final org.eclipse.microprofile.metrics.Counter delegate = registry.counter(
+ new Metadata(name, name, description, COUNTER, "none"));
+ return new Counter() {
+ @Override
+ public void inc() {
+ delegate.inc();
+ }
+
+ @Override
+ public void dec() {
+ delegate.dec();
+ }
+ };
}
@Override
- public void dec(final String value) {
- registry.counter(value).dec();
+ public void gauge(final String name, final String description, final String unit,
+ final Supplier<Long> supplier) {
+ registry.register(new Metadata(name, name, description, GAUGE, unit),
+ (org.eclipse.microprofile.metrics.Gauge<Long>) supplier::get);
+ }
+
+ @Override
+ public Histogram histogram(final String name, final String description) {
+ final org.eclipse.microprofile.metrics.Histogram histogram = registry.histogram(
+ new Metadata(name, name, description, HISTOGRAM, "none"));
+ return histogram::update;
}
}
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/metrics/NoMetrics.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/metrics/NoMetrics.java
new file mode 100644
index 0000000..d8a4beb
--- /dev/null
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/metrics/NoMetrics.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.metrics;
+
+import java.util.function.Supplier;
+
+import javax.enterprise.inject.Vetoed;
+
+@Vetoed
+class NoMetrics implements FaultToleranceMetrics {
+ @Override
+ public Counter counter(final String name, final String description) {
+ return new Counter() {
+ @Override
+ public void inc() {
+ // no-op
+ }
+
+ @Override
+ public void dec() {
+ // no-op
+ }
+ };
+ }
+
+ @Override
+ public void gauge(final String name, final String description, final String unit,
+ final Supplier<Long> supplier) {
+ // no-op
+ }
+
+ @Override
+ public Histogram histogram(final String name, final String description) {
+ return value -> {
+ // no-op
+ };
+ }
+}
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/retry/AfterRetryInterceptor.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/retry/AfterRetryInterceptor.java
index 03d7d13..141f2dc 100644
--- a/safeguard-impl/src/main/java/org/apache/safeguard/impl/retry/AfterRetryInterceptor.java
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/retry/AfterRetryInterceptor.java
@@ -18,13 +18,22 @@
*/
package org.apache.safeguard.impl.retry;
+import java.util.Map;
+
import javax.annotation.Priority;
import javax.interceptor.Interceptor;
+import org.apache.safeguard.impl.metrics.FaultToleranceMetrics;
import org.eclipse.microprofile.faulttolerance.Retry;
@Retry
@Interceptor
@Priority(Interceptor.Priority.PLATFORM_AFTER + 10)
public class AfterRetryInterceptor extends BaseRetryInterceptor {
+ @Override
+ protected void executeFinalCounterAction(final Map<String, Object> contextData, final String counterActionKey,
+ final FaultToleranceMetrics.Counter counter) {
+ // can be used to push it back to the before interceptor:
+ // contextData.put(counterActionKey, (Runnable) counter::inc);
+ }
}
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/retry/BaseRetryInterceptor.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/retry/BaseRetryInterceptor.java
index dcfc848..89acc9f 100644
--- a/safeguard-impl/src/main/java/org/apache/safeguard/impl/retry/BaseRetryInterceptor.java
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/retry/BaseRetryInterceptor.java
@@ -1,20 +1,20 @@
/*
- * 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
+ * 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
+ * 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.
+ * 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;
@@ -27,6 +27,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import javax.enterprise.context.ApplicationScoped;
@@ -36,10 +37,13 @@
import org.apache.safeguard.impl.annotation.AnnotationFinder;
import org.apache.safeguard.impl.config.ConfigurationMapper;
+import org.apache.safeguard.impl.interceptor.IdGeneratorInterceptor;
+import org.apache.safeguard.impl.metrics.FaultToleranceMetrics;
import org.eclipse.microprofile.faulttolerance.Retry;
import org.eclipse.microprofile.faulttolerance.exceptions.FaultToleranceException;
-public class BaseRetryInterceptor implements Serializable {
+public abstract class BaseRetryInterceptor implements Serializable {
+
@Inject
private Cache cache;
@@ -51,63 +55,107 @@
model = cache.create(context);
models.putIfAbsent(context.getMethod(), model);
}
- for (int i = 0; i < model.maxRetries + 1; i++) {
+
+ final Map<String, Object> contextData = context.getContextData();
+ final String counterKey = BaseRetryInterceptor.class.getName() + ".counter_"
+ + contextData.get(IdGeneratorInterceptor.class.getName());
+ final String counterActionKey = BaseRetryInterceptor.class.getName() + ".counterAction_"
+ + contextData.get(IdGeneratorInterceptor.class.getName());
+ AtomicInteger counter = AtomicInteger.class.cast(contextData.get(counterKey));
+ if (counter == null) {
+ counter = new AtomicInteger(model.maxRetries);
+ contextData.put(counterKey, counter);
+ }
+
+ while (counter.get() >= 0) {
try {
- return context.proceed();
- } catch (final RuntimeException re) {
- if (model.abortOn(re)) {
- throw new FaultToleranceException(re);
+ final Object proceed = context.proceed();
+ if (counter.get() == model.maxRetries) {
+ executeFinalCounterAction(contextData, counterActionKey, model.callsSucceededNotRetried);
+ } else {
+ executeFinalCounterAction(contextData, counterActionKey, model.callsSucceededRetried);
}
- if (model.maxRetries == i) {
+ return proceed;
+ } catch (final Exception re) {
+ // refresh the counter from the other interceptors
+ counter = AtomicInteger.class.cast(contextData.get(counterKey));
+
+ if (model.abortOn(re) || counter.decrementAndGet() < 0) {
+ executeFinalCounterAction(contextData, counterActionKey, model.callsFailed);
throw re;
}
- if (model.retryOn(re)) {
- Thread.sleep(model.nextPause());
+ if (!model.retryOn(re)) {
+ throw re;
}
+ model.retries.inc();
+ Thread.sleep(model.nextPause());
}
}
- throw new IllegalStateException("Inaccessible but needed to compile");
+ throw new FaultToleranceException("Inaccessible normally, here for compilation");
}
+ protected abstract void executeFinalCounterAction(Map<String, Object> contextData, String counterActionKey,
+ FaultToleranceMetrics.Counter counter);
+
static class Model {
+
private final Class<? extends Throwable>[] abortOn;
+
private final Class<? extends Throwable>[] retryOn;
+
private final long maxDuration;
+
private final int maxRetries;
+
private final long delay;
+
private final long jitter;
- private Model(final Retry retry) {
- abortOn = retry.abortOn();
- retryOn = retry.retryOn();
- maxDuration = retry.delayUnit().getDuration().toNanos() * retry.maxDuration();
- maxRetries = retry.maxRetries();
- delay = retry.delayUnit().getDuration().toNanos() * retry.delay();
- jitter = retry.jitterDelayUnit().getDuration().toNanos() * retry.jitter();
+ private final FaultToleranceMetrics.Counter callsSucceededNotRetried;
+
+ private final FaultToleranceMetrics.Counter callsSucceededRetried;
+
+ private final FaultToleranceMetrics.Counter callsFailed;
+
+ private final FaultToleranceMetrics.Counter retries;
+
+ private Model(final Retry retry, final FaultToleranceMetrics.Counter callsSucceededNotRetried,
+ final FaultToleranceMetrics.Counter callsSucceededRetried, final FaultToleranceMetrics.Counter callsFailed,
+ final FaultToleranceMetrics.Counter retries) {
+ this.abortOn = retry.abortOn();
+ this.retryOn = retry.retryOn();
+ this.maxDuration = retry.delayUnit().getDuration().toNanos() * retry.maxDuration();
+ this.maxRetries = retry.maxRetries();
+ this.delay = retry.delayUnit().getDuration().toNanos() * retry.delay();
+ this.jitter = retry.jitterDelayUnit().getDuration().toNanos() * retry.jitter();
+ this.callsSucceededNotRetried = callsSucceededNotRetried;
+ this.callsSucceededRetried = callsSucceededRetried;
+ this.callsFailed = callsFailed;
+ this.retries = retries;
}
- private boolean abortOn(final RuntimeException re) {
+ private boolean abortOn(final Exception re) {
return matches(abortOn, re);
}
- private boolean retryOn(final RuntimeException re) {
+ private boolean retryOn(final Exception re) {
return matches(retryOn, re);
}
- private boolean matches(final Class<? extends Throwable>[] list, final RuntimeException re) {
- return list.length > 0 &&
- Stream.of(list).anyMatch(it -> it.isInstance(re) || it.isInstance(re.getCause()));
+ private boolean matches(final Class<? extends Throwable>[] list, final Exception re) {
+ return list.length > 0 && Stream.of(list).anyMatch(it -> it.isInstance(re) || it.isInstance(re.getCause()));
}
private long nextPause() {
final ThreadLocalRandom random = ThreadLocalRandom.current();
- return TimeUnit.NANOSECONDS.toMillis(
- min(maxDuration, max(0, ((random.nextBoolean() ? 1 : -1) * delay) + random.nextLong(jitter))));
+ return TimeUnit.NANOSECONDS
+ .toMillis(min(maxDuration, max(0, ((random.nextBoolean() ? 1 : -1) * delay) + random.nextLong(jitter))));
}
}
@ApplicationScoped
public static class Cache {
+
private final Map<Method, Model> models = new ConcurrentHashMap<>();
@Inject
@@ -116,6 +164,9 @@
@Inject
private ConfigurationMapper configurationMapper;
+ @Inject
+ private FaultToleranceMetrics metrics;
+
public Map<Method, Model> getModels() {
return models;
}
@@ -123,7 +174,16 @@
public Model create(final InvocationContext context) {
final Retry retry = finder.findAnnotation(Retry.class, context);
final Retry configuredRetry = configurationMapper.map(retry, context.getMethod(), Retry.class);
- return new Model(configuredRetry);
+ final String metricsNameBase = "ft." + context.getMethod().getDeclaringClass().getCanonicalName() + "."
+ + context.getMethod().getName() + ".retry.";
+ return new Model(configuredRetry,
+ metrics.counter(metricsNameBase + "callsSucceededNotRetried.total",
+ "The number of times the method was called and succeeded without retrying"),
+ metrics.counter(metricsNameBase + "callsSucceededRetried.total",
+ "The number of times the method was called and succeeded after retrying at least once"),
+ metrics.counter(metricsNameBase + "callsFailed.total",
+ "The number of times the method was called and ultimately failed after retrying"),
+ metrics.counter(metricsNameBase + "retries.total", "The total number of times the method was retried"));
}
}
}
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/retry/BeforeRetryInterceptor.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/retry/BeforeRetryInterceptor.java
index 257c85b..cf8884c 100644
--- a/safeguard-impl/src/main/java/org/apache/safeguard/impl/retry/BeforeRetryInterceptor.java
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/retry/BeforeRetryInterceptor.java
@@ -18,13 +18,22 @@
*/
package org.apache.safeguard.impl.retry;
+import java.util.Map;
+
import javax.annotation.Priority;
import javax.interceptor.Interceptor;
+import org.apache.safeguard.impl.metrics.FaultToleranceMetrics;
import org.eclipse.microprofile.faulttolerance.Retry;
@Retry
@Interceptor
@Priority(Interceptor.Priority.PLATFORM_AFTER)
public class BeforeRetryInterceptor extends BaseRetryInterceptor {
+ @Override
+ protected void executeFinalCounterAction(final Map<String, Object> contextData,
+ final String counterActionKey,
+ final FaultToleranceMetrics.Counter counter) {
+ counter.inc();
+ }
}
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/timeout/TimeoutInterceptor.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/timeout/TimeoutInterceptor.java
index f90f93a..9c7ef0f 100644
--- a/safeguard-impl/src/main/java/org/apache/safeguard/impl/timeout/TimeoutInterceptor.java
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/timeout/TimeoutInterceptor.java
@@ -38,6 +38,7 @@
import org.apache.safeguard.impl.annotation.AnnotationFinder;
import org.apache.safeguard.impl.customizable.Safeguard;
+import org.apache.safeguard.impl.metrics.FaultToleranceMetrics;
import org.eclipse.microprofile.faulttolerance.Timeout;
import org.eclipse.microprofile.faulttolerance.exceptions.FaultToleranceDefinitionException;
@@ -54,22 +55,29 @@
@AroundInvoke
public Object withTimeout(final InvocationContext context) throws Exception {
- final Map<Method, Long> timeouts = cache.getTimeouts();
- Long duration = timeouts.get(context.getMethod());
- if (duration == null) {
- duration = cache.create(context);
- timeouts.putIfAbsent(context.getMethod(), duration);
+ final Map<Method, Model> timeouts = cache.getTimeouts();
+ Model model = timeouts.get(context.getMethod());
+ if (model == null) {
+ model = cache.create(context);
+ timeouts.putIfAbsent(context.getMethod(), model);
}
final FutureTask<Object> task = new FutureTask<>(context::proceed);
+ final long start = System.nanoTime();
executor.execute(task);
try {
- return task.get(duration, NANOSECONDS);
+ final Object result = task.get(model.timeout, NANOSECONDS);
+ model.successes.inc();
+ return result;
} catch (final ExecutionException ee) {
cancel(task);
throw toCause(ee);
} catch (final TimeoutException te) {
+ model.timeouts.inc();
cancel(task);
throw new org.eclipse.microprofile.faulttolerance.exceptions.TimeoutException(te);
+ } finally {
+ final long end = System.nanoTime();
+ model.executionDuration.update(end - start);
}
}
@@ -90,23 +98,47 @@
throw te;
}
+ private static class Model {
+ private final long timeout;
+ private final FaultToleranceMetrics.Histogram executionDuration;
+ private final FaultToleranceMetrics.Counter timeouts;
+ private final FaultToleranceMetrics.Counter successes;
+
+ private Model(final long timeout, final FaultToleranceMetrics.Histogram executionDuration,
+ final FaultToleranceMetrics.Counter timeouts, final FaultToleranceMetrics.Counter successes) {
+ this.timeout = timeout;
+ this.executionDuration = executionDuration;
+ this.timeouts = timeouts;
+ this.successes = successes;
+ }
+ }
+
@ApplicationScoped
public static class Cache {
- private final Map<Method, Long> timeouts = new ConcurrentHashMap<>();
+ private final Map<Method, Model> timeouts = new ConcurrentHashMap<>();
@Inject
private AnnotationFinder finder;
- public Map<Method, Long> getTimeouts() {
+ @Inject
+ private FaultToleranceMetrics metrics;
+
+ public Map<Method, Model> getTimeouts() {
return timeouts;
}
- public long create(final InvocationContext context) {
+ public Model create(final InvocationContext context) {
final Timeout timeout = finder.findAnnotation(Timeout.class, context);
if (timeout.value() < 0) {
throw new FaultToleranceDefinitionException("Timeout can't be < 0: " + context.getMethod());
}
- return timeout.unit().getDuration().toNanos() * timeout.value();
+ final String metricsNameBase = "ft." + context.getMethod().getDeclaringClass().getCanonicalName() + "." +
+ context.getMethod().getName() + ".timeout.";
+ return new Model(
+ timeout.unit().getDuration().toNanos() * timeout.value(),
+ metrics.histogram(metricsNameBase + "executionDuration", "Histogram of execution times for the method"),
+ metrics.counter(metricsNameBase + "callsTimedOut.total", "The number of times the method timed out"),
+ metrics.counter(metricsNameBase + "callsNotTimedOut.total", "The number of times the method completed without timing out"));
}
}
}
diff --git a/safeguard-impl/src/test/java/org/apache/safeguard/ft/tck/IDERunner.java b/safeguard-impl/src/test/java/org/apache/safeguard/ft/tck/IDERunner.java
deleted file mode 100644
index d7d1c0c..0000000
--- a/safeguard-impl/src/test/java/org/apache/safeguard/ft/tck/IDERunner.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package org.apache.safeguard.ft.tck;
-
-import org.eclipse.microprofile.fault.tolerance.tck.circuitbreaker.CircuitBreakerConfigOnMethodTest;
-
-public class IDERunner extends CircuitBreakerConfigOnMethodTest {
-}
diff --git a/safeguard-impl/src/test/resources/dev.xml b/safeguard-impl/src/test/resources/dev.xml
new file mode 100644
index 0000000..7c7c780
--- /dev/null
+++ b/safeguard-impl/src/test/resources/dev.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
+<suite name="Dev Manual Test Run" verbose="2" configfailurepolicy="continue" >
+ <test name="Manual Run">
+ <classes>
+ <!--
+ <class name="org.eclipse.microprofile.fault.tolerance.tck.config.ConfigPropertyGlobalVsClassVsMethodTest">
+ </class>
+ <class name="org.eclipse.microprofile.fault.tolerance.tck.metrics.RetryMetricTest">
+ <methods>
+ <include name="testRetryMetricUnsuccessful" />
+ </methods>
+ </class>
+ <class name="org.eclipse.microprofile.fault.tolerance.tck.bulkhead.BulkheadSynchRetryTest">
+ <methods>
+ <include name="testIgnoreWaitingTaskQueueBulkhead"/>
+ </methods>
+ </class>
+ -->
+ </classes>
+ </test>
+</suite>
+