You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by jo...@apache.org on 2017/09/13 11:35:49 UTC

geronimo-safeguard git commit: GERONIMO-6582 - Adding more execution plans.

Repository: geronimo-safeguard
Updated Branches:
  refs/heads/master cf5a00023 -> 7c93e2983


GERONIMO-6582 - Adding more execution plans.


Project: http://git-wip-us.apache.org/repos/asf/geronimo-safeguard/repo
Commit: http://git-wip-us.apache.org/repos/asf/geronimo-safeguard/commit/7c93e298
Tree: http://git-wip-us.apache.org/repos/asf/geronimo-safeguard/tree/7c93e298
Diff: http://git-wip-us.apache.org/repos/asf/geronimo-safeguard/diff/7c93e298

Branch: refs/heads/master
Commit: 7c93e298388dec63bd1835d71c703fb3507fa332
Parents: cf5a000
Author: John D. Ament <jo...@apache.org>
Authored: Wed Sep 13 07:35:45 2017 -0400
Committer: John D. Ament <jo...@apache.org>
Committed: Wed Sep 13 07:35:45 2017 -0400

----------------------------------------------------------------------
 .../apache/safeguard/api/ExecutionManager.java  |  33 +++++
 .../safeguard/exception/AsyncException.java     |  26 ++++
 .../apache/safeguard/impl/ExecutionPlan.java    |  61 ---------
 .../safeguard/impl/ExecutionPlanFactory.java    |  90 -------------
 .../impl/FailsafeExecutionManager.java          |  43 +++----
 .../impl/MicroprofileAnnotationMapper.java      |  60 ---------
 .../AsyncFailsafeExecutionPlan.java             |  59 +++++++++
 .../executionPlans/AsyncOnlyExecutionPlan.java  |  45 +++++++
 .../AsyncTimeoutExecutionPlan.java              |  48 +++++++
 .../impl/executionPlans/ExecutionPlan.java      |  26 ++++
 .../executionPlans/ExecutionPlanFactory.java    | 125 +++++++++++++++++++
 .../MicroprofileAnnotationMapper.java           |  60 +++++++++
 .../SyncFailsafeExecutionPlan.java              |  66 ++++++++++
 .../AsyncOnlyExecutionPlanTest.java             |  50 ++++++++
 .../AsyncTimeoutExecutionPlanTest.java          |  68 ++++++++++
 .../SyncFailsafeExecutionPlanTest.java          |  33 +++++
 16 files changed, 655 insertions(+), 238 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/geronimo-safeguard/blob/7c93e298/safeguard-api/src/main/java/org/apache/safeguard/api/ExecutionManager.java
