diff --git a/src/main/java/com/uber/cadence/activity/ActivityInterface.java b/src/main/java/com/uber/cadence/activity/ActivityInterface.java index 9a0dd45c5..81b6cc6f8 100644 --- a/src/main/java/com/uber/cadence/activity/ActivityInterface.java +++ b/src/main/java/com/uber/cadence/activity/ActivityInterface.java @@ -55,8 +55,8 @@ * } * * - * When CImpl instance is registered with the {@link com.uber.cadence.worker.Worker} the - * following activities are registered: + * When CImpl instance is registered with the {@link com.uber.cadence.worker.Worker} + * the following activities are registered: * *

* diff --git a/src/main/java/com/uber/cadence/activity/ActivityMethod.java b/src/main/java/com/uber/cadence/activity/ActivityMethod.java index 878821ebf..00118830a 100644 --- a/src/main/java/com/uber/cadence/activity/ActivityMethod.java +++ b/src/main/java/com/uber/cadence/activity/ActivityMethod.java @@ -21,6 +21,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.time.Duration; /** * Indicates that the method is an activity method. This annotation applies only to activity @@ -32,4 +33,46 @@ /** Name of the workflow type. Default is {short class name}::{method name} */ String name() default ""; + + /** + * Overall timeout workflow is willing to wait for activity to complete. It includes time in a + * task list (use {@link #scheduleToStartTimeoutSeconds()} to limit it) plus activity execution + * time (use {@link #startToCloseTimeoutSeconds()} to limit it). Either this option or both + * schedule to start and start to close are required. + * + * @deprecated use {@link ActivityOptions.Builder#setScheduleToCloseTimeout(Duration)} instead. + */ + int scheduleToCloseTimeoutSeconds() default 0; + + /** + * Time activity can stay in task list before it is picked up by a worker. If schedule to close is + * not provided then both this and start to close are required. + * + * @deprecated use {@link ActivityOptions.Builder#setScheduleToStartTimeout(Duration)} instead. + */ + int scheduleToStartTimeoutSeconds() default 0; + + /** + * Maximum activity execution time after it was sent to a worker. If schedule to close is not + * provided then both this and schedule to start are required. + * + * @deprecated use {@link ActivityOptions.Builder#setStartToCloseTimeout(Duration)} instead. + */ + int startToCloseTimeoutSeconds() default 0; + + /** + * Heartbeat interval. Activity must heartbeat before this interval passes after a last heartbeat + * or activity start. + * + * @deprecated use {@link ActivityOptions.Builder#setHeartbeatTimeout(Duration)} instead. + */ + int heartbeatTimeoutSeconds() default 0; + + /** + * Task list to use when dispatching activity task to a worker. By default it is the same task + * list name the workflow was started with. + * + * @deprecated use {@link ActivityOptions.Builder#setTaskList(String)} instead. + */ + String taskList() default ""; } diff --git a/src/main/java/com/uber/cadence/internal/sync/ActivityInvocationHandler.java b/src/main/java/com/uber/cadence/internal/sync/ActivityInvocationHandler.java index e15ed4f94..2bf87603e 100644 --- a/src/main/java/com/uber/cadence/internal/sync/ActivityInvocationHandler.java +++ b/src/main/java/com/uber/cadence/internal/sync/ActivityInvocationHandler.java @@ -17,12 +17,15 @@ package com.uber.cadence.internal.sync; +import com.google.common.base.Strings; +import com.uber.cadence.activity.ActivityMethod; import com.uber.cadence.activity.ActivityOptions; import com.uber.cadence.common.MethodRetry; import com.uber.cadence.workflow.ActivityStub; import com.uber.cadence.workflow.WorkflowInterceptor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; +import java.time.Duration; import java.util.function.Function; class ActivityInvocationHandler extends ActivityInvocationHandlerBase { @@ -41,12 +44,37 @@ private ActivityInvocationHandler( init(activityInterface); } + @SuppressWarnings("deprecation") @Override protected Function getActivityFunc( - Method method, MethodRetry methodRetry, String activityName) { + Method method, MethodRetry methodRetry, ActivityMethod activityMethod, String activityName) { Function function; - ActivityOptions mergedOptions = - ActivityOptions.newBuilder(options).setMethodRetry(methodRetry).build(); + ActivityOptions.Builder optionsBuilder = + ActivityOptions.newBuilder(options).setMethodRetry(methodRetry); + if (activityMethod != null) { + // options always take precedence over activity method annotation. + if (options.getStartToCloseTimeout() == null) { + optionsBuilder.setStartToCloseTimeout( + Duration.ofSeconds(activityMethod.startToCloseTimeoutSeconds())); + } + if (options.getScheduleToStartTimeout() == null) { + optionsBuilder.setScheduleToStartTimeout( + Duration.ofSeconds(activityMethod.scheduleToStartTimeoutSeconds())); + } + if (options.getScheduleToCloseTimeout() == null) { + optionsBuilder.setScheduleToCloseTimeout( + Duration.ofSeconds(activityMethod.scheduleToCloseTimeoutSeconds())); + } + if (options.getHeartbeatTimeout() == null) { + optionsBuilder.setHeartbeatTimeout( + Duration.ofSeconds(activityMethod.heartbeatTimeoutSeconds())); + } + if (Strings.isNullOrEmpty(options.getTaskList()) + && !Strings.isNullOrEmpty(activityMethod.taskList())) { + optionsBuilder.setTaskList(activityMethod.taskList()); + } + } + ActivityOptions mergedOptions = optionsBuilder.build(); ActivityStub stub = ActivityStubImpl.newInstance(mergedOptions, activityExecutor); function = diff --git a/src/main/java/com/uber/cadence/internal/sync/ActivityInvocationHandlerBase.java b/src/main/java/com/uber/cadence/internal/sync/ActivityInvocationHandlerBase.java index 001d6d86e..f1f2bf9ba 100644 --- a/src/main/java/com/uber/cadence/internal/sync/ActivityInvocationHandlerBase.java +++ b/src/main/java/com/uber/cadence/internal/sync/ActivityInvocationHandlerBase.java @@ -47,7 +47,7 @@ static T newProxy(Class activityInterface, InvocationHandler invocationHa invocationHandler); } - protected void init(Class activityInterface) { + void init(Class activityInterface) { Set activityMethods = getAnnotatedInterfaceMethodsFromInterface(activityInterface, ActivityInterface.class); if (activityMethods.isEmpty()) { @@ -66,7 +66,8 @@ protected void init(Class activityInterface) { } MethodRetry methodRetry = method.getAnnotation(MethodRetry.class); - Function function = getActivityFunc(method, methodRetry, activityType); + Function function = + getActivityFunc(method, methodRetry, activityMethod, activityType); methodFunctions.put(method, function); } } @@ -81,5 +82,5 @@ public Object invoke(Object proxy, Method method, Object[] args) { } protected abstract Function getActivityFunc( - Method method, MethodRetry methodRetry, String activityName); + Method method, MethodRetry methodRetry, ActivityMethod activityMethod, String activityName); } diff --git a/src/main/java/com/uber/cadence/internal/sync/LocalActivityInvocationHandler.java b/src/main/java/com/uber/cadence/internal/sync/LocalActivityInvocationHandler.java index cc468fa07..df4ebc085 100644 --- a/src/main/java/com/uber/cadence/internal/sync/LocalActivityInvocationHandler.java +++ b/src/main/java/com/uber/cadence/internal/sync/LocalActivityInvocationHandler.java @@ -17,12 +17,14 @@ package com.uber.cadence.internal.sync; +import com.uber.cadence.activity.ActivityMethod; import com.uber.cadence.activity.LocalActivityOptions; import com.uber.cadence.common.MethodRetry; import com.uber.cadence.workflow.ActivityStub; import com.uber.cadence.workflow.WorkflowInterceptor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; +import java.time.Duration; import java.util.function.Function; class LocalActivityInvocationHandler extends ActivityInvocationHandlerBase { @@ -45,14 +47,21 @@ private LocalActivityInvocationHandler( init(activityInterface); } + @SuppressWarnings("deprecation") @Override protected Function getActivityFunc( - Method method, MethodRetry methodRetry, String activityName) { + Method method, MethodRetry methodRetry, ActivityMethod activityMethod, String activityName) { Function function; - LocalActivityOptions mergedOptions = - LocalActivityOptions.newBuilder(options) - .setMethodRetry(methodRetry) - .validateAndBuildWithDefaults(); + LocalActivityOptions.Builder optionsBuilder = + LocalActivityOptions.newBuilder(options).setMethodRetry(methodRetry); + if (activityMethod != null) { + // options always take precedence over activity method annotation. + if (options.getScheduleToCloseTimeout() == null) { + optionsBuilder.setScheduleToCloseTimeout( + Duration.ofSeconds(activityMethod.scheduleToCloseTimeoutSeconds())); + } + } + LocalActivityOptions mergedOptions = optionsBuilder.validateAndBuildWithDefaults(); ActivityStub stub = LocalActivityStubImpl.newInstance(mergedOptions, activityExecutor); function = (a) -> stub.execute(activityName, method.getReturnType(), method.getGenericReturnType(), a); diff --git a/src/test/java/com/uber/cadence/activity/ActivityOptionsTest.java b/src/test/java/com/uber/cadence/activity/ActivityOptionsTest.java index 2e2a54ecd..654110715 100644 --- a/src/test/java/com/uber/cadence/activity/ActivityOptionsTest.java +++ b/src/test/java/com/uber/cadence/activity/ActivityOptionsTest.java @@ -40,7 +40,6 @@ public void activityAndRetryOptions() {} @Test public void testOnlyAnnotationsPresent() throws NoSuchMethodException { Method method = ActivityOptionsTest.class.getMethod("activityAndRetryOptions"); - ActivityMethod a = method.getAnnotation(ActivityMethod.class); MethodRetry r = method.getAnnotation(MethodRetry.class); ActivityOptions o = ActivityOptions.newBuilder().build(); ActivityOptions merged = ActivityOptions.newBuilder(o).setMethodRetry(r).build();