diff --git a/saas-modules/pom.xml b/saas-modules/pom.xml
index 22213de48b90..91c04bb34770 100644
--- a/saas-modules/pom.xml
+++ b/saas-modules/pom.xml
@@ -25,6 +25,7 @@
twilio
twilio-whatsapp
twitter4j
+ temporal
diff --git a/saas-modules/temporal/pom.xml b/saas-modules/temporal/pom.xml
new file mode 100644
index 000000000000..f2ae5d41dbfd
--- /dev/null
+++ b/saas-modules/temporal/pom.xml
@@ -0,0 +1,52 @@
+
+
+ 4.0.0
+ temporal
+ 1.0
+ temporal
+ Temporal Workflow Engine Tutorial
+
+
+ com.baeldung
+ saas-modules
+ 1.0.0-SNAPSHOT
+
+
+
+
+ io.temporal
+ temporal-sdk
+ ${temporal.version}
+
+
+
+ io.temporal
+ temporal-testing
+ ${temporal.version}
+ test
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven-surefire-plugin.version}
+
+
+ --add-opens java.base/java.lang=ALL-UNNAMED
+
+
+
+
+
+
+
+ 1.31.0
+
+
+
diff --git a/saas-modules/temporal/src/main/java/com/baeldung/temporal/worker/TemporalWorkerRegistrar.java b/saas-modules/temporal/src/main/java/com/baeldung/temporal/worker/TemporalWorkerRegistrar.java
new file mode 100644
index 000000000000..5142fdfcd10e
--- /dev/null
+++ b/saas-modules/temporal/src/main/java/com/baeldung/temporal/worker/TemporalWorkerRegistrar.java
@@ -0,0 +1,10 @@
+package com.baeldung.temporal.worker;
+
+import io.temporal.worker.Worker;
+
+/**
+ * Interface for registering Workflows and Activities to a Temporal Worker.
+ */
+public interface TemporalWorkerRegistrar {
+ void register(Worker worker);
+}
diff --git a/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hello/HelloWorkflow.java b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hello/HelloWorkflow.java
new file mode 100644
index 000000000000..8d915b8c396b
--- /dev/null
+++ b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hello/HelloWorkflow.java
@@ -0,0 +1,10 @@
+package com.baeldung.temporal.workflows.hello;
+
+import io.temporal.workflow.WorkflowInterface;
+import io.temporal.workflow.WorkflowMethod;
+
+@WorkflowInterface
+public interface HelloWorkflow {
+ @WorkflowMethod
+ String hello(String person);
+}
diff --git a/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hello/HelloWorkflowApplication.java b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hello/HelloWorkflowApplication.java
new file mode 100644
index 000000000000..e0dd1db6dadd
--- /dev/null
+++ b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hello/HelloWorkflowApplication.java
@@ -0,0 +1,29 @@
+package com.baeldung.temporal.workflows.hello;
+
+import io.temporal.client.WorkflowClient;
+import io.temporal.serviceclient.WorkflowServiceStubs;
+import io.temporal.worker.Worker;
+import io.temporal.worker.WorkerFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class HelloWorkflowApplication {
+
+ private static final String QUEUE_NAME = "say-hello-queue";
+ private static final Logger log = LoggerFactory.getLogger(HelloWorkflowApplication.class);
+
+ public static void main(String[] args) {
+
+ log.info("Creating worker...");
+ var service = WorkflowServiceStubs.newLocalServiceStubs();
+ var client = WorkflowClient.newInstance(service);
+ var factory = WorkerFactory.newInstance(client);
+ var worker = factory.newWorker(QUEUE_NAME);
+
+ log.info("Registering workflows and activities...");
+ HelloWorkflowRegistrar.newInstance().register(worker);
+
+ log.info("Starting worker...");
+ factory.start();
+ }
+}
diff --git a/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hello/HelloWorkflowImpl.java b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hello/HelloWorkflowImpl.java
new file mode 100644
index 000000000000..fdb718026185
--- /dev/null
+++ b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hello/HelloWorkflowImpl.java
@@ -0,0 +1,22 @@
+package com.baeldung.temporal.workflows.hello;
+
+import com.baeldung.temporal.workflows.hello.activities.SayHelloActivity;
+import io.temporal.activity.ActivityOptions;
+import io.temporal.workflow.Workflow;
+
+import java.time.Duration;
+
+public class HelloWorkflowImpl implements HelloWorkflow {
+
+ private final SayHelloActivity activity = Workflow.newActivityStub(
+ SayHelloActivity.class,
+ ActivityOptions.newBuilder()
+ .setStartToCloseTimeout(Duration.ofSeconds(10))
+ .build()
+ );
+
+ @Override
+ public String hello(String person) {
+ return activity.sayHello(person);
+ }
+}
diff --git a/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hello/HelloWorkflowRegistrar.java b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hello/HelloWorkflowRegistrar.java
new file mode 100644
index 000000000000..ebc61d4992ce
--- /dev/null
+++ b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hello/HelloWorkflowRegistrar.java
@@ -0,0 +1,20 @@
+package com.baeldung.temporal.workflows.hello;
+
+import com.baeldung.temporal.worker.TemporalWorkerRegistrar;
+import com.baeldung.temporal.workflows.hello.activities.SayHelloActivityImpl;
+import io.temporal.worker.Worker;
+
+public class HelloWorkflowRegistrar implements TemporalWorkerRegistrar {
+
+ private HelloWorkflowRegistrar() {}
+
+ @Override
+ public void register(Worker worker) {
+ worker.registerWorkflowImplementationTypes(HelloWorkflowImpl.class);
+ worker.registerActivitiesImplementations(new SayHelloActivityImpl());
+ }
+
+ public static HelloWorkflowRegistrar newInstance() {
+ return new HelloWorkflowRegistrar();
+ }
+}
diff --git a/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hello/activities/SayHelloActivity.java b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hello/activities/SayHelloActivity.java
new file mode 100644
index 000000000000..57a22e0f7591
--- /dev/null
+++ b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hello/activities/SayHelloActivity.java
@@ -0,0 +1,10 @@
+package com.baeldung.temporal.workflows.hello.activities;
+
+import io.temporal.activity.ActivityInterface;
+import io.temporal.activity.ActivityMethod;
+
+@ActivityInterface
+public interface SayHelloActivity {
+ @ActivityMethod
+ String sayHello(String person);
+}
diff --git a/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hello/activities/SayHelloActivityImpl.java b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hello/activities/SayHelloActivityImpl.java
new file mode 100644
index 000000000000..70f8cb8427cb
--- /dev/null
+++ b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hello/activities/SayHelloActivityImpl.java
@@ -0,0 +1,14 @@
+package com.baeldung.temporal.workflows.hello.activities;
+
+import io.temporal.activity.Activity;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SayHelloActivityImpl implements SayHelloActivity {
+ private static final Logger log = LoggerFactory.getLogger(SayHelloActivityImpl.class);
+
+ public String sayHello(String person) {
+ log.info("Saying hello to {}", person);
+ return "Hello, " + person;
+ }
+}
diff --git a/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hellov2/HelloV2WorkerRegistrar.java b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hellov2/HelloV2WorkerRegistrar.java
new file mode 100644
index 000000000000..50b9b25f4cea
--- /dev/null
+++ b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hellov2/HelloV2WorkerRegistrar.java
@@ -0,0 +1,22 @@
+package com.baeldung.temporal.workflows.hellov2;
+
+import com.baeldung.temporal.worker.TemporalWorkerRegistrar;
+import com.baeldung.temporal.workflows.hellov2.activities.HelloV2ActivitiesImpl;
+import io.temporal.worker.Worker;
+
+public class HelloV2WorkerRegistrar implements TemporalWorkerRegistrar {
+
+
+ private HelloV2WorkerRegistrar() {
+ }
+
+ @Override
+ public void register(Worker worker) {
+ worker.registerWorkflowImplementationTypes(HelloWorkflowV2Impl.class);
+ worker.registerActivitiesImplementations(new HelloV2ActivitiesImpl());
+ }
+
+ public static HelloV2WorkerRegistrar newInstance() {
+ return new HelloV2WorkerRegistrar();
+ }
+}
diff --git a/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hellov2/HelloWorkflowV2.java b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hellov2/HelloWorkflowV2.java
new file mode 100644
index 000000000000..5b5f98569706
--- /dev/null
+++ b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hellov2/HelloWorkflowV2.java
@@ -0,0 +1,10 @@
+package com.baeldung.temporal.workflows.hellov2;
+
+import io.temporal.workflow.WorkflowInterface;
+import io.temporal.workflow.WorkflowMethod;
+
+@WorkflowInterface
+public interface HelloWorkflowV2 {
+ @WorkflowMethod
+ String hello(String person);
+}
diff --git a/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hellov2/HelloWorkflowV2Impl.java b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hellov2/HelloWorkflowV2Impl.java
new file mode 100644
index 000000000000..a4171ae3169e
--- /dev/null
+++ b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hellov2/HelloWorkflowV2Impl.java
@@ -0,0 +1,45 @@
+package com.baeldung.temporal.workflows.hellov2;
+
+import com.baeldung.temporal.workflows.hellov2.activities.HelloV2Activities;
+import io.temporal.activity.ActivityOptions;
+import io.temporal.common.RetryOptions;
+import io.temporal.workflow.Workflow;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.Duration;
+
+public class HelloWorkflowV2Impl implements HelloWorkflowV2 {
+
+ private static final Logger log = LoggerFactory.getLogger(HelloWorkflowV2Impl.class);
+
+
+ private final HelloV2Activities activity = Workflow.newActivityStub(
+ HelloV2Activities.class,
+ ActivityOptions.newBuilder()
+ .setStartToCloseTimeout(Duration.ofSeconds(10))
+ .setRetryOptions(RetryOptions.newBuilder()
+ .setMaximumAttempts(3)
+ .setInitialInterval(Duration.ofSeconds(1))
+ .build())
+ .build()
+ );
+
+
+ @Override
+ public String hello(String person) {
+
+ var info = Workflow.getInfo();
+
+ log.info("Running workflow for person {}: id={}, attempt={}",
+ person,
+ info.getWorkflowId(),
+ info.getAttempt());
+
+ var step1result = activity.sayHello(person);
+ var step2result = activity.sayGoodbye(person);
+
+ return "Workflow OK";
+ }
+
+}
diff --git a/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hellov2/activities/HelloV2Activities.java b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hellov2/activities/HelloV2Activities.java
new file mode 100644
index 000000000000..dd32b2beaca1
--- /dev/null
+++ b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hellov2/activities/HelloV2Activities.java
@@ -0,0 +1,11 @@
+package com.baeldung.temporal.workflows.hellov2.activities;
+
+import io.temporal.activity.ActivityInterface;
+import io.temporal.activity.ActivityMethod;
+
+@ActivityInterface
+public interface HelloV2Activities {
+ @ActivityMethod
+ String sayHello(String person);
+ String sayGoodbye(String person);
+}
diff --git a/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hellov2/activities/HelloV2ActivitiesImpl.java b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hellov2/activities/HelloV2ActivitiesImpl.java
new file mode 100644
index 000000000000..85f827807f33
--- /dev/null
+++ b/saas-modules/temporal/src/main/java/com/baeldung/temporal/workflows/hellov2/activities/HelloV2ActivitiesImpl.java
@@ -0,0 +1,38 @@
+package com.baeldung.temporal.workflows.hellov2.activities;
+
+import io.temporal.activity.Activity;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+public class HelloV2ActivitiesImpl implements HelloV2Activities {
+ private static final Logger log = LoggerFactory.getLogger(HelloV2ActivitiesImpl.class);
+
+ @Override
+ public String sayHello(String person) {
+ var info = Activity.getExecutionContext().getInfo();
+
+ log.info("Saying hello to {}, workflowId={}, attempt={}", person,
+ info.getWorkflowId(),
+ info.getAttempt());
+ return "Step1 - OK";
+ }
+
+ @Override
+ public String sayGoodbye(String person) {
+
+ var info = Activity.getExecutionContext().getInfo();
+
+ log.info("Saying goodbye to {}, workflowId={}, attempt={}", person,
+ info.getWorkflowId(),
+ info.getAttempt());
+
+ if ( info.getAttempt() == 1 ) {
+ throw new IllegalStateException("Simulating task failure");
+ }
+ else {
+ return "Step2 - OK";
+ }
+ }
+}
diff --git a/saas-modules/temporal/src/main/resources/logback.xml b/saas-modules/temporal/src/main/resources/logback.xml
new file mode 100644
index 000000000000..adccaeb99807
--- /dev/null
+++ b/saas-modules/temporal/src/main/resources/logback.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/saas-modules/temporal/src/test/java/com/baeldung/temporal/Hello2WorkerIntegrationTest.java b/saas-modules/temporal/src/test/java/com/baeldung/temporal/Hello2WorkerIntegrationTest.java
new file mode 100644
index 000000000000..672513cc1f2a
--- /dev/null
+++ b/saas-modules/temporal/src/test/java/com/baeldung/temporal/Hello2WorkerIntegrationTest.java
@@ -0,0 +1,86 @@
+package com.baeldung.temporal;
+
+import com.baeldung.temporal.workflows.hellov2.HelloV2WorkerRegistrar;
+import com.baeldung.temporal.workflows.hellov2.HelloWorkflowV2;
+import io.temporal.client.WorkflowClient;
+import io.temporal.client.WorkflowOptions;
+import io.temporal.serviceclient.WorkflowServiceStubs;
+import io.temporal.worker.*;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class Hello2WorkerIntegrationTest {
+ private static final String QUEUE_NAME = "say-hello-queue";
+ private static final Logger log = LoggerFactory.getLogger(Hello2WorkerIntegrationTest.class);
+
+ private WorkerFactory factory;
+
+ @BeforeEach
+ public void startWorker() {
+
+ log.info("Creating worker...");
+ WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs();
+ WorkflowClient client = WorkflowClient.newInstance(service);
+ this.factory = WorkerFactory.newInstance(client);
+
+ var workerOptions = WorkerOptions.newBuilder()
+ .setUsingVirtualThreads(true)
+ .build();
+ Worker worker = factory.newWorker(QUEUE_NAME,workerOptions);
+
+ HelloV2WorkerRegistrar.newInstance()
+ .register(worker);
+
+ log.info("Starting worker...");
+ factory.start();
+ log.info("Worker started.");
+ }
+
+ @AfterEach
+ public void stopWorker() {
+ log.info("Stopping worker...");
+ factory.shutdown();
+ log.info("Worker stopped.");
+ }
+
+ @Test
+ void givenPerson_whenSayHello_thenSuccess() {
+
+ WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs();
+ WorkflowClient client = WorkflowClient.newInstance(service);
+
+ var wfid = UUID.randomUUID().toString();
+
+ var workflow = client.newWorkflowStub(
+ HelloWorkflowV2.class,
+ WorkflowOptions.newBuilder()
+ .setTaskQueue(QUEUE_NAME)
+ .setWorkflowId(wfid)
+ .build()
+ );
+
+
+ // Invoke workflow asynchronously.
+ var execution = WorkflowClient.start(workflow::hello,"Baeldung");
+ log.info("Workflow started: id={}, runId={}",
+ execution.getWorkflowId(),
+ execution.getRunId());
+
+ // Create a blocking workflow using the execution's workflow id
+ var syncWorkflow = client.newWorkflowStub(HelloWorkflowV2.class,execution.getWorkflowId());
+
+ // The sync workflow stub will block until it completes. Notice that the call argument here is ignored!
+ assertEquals("Workflow OK", syncWorkflow.hello("ignored"));
+
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/saas-modules/temporal/src/test/java/com/baeldung/temporal/HelloV2WorkerUnitTest.java b/saas-modules/temporal/src/test/java/com/baeldung/temporal/HelloV2WorkerUnitTest.java
new file mode 100644
index 000000000000..503165642ba5
--- /dev/null
+++ b/saas-modules/temporal/src/test/java/com/baeldung/temporal/HelloV2WorkerUnitTest.java
@@ -0,0 +1,68 @@
+package com.baeldung.temporal;
+
+import com.baeldung.temporal.workflows.hellov2.HelloWorkflowV2;
+import com.baeldung.temporal.workflows.hellov2.HelloV2WorkerRegistrar;
+import com.baeldung.temporal.workflows.hello.HelloWorkflow;
+import io.temporal.client.WorkflowClient;
+import io.temporal.client.WorkflowOptions;
+import io.temporal.testing.TestWorkflowEnvironment;
+import io.temporal.worker.Worker;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class HelloV2WorkerUnitTest {
+ private final Logger log = LoggerFactory.getLogger(HelloV2WorkerUnitTest.class);
+ private static final String QUEUE_NAME = "say-hello-queue";
+ private TestWorkflowEnvironment testEnv;
+ private Worker worker;
+ private WorkflowClient client;
+
+ @BeforeEach
+ void startWorker() {
+ log.info("Creating test environment...");
+ testEnv = TestWorkflowEnvironment.newInstance();
+ worker = testEnv.newWorker(QUEUE_NAME);
+ HelloV2WorkerRegistrar.newInstance().register(worker);
+
+ client = testEnv.getWorkflowClient();
+ }
+
+ @AfterEach
+ void stopWorker() {
+ testEnv.close();
+ }
+
+ @Test
+ void givenPerson_whenSayHello_thenSuccess() throws Exception {
+
+ // We must register all activities/worklows before starting the test environment
+ testEnv.start();
+
+ // Create workflow stub wich allow us to create workflow instances
+ var wfid = UUID.randomUUID().toString();
+ var workflow = client.newWorkflowStub(
+ HelloWorkflowV2.class,
+ WorkflowOptions.newBuilder()
+ .setTaskQueue(QUEUE_NAME)
+ .setWorkflowId(wfid)
+ .build()
+ );
+
+ // Invoke workflow asynchronously.
+ var execution = WorkflowClient.start(workflow::hello,"Baeldung");
+
+ // Create a blocking workflow using tbe execution's workflow id
+ var syncWorkflow = client.newWorkflowStub(HelloWorkflow.class,execution.getWorkflowId());
+
+ // The sync workflow stub will block until it completes. Notice that the call argument here is ignored!
+ assertEquals("Workflow OK", syncWorkflow.hello("ignored"));
+ log.info("Test OK!");
+ }
+}
\ No newline at end of file
diff --git a/saas-modules/temporal/src/test/java/com/baeldung/temporal/HelloWorkerUnitTest.java b/saas-modules/temporal/src/test/java/com/baeldung/temporal/HelloWorkerUnitTest.java
new file mode 100644
index 000000000000..f45640e6a83a
--- /dev/null
+++ b/saas-modules/temporal/src/test/java/com/baeldung/temporal/HelloWorkerUnitTest.java
@@ -0,0 +1,94 @@
+package com.baeldung.temporal;
+
+import com.baeldung.temporal.workflows.hello.HelloWorkflow;
+import com.baeldung.temporal.workflows.hello.HelloWorkflowRegistrar;
+import io.temporal.client.WorkflowClient;
+import io.temporal.client.WorkflowOptions;
+import io.temporal.serviceclient.WorkflowServiceStubs;
+import io.temporal.serviceclient.WorkflowServiceStubsOptions;
+import io.temporal.testing.TestWorkflowEnvironment;
+import io.temporal.worker.Worker;
+import io.temporal.workflow.Workflow;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Optional;
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class HelloWorkerUnitTest {
+ private final Logger log = LoggerFactory.getLogger(HelloWorkerUnitTest.class);
+ private static final String QUEUE_NAME = "say-hello-queue";
+
+
+ private TestWorkflowEnvironment testEnv;
+ private Worker worker;
+ private WorkflowClient client;
+
+
+ @BeforeEach
+ public void startWorker() {
+
+ log.info("Creating test environment...");
+ testEnv = TestWorkflowEnvironment.newInstance();
+ worker = testEnv.newWorker(QUEUE_NAME);
+ HelloWorkflowRegistrar.newInstance().register(worker);
+ client = testEnv.getWorkflowClient();
+
+ testEnv.start();
+ }
+
+ @AfterEach
+ public void stopWorker() {
+ testEnv.close();
+ }
+
+ @Test
+ void givenPerson_whenSayHelloAsync_thenSuccess() throws Exception {
+
+ // Create workflow stub wich allow us to create workflow instances
+ var wfid = UUID.randomUUID().toString();
+ var workflow = client.newWorkflowStub(
+ HelloWorkflow.class,
+ WorkflowOptions.newBuilder()
+ .setTaskQueue(QUEUE_NAME)
+ .setWorkflowId(wfid)
+ .build()
+ );
+
+ // Invoke workflow asynchronously.
+ var execution = WorkflowClient.start(workflow::hello,"Baeldung");
+ var workflowStub = client.newUntypedWorkflowStub(execution.getWorkflowId());
+
+ // Retrieve a CompletableFuture we can use to wait for the result.
+ var future = workflowStub.getResultAsync(String.class);
+ log.info("Waiting for workflow to complete...");
+ var result = future.get();
+ log.info("Workflow completed with result: {}", result);
+ assertEquals("Hello, Baeldung", result);
+ }
+
+ @Test
+ void givenPerson_whenSayHelloSync_thenSuccess() throws Exception {
+
+ // Create workflow stub wich allow us to create workflow instances
+ var wfid = UUID.randomUUID().toString();
+ var workflow = client.newWorkflowStub(
+ HelloWorkflow.class,
+ WorkflowOptions.newBuilder()
+ .setTaskQueue(QUEUE_NAME)
+ .setWorkflowId(wfid)
+ .build()
+ );
+
+ // Invoke workflow synchronously.
+ var result = workflow.hello("Baeldung");
+
+ // The sync workflow stub will block until it completes. Notice that the call argumento here is ignored!
+ assertEquals("Hello, Baeldung", result);
+ }
+}
\ No newline at end of file
diff --git a/saas-modules/temporal/src/test/java/com/baeldung/temporal/HelloWorkflowIntegrationTest.java b/saas-modules/temporal/src/test/java/com/baeldung/temporal/HelloWorkflowIntegrationTest.java
new file mode 100644
index 000000000000..2054ccf1d4cc
--- /dev/null
+++ b/saas-modules/temporal/src/test/java/com/baeldung/temporal/HelloWorkflowIntegrationTest.java
@@ -0,0 +1,101 @@
+package com.baeldung.temporal;
+
+import com.baeldung.temporal.workflows.hello.HelloWorkflow;
+import com.baeldung.temporal.workflows.hello.HelloWorkflowRegistrar;
+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 org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.UUID;
+import java.util.concurrent.ExecutionException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class HelloWorkflowIntegrationTest {
+ private static final String QUEUE_NAME = "say-hello-queue";
+ private static final Logger log = LoggerFactory.getLogger(HelloWorkflowIntegrationTest.class);
+
+ private WorkerFactory factory;
+
+ @BeforeEach
+ public void startWorker() {
+
+ log.info("Creating worker...");
+ WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs();
+ WorkflowClient client = WorkflowClient.newInstance(service);
+ this.factory = WorkerFactory.newInstance(client);
+
+ Worker worker = factory.newWorker(QUEUE_NAME);
+
+ HelloWorkflowRegistrar.newInstance().register(worker);
+
+ log.info("Starting worker...");
+ factory.start();
+ log.info("Worker started.");
+ }
+
+ @AfterEach
+ public void stopWorker() {
+ log.info("Stopping worker...");
+ factory.shutdown();
+ log.info("Worker stopped.");
+ }
+
+ @Test
+ void givenPerson_whenSayHello_thenSuccess() {
+
+ var service = WorkflowServiceStubs.newLocalServiceStubs();
+ var client = WorkflowClient.newInstance(service);
+
+ var wfid = UUID.randomUUID().toString();
+
+ var workflow = client.newWorkflowStub(
+ HelloWorkflow.class,
+ WorkflowOptions.newBuilder()
+ .setTaskQueue(QUEUE_NAME)
+ .setWorkflowId(wfid)
+ .build()
+ );
+
+ // Run the workflow synchronously
+ var result = workflow.hello("Baeldung");
+ assertEquals("Hello, Baeldung", result);
+ }
+
+ @Test
+ void givenPerson_whenSayHelloAsync_thenSuccess() throws ExecutionException, InterruptedException {
+
+ var service = WorkflowServiceStubs.newLocalServiceStubs();
+ var client = WorkflowClient.newInstance(service);
+
+ var wfid = UUID.randomUUID().toString();
+
+ var workflow = client.newWorkflowStub(
+ HelloWorkflow.class,
+ WorkflowOptions.newBuilder()
+ .setTaskQueue(QUEUE_NAME)
+ .setWorkflowId(wfid)
+ .build()
+ );
+
+ var execution = WorkflowClient.start(workflow::hello,"Baeldung");
+
+ var workflowStub = client.newUntypedWorkflowStub(execution.getWorkflowId());
+
+ // Retrieve a CompletableFuture we can use to wait for the result.
+ var future = workflowStub.getResultAsync(String.class);
+ log.info("Waiting for workflow to complete...");
+ var result = future.get();
+ log.info("Workflow completed with result: {}", result);
+ assertEquals("Hello, Baeldung", result);
+
+ }
+
+}
\ No newline at end of file
diff --git a/saas-modules/temporal/start-dev-server.sh b/saas-modules/temporal/start-dev-server.sh
new file mode 100644
index 000000000000..299939d2462b
--- /dev/null
+++ b/saas-modules/temporal/start-dev-server.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+exec temporal server start-dev
\ No newline at end of file