----------------------------------------------------------------------
diff --git a/safeguard-api/src/main/java/org/apache/safeguard/api/ExecutionManager.java b/safeguard-api/src/main/java/org/apache/safeguard/api/ExecutionManager.java
new file mode 100644
index 0000000..86a69c8
--- /dev/null
+++ b/safeguard-api/src/main/java/org/apache/safeguard/api/ExecutionManager.java
@@ -0,0 +1,33 @@
+/*
+ *  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 org.apache.safeguard.api.circuitbreaker.CircuitBreakerManager;
+import org.apache.safeguard.api.retry.RetryManager;
+
+import java.util.concurrent.Callable;
+
+public interface ExecutionManager {
+    <T> T execute(String name, Callable<T> callable);
+
+    CircuitBreakerManager getCircuitBreakerManager();
+
+    RetryManager getRetryManager();
+}

http://git-wip-us.apache.org/repos/asf/geronimo-safeguard/blob/7c93e298/safeguard-api/src/main/java/org/apache/safeguard/exception/AsyncException.java
----------------------------------------------------------------------
diff --git a/safeguard-api/src/main/java/org/apache/safeguard/exception/AsyncException.java b/safeguard-api/src/main/java/org/apache/safeguard/exception/AsyncException.java
new file mode 100644
index 0000000..08be3a8
--- /dev/null
+++ b/safeguard-api/src/main/java/org/apache/safeguard/exception/AsyncException.java
@@ -0,0 +1,26 @@
+/*
+ *  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.exception;
+
+public class AsyncException extends RuntimeException {
+    public AsyncException(Exception e) {
+        super(e);
+    }
+}

http://git-wip-us.apache.org/repos/asf/geronimo-safeguard/blob/7c93e298/safeguard-impl/src/main/java/org/apache/safeguard/impl/ExecutionPlan.java
----------------------------------------------------------------------
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/ExecutionPlan.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/ExecutionPlan.java
deleted file mode 100644
index 0fc0e88..0000000
--- a/safeguard-impl/src/main/java/org/apache/safeguard/impl/ExecutionPlan.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- *  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 net.jodah.failsafe.SyncFailsafe;
-import org.apache.safeguard.impl.circuitbreaker.FailsafeCircuitBreaker;
-import org.apache.safeguard.impl.retry.FailsafeRetryDefinition;
-
-import java.time.Duration;
-import java.util.concurrent.Callable;
-
-class ExecutionPlan {
-    private final Duration timeout;
-    private final boolean isAsynchronous;
-    private final FailsafeRetryDefinition retryDefinition;
-    private final FailsafeCircuitBreaker failsafeCircuitBreaker;
-
-    public ExecutionPlan(Duration timeout, boolean isAsynchronous, FailsafeRetryDefinition retryDefinition, FailsafeCircuitBreaker failsafeCircuitBreaker) {
-        this.timeout = timeout;
-        this.isAsynchronous = isAsynchronous;
-        this.retryDefinition = retryDefinition;
-        this.failsafeCircuitBreaker = failsafeCircuitBreaker;
-        if(retryDefinition == null && failsafeCircuitBreaker == null && !isAsynchronous) {
-            throw new IllegalStateException("For non-async invocations, must have at least one of RetryDefintion or CircuitBreaker defined");
-        }
-    }
-
-    public <T> T execute(Callable<T> callable) {
-        SyncFailsafe<?> syncFailsafe;
-        if(retryDefinition == null) {
-            syncFailsafe = Failsafe.with(failsafeCircuitBreaker.getDefinition().getCircuitBreaker());
-        }
-        else {
-            if(failsafeCircuitBreaker == null) {
-                syncFailsafe = Failsafe.with(retryDefinition.getRetryPolicy());
-            }
-            else {
-                syncFailsafe = Failsafe.with(retryDefinition.getRetryPolicy()).with(failsafeCircuitBreaker.getDefinition().getCircuitBreaker());
-            }
-        }
-        return syncFailsafe.get(callable);
-    }
-}

http://git-wip-us.apache.org/repos/asf/geronimo-safeguard/blob/7c93e298/safeguard-impl/src/main/java/org/apache/safeguard/impl/ExecutionPlanFactory.java
----------------------------------------------------------------------
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/ExecutionPlanFactory.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/ExecutionPlanFactory.java
deleted file mode 100644
index 84587d3..0000000
--- a/safeguard-impl/src/main/java/org/apache/safeguard/impl/ExecutionPlanFactory.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- *  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 org.apache.safeguard.impl.circuitbreaker.FailsafeCircuitBreaker;
-import org.apache.safeguard.impl.circuitbreaker.FailsafeCircuitBreakerBuilder;
-import org.apache.safeguard.impl.circuitbreaker.FailsafeCircuitBreakerManager;
-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.CircuitBreaker;
-import org.eclipse.microprofile.faulttolerance.Retry;
-
-import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.apache.safeguard.impl.MicroprofileAnnotationMapper.mapCircuitBreaker;
-import static org.apache.safeguard.impl.MicroprofileAnnotationMapper.mapRetry;
-
-public class ExecutionPlanFactory {
-    private final FailsafeCircuitBreakerManager circuitBreakerManager;
-    private final FailsafeRetryManager retryManager;
-    private Map<String, ExecutionPlan> executionPlanMap = new HashMap<>();
-
-    public ExecutionPlanFactory(FailsafeCircuitBreakerManager circuitBreakerManager, FailsafeRetryManager retryManager) {
-        this.circuitBreakerManager = circuitBreakerManager;
-        this.retryManager = retryManager;
-    }
-
-    ExecutionPlan locateExecutionPlan(String name) {
-        return executionPlanMap.computeIfAbsent(name, name1 -> {
-            FailsafeCircuitBreaker circuitBreaker = circuitBreakerManager.getCircuitBreaker(name1);
-            FailsafeRetryDefinition retryDefinition = retryManager.getRetryDefinition(name1);
-            return new ExecutionPlan(null, false, retryDefinition, circuitBreaker);
-        });
-    }
-
-    ExecutionPlan locateExecutionPlan(Method method) {
-        final String name = NamingUtil.createName(method);
-        return executionPlanMap.computeIfAbsent(name, name1 -> {
-            FailsafeCircuitBreaker circuitBreaker = circuitBreakerManager.getCircuitBreaker(name1);
-            if(circuitBreaker == null) {
-                circuitBreaker = createCBDefinition(name, method);
-            }
-            FailsafeRetryDefinition retryDefinition = retryManager.getRetryDefinition(name1);
-            if(retryDefinition == null) {
-                retryDefinition =createDefinition(name, method);
-            }
-            return new ExecutionPlan(null, false, retryDefinition, circuitBreaker);
-        });
-    }
-
-    private FailsafeRetryDefinition createDefinition(String name, Method method) {
-        Retry retry = AnnotationUtil.getAnnotation(method, Retry.class);
-        if (retry == null) {
-            return null;
-        }
-        FailsafeRetryBuilder retryBuilder = retryManager.newRetryDefinition(name);
-        return mapRetry(retry, retryBuilder);
-    }
-
-    private FailsafeCircuitBreaker createCBDefinition(String name, Method method) {
-        CircuitBreaker circuitBreaker = AnnotationUtil.getAnnotation(method, CircuitBreaker.class);
-        if (circuitBreaker == null) {
-            return null;
-        }
-        FailsafeCircuitBreakerBuilder circuitBreakerBuilder = this.circuitBreakerManager.newCircuitBreaker(name);
-        return new FailsafeCircuitBreaker(mapCircuitBreaker(circuitBreaker, circuitBreakerBuilder));
-    }
-}

http://git-wip-us.apache.org/repos/asf/geronimo-safeguard/blob/7c93e298/safeguard-impl/src/main/java/org/apache/safeguard/impl/FailsafeExecutionManager.java
----------------------------------------------------------------------
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/FailsafeExecutionManager.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/FailsafeExecutionManager.java
index 67cb4a1..35d3fa9 100644
--- a/safeguard-impl/src/main/java/org/apache/safeguard/impl/FailsafeExecutionManager.java
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/FailsafeExecutionManager.java
@@ -19,16 +19,19 @@
 
 package org.apache.safeguard.impl;
 
+import org.apache.safeguard.api.ExecutionManager;
 import org.apache.safeguard.impl.circuitbreaker.FailsafeCircuitBreakerManager;
+import org.apache.safeguard.impl.executionPlans.ExecutionPlanFactory;
 import org.apache.safeguard.impl.retry.FailsafeRetryManager;
 
 import javax.enterprise.inject.Vetoed;
 import javax.interceptor.InvocationContext;
 import java.lang.reflect.Method;
+import java.time.Duration;
 import java.util.concurrent.Callable;
 
 @Vetoed
-public class FailsafeExecutionManager {
+public class FailsafeExecutionManager implements ExecutionManager {
     private FailsafeCircuitBreakerManager circuitBreakerManager;
     private FailsafeRetryManager retryManager;
     private ExecutionPlanFactory executionPlanFactory;
@@ -42,47 +45,33 @@ public class FailsafeExecutionManager {
     public Object execute(InvocationContext invocationContext) {
         Method method = invocationContext.getMethod();
         return executionPlanFactory.locateExecutionPlan(method).execute(invocationContext::proceed);
-//        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);
     }
 
+    @Override
     public <T> T execute(String name, Callable<T> callable) {
-        return executionPlanFactory.locateExecutionPlan(name).execute(callable);
-//        FailsafeRetryDefinition failsafeRetryDefinition = retryManager.getRetryDefinition(name);
-//        return Failsafe.with(failsafeRetryDefinition.getRetryPolicy()).get(callable);
+        return executionPlanFactory.locateExecutionPlan(name, null, false).execute(callable);
+    }
+
+    public <T> T executeAsync(String name, Callable<T> callable) {
+        return executionPlanFactory.locateExecutionPlan(name, null, true).execute(callable);
+    }
+
+    public <T> T executeAsync(String name, Callable<T> callable, Duration timeout) {
+        return executionPlanFactory.locateExecutionPlan(name, timeout, true).execute(callable);
     }
 
     public ExecutionPlanFactory getExecutionPlanFactory() {
         return executionPlanFactory;
     }
 
+    @Override
     public FailsafeCircuitBreakerManager getCircuitBreakerManager() {
         return circuitBreakerManager;
     }
 
+    @Override
     public FailsafeRetryManager getRetryManager() {
         return retryManager;
     }
 
-    //    private FailsafeRetryDefinition createDefinition(String name, Method method) {
-//        Retry retry = AnnotationUtil.getAnnotation(method, Retry.class);
-//        if(retry == null) {
-//            return null;
-//        }
-//        FailsafeRetryBuilder retryBuilder = retryManager.newRetryDefinition(name);
-//        return mapRetry(retry, retryBuilder);
-//    }
-
-//    private FailsafeCircuitBreakerDefinition createCBDefinition(String name, Method method) {
-//        CircuitBreaker circuitBreaker = AnnotationUtil.getAnnotation(method, CircuitBreaker.class);
-//        if (circuitBreaker == null) {
-//            return null;
-//        }
-//        FailsafeCircuitBreakerBuilder circuitBreakerBuilder = this.circuitBreakerManager.newCircuitBreaker(name);
-//        return mapCircuitBreaker(circuitBreaker, circuitBreakerBuilder);
-//    }
 }

http://git-wip-us.apache.org/repos/asf/geronimo-safeguard/blob/7c93e298/safeguard-impl/src/main/java/org/apache/safeguard/impl/MicroprofileAnnotationMapper.java
----------------------------------------------------------------------
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/MicroprofileAnnotationMapper.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/MicroprofileAnnotationMapper.java
deleted file mode 100644
index 7a8f111..0000000
--- a/safeguard-impl/src/main/java/org/apache/safeguard/impl/MicroprofileAnnotationMapper.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- *  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 org.apache.safeguard.impl.circuitbreaker.FailsafeCircuitBreakerBuilder;
-import org.apache.safeguard.impl.circuitbreaker.FailsafeCircuitBreakerDefinition;
-import org.apache.safeguard.impl.retry.FailsafeRetryBuilder;
-import org.apache.safeguard.impl.retry.FailsafeRetryDefinition;
-import org.eclipse.microprofile.faulttolerance.CircuitBreaker;
-import org.eclipse.microprofile.faulttolerance.Retry;
-
-import java.time.Duration;
-
-class MicroprofileAnnotationMapper {
-    static FailsafeRetryDefinition mapRetry(Retry retry, FailsafeRetryBuilder retryBuilder) {
-        retryBuilder.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();
-    }
-
-    static FailsafeCircuitBreakerDefinition mapCircuitBreaker(CircuitBreaker circuitBreaker,
-                                                              FailsafeCircuitBreakerBuilder builder) {
-        int failureCount = (int) (circuitBreaker.failureRatio() * circuitBreaker.requestVolumeThreshold());
-        FailsafeCircuitBreakerBuilder failsafeCircuitBreakerBuilder = builder
-                .withFailOn(circuitBreaker.failOn())
-                .withDelay(Duration.of(circuitBreaker.delay(), circuitBreaker.delayUnit()))
-                .withSuccessCount(circuitBreaker.successThreshold());
-        if (failureCount > 0) {
-            failsafeCircuitBreakerBuilder.withFailures(failureCount, circuitBreaker.requestVolumeThreshold());
-        }
-        return failsafeCircuitBreakerBuilder.build();
-    }
-}

http://git-wip-us.apache.org/repos/asf/geronimo-safeguard/blob/7c93e298/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/AsyncFailsafeExecutionPlan.java
----------------------------------------------------------------------
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/AsyncFailsafeExecutionPlan.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/AsyncFailsafeExecutionPlan.java
new file mode 100644
index 0000000..589883a
--- /dev/null
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/AsyncFailsafeExecutionPlan.java
@@ -0,0 +1,59 @@
+/*
+ *  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.executionPlans;
+
+import net.jodah.failsafe.AsyncFailsafe;
+import org.apache.safeguard.impl.circuitbreaker.FailsafeCircuitBreaker;
+import org.apache.safeguard.impl.retry.FailsafeRetryDefinition;
+
+import java.time.Duration;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public class AsyncFailsafeExecutionPlan extends SyncFailsafeExecutionPlan {
+    private final ScheduledExecutorService executorService;
+    private final Duration timeout;
+
+    public AsyncFailsafeExecutionPlan(FailsafeRetryDefinition retryDefinition,
+                                      FailsafeCircuitBreaker failsafeCircuitBreaker,
+                                      ScheduledExecutorService executorService,
+                                      Duration timeout) {
+        super(retryDefinition, failsafeCircuitBreaker);
+        this.executorService = executorService;
+        this.timeout = timeout;
+    }
+
+    @Override
+    public <T> T execute(Callable<T> callable) {
+        AsyncFailsafe<?> asyncFailsafe = getSyncFailsafe().with(executorService);
+        try {
+            if (this.timeout == null) {
+                return asyncFailsafe.get(callable).get();
+            } else {
+                return asyncFailsafe.get(callable).get(timeout.toMillis(), TimeUnit.MILLISECONDS);
+            }
+        } catch (TimeoutException | InterruptedException | ExecutionException e) {
+            throw new org.eclipse.microprofile.faulttolerance.exceptions.TimeoutException(e);
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/geronimo-safeguard/blob/7c93e298/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/AsyncOnlyExecutionPlan.java
----------------------------------------------------------------------
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/AsyncOnlyExecutionPlan.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/AsyncOnlyExecutionPlan.java
new file mode 100644
index 0000000..9af94c2
--- /dev/null
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/AsyncOnlyExecutionPlan.java
@@ -0,0 +1,45 @@
+/*
+ *  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.executionPlans;
+
+import org.apache.safeguard.exception.AsyncException;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+class AsyncOnlyExecutionPlan implements ExecutionPlan {
+    private final ExecutorService executorService;
+
+    AsyncOnlyExecutionPlan(ExecutorService executorService) {
+        this.executorService = executorService;
+    }
+
+    @Override
+    public <T> T execute(Callable<T> callable) {
+        Future<T> submitted = executorService.submit(callable);
+        try {
+            return submitted.get();
+        } catch (InterruptedException | ExecutionException e) {
+            throw new AsyncException(e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/geronimo-safeguard/blob/7c93e298/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/AsyncTimeoutExecutionPlan.java
----------------------------------------------------------------------
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/AsyncTimeoutExecutionPlan.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/AsyncTimeoutExecutionPlan.java
new file mode 100644
index 0000000..849f7c4
--- /dev/null
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/AsyncTimeoutExecutionPlan.java
@@ -0,0 +1,48 @@
+/*
+ *  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.executionPlans;
+
+import java.time.Duration;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+class AsyncTimeoutExecutionPlan implements ExecutionPlan {
+    private final Duration timeout;
+    private final ExecutorService executorService;
+
+    AsyncTimeoutExecutionPlan(Duration timeout, ExecutorService executorService) {
+        this.timeout = timeout;
+        this.executorService = executorService;
+    }
+
+    @Override
+    public <T> T execute(Callable<T> callable) {
+        Future<T> future = executorService.submit(callable);
+        try {
+            return future.get(timeout.toMillis(), TimeUnit.MILLISECONDS);
+        } catch (InterruptedException | ExecutionException | TimeoutException e) {
+            throw new org.eclipse.microprofile.faulttolerance.exceptions.TimeoutException(e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/geronimo-safeguard/blob/7c93e298/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/ExecutionPlan.java
----------------------------------------------------------------------
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/ExecutionPlan.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/ExecutionPlan.java
new file mode 100644
index 0000000..01c3271
--- /dev/null
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/ExecutionPlan.java
@@ -0,0 +1,26 @@
+/*
+ *  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.executionPlans;
+
+import java.util.concurrent.Callable;
+
+public interface ExecutionPlan {
+    <T> T execute(Callable<T> callable);
+}

http://git-wip-us.apache.org/repos/asf/geronimo-safeguard/blob/7c93e298/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/ExecutionPlanFactory.java
----------------------------------------------------------------------
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/ExecutionPlanFactory.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/ExecutionPlanFactory.java
new file mode 100644
index 0000000..4a5086e
--- /dev/null
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/ExecutionPlanFactory.java
@@ -0,0 +1,125 @@
+/*
+ *  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.executionPlans;
+
+import org.apache.safeguard.impl.circuitbreaker.FailsafeCircuitBreaker;
+import org.apache.safeguard.impl.circuitbreaker.FailsafeCircuitBreakerBuilder;
+import org.apache.safeguard.impl.circuitbreaker.FailsafeCircuitBreakerManager;
+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.Asynchronous;
+import org.eclipse.microprofile.faulttolerance.CircuitBreaker;
+import org.eclipse.microprofile.faulttolerance.Retry;
+import org.eclipse.microprofile.faulttolerance.Timeout;
+
+import java.lang.reflect.Method;
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.safeguard.impl.executionPlans.MicroprofileAnnotationMapper.mapCircuitBreaker;
+import static org.apache.safeguard.impl.executionPlans.MicroprofileAnnotationMapper.mapRetry;
+
+public class ExecutionPlanFactory {
+    private final FailsafeCircuitBreakerManager circuitBreakerManager;
+    private final FailsafeRetryManager retryManager;
+    private Map<String, ExecutionPlan> executionPlanMap = new HashMap<>();
+
+    public ExecutionPlanFactory(FailsafeCircuitBreakerManager circuitBreakerManager, FailsafeRetryManager retryManager) {
+        this.circuitBreakerManager = circuitBreakerManager;
+        this.retryManager = retryManager;
+    }
+
+    public ExecutionPlan locateExecutionPlan(String name, Duration timeout, boolean async) {
+        return executionPlanMap.computeIfAbsent(name, key -> {
+            FailsafeCircuitBreaker circuitBreaker = circuitBreakerManager.getCircuitBreaker(key);
+            FailsafeRetryDefinition retryDefinition = retryManager.getRetryDefinition(key);
+            if (circuitBreaker == null && retryDefinition == null) {
+                return null;
+            } else {
+                return new SyncFailsafeExecutionPlan(retryDefinition, circuitBreaker);
+            }
+        });
+    }
+
+    public ExecutionPlan locateExecutionPlan(Method method) {
+        final String name = NamingUtil.createName(method);
+        return executionPlanMap.computeIfAbsent(name, key -> {
+            FailsafeCircuitBreaker circuitBreaker = circuitBreakerManager.getCircuitBreaker(key);
+            if (circuitBreaker == null) {
+                circuitBreaker = createCBDefinition(name, method);
+            }
+            FailsafeRetryDefinition retryDefinition = retryManager.getRetryDefinition(key);
+            if (retryDefinition == null) {
+                retryDefinition = createDefinition(name, method);
+            }
+            boolean isAsync = isAsync(method);
+            Duration timeout = readTimeout(method);
+            if(circuitBreaker == null && retryDefinition == null && isAsync) {
+                if(timeout == null) {
+                    return new AsyncOnlyExecutionPlan(null);
+                }
+                else {
+                    return new AsyncTimeoutExecutionPlan(timeout, null);
+                }
+            }
+            else {
+                if (isAsync) {
+                    return new AsyncFailsafeExecutionPlan(retryDefinition, circuitBreaker, null, timeout);
+                } else {
+                    return new SyncFailsafeExecutionPlan(retryDefinition, circuitBreaker);
+                }
+            }
+        });
+    }
+
+    private FailsafeRetryDefinition createDefinition(String name, Method method) {
+        Retry retry = AnnotationUtil.getAnnotation(method, Retry.class);
+        if (retry == null) {
+            return null;
+        }
+        FailsafeRetryBuilder retryBuilder = retryManager.newRetryDefinition(name);
+        return mapRetry(retry, retryBuilder);
+    }
+
+    private FailsafeCircuitBreaker createCBDefinition(String name, Method method) {
+        CircuitBreaker circuitBreaker = AnnotationUtil.getAnnotation(method, CircuitBreaker.class);
+        if (circuitBreaker == null) {
+            return null;
+        }
+        FailsafeCircuitBreakerBuilder circuitBreakerBuilder = this.circuitBreakerManager.newCircuitBreaker(name);
+        return new FailsafeCircuitBreaker(mapCircuitBreaker(circuitBreaker, circuitBreakerBuilder));
+    }
+
+    private boolean isAsync(Method method) {
+        return AnnotationUtil.getAnnotation(method, Asynchronous.class) != null;
+    }
+
+    private Duration readTimeout(Method method) {
+        Timeout timeout = AnnotationUtil.getAnnotation(method, Timeout.class);
+        if(timeout == null) {
+            return null;
+        }
+        return Duration.of(timeout.value(), timeout.unit());
+    }
+}

http://git-wip-us.apache.org/repos/asf/geronimo-safeguard/blob/7c93e298/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/MicroprofileAnnotationMapper.java
----------------------------------------------------------------------
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/MicroprofileAnnotationMapper.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/MicroprofileAnnotationMapper.java
new file mode 100644
index 0000000..befd5e5
--- /dev/null
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/MicroprofileAnnotationMapper.java
@@ -0,0 +1,60 @@
+/*
+ *  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.executionPlans;
+
+import org.apache.safeguard.impl.circuitbreaker.FailsafeCircuitBreakerBuilder;
+import org.apache.safeguard.impl.circuitbreaker.FailsafeCircuitBreakerDefinition;
+import org.apache.safeguard.impl.retry.FailsafeRetryBuilder;
+import org.apache.safeguard.impl.retry.FailsafeRetryDefinition;
+import org.eclipse.microprofile.faulttolerance.CircuitBreaker;
+import org.eclipse.microprofile.faulttolerance.Retry;
+
+import java.time.Duration;
+
+class MicroprofileAnnotationMapper {
+    static FailsafeRetryDefinition mapRetry(Retry retry, FailsafeRetryBuilder retryBuilder) {
+        retryBuilder.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();
+    }
+
+    static FailsafeCircuitBreakerDefinition mapCircuitBreaker(CircuitBreaker circuitBreaker,
+                                                              FailsafeCircuitBreakerBuilder builder) {
+        int failureCount = (int) (circuitBreaker.failureRatio() * circuitBreaker.requestVolumeThreshold());
+        FailsafeCircuitBreakerBuilder failsafeCircuitBreakerBuilder = builder
+                .withFailOn(circuitBreaker.failOn())
+                .withDelay(Duration.of(circuitBreaker.delay(), circuitBreaker.delayUnit()))
+                .withSuccessCount(circuitBreaker.successThreshold());
+        if (failureCount > 0) {
+            failsafeCircuitBreakerBuilder.withFailures(failureCount, circuitBreaker.requestVolumeThreshold());
+        }
+        return failsafeCircuitBreakerBuilder.build();
+    }
+}

http://git-wip-us.apache.org/repos/asf/geronimo-safeguard/blob/7c93e298/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/SyncFailsafeExecutionPlan.java
----------------------------------------------------------------------
diff --git a/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/SyncFailsafeExecutionPlan.java b/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/SyncFailsafeExecutionPlan.java
new file mode 100644
index 0000000..613ceab
--- /dev/null
+++ b/safeguard-impl/src/main/java/org/apache/safeguard/impl/executionPlans/SyncFailsafeExecutionPlan.java
@@ -0,0 +1,66 @@
+/*
+ *  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.executionPlans;
+
+import net.jodah.failsafe.Failsafe;
+import net.jodah.failsafe.SyncFailsafe;
+import org.apache.safeguard.impl.circuitbreaker.FailsafeCircuitBreaker;
+import org.apache.safeguard.impl.retry.FailsafeRetryDefinition;
+
+import java.util.concurrent.Callable;
+
+public class SyncFailsafeExecutionPlan implements ExecutionPlan {
+    private final FailsafeRetryDefinition retryDefinition;
+    private final FailsafeCircuitBreaker failsafeCircuitBreaker;
+
+    SyncFailsafeExecutionPlan(FailsafeRetryDefinition retryDefinition, FailsafeCircuitBreaker failsafeCircuitBreaker) {
+        this.retryDefinition = retryDefinition;
+        this.failsafeCircuitBreaker = failsafeCircuitBreaker;
+        validateConfig();
+    }
+
+    private void validateConfig() {
+        if(retryDefinition == null && failsafeCircuitBreaker == null) {
+            throw new IllegalStateException("For non-async invocations, must have at least one of RetryDefintion or CircuitBreaker defined");
+        }
+    }
+
+    @Override
+    public <T> T execute(Callable<T> callable) {
+        SyncFailsafe<?> syncFailsafe = getSyncFailsafe();
+        return syncFailsafe.get(callable);
+    }
+
+    SyncFailsafe<?> getSyncFailsafe() {
+        SyncFailsafe<?> syncFailsafe;
+        if(retryDefinition == null) {
+            syncFailsafe = Failsafe.with(failsafeCircuitBreaker.getDefinition().getCircuitBreaker());
+        }
+        else {
+            if(failsafeCircuitBreaker == null) {
+                syncFailsafe = Failsafe.with(retryDefinition.getRetryPolicy());
+            }
+            else {
+                syncFailsafe = Failsafe.with(retryDefinition.getRetryPolicy()).with(failsafeCircuitBreaker.getDefinition().getCircuitBreaker());
+            }
+        }
+        return syncFailsafe;
+    }
+}

http://git-wip-us.apache.org/repos/asf/geronimo-safeguard/blob/7c93e298/safeguard-impl/src/test/java/org/apache/safeguard/impl/executionPlans/AsyncOnlyExecutionPlanTest.java
----------------------------------------------------------------------
diff --git a/safeguard-impl/src/test/java/org/apache/safeguard/impl/executionPlans/AsyncOnlyExecutionPlanTest.java b/safeguard-impl/src/test/java/org/apache/safeguard/impl/executionPlans/AsyncOnlyExecutionPlanTest.java
new file mode 100644
index 0000000..4c52faf
--- /dev/null
+++ b/safeguard-impl/src/test/java/org/apache/safeguard/impl/executionPlans/AsyncOnlyExecutionPlanTest.java
@@ -0,0 +1,50 @@
+/*
+ *  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.executionPlans;
+
+import org.testng.annotations.Test;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AsyncOnlyExecutionPlanTest {
+
+    @Test
+    public void shouldExecuteAsncWithoutTimeout() {
+        AsyncOnlyExecutionPlan asyncOnlyExecutionPlan = new AsyncOnlyExecutionPlan(Executors.newFixedThreadPool(2));
+        MyCallable callable = new MyCallable();
+        asyncOnlyExecutionPlan.execute(callable);
+        assertThat(callable.calledThread).isNotEqualTo(Thread.currentThread().getName());
+    }
+
+    private static class MyCallable implements Callable<Object> {
+
+        private String calledThread;
+
+        @Override
+        public Object call() throws Exception {
+            this.calledThread = Thread.currentThread().getName();
+            return "";
+        }
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/geronimo-safeguard/blob/7c93e298/safeguard-impl/src/test/java/org/apache/safeguard/impl/executionPlans/AsyncTimeoutExecutionPlanTest.java
----------------------------------------------------------------------
diff --git a/safeguard-impl/src/test/java/org/apache/safeguard/impl/executionPlans/AsyncTimeoutExecutionPlanTest.java b/safeguard-impl/src/test/java/org/apache/safeguard/impl/executionPlans/AsyncTimeoutExecutionPlanTest.java
new file mode 100644
index 0000000..b45727a
--- /dev/null
+++ b/safeguard-impl/src/test/java/org/apache/safeguard/impl/executionPlans/AsyncTimeoutExecutionPlanTest.java
@@ -0,0 +1,68 @@
+/*
+ *  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.executionPlans;
+
+import org.eclipse.microprofile.faulttolerance.exceptions.TimeoutException;
+import org.testng.annotations.Test;
+
+import java.time.Duration;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+public class AsyncTimeoutExecutionPlanTest {
+    @Test
+    public void shouldExecuteSimpleCallable() {
+        AsyncTimeoutExecutionPlan asyncTimeoutExecutionPlan = new AsyncTimeoutExecutionPlan(Duration.ofMillis(1000), Executors.newSingleThreadExecutor());
+        DelayedCaller callable = new DelayedCaller(200);
+
+        asyncTimeoutExecutionPlan.execute(callable);
+
+        String myThreadName = Thread.currentThread().getName();
+        assertThat(callable.executedThread).isNotEqualTo(myThreadName);
+    }
+
+    @Test
+    public void shouldThrowTimeoutWhenDelayHit() {
+        AsyncTimeoutExecutionPlan asyncTimeoutExecutionPlan = new AsyncTimeoutExecutionPlan(Duration.ofMillis(100), Executors.newSingleThreadExecutor());
+        DelayedCaller callable = new DelayedCaller(200);
+
+        assertThatThrownBy(() -> asyncTimeoutExecutionPlan.execute(callable)).isInstanceOf(TimeoutException.class);
+    }
+
+    private static class DelayedCaller implements Callable<Object> {
+
+        private final long delay;
+        private String executedThread;
+
+        public DelayedCaller(long delay) {
+            this.delay = delay;
+        }
+
+        @Override
+        public Object call() throws Exception {
+            this.executedThread = Thread.currentThread().getName();
+            Thread.sleep(delay);
+            return new Object();
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/geronimo-safeguard/blob/7c93e298/safeguard-impl/src/test/java/org/apache/safeguard/impl/executionPlans/SyncFailsafeExecutionPlanTest.java
----------------------------------------------------------------------
diff --git a/safeguard-impl/src/test/java/org/apache/safeguard/impl/executionPlans/SyncFailsafeExecutionPlanTest.java b/safeguard-impl/src/test/java/org/apache/safeguard/impl/executionPlans/SyncFailsafeExecutionPlanTest.java
new file mode 100644
index 0000000..66876e3
--- /dev/null
+++ b/safeguard-impl/src/test/java/org/apache/safeguard/impl/executionPlans/SyncFailsafeExecutionPlanTest.java
@@ -0,0 +1,33 @@
+/*
+ *  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.executionPlans;
+
+import org.testng.annotations.Test;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+public class SyncFailsafeExecutionPlanTest {
+    @Test
+    public void shouldThrowExceptionWithInvalidConfig() {
+        assertThatThrownBy(() -> new SyncFailsafeExecutionPlan(null, null))
+                .isInstanceOf(IllegalStateException.class)
+                .hasMessage("For non-async invocations, must have at least one of RetryDefintion or CircuitBreaker defined");
+    }
+}
\ No newline at end of file