Skip to content

Commit

Permalink
Adding HelloEagerWorkflowStart sample (#670)
Browse files Browse the repository at this point in the history
* adding hello accumulator

* updates

* Accumulator: adding a test for signal after exit signal, switching out default tests in main

* feedback from PR: switching to interface for Deque; removing unnecessary clauses in accumulateGreetings() and processGreeting(); clarifying bad bucket checks

* renaming variables and functions based on code review

* improvement from code review, setting license to match required license

* Accepting Tiho's suggestion to process signals in-loop, reducing use of timers

* adding eager workflow start sample

* Per Antonio's feedback, adding comments to clarify about eager execution.

* formatting changes

* improving comments.

* comment formatting
  • Loading branch information
joshmsmith authored Sep 4, 2024
1 parent 67ca961 commit 16ef78b
Show file tree
Hide file tree
Showing 4 changed files with 286 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ See the README.md file in each main sample directory for cut/paste Gradle comman
- [**HelloChild**](/core/src/main/java/io/temporal/samples/hello/HelloChild.java): Demonstrates how to execute a simple Child Workflow.
- [**HelloCron**](/core/src/main/java/io/temporal/samples/hello/HelloCron.java): Demonstrates how to execute a Workflow according to a cron schedule.
- [**HelloDynamic**](/core/src/main/java/io/temporal/samples/hello/HelloDynamic.java): Demonstrates how to use `DynamicWorkflow` and `DynamicActivity` interfaces.
- [**HelloEagerWorkflowStart**](/core/src/main/java/io/temporal/samples/hello/HelloEagerWorkflowStart.java): Demonstrates the use of a eager workflow start.
- [**HelloPeriodic**](/core/src/main/java/io/temporal/samples/hello/HelloPeriodic.java): Demonstrates the use of the Continue-As-New feature.
- [**HelloException**](/core/src/main/java/io/temporal/samples/hello/HelloException.java): Demonstrates how to handle exception propagation and wrapping.
- [**HelloLocalActivity**](/core/src/main/java/io/temporal/samples/hello/HelloLocalActivity.java): Demonstrates the use of a [Local Activity](https://docs.temporal.io/docs/jargon/mesh/#local-activity).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/*
* 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.hello;

import io.temporal.activity.ActivityInterface;
import io.temporal.activity.ActivityMethod;
import io.temporal.activity.ActivityOptions;
import io.temporal.client.WorkflowClient;
import io.temporal.client.WorkflowOptions;
import io.temporal.serviceclient.WorkflowServiceStubs;
import io.temporal.worker.Worker;
import io.temporal.worker.WorkerFactory;
import io.temporal.workflow.Workflow;
import io.temporal.workflow.WorkflowInterface;
import io.temporal.workflow.WorkflowMethod;
import java.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Sample Temporal Workflow Definition that starts eagerly and executes a single Local Activity.
* Important elements of eager starting are: the client starting the workflow and the worker
* executing it need to be shared, worker options needs to have .setDisableEagerExecution(false)
* set, the activity is recommended to be a local activity for best performance
*/
public class HelloEagerWorkflowStart {

// Define the task queue name
static final String TASK_QUEUE = "HelloEagerWorkflowStartTaskQueue";

// Define our workflow unique id
static final String WORKFLOW_ID = "HelloEagerWorkflowStartWorkflow";

/**
* The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.
*
* <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic
* code, network calls, database operations, etc. Those things should be handled by the
* Activities.
*
* @see io.temporal.workflow.WorkflowInterface
* @see io.temporal.workflow.WorkflowMethod
*/
@WorkflowInterface
public interface GreetingWorkflow {

/**
* This is the method that is executed when the Workflow Execution is started. The Workflow
* Execution completes when this method finishes execution.
*/
@WorkflowMethod
String getGreeting(String name);
}

/**
* This is the Activity Definition's Interface. Activities are building blocks of any Temporal
* Workflow and contain any business logic that could perform long running computation, network
* calls, etc.
*
* <p>Annotating Activity Definition methods with @ActivityMethod is optional.
*
* @see io.temporal.activity.ActivityInterface
* @see io.temporal.activity.ActivityMethod
*/
@ActivityInterface
public interface GreetingActivities {

// Define your activity method which can be called during workflow execution
@ActivityMethod(name = "greet")
String composeGreeting(String greeting, String name);
}

// Define the workflow implementation which implements our getGreeting workflow method.
public static class GreetingWorkflowImpl implements GreetingWorkflow {

/**
* Define the GreetingActivities stub. Activity stubs are proxies for activity invocations that
* are executed outside of the workflow thread on the activity worker, that can be on a
* different host. Temporal is going to dispatch the activity results back to the workflow and
* unblock the stub as soon as activity is completed on the activity worker.
*
* <p>In the {@link ActivityOptions} definition the "setStartToCloseTimeout" option sets the
* overall timeout that our workflow is willing to wait for activity to complete. For this
* example it is set to 2 seconds.
*/
private final GreetingActivities activities =
Workflow.newActivityStub(
GreetingActivities.class,
ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());

@Override
public String getGreeting(String name) {
// This is a blocking call that returns only after the activity has completed.
return activities.composeGreeting("Hello", name);
}
}

/** Simple activity implementation, that concatenates two strings. */
static class GreetingLocalActivitiesImpl implements GreetingActivities {
private static final Logger log = LoggerFactory.getLogger(GreetingLocalActivitiesImpl.class);

@Override
public String composeGreeting(String greeting, String name) {
log.info("Composing greeting...");
return greeting + " " + name + "!";
}
}

/**
* With our Workflow and Activities defined, we can now start execution. The main method starts
* the worker and then the workflow.
*/
public static void main(String[] args) {

// Get a Workflow service stub.
WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs();

/*
* Get a Workflow service client which can be used to start, Signal, and Query Workflow Executions.
*/
WorkflowClient client = WorkflowClient.newInstance(service);

/*
* Define the workflow factory. It is used to create workflow workers for a specific task queue.
* Here it's important that the worker shares the client for eager execution.
*/
WorkerFactory factory = WorkerFactory.newInstance(client);

/*
* Define the workflow worker. Workflow workers listen to a defined task queue and process
* workflows and activities.
*/
Worker worker = factory.newWorker(TASK_QUEUE);

/*
* Register our workflow implementation with the worker.
* Workflow implementations must be known to the worker at runtime in
* order to dispatch workflow tasks.
*/
worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);

/*
* Register our Activity Types with the Worker. Since Activities are stateless and thread-safe,
* the Activity Type is a shared instance.
*/
worker.registerActivitiesImplementations(new GreetingLocalActivitiesImpl());

/*
* Start all the workers registered for a specific task queue.
* The started workers then start polling for workflows and activities.
*/
factory.start();

// Create the workflow client stub. It is used to start our workflow execution.
GreetingWorkflow workflow =
client.newWorkflowStub(
GreetingWorkflow.class,
WorkflowOptions.newBuilder()
.setWorkflowId(WORKFLOW_ID)
.setTaskQueue(TASK_QUEUE)
.setDisableEagerExecution(false) // set this to enable eager execution
.build());

/*
* Execute our workflow and wait for it to complete. The call to our getGreeting method is
* synchronous.
*
* See {@link io.temporal.samples.hello.HelloSignal} for an example of starting workflow
* without waiting synchronously for its result.
*/
String greeting = workflow.getGreeting("World");

// Display workflow execution results
System.out.println(greeting);
System.exit(0);
}
}
1 change: 1 addition & 0 deletions core/src/main/java/io/temporal/samples/hello/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ To run each hello world sample, use one of the following commands:
./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloChild
./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloCron
./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloDynamic
./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloEagerWorkflowStart
./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloException
./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloLocalActivity
./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloPeriodic
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* 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.hello;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;

import io.temporal.client.WorkflowOptions;
import io.temporal.samples.hello.HelloEagerWorkflowStart.GreetingActivities;
import io.temporal.samples.hello.HelloEagerWorkflowStart.GreetingLocalActivitiesImpl;
import io.temporal.samples.hello.HelloEagerWorkflowStart.GreetingWorkflow;
import io.temporal.samples.hello.HelloEagerWorkflowStart.GreetingWorkflowImpl;
import io.temporal.testing.TestWorkflowRule;
import org.junit.Rule;
import org.junit.Test;

/** Unit test for {@link HelloEagerWorkflowStart}. Doesn't use an external Temporal service. */
public class HelloEagerWorkflowStartTest {

@Rule
public TestWorkflowRule testWorkflowRule =
TestWorkflowRule.newBuilder()
.setWorkflowTypes(GreetingWorkflowImpl.class)
.setDoNotStart(true)
.build();

@Test
public void testActivityImpl() {
testWorkflowRule
.getWorker()
.registerActivitiesImplementations(new GreetingLocalActivitiesImpl());
testWorkflowRule.getTestEnvironment().start();

// Get a workflow stub using the same task queue the worker uses.
GreetingWorkflow workflow =
testWorkflowRule
.getWorkflowClient()
.newWorkflowStub(
GreetingWorkflow.class,
WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());
// Execute a workflow waiting for it to complete.
String greeting = workflow.getGreeting("World");
assertEquals("Hello World!", greeting);

testWorkflowRule.getTestEnvironment().shutdown();
}

@Test
public void testMockedActivity() {
// withoutAnnotations() is required to stop Mockito from copying
// method-level annotations from the GreetingActivities interface
GreetingActivities activities =
mock(GreetingActivities.class, withSettings().withoutAnnotations());
when(activities.composeGreeting("Hello", "World")).thenReturn("Hello World!");
testWorkflowRule.getWorker().registerActivitiesImplementations(activities);
testWorkflowRule.getTestEnvironment().start();

// Get a workflow stub using the same task queue the worker uses.
GreetingWorkflow workflow =
testWorkflowRule
.getWorkflowClient()
.newWorkflowStub(
GreetingWorkflow.class,
WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());
// Execute a workflow waiting for it to complete.
String greeting = workflow.getGreeting("World");
assertEquals("Hello World!", greeting);

testWorkflowRule.getTestEnvironment().shutdown();
}
}

0 comments on commit 16ef78b

Please sign in to comment.