From 10207d4bb2f2b0e0847674641ed0b07fdf616323 Mon Sep 17 00:00:00 2001 From: Thomas Hardy Date: Thu, 30 Jan 2025 14:56:08 -0800 Subject: [PATCH 1/2] Add sleepfordays sample --- .../temporal/samples/sleepfordays/README.md | 17 ++++ .../sleepfordays/SendEmailActivity.java | 27 +++++++ .../sleepfordays/SendEmailActivityImpl.java | 27 +++++++ .../sleepfordays/SleepForDaysImpl.java | 54 +++++++++++++ .../sleepfordays/SleepForDaysWorkflow.java | 33 ++++++++ .../samples/sleepfordays/Starter.java | 46 +++++++++++ .../temporal/samples/sleepfordays/Worker.java | 40 +++++++++ .../sleepfordays/SleepForDaysTest.java | 81 +++++++++++++++++++ 8 files changed, 325 insertions(+) create mode 100644 core/src/main/java/io/temporal/samples/sleepfordays/README.md create mode 100644 core/src/main/java/io/temporal/samples/sleepfordays/SendEmailActivity.java create mode 100644 core/src/main/java/io/temporal/samples/sleepfordays/SendEmailActivityImpl.java create mode 100644 core/src/main/java/io/temporal/samples/sleepfordays/SleepForDaysImpl.java create mode 100644 core/src/main/java/io/temporal/samples/sleepfordays/SleepForDaysWorkflow.java create mode 100644 core/src/main/java/io/temporal/samples/sleepfordays/Starter.java create mode 100644 core/src/main/java/io/temporal/samples/sleepfordays/Worker.java create mode 100644 core/src/test/java/io/temporal/samples/sleepfordays/SleepForDaysTest.java diff --git a/core/src/main/java/io/temporal/samples/sleepfordays/README.md b/core/src/main/java/io/temporal/samples/sleepfordays/README.md new file mode 100644 index 000000000..a8800033d --- /dev/null +++ b/core/src/main/java/io/temporal/samples/sleepfordays/README.md @@ -0,0 +1,17 @@ +# Sleep for days + +This sample demonstrates how to use Temporal to run a workflow that periodically sleeps for a number of days. + +## Run the sample + +1. Start the Worker: + +```bash +./gradlew -q execute -PmainClass=io.temporal.samples.sleepfordays.Worker +``` + +2. Start the Starter + +```bash +./gradlew -q execute -PmainClass=io.temporal.samples.sleepfordays.Starter +``` \ No newline at end of file diff --git a/core/src/main/java/io/temporal/samples/sleepfordays/SendEmailActivity.java b/core/src/main/java/io/temporal/samples/sleepfordays/SendEmailActivity.java new file mode 100644 index 000000000..94eba7746 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/sleepfordays/SendEmailActivity.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved + * + * Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Modifications copyright (C) 2017 Uber Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not + * use this file except in compliance with the License. A copy of the License is + * located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 io.temporal.samples.sleepfordays; + +import io.temporal.activity.ActivityInterface; + +@ActivityInterface +public interface SendEmailActivity { + void sendEmail(String email); +} diff --git a/core/src/main/java/io/temporal/samples/sleepfordays/SendEmailActivityImpl.java b/core/src/main/java/io/temporal/samples/sleepfordays/SendEmailActivityImpl.java new file mode 100644 index 000000000..a8cd72536 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/sleepfordays/SendEmailActivityImpl.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved + * + * Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Modifications copyright (C) 2017 Uber Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not + * use this file except in compliance with the License. A copy of the License is + * located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 io.temporal.samples.sleepfordays; + +public class SendEmailActivityImpl implements SendEmailActivity { + @Override + public void sendEmail(String email) { + System.out.println(email); + } +} diff --git a/core/src/main/java/io/temporal/samples/sleepfordays/SleepForDaysImpl.java b/core/src/main/java/io/temporal/samples/sleepfordays/SleepForDaysImpl.java new file mode 100644 index 000000000..2828a3ada --- /dev/null +++ b/core/src/main/java/io/temporal/samples/sleepfordays/SleepForDaysImpl.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved + * + * Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Modifications copyright (C) 2017 Uber Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not + * use this file except in compliance with the License. A copy of the License is + * located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 io.temporal.samples.sleepfordays; + +import io.temporal.activity.ActivityOptions; +import io.temporal.workflow.Promise; +import io.temporal.workflow.Workflow; +import java.time.Duration; + +public class SleepForDaysImpl implements SleepForDaysWorkflow { + + private final SendEmailActivity activity; + private boolean complete = false; + + public SleepForDaysImpl() { + this.activity = + Workflow.newActivityStub( + SendEmailActivity.class, + ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build()); + } + + @Override + public String sleepForDays() { + while (!this.complete) { + activity.sendEmail(String.format("Sleeping for 30 days")); + Promise timer = Workflow.newTimer(Duration.ofDays(30)); + Workflow.await(() -> timer.isCompleted() || this.complete); + } + + return "done!"; + } + + @Override + public void complete() { + this.complete = true; + } +} diff --git a/core/src/main/java/io/temporal/samples/sleepfordays/SleepForDaysWorkflow.java b/core/src/main/java/io/temporal/samples/sleepfordays/SleepForDaysWorkflow.java new file mode 100644 index 000000000..1bced9729 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/sleepfordays/SleepForDaysWorkflow.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved + * + * Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Modifications copyright (C) 2017 Uber Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not + * use this file except in compliance with the License. A copy of the License is + * located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 io.temporal.samples.sleepfordays; + +import io.temporal.workflow.SignalMethod; +import io.temporal.workflow.WorkflowInterface; +import io.temporal.workflow.WorkflowMethod; + +@WorkflowInterface +public interface SleepForDaysWorkflow { + @WorkflowMethod + String sleepForDays(); + + @SignalMethod + void complete(); +} diff --git a/core/src/main/java/io/temporal/samples/sleepfordays/Starter.java b/core/src/main/java/io/temporal/samples/sleepfordays/Starter.java new file mode 100644 index 000000000..a9660de7c --- /dev/null +++ b/core/src/main/java/io/temporal/samples/sleepfordays/Starter.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved + * + * Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Modifications copyright (C) 2017 Uber Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not + * use this file except in compliance with the License. A copy of the License is + * located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 io.temporal.samples.sleepfordays; + +import io.temporal.client.WorkflowClient; +import io.temporal.client.WorkflowOptions; +import io.temporal.client.WorkflowStub; + +public class Starter { + + public static final String TASK_QUEUE = "SleepForDaysTaskQueue"; + + public static void main(String[] args) { + // Start a workflow execution. + SleepForDaysWorkflow workflow = + Worker.client.newWorkflowStub( + SleepForDaysWorkflow.class, + WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).build()); + + // Start the workflow. + WorkflowClient.start(workflow::sleepForDays); + + WorkflowStub stub = WorkflowStub.fromTyped(workflow); + + // Wait for workflow to complete. This will wait indefinitely until a 'complete' signal is sent. + stub.getResult(String.class); + System.exit(0); + } +} diff --git a/core/src/main/java/io/temporal/samples/sleepfordays/Worker.java b/core/src/main/java/io/temporal/samples/sleepfordays/Worker.java new file mode 100644 index 000000000..28ae326cc --- /dev/null +++ b/core/src/main/java/io/temporal/samples/sleepfordays/Worker.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved + * + * Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Modifications copyright (C) 2017 Uber Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not + * use this file except in compliance with the License. A copy of the License is + * located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 io.temporal.samples.sleepfordays; + +import io.temporal.client.WorkflowClient; +import io.temporal.serviceclient.WorkflowServiceStubs; +import io.temporal.worker.WorkerFactory; + +public class Worker { + public static final String TASK_QUEUE = "SleepForDaysTaskQueue"; + public static final WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); + public static final WorkflowClient client = WorkflowClient.newInstance(service); + public static final WorkerFactory factory = WorkerFactory.newInstance(client); + + public static void main(String[] args) { + io.temporal.worker.Worker worker = factory.newWorker(TASK_QUEUE); + worker.registerWorkflowImplementationTypes(SleepForDaysImpl.class); + worker.registerActivitiesImplementations(new SendEmailActivityImpl()); + + factory.start(); + System.out.println("Worker started for task queue: " + TASK_QUEUE); + } +} diff --git a/core/src/test/java/io/temporal/samples/sleepfordays/SleepForDaysTest.java b/core/src/test/java/io/temporal/samples/sleepfordays/SleepForDaysTest.java new file mode 100644 index 000000000..eba11b989 --- /dev/null +++ b/core/src/test/java/io/temporal/samples/sleepfordays/SleepForDaysTest.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved + * + * Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Modifications copyright (C) 2017 Uber Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not + * use this file except in compliance with the License. A copy of the License is + * located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 io.temporal.samples.sleepfordays; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import io.temporal.client.WorkflowClient; +import io.temporal.client.WorkflowOptions; +import io.temporal.testing.TestWorkflowRule; +import java.time.Duration; +import org.junit.Rule; +import org.junit.Test; + +public class SleepForDaysTest { + + @Rule + public TestWorkflowRule testWorkflowRule = + TestWorkflowRule.newBuilder() + .setWorkflowTypes(SleepForDaysImpl.class) + .setDoNotStart(true) + .build(); + + @Test(timeout = 8000) + public void testSleepForDays() { + // Mock activity + SendEmailActivity activities = mock(SendEmailActivity.class); + testWorkflowRule.getWorker().registerActivitiesImplementations(activities); + // Start environment + testWorkflowRule.getTestEnvironment().start(); + + // Create a workflow + WorkflowOptions workflowOptions = + WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build(); + SleepForDaysWorkflow workflow = + testWorkflowRule + .getWorkflowClient() + .newWorkflowStub(SleepForDaysWorkflow.class, workflowOptions); + + // Start workflow + WorkflowClient.start(workflow::sleepForDays); + + long startTime = testWorkflowRule.getTestEnvironment().currentTimeMillis(); + // Time-skip 5 minutes. + testWorkflowRule.getTestEnvironment().sleep(Duration.ofMinutes(5)); + // Check that the activity has been called, we're now waiting for the sleep to finish. + verify(activities, times(1)).sendEmail(anyString()); + // Time-skip 3 days. + testWorkflowRule.getTestEnvironment().sleep(Duration.ofDays(90)); + // Expect 3 more activity calls. + verify(activities, times(4)).sendEmail(anyString()); + // Send the signal to complete the workflow. + workflow.complete(); + // Expect no more activity calls to have been made - workflow is complete. + verify(activities, times(4)).sendEmail(anyString()); + ; + // Expect more than 90 days to have passed. + long endTime = testWorkflowRule.getTestEnvironment().currentTimeMillis(); + assertEquals(true, endTime - startTime > Duration.ofDays(90).toMillis()); + } +} From 7ef5751439d4dca88bd8100fb5ce0c0a4d4bc81e Mon Sep 17 00:00:00 2001 From: Thomas Hardy Date: Thu, 30 Jan 2025 17:19:19 -0800 Subject: [PATCH 2/2] remove extra semicolon --- .../java/io/temporal/samples/sleepfordays/SleepForDaysTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/test/java/io/temporal/samples/sleepfordays/SleepForDaysTest.java b/core/src/test/java/io/temporal/samples/sleepfordays/SleepForDaysTest.java index eba11b989..c79ee0400 100644 --- a/core/src/test/java/io/temporal/samples/sleepfordays/SleepForDaysTest.java +++ b/core/src/test/java/io/temporal/samples/sleepfordays/SleepForDaysTest.java @@ -73,7 +73,6 @@ public void testSleepForDays() { workflow.complete(); // Expect no more activity calls to have been made - workflow is complete. verify(activities, times(4)).sendEmail(anyString()); - ; // Expect more than 90 days to have passed. long endTime = testWorkflowRule.getTestEnvironment().currentTimeMillis(); assertEquals(true, endTime - startTime > Duration.ofDays(90).toMillis());