From 87c691bb11de208c1c361613cf104356453664ad Mon Sep 17 00:00:00 2001 From: Evan Reynolds Date: Wed, 8 Apr 2026 13:30:38 -0700 Subject: [PATCH 1/5] Status checkin --- .../samples/nexus_sync_operations/README.md | 76 ++++++++ .../caller/CallerStarter.java | 30 +++ .../caller/CallerWorker.java | 42 +++++ .../caller/CallerWorkflow.java | 11 ++ .../caller/CallerWorkflowImpl.java | 55 ++++++ .../handler/GreetingActivity.java | 12 ++ .../handler/GreetingActivityImpl.java | 25 +++ .../handler/GreetingServiceImpl.java | 61 ++++++ .../handler/GreetingWorkflow.java | 62 ++++++ .../handler/GreetingWorkflowImpl.java | 109 +++++++++++ .../handler/HandlerWorker.java | 51 +++++ .../service/GreetingService.java | 100 ++++++++++ .../service/Language.java | 11 ++ .../temporal/samples/nexusmessaging/README.MD | 102 ++++++++++ .../nexusmessaging/caller/CallerStarter.java | 32 ++++ .../nexusmessaging/caller/CallerWorker.java | 32 ++++ .../MessageCallerStartHandlerWorkflow.java | 10 + ...MessageCallerStartHandlerWorkflowImpl.java | 69 +++++++ .../caller/MessageCallerWorkflow.java | 10 + .../caller/MessageCallerWorkflowImpl.java | 42 +++++ .../nexusmessaging/handler/HandlerWorker.java | 62 ++++++ .../handler/MessageHandlerRemoteWorkflow.java | 29 +++ .../MessageHandlerRemoteWorkflowImpl.java | 51 +++++ .../handler/MessageHandlerWorkflow.java | 29 +++ .../handler/MessageHandlerWorkflowImpl.java | 47 +++++ .../handler/SampleNexusServiceImpl.java | 129 +++++++++++++ .../nexusmessaging/options/ClientOptions.java | 137 ++++++++++++++ .../service/SampleNexusService.java | 177 ++++++++++++++++++ .../nexusmessaging/service/description.md | 6 + yarn.lock | 4 + 30 files changed, 1613 insertions(+) create mode 100644 core/src/main/java/io/temporal/samples/nexus_sync_operations/README.md create mode 100644 core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerStarter.java create mode 100644 core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorker.java create mode 100644 core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorkflow.java create mode 100644 core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorkflowImpl.java create mode 100644 core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingActivity.java create mode 100644 core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingActivityImpl.java create mode 100644 core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingServiceImpl.java create mode 100644 core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflow.java create mode 100644 core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflowImpl.java create mode 100644 core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/HandlerWorker.java create mode 100644 core/src/main/java/io/temporal/samples/nexus_sync_operations/service/GreetingService.java create mode 100644 core/src/main/java/io/temporal/samples/nexus_sync_operations/service/Language.java create mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/README.MD create mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/caller/CallerStarter.java create mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/caller/CallerWorker.java create mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerStartHandlerWorkflow.java create mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerStartHandlerWorkflowImpl.java create mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerWorkflow.java create mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerWorkflowImpl.java create mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/handler/HandlerWorker.java create mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerRemoteWorkflow.java create mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerRemoteWorkflowImpl.java create mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerWorkflow.java create mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerWorkflowImpl.java create mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/handler/SampleNexusServiceImpl.java create mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/options/ClientOptions.java create mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/service/SampleNexusService.java create mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/service/description.md create mode 100644 yarn.lock diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/README.md b/core/src/main/java/io/temporal/samples/nexus_sync_operations/README.md new file mode 100644 index 00000000..5d76d1fe --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/README.md @@ -0,0 +1,76 @@ +This sample shows how to create a Nexus service that is backed by a long-running workflow and +exposes operations that execute updates and queries against that workflow. The long-running +workflow, and the updates/queries, are private implementation details of the Nexus service: the +caller does not know how the operations are implemented. + +This is a Java port of the +[nexus_sync_operations Python sample](https://github.com/temporalio/samples-python/tree/main/nexus_sync_operations). + +### Sample directory structure + +- [`service/GreetingService.java`](./service/GreetingService.java) — shared Nexus service definition with input/output types +- [`service/Language.java`](./service/Language.java) — shared language enum +- [`handler/`](./handler/) — Nexus operation handlers, the long-running entity workflow they back, an activity, and a handler worker +- [`caller/`](./caller/) — a caller workflow that executes Nexus operations, together with a worker and starter + +### Instructions + +Start a Temporal server: + +```bash +temporal server start-dev +``` + +Create the caller and handler namespaces and the Nexus endpoint: + +```bash +temporal operator namespace create --namespace nexus-sync-operations-handler-namespace +temporal operator namespace create --namespace nexus-sync-operations-caller-namespace + +temporal operator nexus endpoint create \ + --name nexus-sync-operations-nexus-endpoint \ + --target-namespace nexus-sync-operations-handler-namespace \ + --target-task-queue nexus-sync-operations-handler-task-queue +``` + +In one terminal, run the handler worker (starts the long-running entity workflow and polls for +Nexus, workflow, and activity tasks): + +```bash +./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_sync_operations.handler.HandlerWorker +``` + +In a second terminal, run the caller worker: + +```bash +./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_sync_operations.caller.CallerWorker +``` + +In a third terminal, start the caller workflow: + +```bash +./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_sync_operations.caller.CallerStarter +``` + +You should see output like: + +``` +supported languages: [CHINESE, ENGLISH] +language changed: ENGLISH -> ARABIC +``` + +### How it works + +The handler starts a single long-running `GreetingWorkflow` entity workflow when the worker boots. +This workflow holds the current language and a map of greetings, and exposes: + +- `getLanguages` — a `@QueryMethod` listing supported languages +- `getLanguage` — a `@QueryMethod` returning the current language +- `setLanguage` — an `@UpdateMethod` (sync) for switching between already-loaded languages +- `setLanguageUsingActivity` — an `@UpdateMethod` (async) that calls an activity to fetch a + greeting for a new language before switching +- `approve` — a `@SignalMethod` that allows the workflow to complete + +The three `GreetingService` Nexus operations delegate to these workflow handlers via the Temporal +client inside each `OperationHandler.sync` implementation. The caller workflow sees only the Nexus +operations; the entity workflow is a private implementation detail. diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerStarter.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerStarter.java new file mode 100644 index 00000000..48c3758d --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerStarter.java @@ -0,0 +1,30 @@ +package io.temporal.samples.nexus_sync_operations.caller; + +import io.temporal.client.WorkflowClient; +import io.temporal.client.WorkflowClientOptions; +import io.temporal.client.WorkflowOptions; +import io.temporal.serviceclient.WorkflowServiceStubs; +import java.util.List; +import java.util.UUID; + +public class CallerStarter { + + public static void main(String[] args) { + WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); + WorkflowClient client = + WorkflowClient.newInstance( + service, + WorkflowClientOptions.newBuilder().setNamespace(CallerWorker.NAMESPACE).build()); + + CallerWorkflow workflow = + client.newWorkflowStub( + CallerWorkflow.class, + WorkflowOptions.newBuilder() + .setWorkflowId("nexus-sync-operations-caller-" + UUID.randomUUID()) + .setTaskQueue(CallerWorker.TASK_QUEUE) + .build()); + + List log = workflow.run(); + log.forEach(System.out::println); + } +} diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorker.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorker.java new file mode 100644 index 00000000..284bf9bd --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorker.java @@ -0,0 +1,42 @@ +package io.temporal.samples.nexus_sync_operations.caller; + +import io.temporal.client.WorkflowClient; +import io.temporal.client.WorkflowClientOptions; +import io.temporal.serviceclient.WorkflowServiceStubs; +import io.temporal.worker.Worker; +import io.temporal.worker.WorkerFactory; +import io.temporal.worker.WorkflowImplementationOptions; +import io.temporal.workflow.NexusServiceOptions; +import java.util.Collections; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CallerWorker { + private static final Logger logger = LoggerFactory.getLogger(CallerWorker.class); + + public static final String NAMESPACE = "nexus-sync-operations-caller-namespace"; + public static final String TASK_QUEUE = "nexus-sync-operations-caller-task-queue"; + static final String NEXUS_ENDPOINT = "nexus-sync-operations-nexus-endpoint"; + + public static void main(String[] args) throws InterruptedException { + WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); + WorkflowClient client = + WorkflowClient.newInstance( + service, WorkflowClientOptions.newBuilder().setNamespace(NAMESPACE).build()); + + WorkerFactory factory = WorkerFactory.newInstance(client); + Worker worker = factory.newWorker(TASK_QUEUE); + worker.registerWorkflowImplementationTypes( + WorkflowImplementationOptions.newBuilder() + .setNexusServiceOptions( + Collections.singletonMap( + "GreetingService", + NexusServiceOptions.newBuilder().setEndpoint(NEXUS_ENDPOINT).build())) + .build(), + CallerWorkflowImpl.class); + + factory.start(); + logger.info("Caller worker started, ctrl+c to exit"); + Thread.currentThread().join(); + } +} diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorkflow.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorkflow.java new file mode 100644 index 00000000..1061f09a --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorkflow.java @@ -0,0 +1,11 @@ +package io.temporal.samples.nexus_sync_operations.caller; + +import io.temporal.workflow.WorkflowInterface; +import io.temporal.workflow.WorkflowMethod; +import java.util.List; + +@WorkflowInterface +public interface CallerWorkflow { + @WorkflowMethod + List run(); +} diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorkflowImpl.java new file mode 100644 index 00000000..82e96c05 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorkflowImpl.java @@ -0,0 +1,55 @@ +package io.temporal.samples.nexus_sync_operations.caller; + +import io.temporal.failure.ApplicationFailure; +import io.temporal.samples.nexus_sync_operations.service.GreetingService; +import io.temporal.samples.nexus_sync_operations.service.Language; +import io.temporal.workflow.NexusOperationOptions; +import io.temporal.workflow.NexusServiceOptions; +import io.temporal.workflow.Workflow; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +public class CallerWorkflowImpl implements CallerWorkflow { + + // The endpoint is configured at the worker level in CallerWorker; only operation options are + // set here. + GreetingService greetingService = + Workflow.newNexusServiceStub( + GreetingService.class, + NexusServiceOptions.newBuilder() + .setOperationOptions( + NexusOperationOptions.newBuilder() + .setScheduleToCloseTimeout(Duration.ofSeconds(10)) + .build()) + .build()); + + @Override + public List run() { + List log = new ArrayList<>(); + + // 👉 Call a Nexus operation backed by a query against the entity workflow. + GreetingService.GetLanguagesOutput languagesOutput = + greetingService.getLanguages(new GreetingService.GetLanguagesInput(false)); + log.add("supported languages: " + languagesOutput.getLanguages()); + + // 👉 Call a Nexus operation backed by an update against the entity workflow. + Language previousLanguage = + greetingService.setLanguage(new GreetingService.SetLanguageInput(Language.ARABIC)); + + // 👉 Call a Nexus operation backed by a query to confirm the language change. + Language currentLanguage = greetingService.getLanguage(new GreetingService.GetLanguageInput()); + if (currentLanguage != Language.ARABIC) { + throw ApplicationFailure.newFailure( + "expected language ARABIC, got " + currentLanguage, "AssertionError"); + } + + log.add("language changed: " + previousLanguage.name() + " -> " + Language.ARABIC.name()); + + // 👉 Call a Nexus operation backed by a signal against the entity workflow. + greetingService.approve(new GreetingService.ApproveInput("caller")); + log.add("workflow approved"); + + return log; + } +} diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingActivity.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingActivity.java new file mode 100644 index 00000000..ed150780 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingActivity.java @@ -0,0 +1,12 @@ +package io.temporal.samples.nexus_sync_operations.handler; + +import io.temporal.activity.ActivityInterface; +import io.temporal.activity.ActivityMethod; +import io.temporal.samples.nexus_sync_operations.service.Language; + +@ActivityInterface +public interface GreetingActivity { + // Simulates a call to a remote greeting service. Returns null if the language is not supported. + @ActivityMethod + String callGreetingService(Language language); +} diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingActivityImpl.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingActivityImpl.java new file mode 100644 index 00000000..b7ab336f --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingActivityImpl.java @@ -0,0 +1,25 @@ +package io.temporal.samples.nexus_sync_operations.handler; + +import io.temporal.samples.nexus_sync_operations.service.Language; +import java.util.EnumMap; +import java.util.Map; + +public class GreetingActivityImpl implements GreetingActivity { + + private static final Map GREETINGS = new EnumMap<>(Language.class); + + static { + GREETINGS.put(Language.ARABIC, "مرحبا بالعالم"); + GREETINGS.put(Language.CHINESE, "你好,世界"); + GREETINGS.put(Language.ENGLISH, "Hello, world"); + GREETINGS.put(Language.FRENCH, "Bonjour, monde"); + GREETINGS.put(Language.HINDI, "नमस्ते दुनिया"); + GREETINGS.put(Language.PORTUGUESE, "Olá mundo"); + GREETINGS.put(Language.SPANISH, "Hola mundo"); + } + + @Override + public String callGreetingService(Language language) { + return GREETINGS.get(language); + } +} diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingServiceImpl.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingServiceImpl.java new file mode 100644 index 00000000..726ae5b2 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingServiceImpl.java @@ -0,0 +1,61 @@ +package io.temporal.samples.nexus_sync_operations.handler; + +import io.nexusrpc.handler.OperationHandler; +import io.nexusrpc.handler.OperationImpl; +import io.nexusrpc.handler.ServiceImpl; +import io.temporal.nexus.Nexus; +import io.temporal.samples.nexus_sync_operations.service.GreetingService; +import io.temporal.samples.nexus_sync_operations.service.Language; + +/** + * Nexus operation handler implementation. Each operation is backed by the long-running + * GreetingWorkflow entity. The operations are synchronous (sync_operation) because queries and + * updates against a running workflow complete quickly. + */ +@ServiceImpl(service = GreetingService.class) +public class GreetingServiceImpl { + + private final String workflowId; + + public GreetingServiceImpl(String workflowId) { + this.workflowId = workflowId; + } + + private GreetingWorkflow getWorkflowStub() { + return Nexus.getOperationContext() + .getWorkflowClient() + .newWorkflowStub(GreetingWorkflow.class, workflowId); + } + + // 👉 Backed by a query against the long-running entity workflow. + @OperationImpl + public OperationHandler + getLanguages() { + return OperationHandler.sync((ctx, details, input) -> getWorkflowStub().getLanguages(input)); + } + + // 👉 Backed by a query against the long-running entity workflow. + @OperationImpl + public OperationHandler getLanguage() { + return OperationHandler.sync((ctx, details, input) -> getWorkflowStub().getLanguage()); + } + + // 👉 Backed by an update against the long-running entity workflow. Although updates can run for + // an arbitrarily long time, when exposed via a sync Nexus operation the update should complete + // quickly (sync operations must finish in under 10s). + @OperationImpl + public OperationHandler setLanguage() { + return OperationHandler.sync( + (ctx, details, input) -> getWorkflowStub().setLanguageUsingActivity(input)); + } + + // 👉 Backed by a signal against the long-running entity workflow. + @OperationImpl + public OperationHandler approve() { + return OperationHandler.sync( + (ctx, details, input) -> { + getWorkflowStub().approve(new GreetingWorkflow.ApproveInput(input.getName())); + return new GreetingService.ApproveOutput(); + }); + } +} diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflow.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflow.java new file mode 100644 index 00000000..05b46828 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflow.java @@ -0,0 +1,62 @@ +package io.temporal.samples.nexus_sync_operations.handler; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.temporal.samples.nexus_sync_operations.service.GreetingService; +import io.temporal.samples.nexus_sync_operations.service.Language; +import io.temporal.workflow.QueryMethod; +import io.temporal.workflow.SignalMethod; +import io.temporal.workflow.UpdateMethod; +import io.temporal.workflow.UpdateValidatorMethod; +import io.temporal.workflow.WorkflowInterface; +import io.temporal.workflow.WorkflowMethod; + +/** + * A long-running "entity" workflow that backs the GreetingService Nexus operations. The workflow + * exposes queries, an update, and a signal. These are private implementation details of the Nexus + * service: the caller only interacts via Nexus operations. + */ +@WorkflowInterface +public interface GreetingWorkflow { + + class ApproveInput { + private final String name; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public ApproveInput(@JsonProperty("name") String name) { + this.name = name; + } + + @JsonProperty("name") + public String getName() { + return name; + } + } + + @WorkflowMethod + String run(); + + // Returns the languages currently supported by the workflow. + @QueryMethod + GreetingService.GetLanguagesOutput getLanguages(GreetingService.GetLanguagesInput input); + + // Returns the currently active language. + @QueryMethod + Language getLanguage(); + + // Approves the workflow, allowing it to complete. + @SignalMethod + void approve(ApproveInput input); + + // Changes the active language synchronously (only supports languages already in the greetings + // map). + @UpdateMethod + Language setLanguage(GreetingService.SetLanguageInput input); + + @UpdateValidatorMethod(updateName = "setLanguage") + void validateSetLanguage(GreetingService.SetLanguageInput input); + + // Changes the active language, calling an activity to fetch a greeting for new languages. + @UpdateMethod + Language setLanguageUsingActivity(GreetingService.SetLanguageInput input); +} diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflowImpl.java new file mode 100644 index 00000000..1f1e6af8 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflowImpl.java @@ -0,0 +1,109 @@ +package io.temporal.samples.nexus_sync_operations.handler; + +import io.temporal.activity.ActivityOptions; +import io.temporal.failure.ApplicationFailure; +import io.temporal.samples.nexus_sync_operations.service.GreetingService; +import io.temporal.samples.nexus_sync_operations.service.Language; +import io.temporal.workflow.Workflow; +import io.temporal.workflow.WorkflowLock; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GreetingWorkflowImpl implements GreetingWorkflow { + + private boolean approvedForRelease = false; + private final Map greetings = new EnumMap<>(Language.class); + private Language language = Language.ENGLISH; + + private static final Logger logger = LoggerFactory.getLogger(HandlerWorker.class); + + // Used to serialize concurrent setLanguageUsingActivity calls so that only one activity runs at + // a time per update handler execution. + private final WorkflowLock lock = Workflow.newWorkflowLock(); + + private final GreetingActivity greetingActivity = + Workflow.newActivityStub( + GreetingActivity.class, + ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build()); + + public GreetingWorkflowImpl() { + greetings.put(Language.CHINESE, "你好,世界"); + greetings.put(Language.ENGLISH, "Hello, world"); + } + + @Override + public String run() { + // Wait until approved and all in-flight update handlers have finished. + Workflow.await(() -> approvedForRelease && Workflow.isEveryHandlerFinished()); + return greetings.get(language); + } + + @Override + public GreetingService.GetLanguagesOutput getLanguages(GreetingService.GetLanguagesInput input) { + List result; + if (input.isIncludeUnsupported()) { + result = new ArrayList<>(Arrays.asList(Language.values())); + } else { + result = new ArrayList<>(greetings.keySet()); + } + Collections.sort(result); + return new GreetingService.GetLanguagesOutput(result); + } + + @Override + public Language getLanguage() { + return language; + } + + @Override + public void approve(ApproveInput input) { + logger.info("Approval signal received"); + approvedForRelease = true; + } + + @Override + public Language setLanguage(GreetingService.SetLanguageInput input) { + logger.info("setLanguage update received"); + Language previous = language; + language = input.getLanguage(); + return previous; + } + + @Override + public void validateSetLanguage(GreetingService.SetLanguageInput input) { + logger.info("validateSetLanguage called"); + if (!greetings.containsKey(input.getLanguage())) { + throw new IllegalArgumentException(input.getLanguage().name() + " is not supported"); + } + } + + @Override + public Language setLanguageUsingActivity(GreetingService.SetLanguageInput input) { + if (!greetings.containsKey(input.getLanguage())) { + // Use a lock so that if this handler is called concurrently, each call executes its activity + // only after the previous one has completed. This ensures updates are processed in order. + lock.lock(); + try { + String greeting = greetingActivity.callGreetingService(input.getLanguage()); + if (greeting == null) { + throw ApplicationFailure.newFailure( + "Greeting service does not support " + input.getLanguage().name(), + "UnsupportedLanguage"); + } + greetings.put(input.getLanguage(), greeting); + } finally { + lock.unlock(); + } + } + Language previous = language; + language = input.getLanguage(); + return previous; + } +} diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/HandlerWorker.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/HandlerWorker.java new file mode 100644 index 00000000..05851047 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/HandlerWorker.java @@ -0,0 +1,51 @@ +package io.temporal.samples.nexus_sync_operations.handler; + +import io.temporal.client.WorkflowClient; +import io.temporal.client.WorkflowClientOptions; +import io.temporal.client.WorkflowExecutionAlreadyStarted; +import io.temporal.client.WorkflowOptions; +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 HandlerWorker { + private static final Logger logger = LoggerFactory.getLogger(HandlerWorker.class); + + public static final String NAMESPACE = "nexus-sync-operations-handler-namespace"; + public static final String TASK_QUEUE = "nexus-sync-operations-handler-task-queue"; + static final String WORKFLOW_ID = "nexus-sync-operations-greeting-workflow"; + + public static void main(String[] args) throws InterruptedException { + WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); + WorkflowClient client = + WorkflowClient.newInstance( + service, WorkflowClientOptions.newBuilder().setNamespace(NAMESPACE).build()); + + // Start the long-running entity workflow that backs the Nexus service, if not already running. + GreetingWorkflow greetingWorkflow = + client.newWorkflowStub( + GreetingWorkflow.class, + WorkflowOptions.newBuilder() + .setWorkflowId(WORKFLOW_ID) + .setTaskQueue(TASK_QUEUE) + .build()); + try { + WorkflowClient.start(greetingWorkflow::run); + logger.info("Started greeting workflow: {}", WORKFLOW_ID); + } catch (WorkflowExecutionAlreadyStarted e) { + logger.info("Greeting workflow already running: {}", WORKFLOW_ID); + } + + WorkerFactory factory = WorkerFactory.newInstance(client); + Worker worker = factory.newWorker(TASK_QUEUE); + worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class); + worker.registerActivitiesImplementations(new GreetingActivityImpl()); + worker.registerNexusServiceImplementation(new GreetingServiceImpl(WORKFLOW_ID)); + + factory.start(); + logger.info("Handler worker started, ctrl+c to exit"); + Thread.currentThread().join(); + } +} diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/service/GreetingService.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/service/GreetingService.java new file mode 100644 index 00000000..407d309d --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/service/GreetingService.java @@ -0,0 +1,100 @@ +package io.temporal.samples.nexus_sync_operations.service; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.nexusrpc.Operation; +import io.nexusrpc.Service; +import java.util.List; + +/** + * Nexus service definition. Shared between the handler and caller. The caller uses this to create a + * type-safe Nexus client stub; the handler implements the operations. + */ +@Service +public interface GreetingService { + + class GetLanguagesInput { + private final boolean includeUnsupported; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public GetLanguagesInput(@JsonProperty("includeUnsupported") boolean includeUnsupported) { + this.includeUnsupported = includeUnsupported; + } + + @JsonProperty("includeUnsupported") + public boolean isIncludeUnsupported() { + return includeUnsupported; + } + } + + class GetLanguagesOutput { + private final List languages; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public GetLanguagesOutput(@JsonProperty("languages") List languages) { + this.languages = languages; + } + + @JsonProperty("languages") + public List getLanguages() { + return languages; + } + } + + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) + class GetLanguageInput { + @JsonCreator + public GetLanguageInput() {} + } + + class ApproveInput { + private final String name; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public ApproveInput(@JsonProperty("name") String name) { + this.name = name; + } + + @JsonProperty("name") + public String getName() { + return name; + } + } + + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) + class ApproveOutput { + @JsonCreator + public ApproveOutput() {} + } + + class SetLanguageInput { + private final Language language; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public SetLanguageInput(@JsonProperty("language") Language language) { + this.language = language; + } + + @JsonProperty("language") + public Language getLanguage() { + return language; + } + } + + // Returns the languages supported by the greeting workflow. + @Operation + GetLanguagesOutput getLanguages(GetLanguagesInput input); + + // Returns the currently active language. + @Operation + Language getLanguage(GetLanguageInput input); + + // Changes the active language, returning the previous one. + @Operation + Language setLanguage(SetLanguageInput input); + + // Approves the workflow, allowing it to complete. + @Operation + ApproveOutput approve(ApproveInput input); +} diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/service/Language.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/service/Language.java new file mode 100644 index 00000000..5bc9f0dd --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/service/Language.java @@ -0,0 +1,11 @@ +package io.temporal.samples.nexus_sync_operations.service; + +public enum Language { + ARABIC, + CHINESE, + ENGLISH, + FRENCH, + HINDI, + PORTUGUESE, + SPANISH +} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/README.MD b/core/src/main/java/io/temporal/samples/nexusmessaging/README.MD new file mode 100644 index 00000000..a8fe380e --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/README.MD @@ -0,0 +1,102 @@ +# Nexus + +Temporal Nexus is a new feature of the Temporal platform designed to connect durable executions across team, namespace, +region, and cloud boundaries. It promotes a more modular architecture for sharing a subset of your team’s capabilities +via well-defined service API contracts for other teams to use, that abstract underlying Temporal primitives, like +Workflows, or execute arbitrary code. + +Learn more at [temporal.io/nexus](https://temporal.io/nexus). + +This sample shows how to use Temporal for authoring a Nexus service and call it from a workflow. + +### Sample directory structure + +- [service](./service) - shared service definition +- [caller](./caller) - caller workflows, worker, and starter +- [handler](./handler) - handler workflow, operations, and worker +- [options](./options) - command line argument parsing utility + +## Getting started locally + +### Get `temporal` CLI to enable local development + +1. Follow the instructions on the [docs + site](https://learn.temporal.io/getting_started/go/dev_environment/#set-up-a-local-temporal-service-for-development-with-temporal-cli) + to install Temporal CLI. + +> NOTE: The recommended version is at least v1.3.0. + +### Spin up environment + +#### Start temporal server + +> HTTP port is required for Nexus communications + +``` +temporal server start-dev +``` + +### Initialize environment + +In a separate terminal window + +#### Create caller and target namespaces + +``` +temporal operator namespace create --namespace my-target-namespace +temporal operator namespace create --namespace my-caller-namespace +``` + +#### Create Nexus endpoint + +``` +temporal operator nexus endpoint create \ + --name my-nexus-endpoint-name \ + --target-namespace my-target-namespace \ + --target-task-queue my-handler-task-queue \ + --description-file ./core/src/main/java/io/temporal/samples/nexusmessaging/service/description.md +``` + +## Getting started with a self-hosted service or Temporal Cloud + +Nexus is currently available as +[Public Preview](https://docs.temporal.io/evaluate/development-production-features/release-stages). + +Self hosted users can [try Nexus +out](https://github.com/temporalio/temporal/blob/main/docs/architecture/nexus.md#trying-nexus-out) in single cluster +deployments with server version 1.25.0. + +### Make Nexus calls across namespace boundaries + +> Instructions apply for local development, for Temporal Cloud or a self-hosted setups, supply the relevant [CLI +> flags](./options/ClientOptions.java) to properly set up the connection. + +In separate terminal windows: + +### Nexus handler worker + +``` +./gradlew -q execute -PmainClass=io.temporal.samples.nexusmessaging.handler.HandlerWorker \ + --args="-target-host localhost:7233 -namespace my-target-namespace" +``` + +### Nexus caller worker + +``` +./gradlew -q execute -PmainClass=io.temporal.samples.nexusmessaging.caller.CallerWorker \ + --args="-target-host localhost:7233 -namespace my-caller-namespace" +``` + +### Start caller workflow + +``` +./gradlew -q execute -PmainClass=io.temporal.samples.nexusmessaging.caller.CallerStarter \ + --args="-target-host localhost:7233 -namespace my-caller-namespace" +``` + +### Output + +which should result in: +``` +[main] INFO i.t.s.nexusmessaging.caller.CallerStarter - Workflow result: Nexus Echo 👋 +``` diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/caller/CallerStarter.java b/core/src/main/java/io/temporal/samples/nexusmessaging/caller/CallerStarter.java new file mode 100644 index 00000000..498ba524 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/caller/CallerStarter.java @@ -0,0 +1,32 @@ +package io.temporal.samples.nexusmessaging.caller; + +import io.temporal.api.common.v1.WorkflowExecution; +import io.temporal.client.WorkflowClient; +import io.temporal.client.WorkflowOptions; +import io.temporal.samples.nexusmessaging.options.ClientOptions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CallerStarter { + private static final Logger logger = LoggerFactory.getLogger(CallerStarter.class); + + public static void main(String[] args) { + WorkflowClient client = ClientOptions.getWorkflowClient(args); + + WorkflowOptions workflowOptions = + WorkflowOptions.newBuilder().setTaskQueue(CallerWorker.DEFAULT_TASK_QUEUE_NAME).build(); + + MessageCallerStartHandlerWorkflow messageWorkflow = + client.newWorkflowStub(MessageCallerStartHandlerWorkflow.class, workflowOptions); + + // MessageCallerWorkflow messageWorkflow = + // client.newWorkflowStub(MessageCallerWorkflow.class, workflowOptions); + WorkflowExecution execution = WorkflowClient.start(messageWorkflow::sentMessage); + logger.info( + "Started readMessage workflowId: {} runId: {}", + execution.getWorkflowId(), + execution.getRunId()); + String returnVal = messageWorkflow.sentMessage(); + logger.info("Workflow readMessage done - retval is {}", returnVal); + } +} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/caller/CallerWorker.java b/core/src/main/java/io/temporal/samples/nexusmessaging/caller/CallerWorker.java new file mode 100644 index 00000000..182c96f3 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/caller/CallerWorker.java @@ -0,0 +1,32 @@ +package io.temporal.samples.nexusmessaging.caller; + +import io.temporal.client.WorkflowClient; +import io.temporal.samples.nexusmessaging.options.ClientOptions; +import io.temporal.worker.Worker; +import io.temporal.worker.WorkerFactory; +import io.temporal.worker.WorkflowImplementationOptions; +import io.temporal.workflow.NexusServiceOptions; +import java.util.Collections; + +public class CallerWorker { + public static final String DEFAULT_TASK_QUEUE_NAME = "my-caller-workflow-task-queue"; + + public static void main(String[] args) { + WorkflowClient client = ClientOptions.getWorkflowClient(args); + + WorkerFactory factory = WorkerFactory.newInstance(client); + + Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME); + worker.registerWorkflowImplementationTypes( + WorkflowImplementationOptions.newBuilder() + .setNexusServiceOptions( + Collections.singletonMap( + "SampleNexusService", + NexusServiceOptions.newBuilder().setEndpoint("my-nexus-endpoint-name").build())) + .build(), + // MessageCallerWorkflowImpl.class, + MessageCallerStartHandlerWorkflowImpl.class); + + factory.start(); + } +} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerStartHandlerWorkflow.java b/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerStartHandlerWorkflow.java new file mode 100644 index 00000000..ba04df24 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerStartHandlerWorkflow.java @@ -0,0 +1,10 @@ +package io.temporal.samples.nexusmessaging.caller; + +import io.temporal.workflow.WorkflowInterface; +import io.temporal.workflow.WorkflowMethod; + +@WorkflowInterface +public interface MessageCallerStartHandlerWorkflow { + @WorkflowMethod + String sentMessage(); +} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerStartHandlerWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerStartHandlerWorkflowImpl.java new file mode 100644 index 00000000..f44bf4ec --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerStartHandlerWorkflowImpl.java @@ -0,0 +1,69 @@ +package io.temporal.samples.nexusmessaging.caller; + +import io.temporal.samples.nexusmessaging.service.SampleNexusService; +import io.temporal.workflow.NexusOperationHandle; +import io.temporal.workflow.NexusOperationOptions; +import io.temporal.workflow.NexusServiceOptions; +import io.temporal.workflow.Workflow; +import java.time.Duration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MessageCallerStartHandlerWorkflowImpl implements MessageCallerStartHandlerWorkflow { + + private static final Logger logger = + LoggerFactory.getLogger(MessageCallerStartHandlerWorkflowImpl.class); + + private final String workflowId = "Remote Start Workflow"; + + SampleNexusService sampleNexusService = + Workflow.newNexusServiceStub( + SampleNexusService.class, + NexusServiceOptions.newBuilder() + .setOperationOptions( + NexusOperationOptions.newBuilder() + .setScheduleToCloseTimeout(Duration.ofSeconds(10)) + .build()) + .build()); + + @Override + public String sentMessage() { + /* + SampleNexusService.QueryWorkflowOutput queryOutput = + sampleNexusService.queryWorkflow( + new SampleNexusService.QueryWorkflowInput("query string going in")); + logger.info("Query output: {}", queryOutput.getMessage()); + + SampleNexusService.UpdateWorkflowOutput updateOutput = + sampleNexusService.updateWorkflow( + new SampleNexusService.UpdateWorkflowInput("update input")); + logger.info("Update output: {}", updateOutput.getResult()); + + sampleNexusService.signalWorkflow(new SampleNexusService.SignalWorkflowInput("signal input")); + logger.info("Signal sent via Nexus"); + + return "Query: " + queryOutput.getMessage() + ", Update: " + updateOutput.getResult(); + + */ + + NexusOperationHandle handle = + Workflow.startNexusOperation( + sampleNexusService::runFromRemote, + new SampleNexusService.RunFromRemoteInput(workflowId)); + // Optionally wait for the operation to be started. NexusOperationExecution will contain the + // operation token in case this operation is asynchronous. + handle.getExecution().get(); + + SampleNexusService.QueryWorkflowOutput queryWorkflowOutput = + sampleNexusService.queryWorkflowRemoteStart( + new SampleNexusService.QueryWorkflowRemoteStartInput( + "query string going in", workflowId)); + logger.info("Caller has query output of {}", queryWorkflowOutput); + + sampleNexusService.signalWorkflowRemoteStart( + new SampleNexusService.SignalWorkflowRemoteStartInput("signal input", workflowId)); + logger.info("Signal sent via Nexus"); + + return handle.getResult().get().getMessage(); + } +} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerWorkflow.java b/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerWorkflow.java new file mode 100644 index 00000000..1a9a9b71 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerWorkflow.java @@ -0,0 +1,10 @@ +package io.temporal.samples.nexusmessaging.caller; + +import io.temporal.workflow.WorkflowInterface; +import io.temporal.workflow.WorkflowMethod; + +@WorkflowInterface +public interface MessageCallerWorkflow { + @WorkflowMethod + String sentMessage(); +} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerWorkflowImpl.java new file mode 100644 index 00000000..55a6cf66 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerWorkflowImpl.java @@ -0,0 +1,42 @@ +package io.temporal.samples.nexusmessaging.caller; + +import io.temporal.samples.nexusmessaging.service.SampleNexusService; +import io.temporal.workflow.NexusOperationOptions; +import io.temporal.workflow.NexusServiceOptions; +import io.temporal.workflow.Workflow; +import java.time.Duration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MessageCallerWorkflowImpl implements MessageCallerWorkflow { + + private static final Logger logger = LoggerFactory.getLogger(MessageCallerWorkflowImpl.class); + + SampleNexusService sampleNexusService = + Workflow.newNexusServiceStub( + SampleNexusService.class, + NexusServiceOptions.newBuilder() + .setOperationOptions( + NexusOperationOptions.newBuilder() + .setScheduleToCloseTimeout(Duration.ofSeconds(10)) + .build()) + .build()); + + @Override + public String sentMessage() { + SampleNexusService.QueryWorkflowOutput queryOutput = + sampleNexusService.queryWorkflow( + new SampleNexusService.QueryWorkflowInput("query string going in")); + logger.info("Query output: {}", queryOutput.getMessage()); + + SampleNexusService.UpdateWorkflowOutput updateOutput = + sampleNexusService.updateWorkflow( + new SampleNexusService.UpdateWorkflowInput("update input")); + logger.info("Update output: {}", updateOutput.getResult()); + + sampleNexusService.signalWorkflow(new SampleNexusService.SignalWorkflowInput("signal input")); + logger.info("Signal sent via Nexus"); + + return "Query: " + queryOutput.getMessage() + ", Update: " + updateOutput.getResult(); + } +} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/handler/HandlerWorker.java b/core/src/main/java/io/temporal/samples/nexusmessaging/handler/HandlerWorker.java new file mode 100644 index 00000000..8af0c721 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/handler/HandlerWorker.java @@ -0,0 +1,62 @@ +package io.temporal.samples.nexusmessaging.handler; + +import io.temporal.client.WorkflowClient; +import io.temporal.client.WorkflowClientOptions; +import io.temporal.client.WorkflowExecutionAlreadyStarted; +import io.temporal.client.WorkflowOptions; +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 HandlerWorker { + + public static final String NAMESPACE = "my-target-namespace"; + public static final String DEFAULT_TASK_QUEUE_NAME = "my-handler-task-queue"; + static final String WORKFLOW_ID = "my-handler-workflow"; + + private static final Logger logger = LoggerFactory.getLogger(HandlerWorker.class); + + public static void main(String[] args) { + WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); + WorkflowClient client = + WorkflowClient.newInstance( + service, WorkflowClientOptions.newBuilder().setNamespace(NAMESPACE).build()); + + // Start the long-running entity workflow that backs the Nexus service, if not already running. + MessageHandlerWorkflow messageHandlerWorkflow = + client.newWorkflowStub( + MessageHandlerWorkflow.class, + WorkflowOptions.newBuilder() + .setWorkflowId(WORKFLOW_ID) + .setTaskQueue(DEFAULT_TASK_QUEUE_NAME) + .build()); + try { + WorkflowClient.start(messageHandlerWorkflow::run); + logger.info("Started message handler workflow: {}", WORKFLOW_ID); + } catch (WorkflowExecutionAlreadyStarted e) { + logger.info("Message handler workflow already running: {}", WORKFLOW_ID); + } + + WorkerFactory factory = WorkerFactory.newInstance(client); + Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME); + worker.registerWorkflowImplementationTypes( + MessageHandlerWorkflowImpl.class, MessageHandlerRemoteWorkflowImpl.class); + // worker.registerActivitiesImplementations(new MessageHandlerWorkflowImpl()); + worker.registerNexusServiceImplementation(new SampleNexusServiceImpl(WORKFLOW_ID)); + + factory.start(); + logger.info("Handler worker started, ctrl+c to exit"); + + // WorkflowClient client = ClientOptions.getWorkflowClient(args); + // + // WorkerFactory factory = WorkerFactory.newInstance(client); + // + // Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME); + // worker.registerWorkflowImplementationTypes(MessageHandlerWorkflowImpl.class); + // worker.registerNexusServiceImplementation(new SampleNexusServiceImpl()); + // + // factory.start(); + } +} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerRemoteWorkflow.java b/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerRemoteWorkflow.java new file mode 100644 index 00000000..dad19f81 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerRemoteWorkflow.java @@ -0,0 +1,29 @@ +package io.temporal.samples.nexusmessaging.handler; + +import io.temporal.samples.nexusmessaging.service.SampleNexusService; +import io.temporal.workflow.QueryMethod; +import io.temporal.workflow.SignalMethod; +import io.temporal.workflow.UpdateMethod; +import io.temporal.workflow.UpdateValidatorMethod; +import io.temporal.workflow.WorkflowInterface; +import io.temporal.workflow.WorkflowMethod; + +@WorkflowInterface +public interface MessageHandlerRemoteWorkflow { + + @WorkflowMethod + SampleNexusService.RunFromRemoteOutput runFromRemote(SampleNexusService.RunFromRemoteInput input); + + @QueryMethod + SampleNexusService.QueryWorkflowOutput queryWorkflow(SampleNexusService.QueryWorkflowInput name); + + @SignalMethod + void signalWorkflow(SampleNexusService.SignalWorkflowInput name); + + @UpdateMethod + SampleNexusService.UpdateWorkflowOutput updateWorkflow( + SampleNexusService.UpdateWorkflowInput name); + + @UpdateValidatorMethod(updateName = "updateWorkflow") + void setLanguageValidator(SampleNexusService.UpdateWorkflowInput name); +} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerRemoteWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerRemoteWorkflowImpl.java new file mode 100644 index 00000000..e6e2c5a0 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerRemoteWorkflowImpl.java @@ -0,0 +1,51 @@ +package io.temporal.samples.nexusmessaging.handler; + +import io.temporal.samples.nexusmessaging.service.SampleNexusService; +import io.temporal.workflow.Workflow; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MessageHandlerRemoteWorkflowImpl implements MessageHandlerRemoteWorkflow { + private static final Logger logger = + LoggerFactory.getLogger(MessageHandlerRemoteWorkflowImpl.class); + private boolean keepRunning = true; + + @Override + public SampleNexusService.RunFromRemoteOutput runFromRemote( + SampleNexusService.RunFromRemoteInput input) { + Workflow.await(() -> !keepRunning); + + String logMessage = "runFromRemote was told to stop, and did."; + logger.info(logMessage); + return new SampleNexusService.RunFromRemoteOutput(logMessage); + } + + @Override + public SampleNexusService.QueryWorkflowOutput queryWorkflow( + SampleNexusService.QueryWorkflowInput name) { + logger.info("Query '{}' was received", name.getName()); + return new SampleNexusService.QueryWorkflowOutput("Query received"); + } + + @Override + public void signalWorkflow(SampleNexusService.SignalWorkflowInput name) { + logger.info("Signal was received"); + keepRunning = false; + } + + @Override + public SampleNexusService.UpdateWorkflowOutput updateWorkflow( + SampleNexusService.UpdateWorkflowInput name) { + logger.info("Update {} was received", name.getName()); + return new SampleNexusService.UpdateWorkflowOutput(10); + } + + @Override + public void setLanguageValidator(SampleNexusService.UpdateWorkflowInput name) { + if (name.getName().equals("invalid")) { + logger.info("Update {} was rejected", name.getName()); + throw new IllegalArgumentException("Invalid update name!"); + } + logger.info("Update {} was validated", name.getName()); + } +} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerWorkflow.java b/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerWorkflow.java new file mode 100644 index 00000000..897e4a51 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerWorkflow.java @@ -0,0 +1,29 @@ +package io.temporal.samples.nexusmessaging.handler; + +import io.temporal.samples.nexusmessaging.service.SampleNexusService; +import io.temporal.workflow.QueryMethod; +import io.temporal.workflow.SignalMethod; +import io.temporal.workflow.UpdateMethod; +import io.temporal.workflow.UpdateValidatorMethod; +import io.temporal.workflow.WorkflowInterface; +import io.temporal.workflow.WorkflowMethod; + +@WorkflowInterface +public interface MessageHandlerWorkflow { + + @WorkflowMethod + void run(); + + @QueryMethod + SampleNexusService.QueryWorkflowOutput queryWorkflow(SampleNexusService.QueryWorkflowInput name); + + @SignalMethod + void signalWorkflow(SampleNexusService.SignalWorkflowInput name); + + @UpdateMethod + SampleNexusService.UpdateWorkflowOutput updateWorkflow( + SampleNexusService.UpdateWorkflowInput name); + + @UpdateValidatorMethod(updateName = "updateWorkflow") + void setLanguageValidator(SampleNexusService.UpdateWorkflowInput name); +} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerWorkflowImpl.java new file mode 100644 index 00000000..68129be0 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerWorkflowImpl.java @@ -0,0 +1,47 @@ +package io.temporal.samples.nexusmessaging.handler; + +import io.temporal.samples.nexusmessaging.service.SampleNexusService; +import io.temporal.workflow.Workflow; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MessageHandlerWorkflowImpl implements MessageHandlerWorkflow { + private static final Logger logger = LoggerFactory.getLogger(MessageHandlerWorkflowImpl.class); + private boolean keepRunning = true; + + @Override + public void run() { + // Long-running entity workflow: stays alive to receive signals, queries, and updates via Nexus. + Workflow.await(() -> !keepRunning); + logger.info("Handler workflow stopped via signal"); + } + + @Override + public SampleNexusService.QueryWorkflowOutput queryWorkflow( + SampleNexusService.QueryWorkflowInput name) { + logger.info("Query '{}' was received", name.getName()); + return new SampleNexusService.QueryWorkflowOutput("Query received"); + } + + @Override + public void signalWorkflow(SampleNexusService.SignalWorkflowInput name) { + logger.info("Signal was received"); + keepRunning = false; + } + + @Override + public SampleNexusService.UpdateWorkflowOutput updateWorkflow( + SampleNexusService.UpdateWorkflowInput name) { + logger.info("Update {} was received", name.getName()); + return new SampleNexusService.UpdateWorkflowOutput(10); + } + + @Override + public void setLanguageValidator(SampleNexusService.UpdateWorkflowInput name) { + if (name.getName().equals("invalid")) { + logger.info("Update {} was rejected", name.getName()); + throw new IllegalArgumentException("Invalid update name!"); + } + logger.info("Update {} was validated", name.getName()); + } +} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/handler/SampleNexusServiceImpl.java b/core/src/main/java/io/temporal/samples/nexusmessaging/handler/SampleNexusServiceImpl.java new file mode 100644 index 00000000..675bf7fe --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/handler/SampleNexusServiceImpl.java @@ -0,0 +1,129 @@ +package io.temporal.samples.nexusmessaging.handler; + +import io.nexusrpc.handler.OperationHandler; +import io.nexusrpc.handler.OperationImpl; +import io.nexusrpc.handler.ServiceImpl; +import io.temporal.client.WorkflowOptions; +import io.temporal.nexus.Nexus; +import io.temporal.nexus.WorkflowRunOperation; +import io.temporal.samples.nexusmessaging.service.SampleNexusService; + +@ServiceImpl(service = SampleNexusService.class) +public class SampleNexusServiceImpl { + + private final String workflowId; + + public SampleNexusServiceImpl(String workflowId) { + this.workflowId = workflowId; + } + + private MessageHandlerWorkflow getWorkflowStub() { + return Nexus.getOperationContext() + .getWorkflowClient() + .newWorkflowStub(MessageHandlerWorkflow.class, workflowId); + } + + @OperationImpl + public OperationHandler< + SampleNexusService.RunFromRemoteInput, SampleNexusService.RunFromRemoteOutput> + runFromRemote() { + return WorkflowRunOperation.fromWorkflowMethod( + (ctx, details, input) -> + Nexus.getOperationContext() + .getWorkflowClient() + .newWorkflowStub( + MessageHandlerRemoteWorkflow.class, + WorkflowOptions.newBuilder().setWorkflowId(input.getWorkflowId()).build()) + ::runFromRemote); + } + + @OperationImpl + public OperationHandler< + SampleNexusService.SignalWorkflowInput, SampleNexusService.SignalWorkflowOutput> + signalWorkflow() { + return OperationHandler.sync( + (ctx, details, input) -> { + getWorkflowStub().signalWorkflow(input); + return new SampleNexusService.SignalWorkflowOutput(); + }); + } + + @OperationImpl + public OperationHandler< + SampleNexusService.QueryWorkflowInput, SampleNexusService.QueryWorkflowOutput> + queryWorkflow() { + return OperationHandler.sync((ctx, details, input) -> getWorkflowStub().queryWorkflow(input)); + } + + @OperationImpl + public OperationHandler< + SampleNexusService.UpdateWorkflowInput, SampleNexusService.UpdateWorkflowOutput> + updateWorkflow() { + return OperationHandler.sync((ctx, details, input) -> getWorkflowStub().updateWorkflow(input)); + } + + @OperationImpl + public OperationHandler< + SampleNexusService.QueryWorkflowRemoteStartInput, SampleNexusService.QueryWorkflowOutput> + queryWorkflowRemoteStart() { + return OperationHandler.sync( + (ctx, details, input) -> { + MessageHandlerRemoteWorkflow stub = + Nexus.getOperationContext() + .getWorkflowClient() + .newWorkflowStub(MessageHandlerRemoteWorkflow.class, input.getWorkflowId()); + return stub.queryWorkflow(new SampleNexusService.QueryWorkflowInput(input.getName())); + }); + } + + @OperationImpl + public OperationHandler< + SampleNexusService.SignalWorkflowRemoteStartInput, + SampleNexusService.SignalWorkflowOutput> + signalWorkflowRemoteStart() { + return OperationHandler.sync( + (ctx, details, input) -> { + MessageHandlerRemoteWorkflow stub = + Nexus.getOperationContext() + .getWorkflowClient() + .newWorkflowStub(MessageHandlerRemoteWorkflow.class, input.getWorkflowId()); + stub.signalWorkflow(new SampleNexusService.SignalWorkflowInput(input.getName())); + return new SampleNexusService.SignalWorkflowOutput(); + }); + } + + /* + + @OperationImpl + public OperationHandler< + SampleNexusService.SignalWorkflowInput, SampleNexusService.SignalWorkflowOutput> + signalWorkflow() { + return OperationHandler.sync( + (ctx, details, input) -> { + MessageHandlerWorkflow stub = + Nexus.getOperationContext() + .getWorkflowClient() + .newWorkflowStub(MessageHandlerWorkflow.class, input.getWorkflowId()); + stub.signalWorkflow(input.getName()); + return new SampleNexusService.SignalWorkflowOutput(); + }); + } + + + + @OperationImpl + public OperationHandler< + SampleNexusService.UpdateWorkflowInput, SampleNexusService.UpdateWorkflowOutput> + updateWorkflow() { + return OperationHandler.sync( + (ctx, details, input) -> { + MessageHandlerWorkflow stub = + Nexus.getOperationContext() + .getWorkflowClient() + .newWorkflowStub(MessageHandlerWorkflow.class, input.getWorkflowId()); + int result = stub.updateWorkflow(input.getName()); + return new SampleNexusService.UpdateWorkflowOutput(result); + }); + } + */ +} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/options/ClientOptions.java b/core/src/main/java/io/temporal/samples/nexusmessaging/options/ClientOptions.java new file mode 100644 index 00000000..60e88c0f --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/options/ClientOptions.java @@ -0,0 +1,137 @@ +package io.temporal.samples.nexusmessaging.options; + +import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts; +import io.grpc.netty.shaded.io.netty.handler.ssl.SslContextBuilder; +import io.grpc.netty.shaded.io.netty.handler.ssl.util.InsecureTrustManagerFactory; +import io.temporal.client.WorkflowClient; +import io.temporal.client.WorkflowClientOptions; +import io.temporal.serviceclient.WorkflowServiceStubs; +import io.temporal.serviceclient.WorkflowServiceStubsOptions; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import javax.net.ssl.SSLException; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; + +public class ClientOptions { + + public static WorkflowClient getWorkflowClient(String[] args) { + return getWorkflowClient(args, WorkflowClientOptions.newBuilder()); + } + + public static WorkflowClient getWorkflowClient( + String[] args, WorkflowClientOptions.Builder clientOptions) { + Options options = new Options(); + Option targetHostOption = new Option("target-host", true, "Host:port for the Temporal service"); + targetHostOption.setRequired(false); + options.addOption(targetHostOption); + + Option namespaceOption = new Option("namespace", true, "Namespace to connect to"); + namespaceOption.setRequired(false); + options.addOption(namespaceOption); + + Option serverRootCaOption = + new Option("server-root-ca-cert", true, "Optional path to root server CA cert"); + serverRootCaOption.setRequired(false); + options.addOption(serverRootCaOption); + + Option clientCertOption = + new Option( + "client-cert", true, "Optional path to client cert, mutually exclusive with API key"); + clientCertOption.setRequired(false); + options.addOption(clientCertOption); + + Option clientKeyOption = + new Option( + "client-key", true, "Optional path to client key, mutually exclusive with API key"); + clientKeyOption.setRequired(false); + options.addOption(clientKeyOption); + + Option apiKeyOption = + new Option("api-key", true, "Optional API key, mutually exclusive with cert/key"); + apiKeyOption.setRequired(false); + options.addOption(apiKeyOption); + + Option serverNameOption = + new Option( + "server-name", true, "Server name to use for verifying the server's certificate"); + serverNameOption.setRequired(false); + options.addOption(serverNameOption); + + Option insercureSkipVerifyOption = + new Option( + "insecure-skip-verify", + false, + "Skip verification of the server's certificate and host name"); + insercureSkipVerifyOption.setRequired(false); + options.addOption(insercureSkipVerifyOption); + + CommandLineParser parser = new DefaultParser(); + HelpFormatter formatter = new HelpFormatter(); + CommandLine cmd = null; + + try { + cmd = parser.parse(options, args); + } catch (ParseException e) { + System.out.println(e.getMessage()); + formatter.printHelp("utility-name", options); + + System.exit(1); + } + + String targetHost = cmd.getOptionValue("target-host", "localhost:7233"); + String namespace = cmd.getOptionValue("namespace", "default"); + String serverRootCaCert = cmd.getOptionValue("server-root-ca-cert", ""); + String clientCert = cmd.getOptionValue("client-cert", ""); + String clientKey = cmd.getOptionValue("client-key", ""); + String serverName = cmd.getOptionValue("server-name", ""); + boolean insecureSkipVerify = cmd.hasOption("insecure-skip-verify"); + String apiKey = cmd.getOptionValue("api-key", ""); + + // API key and client cert/key are mutually exclusive + if (!apiKey.isEmpty() && (!clientCert.isEmpty() || !clientKey.isEmpty())) { + throw new IllegalArgumentException("API key and client cert/key are mutually exclusive"); + } + WorkflowServiceStubsOptions.Builder serviceStubOptionsBuilder = + WorkflowServiceStubsOptions.newBuilder().setTarget(targetHost); + // Configure TLS if client cert and key are provided + if (!clientCert.isEmpty() || !clientKey.isEmpty()) { + if (clientCert.isEmpty() || clientKey.isEmpty()) { + throw new IllegalArgumentException("Both client-cert and client-key must be provided"); + } + try { + SslContextBuilder sslContext = + SslContextBuilder.forClient() + .keyManager(new FileInputStream(clientCert), new FileInputStream(clientKey)); + if (serverRootCaCert != null && !serverRootCaCert.isEmpty()) { + sslContext.trustManager(new FileInputStream(serverRootCaCert)); + } + if (insecureSkipVerify) { + sslContext.trustManager(InsecureTrustManagerFactory.INSTANCE); + } + serviceStubOptionsBuilder.setSslContext(GrpcSslContexts.configure(sslContext).build()); + } catch (SSLException e) { + throw new RuntimeException(e); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + if (serverName != null && !serverName.isEmpty()) { + serviceStubOptionsBuilder.setChannelInitializer(c -> c.overrideAuthority(serverName)); + } + } + // Configure API key if provided + if (!apiKey.isEmpty()) { + serviceStubOptionsBuilder.setEnableHttps(true); + serviceStubOptionsBuilder.addApiKey(() -> apiKey); + } + + WorkflowServiceStubs service = + WorkflowServiceStubs.newServiceStubs(serviceStubOptionsBuilder.build()); + return WorkflowClient.newInstance(service, clientOptions.setNamespace(namespace).build()); + } +} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/service/SampleNexusService.java b/core/src/main/java/io/temporal/samples/nexusmessaging/service/SampleNexusService.java new file mode 100644 index 00000000..edb72864 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/service/SampleNexusService.java @@ -0,0 +1,177 @@ +package io.temporal.samples.nexusmessaging.service; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.nexusrpc.Operation; +import io.nexusrpc.Service; + +@Service +public interface SampleNexusService { + + class SignalWorkflowInput { + private final String name; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public SignalWorkflowInput(@JsonProperty("name") String name) { + this.name = name; + } + + @JsonProperty("name") + public String getName() { + return name; + } + } + + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) + class SignalWorkflowOutput { + @JsonCreator + public SignalWorkflowOutput() {} + } + + class QueryWorkflowInput { + private final String name; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public QueryWorkflowInput(@JsonProperty("name") String name) { + this.name = name; + } + + @JsonProperty("name") + public String getName() { + return name; + } + } + + class QueryWorkflowRemoteStartInput { + private final String name; + private final String workflowId; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public QueryWorkflowRemoteStartInput( + @JsonProperty("name") String name, @JsonProperty("workflowId") String workflowId) { + this.name = name; + this.workflowId = workflowId; + } + + @JsonProperty("name") + public String getName() { + return name; + } + + @JsonProperty("workflowId") + public String getWorkflowId() { + return workflowId; + } + } + + class QueryWorkflowOutput { + private final String message; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public QueryWorkflowOutput(@JsonProperty("message") String message) { + this.message = message; + } + + @JsonProperty("message") + public String getMessage() { + return message; + } + } + + class UpdateWorkflowInput { + private final String name; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public UpdateWorkflowInput(@JsonProperty("name") String name) { + this.name = name; + } + + @JsonProperty("name") + public String getName() { + return name; + } + } + + class UpdateWorkflowOutput { + private final int result; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public UpdateWorkflowOutput(@JsonProperty("result") int result) { + this.result = result; + } + + @JsonProperty("result") + public int getResult() { + return result; + } + } + + class RunFromRemoteInput { + private final String workflowId; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public RunFromRemoteInput(@JsonProperty("workflowId") String workflowId) { + this.workflowId = workflowId; + } + + @JsonProperty("workflowId") + public String getWorkflowId() { + return workflowId; + } + } + + class RunFromRemoteOutput { + private final String message; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public RunFromRemoteOutput(@JsonProperty("message") String message) { + this.message = message; + } + + @JsonProperty("message") + public String getMessage() { + return message; + } + } + + @Operation + RunFromRemoteOutput runFromRemote(RunFromRemoteInput input); + + class SignalWorkflowRemoteStartInput { + private final String name; + private final String workflowId; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public SignalWorkflowRemoteStartInput( + @JsonProperty("name") String name, @JsonProperty("workflowId") String workflowId) { + this.name = name; + this.workflowId = workflowId; + } + + @JsonProperty("name") + public String getName() { + return name; + } + + @JsonProperty("workflowId") + public String getWorkflowId() { + return workflowId; + } + } + + @Operation + SignalWorkflowOutput signalWorkflow(SignalWorkflowInput input); + + @Operation + SignalWorkflowOutput signalWorkflowRemoteStart(SignalWorkflowRemoteStartInput input); + + @Operation + UpdateWorkflowOutput updateWorkflow(UpdateWorkflowInput input); + + @Operation + QueryWorkflowOutput queryWorkflow(QueryWorkflowInput input); + + @Operation + QueryWorkflowOutput queryWorkflowRemoteStart(QueryWorkflowRemoteStartInput input); +} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/service/description.md b/core/src/main/java/io/temporal/samples/nexusmessaging/service/description.md new file mode 100644 index 00000000..0d607d9a --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/service/description.md @@ -0,0 +1,6 @@ +## Service: [SampleNexusService](https://github.com/temporalio/samples-java/blob/main/core/src/main/java/io/temporal/samples/nexusmessaging/service/SampleNexusService.java) + - operation: `signalWorkflow` + - operation: `queryWorkflow` + - operation: `updateWorkflow` + +See https://github.com/temporalio/samples-java/blob/main/core/src/main/java/io/temporal/samples/nexusmessaging/service/SampleNexusService.java for Input / Output types. diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000..fb57ccd1 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + From 12ae22fdef59983fedfbb26cbe7d9b36808e99e6 Mon Sep 17 00:00:00 2001 From: Evan Reynolds Date: Wed, 8 Apr 2026 14:17:21 -0700 Subject: [PATCH 2/5] Updating --- .../samples/nexus_sync_operations/README.md | 121 ++++++++++++---- .../caller/CallerWorker.java | 2 +- .../caller/CallerWorkflowImpl.java | 17 +-- .../caller_remote/CallerRemoteStarter.java | 30 ++++ .../caller_remote/CallerRemoteWorker.java | 42 ++++++ .../caller_remote/CallerRemoteWorkflow.java | 11 ++ .../CallerRemoteWorkflowImpl.java | 74 ++++++++++ .../handler/GreetingWorkflow.java | 17 +-- .../handler/GreetingWorkflowImpl.java | 13 +- .../handler/HandlerWorker.java | 3 +- ...mpl.java => NexusGreetingServiceImpl.java} | 42 ++++-- .../NexusRemoteGreetingServiceImpl.java | 99 +++++++++++++ ...Service.java => NexusGreetingService.java} | 2 +- .../service/NexusRemoteGreetingService.java | 134 ++++++++++++++++++ node_modules/.yarn-integrity | 10 ++ 15 files changed, 549 insertions(+), 68 deletions(-) create mode 100644 core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteStarter.java create mode 100644 core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteWorker.java create mode 100644 core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteWorkflow.java create mode 100644 core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteWorkflowImpl.java rename core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/{GreetingServiceImpl.java => NexusGreetingServiceImpl.java} (51%) create mode 100644 core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/NexusRemoteGreetingServiceImpl.java rename core/src/main/java/io/temporal/samples/nexus_sync_operations/service/{GreetingService.java => NexusGreetingService.java} (98%) create mode 100644 core/src/main/java/io/temporal/samples/nexus_sync_operations/service/NexusRemoteGreetingService.java create mode 100644 node_modules/.yarn-integrity diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/README.md b/core/src/main/java/io/temporal/samples/nexus_sync_operations/README.md index 5d76d1fe..15dcf21a 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/README.md +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/README.md @@ -1,29 +1,30 @@ -This sample shows how to create a Nexus service that is backed by a long-running workflow and -exposes operations that execute updates and queries against that workflow. The long-running -workflow, and the updates/queries, are private implementation details of the Nexus service: the -caller does not know how the operations are implemented. +This sample shows how to expose a long-running workflow's queries, updates, and signals as Nexus +operations. The caller interacts only with the Nexus service; the workflow is a private +implementation detail. -This is a Java port of the -[nexus_sync_operations Python sample](https://github.com/temporalio/samples-python/tree/main/nexus_sync_operations). +There are **two caller patterns** that share the same handler workflow (`GreetingWorkflow`): -### Sample directory structure +| | `caller/` (entity pattern) | `caller_remote/` (remote-start pattern) | +|---|---|---| +| **Who creates the workflow?** | The handler worker starts it on boot | The caller starts it via a `runFromRemote` Nexus operation | +| **Who knows the workflow ID?** | Only the handler | The caller chooses and passes it in every operation | +| **Nexus service** | `NexusGreetingService` — inputs carry only business data | `NexusRemoteGreetingService` — every input includes a `workflowId` | +| **When to use** | Single shared entity; callers don't need lifecycle control | Caller needs to create and target specific workflow instances | -- [`service/GreetingService.java`](./service/GreetingService.java) — shared Nexus service definition with input/output types -- [`service/Language.java`](./service/Language.java) — shared language enum -- [`handler/`](./handler/) — Nexus operation handlers, the long-running entity workflow they back, an activity, and a handler worker -- [`caller/`](./caller/) — a caller workflow that executes Nexus operations, together with a worker and starter +### Directory structure -### Instructions +- `service/` — shared Nexus service definitions (`NexusGreetingService`, `NexusRemoteGreetingService`) and `Language` enum +- `handler/` — `GreetingWorkflow` and its implementation, `GreetingActivity`, both Nexus service impls (`NexusGreetingServiceImpl`, `NexusRemoteGreetingServiceImpl`), and the handler worker +- `caller/` — entity-pattern caller (workflow, worker, starter) +- `caller_remote/` — remote-start caller (workflow, worker, starter) -Start a Temporal server: +### Running + +Start a Temporal server and create namespaces/endpoint: ```bash temporal server start-dev -``` -Create the caller and handler namespaces and the Nexus endpoint: - -```bash temporal operator namespace create --namespace nexus-sync-operations-handler-namespace temporal operator namespace create --namespace nexus-sync-operations-caller-namespace @@ -33,13 +34,14 @@ temporal operator nexus endpoint create \ --target-task-queue nexus-sync-operations-handler-task-queue ``` -In one terminal, run the handler worker (starts the long-running entity workflow and polls for -Nexus, workflow, and activity tasks): +In one terminal, start the handler worker (shared by both patterns): ```bash ./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_sync_operations.handler.HandlerWorker ``` +#### Entity pattern + In a second terminal, run the caller worker: ```bash @@ -52,25 +54,82 @@ In a third terminal, start the caller workflow: ./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_sync_operations.caller.CallerStarter ``` -You should see output like: +Expected output: ``` supported languages: [CHINESE, ENGLISH] language changed: ENGLISH -> ARABIC +workflow approved +``` + +#### Remote-start pattern + +In a second terminal, run the remote caller worker: + +```bash +./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_sync_operations.caller_remote.CallerRemoteWorker +``` + +In a third terminal, start the remote caller workflow: + +```bash +./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_sync_operations.caller_remote.CallerRemoteStarter +``` + +Expected output: + +``` +started remote greeting workflow: nexus-sync-operations-remote-greeting-workflow +supported languages: [CHINESE, ENGLISH] +language changed: ENGLISH -> ARABIC +workflow approved +workflow result: مرحبا بالعالم ``` ### How it works -The handler starts a single long-running `GreetingWorkflow` entity workflow when the worker boots. -This workflow holds the current language and a map of greetings, and exposes: +#### The handler (shared by both patterns) + +`GreetingWorkflow` is a long-running "entity" workflow that holds the current language and a map of +greetings. It exposes its state through standard Temporal primitives: + +- `getLanguages` / `getLanguage` — `@QueryMethod`s for reading state +- `setLanguage` — an `@UpdateMethod` for switching between already-loaded languages +- `setLanguageUsingActivity` — an `@UpdateMethod` that calls an activity to fetch a greeting for + a language not yet in the map (uses `WorkflowLock` to serialize concurrent activity calls) +- `approve` — a `@SignalMethod` that lets the workflow complete + +The workflow waits until approved and all in-flight update handlers have finished, then returns the +greeting in the current language. + +Both Nexus service implementations translate incoming Nexus operations into calls against +`GreetingWorkflow` stubs — queries, updates, and signals. The caller never interacts with the +workflow directly. + +#### Entity pattern (`caller/` + `NexusGreetingService`) + +The handler worker starts a single `GreetingWorkflow` on boot with a fixed workflow ID. +`NexusGreetingServiceImpl` holds that workflow ID in its constructor and routes every operation to +it. The caller's inputs contain only business data (language, name), not workflow IDs. + +`CallerWorkflowImpl` creates a `NexusGreetingService` stub and: +1. Queries for supported languages (`getLanguages` — backed by a `@QueryMethod`) +2. Changes the language to Arabic (`setLanguage` — backed by an `@UpdateMethod` that calls an activity) +3. Confirms the change via a second query (`getLanguage`) +4. Approves the workflow (`approve` — backed by a `@SignalMethod`) + +#### Remote-start pattern (`caller_remote/` + `NexusRemoteGreetingService`) + +No workflow is pre-started. Instead, `NexusRemoteGreetingService` adds a `runFromRemote` operation +that starts a new `GreetingWorkflow` with a caller-chosen workflow ID using +`WorkflowRunOperation`. Every other operation also includes the `workflowId` in its input so that +`NexusRemoteGreetingServiceImpl` can look up the right workflow stub. -- `getLanguages` — a `@QueryMethod` listing supported languages -- `getLanguage` — a `@QueryMethod` returning the current language -- `setLanguage` — an `@UpdateMethod` (sync) for switching between already-loaded languages -- `setLanguageUsingActivity` — an `@UpdateMethod` (async) that calls an activity to fetch a - greeting for a new language before switching -- `approve` — a `@SignalMethod` that allows the workflow to complete +`CallerRemoteWorkflowImpl` creates a `NexusRemoteGreetingService` stub and: +1. Starts a remote `GreetingWorkflow` via `runFromRemote` and waits for it to be running +2. Queries, updates, and approves that workflow — same operations as the entity pattern, but each + input carries the workflow ID +3. Waits for the remote workflow to complete and returns its result (the greeting string) -The three `GreetingService` Nexus operations delegate to these workflow handlers via the Temporal -client inside each `OperationHandler.sync` implementation. The caller workflow sees only the Nexus -operations; the entity workflow is a private implementation detail. +This pattern is useful when the caller needs to control the lifecycle of individual workflow +instances rather than sharing a single entity. diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorker.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorker.java index 284bf9bd..083e7b14 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorker.java +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorker.java @@ -30,7 +30,7 @@ public static void main(String[] args) throws InterruptedException { WorkflowImplementationOptions.newBuilder() .setNexusServiceOptions( Collections.singletonMap( - "GreetingService", + "NexusGreetingService", NexusServiceOptions.newBuilder().setEndpoint(NEXUS_ENDPOINT).build())) .build(), CallerWorkflowImpl.class); diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorkflowImpl.java index 82e96c05..e639f54e 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorkflowImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorkflowImpl.java @@ -1,8 +1,8 @@ package io.temporal.samples.nexus_sync_operations.caller; import io.temporal.failure.ApplicationFailure; -import io.temporal.samples.nexus_sync_operations.service.GreetingService; import io.temporal.samples.nexus_sync_operations.service.Language; +import io.temporal.samples.nexus_sync_operations.service.NexusGreetingService; import io.temporal.workflow.NexusOperationOptions; import io.temporal.workflow.NexusServiceOptions; import io.temporal.workflow.Workflow; @@ -14,9 +14,9 @@ public class CallerWorkflowImpl implements CallerWorkflow { // The endpoint is configured at the worker level in CallerWorker; only operation options are // set here. - GreetingService greetingService = + NexusGreetingService greetingService = Workflow.newNexusServiceStub( - GreetingService.class, + NexusGreetingService.class, NexusServiceOptions.newBuilder() .setOperationOptions( NexusOperationOptions.newBuilder() @@ -29,16 +29,17 @@ public List run() { List log = new ArrayList<>(); // 👉 Call a Nexus operation backed by a query against the entity workflow. - GreetingService.GetLanguagesOutput languagesOutput = - greetingService.getLanguages(new GreetingService.GetLanguagesInput(false)); + NexusGreetingService.GetLanguagesOutput languagesOutput = + greetingService.getLanguages(new NexusGreetingService.GetLanguagesInput(false)); log.add("supported languages: " + languagesOutput.getLanguages()); // 👉 Call a Nexus operation backed by an update against the entity workflow. Language previousLanguage = - greetingService.setLanguage(new GreetingService.SetLanguageInput(Language.ARABIC)); + greetingService.setLanguage(new NexusGreetingService.SetLanguageInput(Language.ARABIC)); // 👉 Call a Nexus operation backed by a query to confirm the language change. - Language currentLanguage = greetingService.getLanguage(new GreetingService.GetLanguageInput()); + Language currentLanguage = + greetingService.getLanguage(new NexusGreetingService.GetLanguageInput()); if (currentLanguage != Language.ARABIC) { throw ApplicationFailure.newFailure( "expected language ARABIC, got " + currentLanguage, "AssertionError"); @@ -47,7 +48,7 @@ public List run() { log.add("language changed: " + previousLanguage.name() + " -> " + Language.ARABIC.name()); // 👉 Call a Nexus operation backed by a signal against the entity workflow. - greetingService.approve(new GreetingService.ApproveInput("caller")); + greetingService.approve(new NexusGreetingService.ApproveInput("caller")); log.add("workflow approved"); return log; diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteStarter.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteStarter.java new file mode 100644 index 00000000..4ca946ba --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteStarter.java @@ -0,0 +1,30 @@ +package io.temporal.samples.nexus_sync_operations.caller_remote; + +import io.temporal.client.WorkflowClient; +import io.temporal.client.WorkflowClientOptions; +import io.temporal.client.WorkflowOptions; +import io.temporal.serviceclient.WorkflowServiceStubs; +import java.util.List; +import java.util.UUID; + +public class CallerRemoteStarter { + + public static void main(String[] args) { + WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); + WorkflowClient client = + WorkflowClient.newInstance( + service, + WorkflowClientOptions.newBuilder().setNamespace(CallerRemoteWorker.NAMESPACE).build()); + + CallerRemoteWorkflow workflow = + client.newWorkflowStub( + CallerRemoteWorkflow.class, + WorkflowOptions.newBuilder() + .setWorkflowId("nexus-sync-operations-remote-caller-" + UUID.randomUUID()) + .setTaskQueue(CallerRemoteWorker.TASK_QUEUE) + .build()); + + List log = workflow.run(); + log.forEach(System.out::println); + } +} diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteWorker.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteWorker.java new file mode 100644 index 00000000..32073476 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteWorker.java @@ -0,0 +1,42 @@ +package io.temporal.samples.nexus_sync_operations.caller_remote; + +import io.temporal.client.WorkflowClient; +import io.temporal.client.WorkflowClientOptions; +import io.temporal.serviceclient.WorkflowServiceStubs; +import io.temporal.worker.Worker; +import io.temporal.worker.WorkerFactory; +import io.temporal.worker.WorkflowImplementationOptions; +import io.temporal.workflow.NexusServiceOptions; +import java.util.Collections; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CallerRemoteWorker { + private static final Logger logger = LoggerFactory.getLogger(CallerRemoteWorker.class); + + public static final String NAMESPACE = "nexus-sync-operations-caller-namespace"; + public static final String TASK_QUEUE = "nexus-sync-operations-caller-remote-task-queue"; + static final String NEXUS_ENDPOINT = "nexus-sync-operations-nexus-endpoint"; + + public static void main(String[] args) throws InterruptedException { + WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); + WorkflowClient client = + WorkflowClient.newInstance( + service, WorkflowClientOptions.newBuilder().setNamespace(NAMESPACE).build()); + + WorkerFactory factory = WorkerFactory.newInstance(client); + Worker worker = factory.newWorker(TASK_QUEUE); + worker.registerWorkflowImplementationTypes( + WorkflowImplementationOptions.newBuilder() + .setNexusServiceOptions( + Collections.singletonMap( + "NexusRemoteGreetingService", + NexusServiceOptions.newBuilder().setEndpoint(NEXUS_ENDPOINT).build())) + .build(), + CallerRemoteWorkflowImpl.class); + + factory.start(); + logger.info("Caller remote worker started, ctrl+c to exit"); + Thread.currentThread().join(); + } +} diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteWorkflow.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteWorkflow.java new file mode 100644 index 00000000..b6e49bf7 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteWorkflow.java @@ -0,0 +1,11 @@ +package io.temporal.samples.nexus_sync_operations.caller_remote; + +import io.temporal.workflow.WorkflowInterface; +import io.temporal.workflow.WorkflowMethod; +import java.util.List; + +@WorkflowInterface +public interface CallerRemoteWorkflow { + @WorkflowMethod + List run(); +} diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteWorkflowImpl.java new file mode 100644 index 00000000..08f29bd8 --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteWorkflowImpl.java @@ -0,0 +1,74 @@ +package io.temporal.samples.nexus_sync_operations.caller_remote; + +import io.temporal.samples.nexus_sync_operations.service.Language; +import io.temporal.samples.nexus_sync_operations.service.NexusGreetingService; +import io.temporal.samples.nexus_sync_operations.service.NexusRemoteGreetingService; +import io.temporal.workflow.NexusOperationHandle; +import io.temporal.workflow.NexusOperationOptions; +import io.temporal.workflow.NexusServiceOptions; +import io.temporal.workflow.Workflow; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CallerRemoteWorkflowImpl implements CallerRemoteWorkflow { + + private static final Logger logger = LoggerFactory.getLogger(CallerRemoteWorkflowImpl.class); + + private static final String REMOTE_WORKFLOW_ID = "nexus-sync-operations-remote-greeting-workflow"; + + NexusRemoteGreetingService greetingRemoteService = + Workflow.newNexusServiceStub( + NexusRemoteGreetingService.class, + NexusServiceOptions.newBuilder() + .setOperationOptions( + NexusOperationOptions.newBuilder() + .setScheduleToCloseTimeout(Duration.ofSeconds(10)) + .build()) + .build()); + + @Override + public List run() { + List log = new ArrayList<>(); + + // Start a new GreetingWorkflow on the handler side via Nexus. + NexusOperationHandle handle = + Workflow.startNexusOperation( + greetingRemoteService::runFromRemote, + new NexusRemoteGreetingService.RunFromRemoteInput(REMOTE_WORKFLOW_ID)); + // Wait for the operation to be started (workflow is now running on the handler). + handle.getExecution().get(); + log.add("started remote greeting workflow: " + REMOTE_WORKFLOW_ID); + + // Query the remote workflow for supported languages. + NexusGreetingService.GetLanguagesOutput languagesOutput = + greetingRemoteService.getLanguages( + new NexusRemoteGreetingService.GetLanguagesInput(false, REMOTE_WORKFLOW_ID)); + log.add("supported languages: " + languagesOutput.getLanguages()); + + // Update the language on the remote workflow. + Language previousLanguage = + greetingRemoteService.setLanguage( + new NexusRemoteGreetingService.SetLanguageInput(Language.ARABIC, REMOTE_WORKFLOW_ID)); + logger.info("Language changed from {}", previousLanguage); + + // Confirm the change by querying. + Language currentLanguage = + greetingRemoteService.getLanguage( + new NexusRemoteGreetingService.GetLanguageInput(REMOTE_WORKFLOW_ID)); + log.add("language changed: " + previousLanguage.name() + " -> " + currentLanguage.name()); + + // Approve the remote workflow so it can complete. + greetingRemoteService.approve( + new NexusRemoteGreetingService.ApproveInput("remote-caller", REMOTE_WORKFLOW_ID)); + log.add("workflow approved"); + + // Wait for the remote workflow to finish and return its result. + String result = handle.getResult().get(); + log.add("workflow result: " + result); + + return log; + } +} diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflow.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflow.java index 05b46828..8604c47d 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflow.java +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflow.java @@ -2,8 +2,8 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import io.temporal.samples.nexus_sync_operations.service.GreetingService; import io.temporal.samples.nexus_sync_operations.service.Language; +import io.temporal.samples.nexus_sync_operations.service.NexusGreetingService; import io.temporal.workflow.QueryMethod; import io.temporal.workflow.SignalMethod; import io.temporal.workflow.UpdateMethod; @@ -12,9 +12,9 @@ import io.temporal.workflow.WorkflowMethod; /** - * A long-running "entity" workflow that backs the GreetingService Nexus operations. The workflow - * exposes queries, an update, and a signal. These are private implementation details of the Nexus - * service: the caller only interacts via Nexus operations. + * A long-running "entity" workflow that backs the NexusGreetingService Nexus operations. The + * workflow exposes queries, an update, and a signal. These are private implementation details of + * the Nexus service: the caller only interacts via Nexus operations. */ @WorkflowInterface public interface GreetingWorkflow { @@ -38,7 +38,8 @@ public String getName() { // Returns the languages currently supported by the workflow. @QueryMethod - GreetingService.GetLanguagesOutput getLanguages(GreetingService.GetLanguagesInput input); + NexusGreetingService.GetLanguagesOutput getLanguages( + NexusGreetingService.GetLanguagesInput input); // Returns the currently active language. @QueryMethod @@ -51,12 +52,12 @@ public String getName() { // Changes the active language synchronously (only supports languages already in the greetings // map). @UpdateMethod - Language setLanguage(GreetingService.SetLanguageInput input); + Language setLanguage(NexusGreetingService.SetLanguageInput input); @UpdateValidatorMethod(updateName = "setLanguage") - void validateSetLanguage(GreetingService.SetLanguageInput input); + void validateSetLanguage(NexusGreetingService.SetLanguageInput input); // Changes the active language, calling an activity to fetch a greeting for new languages. @UpdateMethod - Language setLanguageUsingActivity(GreetingService.SetLanguageInput input); + Language setLanguageUsingActivity(NexusGreetingService.SetLanguageInput input); } diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflowImpl.java index 1f1e6af8..22d66c45 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflowImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflowImpl.java @@ -2,8 +2,8 @@ import io.temporal.activity.ActivityOptions; import io.temporal.failure.ApplicationFailure; -import io.temporal.samples.nexus_sync_operations.service.GreetingService; import io.temporal.samples.nexus_sync_operations.service.Language; +import io.temporal.samples.nexus_sync_operations.service.NexusGreetingService; import io.temporal.workflow.Workflow; import io.temporal.workflow.WorkflowLock; import java.time.Duration; @@ -46,7 +46,8 @@ public String run() { } @Override - public GreetingService.GetLanguagesOutput getLanguages(GreetingService.GetLanguagesInput input) { + public NexusGreetingService.GetLanguagesOutput getLanguages( + NexusGreetingService.GetLanguagesInput input) { List result; if (input.isIncludeUnsupported()) { result = new ArrayList<>(Arrays.asList(Language.values())); @@ -54,7 +55,7 @@ public GreetingService.GetLanguagesOutput getLanguages(GreetingService.GetLangua result = new ArrayList<>(greetings.keySet()); } Collections.sort(result); - return new GreetingService.GetLanguagesOutput(result); + return new NexusGreetingService.GetLanguagesOutput(result); } @Override @@ -69,7 +70,7 @@ public void approve(ApproveInput input) { } @Override - public Language setLanguage(GreetingService.SetLanguageInput input) { + public Language setLanguage(NexusGreetingService.SetLanguageInput input) { logger.info("setLanguage update received"); Language previous = language; language = input.getLanguage(); @@ -77,7 +78,7 @@ public Language setLanguage(GreetingService.SetLanguageInput input) { } @Override - public void validateSetLanguage(GreetingService.SetLanguageInput input) { + public void validateSetLanguage(NexusGreetingService.SetLanguageInput input) { logger.info("validateSetLanguage called"); if (!greetings.containsKey(input.getLanguage())) { throw new IllegalArgumentException(input.getLanguage().name() + " is not supported"); @@ -85,7 +86,7 @@ public void validateSetLanguage(GreetingService.SetLanguageInput input) { } @Override - public Language setLanguageUsingActivity(GreetingService.SetLanguageInput input) { + public Language setLanguageUsingActivity(NexusGreetingService.SetLanguageInput input) { if (!greetings.containsKey(input.getLanguage())) { // Use a lock so that if this handler is called concurrently, each call executes its activity // only after the previous one has completed. This ensures updates are processed in order. diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/HandlerWorker.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/HandlerWorker.java index 05851047..85dbc6f1 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/HandlerWorker.java +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/HandlerWorker.java @@ -42,7 +42,8 @@ public static void main(String[] args) throws InterruptedException { Worker worker = factory.newWorker(TASK_QUEUE); worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class); worker.registerActivitiesImplementations(new GreetingActivityImpl()); - worker.registerNexusServiceImplementation(new GreetingServiceImpl(WORKFLOW_ID)); + worker.registerNexusServiceImplementation(new NexusGreetingServiceImpl(WORKFLOW_ID)); + worker.registerNexusServiceImplementation(new NexusRemoteGreetingServiceImpl()); factory.start(); logger.info("Handler worker started, ctrl+c to exit"); diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingServiceImpl.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/NexusGreetingServiceImpl.java similarity index 51% rename from core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingServiceImpl.java rename to core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/NexusGreetingServiceImpl.java index 726ae5b2..f88839f3 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingServiceImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/NexusGreetingServiceImpl.java @@ -4,20 +4,24 @@ import io.nexusrpc.handler.OperationImpl; import io.nexusrpc.handler.ServiceImpl; import io.temporal.nexus.Nexus; -import io.temporal.samples.nexus_sync_operations.service.GreetingService; import io.temporal.samples.nexus_sync_operations.service.Language; +import io.temporal.samples.nexus_sync_operations.service.NexusGreetingService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Nexus operation handler implementation. Each operation is backed by the long-running * GreetingWorkflow entity. The operations are synchronous (sync_operation) because queries and * updates against a running workflow complete quickly. */ -@ServiceImpl(service = GreetingService.class) -public class GreetingServiceImpl { +@ServiceImpl(service = NexusGreetingService.class) +public class NexusGreetingServiceImpl { + + private static final Logger logger = LoggerFactory.getLogger(NexusGreetingServiceImpl.class); private final String workflowId; - public GreetingServiceImpl(String workflowId) { + public NexusGreetingServiceImpl(String workflowId) { this.workflowId = workflowId; } @@ -29,33 +33,47 @@ private GreetingWorkflow getWorkflowStub() { // 👉 Backed by a query against the long-running entity workflow. @OperationImpl - public OperationHandler + public OperationHandler< + NexusGreetingService.GetLanguagesInput, NexusGreetingService.GetLanguagesOutput> getLanguages() { - return OperationHandler.sync((ctx, details, input) -> getWorkflowStub().getLanguages(input)); + return OperationHandler.sync( + (ctx, details, input) -> { + logger.info("Query for GetLanguages was received"); + return getWorkflowStub().getLanguages(input); + }); } // 👉 Backed by a query against the long-running entity workflow. @OperationImpl - public OperationHandler getLanguage() { - return OperationHandler.sync((ctx, details, input) -> getWorkflowStub().getLanguage()); + public OperationHandler getLanguage() { + return OperationHandler.sync( + (ctx, details, input) -> { + logger.info("Query for GetLanguage was received"); + return getWorkflowStub().getLanguage(); + }); } // 👉 Backed by an update against the long-running entity workflow. Although updates can run for // an arbitrarily long time, when exposed via a sync Nexus operation the update should complete // quickly (sync operations must finish in under 10s). @OperationImpl - public OperationHandler setLanguage() { + public OperationHandler setLanguage() { return OperationHandler.sync( - (ctx, details, input) -> getWorkflowStub().setLanguageUsingActivity(input)); + (ctx, details, input) -> { + logger.info("Update for SetLanguage was received"); + return getWorkflowStub().setLanguageUsingActivity(input); + }); } // 👉 Backed by a signal against the long-running entity workflow. @OperationImpl - public OperationHandler approve() { + public OperationHandler + approve() { return OperationHandler.sync( (ctx, details, input) -> { + logger.info("Signal for Approve was received"); getWorkflowStub().approve(new GreetingWorkflow.ApproveInput(input.getName())); - return new GreetingService.ApproveOutput(); + return new NexusGreetingService.ApproveOutput(); }); } } diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/NexusRemoteGreetingServiceImpl.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/NexusRemoteGreetingServiceImpl.java new file mode 100644 index 00000000..0358f58d --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/NexusRemoteGreetingServiceImpl.java @@ -0,0 +1,99 @@ +package io.temporal.samples.nexus_sync_operations.handler; + +import io.nexusrpc.handler.OperationHandler; +import io.nexusrpc.handler.OperationImpl; +import io.nexusrpc.handler.ServiceImpl; +import io.temporal.client.WorkflowOptions; +import io.temporal.nexus.Nexus; +import io.temporal.nexus.WorkflowHandle; +import io.temporal.nexus.WorkflowRunOperation; +import io.temporal.samples.nexus_sync_operations.service.Language; +import io.temporal.samples.nexus_sync_operations.service.NexusGreetingService; +import io.temporal.samples.nexus_sync_operations.service.NexusRemoteGreetingService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Nexus operation handler for the remote-start pattern. Unlike {@link NexusGreetingServiceImpl}, + * this implementation does not hold a fixed workflow ID. Instead, each operation receives the + * target workflow ID in its input, and {@code runFromRemote} starts a brand-new GreetingWorkflow. + */ +@ServiceImpl(service = NexusRemoteGreetingService.class) +public class NexusRemoteGreetingServiceImpl { + + private static final Logger logger = + LoggerFactory.getLogger(NexusRemoteGreetingServiceImpl.class); + + private GreetingWorkflow getWorkflowStub(String workflowId) { + return Nexus.getOperationContext() + .getWorkflowClient() + .newWorkflowStub(GreetingWorkflow.class, workflowId); + } + + // Starts a new GreetingWorkflow with the caller-specified workflow ID. This is an async + // Nexus operation backed by WorkflowRunOperation. + @OperationImpl + public OperationHandler runFromRemote() { + return WorkflowRunOperation.fromWorkflowHandle( + (ctx, details, input) -> { + logger.info("RunFromRemote was received for workflow {}", input.getWorkflowId()); + return WorkflowHandle.fromWorkflowMethod( + Nexus.getOperationContext() + .getWorkflowClient() + .newWorkflowStub( + GreetingWorkflow.class, + WorkflowOptions.newBuilder() + .setWorkflowId(input.getWorkflowId()) + .setTaskQueue(HandlerWorker.TASK_QUEUE) + .build()) + ::run); + }); + } + + @OperationImpl + public OperationHandler< + NexusRemoteGreetingService.GetLanguagesInput, NexusGreetingService.GetLanguagesOutput> + getLanguages() { + return OperationHandler.sync( + (ctx, details, input) -> { + logger.info("Query for GetLanguages was received for workflow {}", input.getWorkflowId()); + return getWorkflowStub(input.getWorkflowId()) + .getLanguages( + new NexusGreetingService.GetLanguagesInput(input.isIncludeUnsupported())); + }); + } + + @OperationImpl + public OperationHandler getLanguage() { + return OperationHandler.sync( + (ctx, details, input) -> { + logger.info("Query for GetLanguage was received for workflow {}", input.getWorkflowId()); + return getWorkflowStub(input.getWorkflowId()).getLanguage(); + }); + } + + // Uses setLanguageUsingActivity so that new languages are fetched via an activity. + @OperationImpl + public OperationHandler setLanguage() { + return OperationHandler.sync( + (ctx, details, input) -> { + logger.info("Update for SetLanguage was received for workflow {}", input.getWorkflowId()); + return getWorkflowStub(input.getWorkflowId()) + .setLanguageUsingActivity( + new NexusGreetingService.SetLanguageInput(input.getLanguage())); + }); + } + + @OperationImpl + public OperationHandler< + NexusRemoteGreetingService.ApproveInput, NexusGreetingService.ApproveOutput> + approve() { + return OperationHandler.sync( + (ctx, details, input) -> { + logger.info("Signal for Approve was received for workflow {}", input.getWorkflowId()); + getWorkflowStub(input.getWorkflowId()) + .approve(new GreetingWorkflow.ApproveInput(input.getName())); + return new NexusGreetingService.ApproveOutput(); + }); + } +} diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/service/GreetingService.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/service/NexusGreetingService.java similarity index 98% rename from core/src/main/java/io/temporal/samples/nexus_sync_operations/service/GreetingService.java rename to core/src/main/java/io/temporal/samples/nexus_sync_operations/service/NexusGreetingService.java index 407d309d..69f1d853 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/service/GreetingService.java +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/service/NexusGreetingService.java @@ -12,7 +12,7 @@ * type-safe Nexus client stub; the handler implements the operations. */ @Service -public interface GreetingService { +public interface NexusGreetingService { class GetLanguagesInput { private final boolean includeUnsupported; diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/service/NexusRemoteGreetingService.java b/core/src/main/java/io/temporal/samples/nexus_sync_operations/service/NexusRemoteGreetingService.java new file mode 100644 index 00000000..4841ae0a --- /dev/null +++ b/core/src/main/java/io/temporal/samples/nexus_sync_operations/service/NexusRemoteGreetingService.java @@ -0,0 +1,134 @@ +package io.temporal.samples.nexus_sync_operations.service; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.nexusrpc.Operation; +import io.nexusrpc.Service; + +/** + * Nexus service definition for the remote-start pattern. Unlike {@link NexusGreetingService}, every + * operation includes a {@code workflowId} so the caller controls which workflow instance is + * targeted. This also exposes a {@code runFromRemote} operation that starts a new GreetingWorkflow. + */ +@Service +public interface NexusRemoteGreetingService { + + class RunFromRemoteInput { + private final String workflowId; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public RunFromRemoteInput(@JsonProperty("workflowId") String workflowId) { + this.workflowId = workflowId; + } + + @JsonProperty("workflowId") + public String getWorkflowId() { + return workflowId; + } + } + + class GetLanguagesInput { + private final boolean includeUnsupported; + private final String workflowId; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public GetLanguagesInput( + @JsonProperty("includeUnsupported") boolean includeUnsupported, + @JsonProperty("workflowId") String workflowId) { + this.includeUnsupported = includeUnsupported; + this.workflowId = workflowId; + } + + @JsonProperty("includeUnsupported") + public boolean isIncludeUnsupported() { + return includeUnsupported; + } + + @JsonProperty("workflowId") + public String getWorkflowId() { + return workflowId; + } + } + + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) + class GetLanguageInput { + private final String workflowId; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public GetLanguageInput(@JsonProperty("workflowId") String workflowId) { + this.workflowId = workflowId; + } + + @JsonProperty("workflowId") + public String getWorkflowId() { + return workflowId; + } + } + + class SetLanguageInput { + private final Language language; + private final String workflowId; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public SetLanguageInput( + @JsonProperty("language") Language language, + @JsonProperty("workflowId") String workflowId) { + this.language = language; + this.workflowId = workflowId; + } + + @JsonProperty("language") + public Language getLanguage() { + return language; + } + + @JsonProperty("workflowId") + public String getWorkflowId() { + return workflowId; + } + } + + class ApproveInput { + private final String name; + private final String workflowId; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public ApproveInput( + @JsonProperty("name") String name, @JsonProperty("workflowId") String workflowId) { + this.name = name; + this.workflowId = workflowId; + } + + @JsonProperty("name") + public String getName() { + return name; + } + + @JsonProperty("workflowId") + public String getWorkflowId() { + return workflowId; + } + } + + // Starts a new GreetingWorkflow with the given workflow ID. This is an asynchronous Nexus + // operation: the caller receives a handle and can wait for the workflow to complete. + @Operation + String runFromRemote(RunFromRemoteInput input); + + // Returns the languages supported by the specified workflow. + @Operation + NexusGreetingService.GetLanguagesOutput getLanguages(GetLanguagesInput input); + + // Returns the currently active language of the specified workflow. + @Operation + Language getLanguage(GetLanguageInput input); + + // Changes the active language on the specified workflow, returning the previous one. + @Operation + Language setLanguage(SetLanguageInput input); + + // Approves the specified workflow, allowing it to complete. + @Operation + NexusGreetingService.ApproveOutput approve(ApproveInput input); +} diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity new file mode 100644 index 00000000..044a5ddd --- /dev/null +++ b/node_modules/.yarn-integrity @@ -0,0 +1,10 @@ +{ + "systemParams": "darwin-arm64-115", + "modulesFolders": [], + "flags": [], + "linkedModules": [], + "topLevelPatterns": [], + "lockfileEntries": {}, + "files": [], + "artifacts": {} +} \ No newline at end of file From 85bec3e928e87a4752bdfce77714f685515859f2 Mon Sep 17 00:00:00 2001 From: Evan Reynolds Date: Wed, 8 Apr 2026 14:18:17 -0700 Subject: [PATCH 3/5] Moving packages --- .../README.md | 0 .../caller/CallerStarter.java | 2 +- .../caller/CallerWorker.java | 2 +- .../caller/CallerWorkflow.java | 2 +- .../caller/CallerWorkflowImpl.java | 6 +- .../caller_remote/CallerRemoteStarter.java | 2 +- .../caller_remote/CallerRemoteWorker.java | 2 +- .../caller_remote/CallerRemoteWorkflow.java | 2 +- .../CallerRemoteWorkflowImpl.java | 8 +- .../handler/GreetingActivity.java | 4 +- .../handler/GreetingActivityImpl.java | 4 +- .../handler/GreetingWorkflow.java | 6 +- .../handler/GreetingWorkflowImpl.java | 6 +- .../handler/HandlerWorker.java | 2 +- .../handler/NexusGreetingServiceImpl.java | 6 +- .../NexusRemoteGreetingServiceImpl.java | 8 +- .../service/Language.java | 2 +- .../service/NexusGreetingService.java | 2 +- .../service/NexusRemoteGreetingService.java | 2 +- .../temporal/samples/nexusmessaging/README.MD | 102 ---------- .../nexusmessaging/caller/CallerStarter.java | 32 ---- .../nexusmessaging/caller/CallerWorker.java | 32 ---- .../MessageCallerStartHandlerWorkflow.java | 10 - ...MessageCallerStartHandlerWorkflowImpl.java | 69 ------- .../caller/MessageCallerWorkflow.java | 10 - .../caller/MessageCallerWorkflowImpl.java | 42 ----- .../nexusmessaging/handler/HandlerWorker.java | 62 ------ .../handler/MessageHandlerRemoteWorkflow.java | 29 --- .../MessageHandlerRemoteWorkflowImpl.java | 51 ----- .../handler/MessageHandlerWorkflow.java | 29 --- .../handler/MessageHandlerWorkflowImpl.java | 47 ----- .../handler/SampleNexusServiceImpl.java | 129 ------------- .../nexusmessaging/options/ClientOptions.java | 137 -------------- .../service/SampleNexusService.java | 177 ------------------ .../nexusmessaging/service/description.md | 6 - 35 files changed, 34 insertions(+), 998 deletions(-) rename core/src/main/java/io/temporal/samples/{nexus_sync_operations => nexus_messaging}/README.md (100%) rename core/src/main/java/io/temporal/samples/{nexus_sync_operations => nexus_messaging}/caller/CallerStarter.java (94%) rename core/src/main/java/io/temporal/samples/{nexus_sync_operations => nexus_messaging}/caller/CallerWorker.java (96%) rename core/src/main/java/io/temporal/samples/{nexus_sync_operations => nexus_messaging}/caller/CallerWorkflow.java (78%) rename core/src/main/java/io/temporal/samples/{nexus_sync_operations => nexus_messaging}/caller/CallerWorkflowImpl.java (91%) rename core/src/main/java/io/temporal/samples/{nexus_sync_operations => nexus_messaging}/caller_remote/CallerRemoteStarter.java (93%) rename core/src/main/java/io/temporal/samples/{nexus_sync_operations => nexus_messaging}/caller_remote/CallerRemoteWorker.java (96%) rename core/src/main/java/io/temporal/samples/{nexus_sync_operations => nexus_messaging}/caller_remote/CallerRemoteWorkflow.java (76%) rename core/src/main/java/io/temporal/samples/{nexus_sync_operations => nexus_messaging}/caller_remote/CallerRemoteWorkflowImpl.java (91%) rename core/src/main/java/io/temporal/samples/{nexus_sync_operations => nexus_messaging}/handler/GreetingActivity.java (71%) rename core/src/main/java/io/temporal/samples/{nexus_sync_operations => nexus_messaging}/handler/GreetingActivityImpl.java (85%) rename core/src/main/java/io/temporal/samples/{nexus_sync_operations => nexus_messaging}/handler/GreetingWorkflow.java (90%) rename core/src/main/java/io/temporal/samples/{nexus_sync_operations => nexus_messaging}/handler/GreetingWorkflowImpl.java (94%) rename core/src/main/java/io/temporal/samples/{nexus_sync_operations => nexus_messaging}/handler/HandlerWorker.java (97%) rename core/src/main/java/io/temporal/samples/{nexus_sync_operations => nexus_messaging}/handler/NexusGreetingServiceImpl.java (93%) rename core/src/main/java/io/temporal/samples/{nexus_sync_operations => nexus_messaging}/handler/NexusRemoteGreetingServiceImpl.java (93%) rename core/src/main/java/io/temporal/samples/{nexus_sync_operations => nexus_messaging}/service/Language.java (63%) rename core/src/main/java/io/temporal/samples/{nexus_sync_operations => nexus_messaging}/service/NexusGreetingService.java (97%) rename core/src/main/java/io/temporal/samples/{nexus_sync_operations => nexus_messaging}/service/NexusRemoteGreetingService.java (98%) delete mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/README.MD delete mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/caller/CallerStarter.java delete mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/caller/CallerWorker.java delete mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerStartHandlerWorkflow.java delete mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerStartHandlerWorkflowImpl.java delete mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerWorkflow.java delete mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerWorkflowImpl.java delete mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/handler/HandlerWorker.java delete mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerRemoteWorkflow.java delete mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerRemoteWorkflowImpl.java delete mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerWorkflow.java delete mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerWorkflowImpl.java delete mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/handler/SampleNexusServiceImpl.java delete mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/options/ClientOptions.java delete mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/service/SampleNexusService.java delete mode 100644 core/src/main/java/io/temporal/samples/nexusmessaging/service/description.md diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/README.md b/core/src/main/java/io/temporal/samples/nexus_messaging/README.md similarity index 100% rename from core/src/main/java/io/temporal/samples/nexus_sync_operations/README.md rename to core/src/main/java/io/temporal/samples/nexus_messaging/README.md diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerStarter.java b/core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerStarter.java similarity index 94% rename from core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerStarter.java rename to core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerStarter.java index 48c3758d..bb4163da 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerStarter.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerStarter.java @@ -1,4 +1,4 @@ -package io.temporal.samples.nexus_sync_operations.caller; +package io.temporal.samples.nexus_messaging.caller; import io.temporal.client.WorkflowClient; import io.temporal.client.WorkflowClientOptions; diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorker.java b/core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerWorker.java similarity index 96% rename from core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorker.java rename to core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerWorker.java index 083e7b14..1ac68d76 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorker.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerWorker.java @@ -1,4 +1,4 @@ -package io.temporal.samples.nexus_sync_operations.caller; +package io.temporal.samples.nexus_messaging.caller; import io.temporal.client.WorkflowClient; import io.temporal.client.WorkflowClientOptions; diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorkflow.java b/core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerWorkflow.java similarity index 78% rename from core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorkflow.java rename to core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerWorkflow.java index 1061f09a..6c830d7d 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorkflow.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerWorkflow.java @@ -1,4 +1,4 @@ -package io.temporal.samples.nexus_sync_operations.caller; +package io.temporal.samples.nexus_messaging.caller; import io.temporal.workflow.WorkflowInterface; import io.temporal.workflow.WorkflowMethod; diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerWorkflowImpl.java similarity index 91% rename from core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorkflowImpl.java rename to core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerWorkflowImpl.java index e639f54e..5a37caf0 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller/CallerWorkflowImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerWorkflowImpl.java @@ -1,8 +1,8 @@ -package io.temporal.samples.nexus_sync_operations.caller; +package io.temporal.samples.nexus_messaging.caller; import io.temporal.failure.ApplicationFailure; -import io.temporal.samples.nexus_sync_operations.service.Language; -import io.temporal.samples.nexus_sync_operations.service.NexusGreetingService; +import io.temporal.samples.nexus_messaging.service.Language; +import io.temporal.samples.nexus_messaging.service.NexusGreetingService; import io.temporal.workflow.NexusOperationOptions; import io.temporal.workflow.NexusServiceOptions; import io.temporal.workflow.Workflow; diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteStarter.java b/core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteStarter.java similarity index 93% rename from core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteStarter.java rename to core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteStarter.java index 4ca946ba..c255f288 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteStarter.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteStarter.java @@ -1,4 +1,4 @@ -package io.temporal.samples.nexus_sync_operations.caller_remote; +package io.temporal.samples.nexus_messaging.caller_remote; import io.temporal.client.WorkflowClient; import io.temporal.client.WorkflowClientOptions; diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteWorker.java b/core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteWorker.java similarity index 96% rename from core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteWorker.java rename to core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteWorker.java index 32073476..1e89c59b 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteWorker.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteWorker.java @@ -1,4 +1,4 @@ -package io.temporal.samples.nexus_sync_operations.caller_remote; +package io.temporal.samples.nexus_messaging.caller_remote; import io.temporal.client.WorkflowClient; import io.temporal.client.WorkflowClientOptions; diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteWorkflow.java b/core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteWorkflow.java similarity index 76% rename from core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteWorkflow.java rename to core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteWorkflow.java index b6e49bf7..bd72d4ac 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteWorkflow.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteWorkflow.java @@ -1,4 +1,4 @@ -package io.temporal.samples.nexus_sync_operations.caller_remote; +package io.temporal.samples.nexus_messaging.caller_remote; import io.temporal.workflow.WorkflowInterface; import io.temporal.workflow.WorkflowMethod; diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteWorkflowImpl.java similarity index 91% rename from core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteWorkflowImpl.java rename to core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteWorkflowImpl.java index 08f29bd8..8477c10e 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/caller_remote/CallerRemoteWorkflowImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteWorkflowImpl.java @@ -1,8 +1,8 @@ -package io.temporal.samples.nexus_sync_operations.caller_remote; +package io.temporal.samples.nexus_messaging.caller_remote; -import io.temporal.samples.nexus_sync_operations.service.Language; -import io.temporal.samples.nexus_sync_operations.service.NexusGreetingService; -import io.temporal.samples.nexus_sync_operations.service.NexusRemoteGreetingService; +import io.temporal.samples.nexus_messaging.service.Language; +import io.temporal.samples.nexus_messaging.service.NexusGreetingService; +import io.temporal.samples.nexus_messaging.service.NexusRemoteGreetingService; import io.temporal.workflow.NexusOperationHandle; import io.temporal.workflow.NexusOperationOptions; import io.temporal.workflow.NexusServiceOptions; diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingActivity.java b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/GreetingActivity.java similarity index 71% rename from core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingActivity.java rename to core/src/main/java/io/temporal/samples/nexus_messaging/handler/GreetingActivity.java index ed150780..83f97d1f 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingActivity.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/GreetingActivity.java @@ -1,8 +1,8 @@ -package io.temporal.samples.nexus_sync_operations.handler; +package io.temporal.samples.nexus_messaging.handler; import io.temporal.activity.ActivityInterface; import io.temporal.activity.ActivityMethod; -import io.temporal.samples.nexus_sync_operations.service.Language; +import io.temporal.samples.nexus_messaging.service.Language; @ActivityInterface public interface GreetingActivity { diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingActivityImpl.java b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/GreetingActivityImpl.java similarity index 85% rename from core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingActivityImpl.java rename to core/src/main/java/io/temporal/samples/nexus_messaging/handler/GreetingActivityImpl.java index b7ab336f..e7927eba 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingActivityImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/GreetingActivityImpl.java @@ -1,6 +1,6 @@ -package io.temporal.samples.nexus_sync_operations.handler; +package io.temporal.samples.nexus_messaging.handler; -import io.temporal.samples.nexus_sync_operations.service.Language; +import io.temporal.samples.nexus_messaging.service.Language; import java.util.EnumMap; import java.util.Map; diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflow.java b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/GreetingWorkflow.java similarity index 90% rename from core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflow.java rename to core/src/main/java/io/temporal/samples/nexus_messaging/handler/GreetingWorkflow.java index 8604c47d..786309f3 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflow.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/GreetingWorkflow.java @@ -1,9 +1,9 @@ -package io.temporal.samples.nexus_sync_operations.handler; +package io.temporal.samples.nexus_messaging.handler; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import io.temporal.samples.nexus_sync_operations.service.Language; -import io.temporal.samples.nexus_sync_operations.service.NexusGreetingService; +import io.temporal.samples.nexus_messaging.service.Language; +import io.temporal.samples.nexus_messaging.service.NexusGreetingService; import io.temporal.workflow.QueryMethod; import io.temporal.workflow.SignalMethod; import io.temporal.workflow.UpdateMethod; diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/GreetingWorkflowImpl.java similarity index 94% rename from core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflowImpl.java rename to core/src/main/java/io/temporal/samples/nexus_messaging/handler/GreetingWorkflowImpl.java index 22d66c45..c7140e60 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/GreetingWorkflowImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/GreetingWorkflowImpl.java @@ -1,9 +1,9 @@ -package io.temporal.samples.nexus_sync_operations.handler; +package io.temporal.samples.nexus_messaging.handler; import io.temporal.activity.ActivityOptions; import io.temporal.failure.ApplicationFailure; -import io.temporal.samples.nexus_sync_operations.service.Language; -import io.temporal.samples.nexus_sync_operations.service.NexusGreetingService; +import io.temporal.samples.nexus_messaging.service.Language; +import io.temporal.samples.nexus_messaging.service.NexusGreetingService; import io.temporal.workflow.Workflow; import io.temporal.workflow.WorkflowLock; import java.time.Duration; diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/HandlerWorker.java b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/HandlerWorker.java similarity index 97% rename from core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/HandlerWorker.java rename to core/src/main/java/io/temporal/samples/nexus_messaging/handler/HandlerWorker.java index 85dbc6f1..ea219b0c 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/HandlerWorker.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/HandlerWorker.java @@ -1,4 +1,4 @@ -package io.temporal.samples.nexus_sync_operations.handler; +package io.temporal.samples.nexus_messaging.handler; import io.temporal.client.WorkflowClient; import io.temporal.client.WorkflowClientOptions; diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/NexusGreetingServiceImpl.java b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/NexusGreetingServiceImpl.java similarity index 93% rename from core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/NexusGreetingServiceImpl.java rename to core/src/main/java/io/temporal/samples/nexus_messaging/handler/NexusGreetingServiceImpl.java index f88839f3..afe9075e 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/NexusGreetingServiceImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/NexusGreetingServiceImpl.java @@ -1,11 +1,11 @@ -package io.temporal.samples.nexus_sync_operations.handler; +package io.temporal.samples.nexus_messaging.handler; import io.nexusrpc.handler.OperationHandler; import io.nexusrpc.handler.OperationImpl; import io.nexusrpc.handler.ServiceImpl; import io.temporal.nexus.Nexus; -import io.temporal.samples.nexus_sync_operations.service.Language; -import io.temporal.samples.nexus_sync_operations.service.NexusGreetingService; +import io.temporal.samples.nexus_messaging.service.Language; +import io.temporal.samples.nexus_messaging.service.NexusGreetingService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/NexusRemoteGreetingServiceImpl.java b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/NexusRemoteGreetingServiceImpl.java similarity index 93% rename from core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/NexusRemoteGreetingServiceImpl.java rename to core/src/main/java/io/temporal/samples/nexus_messaging/handler/NexusRemoteGreetingServiceImpl.java index 0358f58d..1deecf60 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/handler/NexusRemoteGreetingServiceImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/NexusRemoteGreetingServiceImpl.java @@ -1,4 +1,4 @@ -package io.temporal.samples.nexus_sync_operations.handler; +package io.temporal.samples.nexus_messaging.handler; import io.nexusrpc.handler.OperationHandler; import io.nexusrpc.handler.OperationImpl; @@ -7,9 +7,9 @@ import io.temporal.nexus.Nexus; import io.temporal.nexus.WorkflowHandle; import io.temporal.nexus.WorkflowRunOperation; -import io.temporal.samples.nexus_sync_operations.service.Language; -import io.temporal.samples.nexus_sync_operations.service.NexusGreetingService; -import io.temporal.samples.nexus_sync_operations.service.NexusRemoteGreetingService; +import io.temporal.samples.nexus_messaging.service.Language; +import io.temporal.samples.nexus_messaging.service.NexusGreetingService; +import io.temporal.samples.nexus_messaging.service.NexusRemoteGreetingService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/service/Language.java b/core/src/main/java/io/temporal/samples/nexus_messaging/service/Language.java similarity index 63% rename from core/src/main/java/io/temporal/samples/nexus_sync_operations/service/Language.java rename to core/src/main/java/io/temporal/samples/nexus_messaging/service/Language.java index 5bc9f0dd..3c81c7c5 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/service/Language.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/service/Language.java @@ -1,4 +1,4 @@ -package io.temporal.samples.nexus_sync_operations.service; +package io.temporal.samples.nexus_messaging.service; public enum Language { ARABIC, diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/service/NexusGreetingService.java b/core/src/main/java/io/temporal/samples/nexus_messaging/service/NexusGreetingService.java similarity index 97% rename from core/src/main/java/io/temporal/samples/nexus_sync_operations/service/NexusGreetingService.java rename to core/src/main/java/io/temporal/samples/nexus_messaging/service/NexusGreetingService.java index 69f1d853..841efb76 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/service/NexusGreetingService.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/service/NexusGreetingService.java @@ -1,4 +1,4 @@ -package io.temporal.samples.nexus_sync_operations.service; +package io.temporal.samples.nexus_messaging.service; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonCreator; diff --git a/core/src/main/java/io/temporal/samples/nexus_sync_operations/service/NexusRemoteGreetingService.java b/core/src/main/java/io/temporal/samples/nexus_messaging/service/NexusRemoteGreetingService.java similarity index 98% rename from core/src/main/java/io/temporal/samples/nexus_sync_operations/service/NexusRemoteGreetingService.java rename to core/src/main/java/io/temporal/samples/nexus_messaging/service/NexusRemoteGreetingService.java index 4841ae0a..60447a61 100644 --- a/core/src/main/java/io/temporal/samples/nexus_sync_operations/service/NexusRemoteGreetingService.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/service/NexusRemoteGreetingService.java @@ -1,4 +1,4 @@ -package io.temporal.samples.nexus_sync_operations.service; +package io.temporal.samples.nexus_messaging.service; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonCreator; diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/README.MD b/core/src/main/java/io/temporal/samples/nexusmessaging/README.MD deleted file mode 100644 index a8fe380e..00000000 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/README.MD +++ /dev/null @@ -1,102 +0,0 @@ -# Nexus - -Temporal Nexus is a new feature of the Temporal platform designed to connect durable executions across team, namespace, -region, and cloud boundaries. It promotes a more modular architecture for sharing a subset of your team’s capabilities -via well-defined service API contracts for other teams to use, that abstract underlying Temporal primitives, like -Workflows, or execute arbitrary code. - -Learn more at [temporal.io/nexus](https://temporal.io/nexus). - -This sample shows how to use Temporal for authoring a Nexus service and call it from a workflow. - -### Sample directory structure - -- [service](./service) - shared service definition -- [caller](./caller) - caller workflows, worker, and starter -- [handler](./handler) - handler workflow, operations, and worker -- [options](./options) - command line argument parsing utility - -## Getting started locally - -### Get `temporal` CLI to enable local development - -1. Follow the instructions on the [docs - site](https://learn.temporal.io/getting_started/go/dev_environment/#set-up-a-local-temporal-service-for-development-with-temporal-cli) - to install Temporal CLI. - -> NOTE: The recommended version is at least v1.3.0. - -### Spin up environment - -#### Start temporal server - -> HTTP port is required for Nexus communications - -``` -temporal server start-dev -``` - -### Initialize environment - -In a separate terminal window - -#### Create caller and target namespaces - -``` -temporal operator namespace create --namespace my-target-namespace -temporal operator namespace create --namespace my-caller-namespace -``` - -#### Create Nexus endpoint - -``` -temporal operator nexus endpoint create \ - --name my-nexus-endpoint-name \ - --target-namespace my-target-namespace \ - --target-task-queue my-handler-task-queue \ - --description-file ./core/src/main/java/io/temporal/samples/nexusmessaging/service/description.md -``` - -## Getting started with a self-hosted service or Temporal Cloud - -Nexus is currently available as -[Public Preview](https://docs.temporal.io/evaluate/development-production-features/release-stages). - -Self hosted users can [try Nexus -out](https://github.com/temporalio/temporal/blob/main/docs/architecture/nexus.md#trying-nexus-out) in single cluster -deployments with server version 1.25.0. - -### Make Nexus calls across namespace boundaries - -> Instructions apply for local development, for Temporal Cloud or a self-hosted setups, supply the relevant [CLI -> flags](./options/ClientOptions.java) to properly set up the connection. - -In separate terminal windows: - -### Nexus handler worker - -``` -./gradlew -q execute -PmainClass=io.temporal.samples.nexusmessaging.handler.HandlerWorker \ - --args="-target-host localhost:7233 -namespace my-target-namespace" -``` - -### Nexus caller worker - -``` -./gradlew -q execute -PmainClass=io.temporal.samples.nexusmessaging.caller.CallerWorker \ - --args="-target-host localhost:7233 -namespace my-caller-namespace" -``` - -### Start caller workflow - -``` -./gradlew -q execute -PmainClass=io.temporal.samples.nexusmessaging.caller.CallerStarter \ - --args="-target-host localhost:7233 -namespace my-caller-namespace" -``` - -### Output - -which should result in: -``` -[main] INFO i.t.s.nexusmessaging.caller.CallerStarter - Workflow result: Nexus Echo 👋 -``` diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/caller/CallerStarter.java b/core/src/main/java/io/temporal/samples/nexusmessaging/caller/CallerStarter.java deleted file mode 100644 index 498ba524..00000000 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/caller/CallerStarter.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.temporal.samples.nexusmessaging.caller; - -import io.temporal.api.common.v1.WorkflowExecution; -import io.temporal.client.WorkflowClient; -import io.temporal.client.WorkflowOptions; -import io.temporal.samples.nexusmessaging.options.ClientOptions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class CallerStarter { - private static final Logger logger = LoggerFactory.getLogger(CallerStarter.class); - - public static void main(String[] args) { - WorkflowClient client = ClientOptions.getWorkflowClient(args); - - WorkflowOptions workflowOptions = - WorkflowOptions.newBuilder().setTaskQueue(CallerWorker.DEFAULT_TASK_QUEUE_NAME).build(); - - MessageCallerStartHandlerWorkflow messageWorkflow = - client.newWorkflowStub(MessageCallerStartHandlerWorkflow.class, workflowOptions); - - // MessageCallerWorkflow messageWorkflow = - // client.newWorkflowStub(MessageCallerWorkflow.class, workflowOptions); - WorkflowExecution execution = WorkflowClient.start(messageWorkflow::sentMessage); - logger.info( - "Started readMessage workflowId: {} runId: {}", - execution.getWorkflowId(), - execution.getRunId()); - String returnVal = messageWorkflow.sentMessage(); - logger.info("Workflow readMessage done - retval is {}", returnVal); - } -} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/caller/CallerWorker.java b/core/src/main/java/io/temporal/samples/nexusmessaging/caller/CallerWorker.java deleted file mode 100644 index 182c96f3..00000000 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/caller/CallerWorker.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.temporal.samples.nexusmessaging.caller; - -import io.temporal.client.WorkflowClient; -import io.temporal.samples.nexusmessaging.options.ClientOptions; -import io.temporal.worker.Worker; -import io.temporal.worker.WorkerFactory; -import io.temporal.worker.WorkflowImplementationOptions; -import io.temporal.workflow.NexusServiceOptions; -import java.util.Collections; - -public class CallerWorker { - public static final String DEFAULT_TASK_QUEUE_NAME = "my-caller-workflow-task-queue"; - - public static void main(String[] args) { - WorkflowClient client = ClientOptions.getWorkflowClient(args); - - WorkerFactory factory = WorkerFactory.newInstance(client); - - Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME); - worker.registerWorkflowImplementationTypes( - WorkflowImplementationOptions.newBuilder() - .setNexusServiceOptions( - Collections.singletonMap( - "SampleNexusService", - NexusServiceOptions.newBuilder().setEndpoint("my-nexus-endpoint-name").build())) - .build(), - // MessageCallerWorkflowImpl.class, - MessageCallerStartHandlerWorkflowImpl.class); - - factory.start(); - } -} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerStartHandlerWorkflow.java b/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerStartHandlerWorkflow.java deleted file mode 100644 index ba04df24..00000000 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerStartHandlerWorkflow.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.temporal.samples.nexusmessaging.caller; - -import io.temporal.workflow.WorkflowInterface; -import io.temporal.workflow.WorkflowMethod; - -@WorkflowInterface -public interface MessageCallerStartHandlerWorkflow { - @WorkflowMethod - String sentMessage(); -} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerStartHandlerWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerStartHandlerWorkflowImpl.java deleted file mode 100644 index f44bf4ec..00000000 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerStartHandlerWorkflowImpl.java +++ /dev/null @@ -1,69 +0,0 @@ -package io.temporal.samples.nexusmessaging.caller; - -import io.temporal.samples.nexusmessaging.service.SampleNexusService; -import io.temporal.workflow.NexusOperationHandle; -import io.temporal.workflow.NexusOperationOptions; -import io.temporal.workflow.NexusServiceOptions; -import io.temporal.workflow.Workflow; -import java.time.Duration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class MessageCallerStartHandlerWorkflowImpl implements MessageCallerStartHandlerWorkflow { - - private static final Logger logger = - LoggerFactory.getLogger(MessageCallerStartHandlerWorkflowImpl.class); - - private final String workflowId = "Remote Start Workflow"; - - SampleNexusService sampleNexusService = - Workflow.newNexusServiceStub( - SampleNexusService.class, - NexusServiceOptions.newBuilder() - .setOperationOptions( - NexusOperationOptions.newBuilder() - .setScheduleToCloseTimeout(Duration.ofSeconds(10)) - .build()) - .build()); - - @Override - public String sentMessage() { - /* - SampleNexusService.QueryWorkflowOutput queryOutput = - sampleNexusService.queryWorkflow( - new SampleNexusService.QueryWorkflowInput("query string going in")); - logger.info("Query output: {}", queryOutput.getMessage()); - - SampleNexusService.UpdateWorkflowOutput updateOutput = - sampleNexusService.updateWorkflow( - new SampleNexusService.UpdateWorkflowInput("update input")); - logger.info("Update output: {}", updateOutput.getResult()); - - sampleNexusService.signalWorkflow(new SampleNexusService.SignalWorkflowInput("signal input")); - logger.info("Signal sent via Nexus"); - - return "Query: " + queryOutput.getMessage() + ", Update: " + updateOutput.getResult(); - - */ - - NexusOperationHandle handle = - Workflow.startNexusOperation( - sampleNexusService::runFromRemote, - new SampleNexusService.RunFromRemoteInput(workflowId)); - // Optionally wait for the operation to be started. NexusOperationExecution will contain the - // operation token in case this operation is asynchronous. - handle.getExecution().get(); - - SampleNexusService.QueryWorkflowOutput queryWorkflowOutput = - sampleNexusService.queryWorkflowRemoteStart( - new SampleNexusService.QueryWorkflowRemoteStartInput( - "query string going in", workflowId)); - logger.info("Caller has query output of {}", queryWorkflowOutput); - - sampleNexusService.signalWorkflowRemoteStart( - new SampleNexusService.SignalWorkflowRemoteStartInput("signal input", workflowId)); - logger.info("Signal sent via Nexus"); - - return handle.getResult().get().getMessage(); - } -} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerWorkflow.java b/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerWorkflow.java deleted file mode 100644 index 1a9a9b71..00000000 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerWorkflow.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.temporal.samples.nexusmessaging.caller; - -import io.temporal.workflow.WorkflowInterface; -import io.temporal.workflow.WorkflowMethod; - -@WorkflowInterface -public interface MessageCallerWorkflow { - @WorkflowMethod - String sentMessage(); -} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerWorkflowImpl.java deleted file mode 100644 index 55a6cf66..00000000 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/caller/MessageCallerWorkflowImpl.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.temporal.samples.nexusmessaging.caller; - -import io.temporal.samples.nexusmessaging.service.SampleNexusService; -import io.temporal.workflow.NexusOperationOptions; -import io.temporal.workflow.NexusServiceOptions; -import io.temporal.workflow.Workflow; -import java.time.Duration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class MessageCallerWorkflowImpl implements MessageCallerWorkflow { - - private static final Logger logger = LoggerFactory.getLogger(MessageCallerWorkflowImpl.class); - - SampleNexusService sampleNexusService = - Workflow.newNexusServiceStub( - SampleNexusService.class, - NexusServiceOptions.newBuilder() - .setOperationOptions( - NexusOperationOptions.newBuilder() - .setScheduleToCloseTimeout(Duration.ofSeconds(10)) - .build()) - .build()); - - @Override - public String sentMessage() { - SampleNexusService.QueryWorkflowOutput queryOutput = - sampleNexusService.queryWorkflow( - new SampleNexusService.QueryWorkflowInput("query string going in")); - logger.info("Query output: {}", queryOutput.getMessage()); - - SampleNexusService.UpdateWorkflowOutput updateOutput = - sampleNexusService.updateWorkflow( - new SampleNexusService.UpdateWorkflowInput("update input")); - logger.info("Update output: {}", updateOutput.getResult()); - - sampleNexusService.signalWorkflow(new SampleNexusService.SignalWorkflowInput("signal input")); - logger.info("Signal sent via Nexus"); - - return "Query: " + queryOutput.getMessage() + ", Update: " + updateOutput.getResult(); - } -} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/handler/HandlerWorker.java b/core/src/main/java/io/temporal/samples/nexusmessaging/handler/HandlerWorker.java deleted file mode 100644 index 8af0c721..00000000 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/handler/HandlerWorker.java +++ /dev/null @@ -1,62 +0,0 @@ -package io.temporal.samples.nexusmessaging.handler; - -import io.temporal.client.WorkflowClient; -import io.temporal.client.WorkflowClientOptions; -import io.temporal.client.WorkflowExecutionAlreadyStarted; -import io.temporal.client.WorkflowOptions; -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 HandlerWorker { - - public static final String NAMESPACE = "my-target-namespace"; - public static final String DEFAULT_TASK_QUEUE_NAME = "my-handler-task-queue"; - static final String WORKFLOW_ID = "my-handler-workflow"; - - private static final Logger logger = LoggerFactory.getLogger(HandlerWorker.class); - - public static void main(String[] args) { - WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); - WorkflowClient client = - WorkflowClient.newInstance( - service, WorkflowClientOptions.newBuilder().setNamespace(NAMESPACE).build()); - - // Start the long-running entity workflow that backs the Nexus service, if not already running. - MessageHandlerWorkflow messageHandlerWorkflow = - client.newWorkflowStub( - MessageHandlerWorkflow.class, - WorkflowOptions.newBuilder() - .setWorkflowId(WORKFLOW_ID) - .setTaskQueue(DEFAULT_TASK_QUEUE_NAME) - .build()); - try { - WorkflowClient.start(messageHandlerWorkflow::run); - logger.info("Started message handler workflow: {}", WORKFLOW_ID); - } catch (WorkflowExecutionAlreadyStarted e) { - logger.info("Message handler workflow already running: {}", WORKFLOW_ID); - } - - WorkerFactory factory = WorkerFactory.newInstance(client); - Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME); - worker.registerWorkflowImplementationTypes( - MessageHandlerWorkflowImpl.class, MessageHandlerRemoteWorkflowImpl.class); - // worker.registerActivitiesImplementations(new MessageHandlerWorkflowImpl()); - worker.registerNexusServiceImplementation(new SampleNexusServiceImpl(WORKFLOW_ID)); - - factory.start(); - logger.info("Handler worker started, ctrl+c to exit"); - - // WorkflowClient client = ClientOptions.getWorkflowClient(args); - // - // WorkerFactory factory = WorkerFactory.newInstance(client); - // - // Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME); - // worker.registerWorkflowImplementationTypes(MessageHandlerWorkflowImpl.class); - // worker.registerNexusServiceImplementation(new SampleNexusServiceImpl()); - // - // factory.start(); - } -} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerRemoteWorkflow.java b/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerRemoteWorkflow.java deleted file mode 100644 index dad19f81..00000000 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerRemoteWorkflow.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.temporal.samples.nexusmessaging.handler; - -import io.temporal.samples.nexusmessaging.service.SampleNexusService; -import io.temporal.workflow.QueryMethod; -import io.temporal.workflow.SignalMethod; -import io.temporal.workflow.UpdateMethod; -import io.temporal.workflow.UpdateValidatorMethod; -import io.temporal.workflow.WorkflowInterface; -import io.temporal.workflow.WorkflowMethod; - -@WorkflowInterface -public interface MessageHandlerRemoteWorkflow { - - @WorkflowMethod - SampleNexusService.RunFromRemoteOutput runFromRemote(SampleNexusService.RunFromRemoteInput input); - - @QueryMethod - SampleNexusService.QueryWorkflowOutput queryWorkflow(SampleNexusService.QueryWorkflowInput name); - - @SignalMethod - void signalWorkflow(SampleNexusService.SignalWorkflowInput name); - - @UpdateMethod - SampleNexusService.UpdateWorkflowOutput updateWorkflow( - SampleNexusService.UpdateWorkflowInput name); - - @UpdateValidatorMethod(updateName = "updateWorkflow") - void setLanguageValidator(SampleNexusService.UpdateWorkflowInput name); -} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerRemoteWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerRemoteWorkflowImpl.java deleted file mode 100644 index e6e2c5a0..00000000 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerRemoteWorkflowImpl.java +++ /dev/null @@ -1,51 +0,0 @@ -package io.temporal.samples.nexusmessaging.handler; - -import io.temporal.samples.nexusmessaging.service.SampleNexusService; -import io.temporal.workflow.Workflow; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class MessageHandlerRemoteWorkflowImpl implements MessageHandlerRemoteWorkflow { - private static final Logger logger = - LoggerFactory.getLogger(MessageHandlerRemoteWorkflowImpl.class); - private boolean keepRunning = true; - - @Override - public SampleNexusService.RunFromRemoteOutput runFromRemote( - SampleNexusService.RunFromRemoteInput input) { - Workflow.await(() -> !keepRunning); - - String logMessage = "runFromRemote was told to stop, and did."; - logger.info(logMessage); - return new SampleNexusService.RunFromRemoteOutput(logMessage); - } - - @Override - public SampleNexusService.QueryWorkflowOutput queryWorkflow( - SampleNexusService.QueryWorkflowInput name) { - logger.info("Query '{}' was received", name.getName()); - return new SampleNexusService.QueryWorkflowOutput("Query received"); - } - - @Override - public void signalWorkflow(SampleNexusService.SignalWorkflowInput name) { - logger.info("Signal was received"); - keepRunning = false; - } - - @Override - public SampleNexusService.UpdateWorkflowOutput updateWorkflow( - SampleNexusService.UpdateWorkflowInput name) { - logger.info("Update {} was received", name.getName()); - return new SampleNexusService.UpdateWorkflowOutput(10); - } - - @Override - public void setLanguageValidator(SampleNexusService.UpdateWorkflowInput name) { - if (name.getName().equals("invalid")) { - logger.info("Update {} was rejected", name.getName()); - throw new IllegalArgumentException("Invalid update name!"); - } - logger.info("Update {} was validated", name.getName()); - } -} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerWorkflow.java b/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerWorkflow.java deleted file mode 100644 index 897e4a51..00000000 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerWorkflow.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.temporal.samples.nexusmessaging.handler; - -import io.temporal.samples.nexusmessaging.service.SampleNexusService; -import io.temporal.workflow.QueryMethod; -import io.temporal.workflow.SignalMethod; -import io.temporal.workflow.UpdateMethod; -import io.temporal.workflow.UpdateValidatorMethod; -import io.temporal.workflow.WorkflowInterface; -import io.temporal.workflow.WorkflowMethod; - -@WorkflowInterface -public interface MessageHandlerWorkflow { - - @WorkflowMethod - void run(); - - @QueryMethod - SampleNexusService.QueryWorkflowOutput queryWorkflow(SampleNexusService.QueryWorkflowInput name); - - @SignalMethod - void signalWorkflow(SampleNexusService.SignalWorkflowInput name); - - @UpdateMethod - SampleNexusService.UpdateWorkflowOutput updateWorkflow( - SampleNexusService.UpdateWorkflowInput name); - - @UpdateValidatorMethod(updateName = "updateWorkflow") - void setLanguageValidator(SampleNexusService.UpdateWorkflowInput name); -} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerWorkflowImpl.java deleted file mode 100644 index 68129be0..00000000 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/handler/MessageHandlerWorkflowImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.temporal.samples.nexusmessaging.handler; - -import io.temporal.samples.nexusmessaging.service.SampleNexusService; -import io.temporal.workflow.Workflow; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class MessageHandlerWorkflowImpl implements MessageHandlerWorkflow { - private static final Logger logger = LoggerFactory.getLogger(MessageHandlerWorkflowImpl.class); - private boolean keepRunning = true; - - @Override - public void run() { - // Long-running entity workflow: stays alive to receive signals, queries, and updates via Nexus. - Workflow.await(() -> !keepRunning); - logger.info("Handler workflow stopped via signal"); - } - - @Override - public SampleNexusService.QueryWorkflowOutput queryWorkflow( - SampleNexusService.QueryWorkflowInput name) { - logger.info("Query '{}' was received", name.getName()); - return new SampleNexusService.QueryWorkflowOutput("Query received"); - } - - @Override - public void signalWorkflow(SampleNexusService.SignalWorkflowInput name) { - logger.info("Signal was received"); - keepRunning = false; - } - - @Override - public SampleNexusService.UpdateWorkflowOutput updateWorkflow( - SampleNexusService.UpdateWorkflowInput name) { - logger.info("Update {} was received", name.getName()); - return new SampleNexusService.UpdateWorkflowOutput(10); - } - - @Override - public void setLanguageValidator(SampleNexusService.UpdateWorkflowInput name) { - if (name.getName().equals("invalid")) { - logger.info("Update {} was rejected", name.getName()); - throw new IllegalArgumentException("Invalid update name!"); - } - logger.info("Update {} was validated", name.getName()); - } -} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/handler/SampleNexusServiceImpl.java b/core/src/main/java/io/temporal/samples/nexusmessaging/handler/SampleNexusServiceImpl.java deleted file mode 100644 index 675bf7fe..00000000 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/handler/SampleNexusServiceImpl.java +++ /dev/null @@ -1,129 +0,0 @@ -package io.temporal.samples.nexusmessaging.handler; - -import io.nexusrpc.handler.OperationHandler; -import io.nexusrpc.handler.OperationImpl; -import io.nexusrpc.handler.ServiceImpl; -import io.temporal.client.WorkflowOptions; -import io.temporal.nexus.Nexus; -import io.temporal.nexus.WorkflowRunOperation; -import io.temporal.samples.nexusmessaging.service.SampleNexusService; - -@ServiceImpl(service = SampleNexusService.class) -public class SampleNexusServiceImpl { - - private final String workflowId; - - public SampleNexusServiceImpl(String workflowId) { - this.workflowId = workflowId; - } - - private MessageHandlerWorkflow getWorkflowStub() { - return Nexus.getOperationContext() - .getWorkflowClient() - .newWorkflowStub(MessageHandlerWorkflow.class, workflowId); - } - - @OperationImpl - public OperationHandler< - SampleNexusService.RunFromRemoteInput, SampleNexusService.RunFromRemoteOutput> - runFromRemote() { - return WorkflowRunOperation.fromWorkflowMethod( - (ctx, details, input) -> - Nexus.getOperationContext() - .getWorkflowClient() - .newWorkflowStub( - MessageHandlerRemoteWorkflow.class, - WorkflowOptions.newBuilder().setWorkflowId(input.getWorkflowId()).build()) - ::runFromRemote); - } - - @OperationImpl - public OperationHandler< - SampleNexusService.SignalWorkflowInput, SampleNexusService.SignalWorkflowOutput> - signalWorkflow() { - return OperationHandler.sync( - (ctx, details, input) -> { - getWorkflowStub().signalWorkflow(input); - return new SampleNexusService.SignalWorkflowOutput(); - }); - } - - @OperationImpl - public OperationHandler< - SampleNexusService.QueryWorkflowInput, SampleNexusService.QueryWorkflowOutput> - queryWorkflow() { - return OperationHandler.sync((ctx, details, input) -> getWorkflowStub().queryWorkflow(input)); - } - - @OperationImpl - public OperationHandler< - SampleNexusService.UpdateWorkflowInput, SampleNexusService.UpdateWorkflowOutput> - updateWorkflow() { - return OperationHandler.sync((ctx, details, input) -> getWorkflowStub().updateWorkflow(input)); - } - - @OperationImpl - public OperationHandler< - SampleNexusService.QueryWorkflowRemoteStartInput, SampleNexusService.QueryWorkflowOutput> - queryWorkflowRemoteStart() { - return OperationHandler.sync( - (ctx, details, input) -> { - MessageHandlerRemoteWorkflow stub = - Nexus.getOperationContext() - .getWorkflowClient() - .newWorkflowStub(MessageHandlerRemoteWorkflow.class, input.getWorkflowId()); - return stub.queryWorkflow(new SampleNexusService.QueryWorkflowInput(input.getName())); - }); - } - - @OperationImpl - public OperationHandler< - SampleNexusService.SignalWorkflowRemoteStartInput, - SampleNexusService.SignalWorkflowOutput> - signalWorkflowRemoteStart() { - return OperationHandler.sync( - (ctx, details, input) -> { - MessageHandlerRemoteWorkflow stub = - Nexus.getOperationContext() - .getWorkflowClient() - .newWorkflowStub(MessageHandlerRemoteWorkflow.class, input.getWorkflowId()); - stub.signalWorkflow(new SampleNexusService.SignalWorkflowInput(input.getName())); - return new SampleNexusService.SignalWorkflowOutput(); - }); - } - - /* - - @OperationImpl - public OperationHandler< - SampleNexusService.SignalWorkflowInput, SampleNexusService.SignalWorkflowOutput> - signalWorkflow() { - return OperationHandler.sync( - (ctx, details, input) -> { - MessageHandlerWorkflow stub = - Nexus.getOperationContext() - .getWorkflowClient() - .newWorkflowStub(MessageHandlerWorkflow.class, input.getWorkflowId()); - stub.signalWorkflow(input.getName()); - return new SampleNexusService.SignalWorkflowOutput(); - }); - } - - - - @OperationImpl - public OperationHandler< - SampleNexusService.UpdateWorkflowInput, SampleNexusService.UpdateWorkflowOutput> - updateWorkflow() { - return OperationHandler.sync( - (ctx, details, input) -> { - MessageHandlerWorkflow stub = - Nexus.getOperationContext() - .getWorkflowClient() - .newWorkflowStub(MessageHandlerWorkflow.class, input.getWorkflowId()); - int result = stub.updateWorkflow(input.getName()); - return new SampleNexusService.UpdateWorkflowOutput(result); - }); - } - */ -} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/options/ClientOptions.java b/core/src/main/java/io/temporal/samples/nexusmessaging/options/ClientOptions.java deleted file mode 100644 index 60e88c0f..00000000 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/options/ClientOptions.java +++ /dev/null @@ -1,137 +0,0 @@ -package io.temporal.samples.nexusmessaging.options; - -import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts; -import io.grpc.netty.shaded.io.netty.handler.ssl.SslContextBuilder; -import io.grpc.netty.shaded.io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import io.temporal.client.WorkflowClient; -import io.temporal.client.WorkflowClientOptions; -import io.temporal.serviceclient.WorkflowServiceStubs; -import io.temporal.serviceclient.WorkflowServiceStubsOptions; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import javax.net.ssl.SSLException; -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.CommandLineParser; -import org.apache.commons.cli.DefaultParser; -import org.apache.commons.cli.HelpFormatter; -import org.apache.commons.cli.Option; -import org.apache.commons.cli.Options; -import org.apache.commons.cli.ParseException; - -public class ClientOptions { - - public static WorkflowClient getWorkflowClient(String[] args) { - return getWorkflowClient(args, WorkflowClientOptions.newBuilder()); - } - - public static WorkflowClient getWorkflowClient( - String[] args, WorkflowClientOptions.Builder clientOptions) { - Options options = new Options(); - Option targetHostOption = new Option("target-host", true, "Host:port for the Temporal service"); - targetHostOption.setRequired(false); - options.addOption(targetHostOption); - - Option namespaceOption = new Option("namespace", true, "Namespace to connect to"); - namespaceOption.setRequired(false); - options.addOption(namespaceOption); - - Option serverRootCaOption = - new Option("server-root-ca-cert", true, "Optional path to root server CA cert"); - serverRootCaOption.setRequired(false); - options.addOption(serverRootCaOption); - - Option clientCertOption = - new Option( - "client-cert", true, "Optional path to client cert, mutually exclusive with API key"); - clientCertOption.setRequired(false); - options.addOption(clientCertOption); - - Option clientKeyOption = - new Option( - "client-key", true, "Optional path to client key, mutually exclusive with API key"); - clientKeyOption.setRequired(false); - options.addOption(clientKeyOption); - - Option apiKeyOption = - new Option("api-key", true, "Optional API key, mutually exclusive with cert/key"); - apiKeyOption.setRequired(false); - options.addOption(apiKeyOption); - - Option serverNameOption = - new Option( - "server-name", true, "Server name to use for verifying the server's certificate"); - serverNameOption.setRequired(false); - options.addOption(serverNameOption); - - Option insercureSkipVerifyOption = - new Option( - "insecure-skip-verify", - false, - "Skip verification of the server's certificate and host name"); - insercureSkipVerifyOption.setRequired(false); - options.addOption(insercureSkipVerifyOption); - - CommandLineParser parser = new DefaultParser(); - HelpFormatter formatter = new HelpFormatter(); - CommandLine cmd = null; - - try { - cmd = parser.parse(options, args); - } catch (ParseException e) { - System.out.println(e.getMessage()); - formatter.printHelp("utility-name", options); - - System.exit(1); - } - - String targetHost = cmd.getOptionValue("target-host", "localhost:7233"); - String namespace = cmd.getOptionValue("namespace", "default"); - String serverRootCaCert = cmd.getOptionValue("server-root-ca-cert", ""); - String clientCert = cmd.getOptionValue("client-cert", ""); - String clientKey = cmd.getOptionValue("client-key", ""); - String serverName = cmd.getOptionValue("server-name", ""); - boolean insecureSkipVerify = cmd.hasOption("insecure-skip-verify"); - String apiKey = cmd.getOptionValue("api-key", ""); - - // API key and client cert/key are mutually exclusive - if (!apiKey.isEmpty() && (!clientCert.isEmpty() || !clientKey.isEmpty())) { - throw new IllegalArgumentException("API key and client cert/key are mutually exclusive"); - } - WorkflowServiceStubsOptions.Builder serviceStubOptionsBuilder = - WorkflowServiceStubsOptions.newBuilder().setTarget(targetHost); - // Configure TLS if client cert and key are provided - if (!clientCert.isEmpty() || !clientKey.isEmpty()) { - if (clientCert.isEmpty() || clientKey.isEmpty()) { - throw new IllegalArgumentException("Both client-cert and client-key must be provided"); - } - try { - SslContextBuilder sslContext = - SslContextBuilder.forClient() - .keyManager(new FileInputStream(clientCert), new FileInputStream(clientKey)); - if (serverRootCaCert != null && !serverRootCaCert.isEmpty()) { - sslContext.trustManager(new FileInputStream(serverRootCaCert)); - } - if (insecureSkipVerify) { - sslContext.trustManager(InsecureTrustManagerFactory.INSTANCE); - } - serviceStubOptionsBuilder.setSslContext(GrpcSslContexts.configure(sslContext).build()); - } catch (SSLException e) { - throw new RuntimeException(e); - } catch (FileNotFoundException e) { - throw new RuntimeException(e); - } - if (serverName != null && !serverName.isEmpty()) { - serviceStubOptionsBuilder.setChannelInitializer(c -> c.overrideAuthority(serverName)); - } - } - // Configure API key if provided - if (!apiKey.isEmpty()) { - serviceStubOptionsBuilder.setEnableHttps(true); - serviceStubOptionsBuilder.addApiKey(() -> apiKey); - } - - WorkflowServiceStubs service = - WorkflowServiceStubs.newServiceStubs(serviceStubOptionsBuilder.build()); - return WorkflowClient.newInstance(service, clientOptions.setNamespace(namespace).build()); - } -} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/service/SampleNexusService.java b/core/src/main/java/io/temporal/samples/nexusmessaging/service/SampleNexusService.java deleted file mode 100644 index edb72864..00000000 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/service/SampleNexusService.java +++ /dev/null @@ -1,177 +0,0 @@ -package io.temporal.samples.nexusmessaging.service; - -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.nexusrpc.Operation; -import io.nexusrpc.Service; - -@Service -public interface SampleNexusService { - - class SignalWorkflowInput { - private final String name; - - @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - public SignalWorkflowInput(@JsonProperty("name") String name) { - this.name = name; - } - - @JsonProperty("name") - public String getName() { - return name; - } - } - - @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) - class SignalWorkflowOutput { - @JsonCreator - public SignalWorkflowOutput() {} - } - - class QueryWorkflowInput { - private final String name; - - @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - public QueryWorkflowInput(@JsonProperty("name") String name) { - this.name = name; - } - - @JsonProperty("name") - public String getName() { - return name; - } - } - - class QueryWorkflowRemoteStartInput { - private final String name; - private final String workflowId; - - @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - public QueryWorkflowRemoteStartInput( - @JsonProperty("name") String name, @JsonProperty("workflowId") String workflowId) { - this.name = name; - this.workflowId = workflowId; - } - - @JsonProperty("name") - public String getName() { - return name; - } - - @JsonProperty("workflowId") - public String getWorkflowId() { - return workflowId; - } - } - - class QueryWorkflowOutput { - private final String message; - - @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - public QueryWorkflowOutput(@JsonProperty("message") String message) { - this.message = message; - } - - @JsonProperty("message") - public String getMessage() { - return message; - } - } - - class UpdateWorkflowInput { - private final String name; - - @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - public UpdateWorkflowInput(@JsonProperty("name") String name) { - this.name = name; - } - - @JsonProperty("name") - public String getName() { - return name; - } - } - - class UpdateWorkflowOutput { - private final int result; - - @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - public UpdateWorkflowOutput(@JsonProperty("result") int result) { - this.result = result; - } - - @JsonProperty("result") - public int getResult() { - return result; - } - } - - class RunFromRemoteInput { - private final String workflowId; - - @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - public RunFromRemoteInput(@JsonProperty("workflowId") String workflowId) { - this.workflowId = workflowId; - } - - @JsonProperty("workflowId") - public String getWorkflowId() { - return workflowId; - } - } - - class RunFromRemoteOutput { - private final String message; - - @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - public RunFromRemoteOutput(@JsonProperty("message") String message) { - this.message = message; - } - - @JsonProperty("message") - public String getMessage() { - return message; - } - } - - @Operation - RunFromRemoteOutput runFromRemote(RunFromRemoteInput input); - - class SignalWorkflowRemoteStartInput { - private final String name; - private final String workflowId; - - @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - public SignalWorkflowRemoteStartInput( - @JsonProperty("name") String name, @JsonProperty("workflowId") String workflowId) { - this.name = name; - this.workflowId = workflowId; - } - - @JsonProperty("name") - public String getName() { - return name; - } - - @JsonProperty("workflowId") - public String getWorkflowId() { - return workflowId; - } - } - - @Operation - SignalWorkflowOutput signalWorkflow(SignalWorkflowInput input); - - @Operation - SignalWorkflowOutput signalWorkflowRemoteStart(SignalWorkflowRemoteStartInput input); - - @Operation - UpdateWorkflowOutput updateWorkflow(UpdateWorkflowInput input); - - @Operation - QueryWorkflowOutput queryWorkflow(QueryWorkflowInput input); - - @Operation - QueryWorkflowOutput queryWorkflowRemoteStart(QueryWorkflowRemoteStartInput input); -} diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/service/description.md b/core/src/main/java/io/temporal/samples/nexusmessaging/service/description.md deleted file mode 100644 index 0d607d9a..00000000 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/service/description.md +++ /dev/null @@ -1,6 +0,0 @@ -## Service: [SampleNexusService](https://github.com/temporalio/samples-java/blob/main/core/src/main/java/io/temporal/samples/nexusmessaging/service/SampleNexusService.java) - - operation: `signalWorkflow` - - operation: `queryWorkflow` - - operation: `updateWorkflow` - -See https://github.com/temporalio/samples-java/blob/main/core/src/main/java/io/temporal/samples/nexusmessaging/service/SampleNexusService.java for Input / Output types. From f6f20ab41cce39e9812ed8844c5332ea0f53ccc4 Mon Sep 17 00:00:00 2001 From: Evan Reynolds Date: Wed, 8 Apr 2026 14:23:08 -0700 Subject: [PATCH 4/5] Remove yarn.lock and node_modules/.yarn-integrity from tracking --- node_modules/.yarn-integrity | 10 ---------- yarn.lock | 4 ---- 2 files changed, 14 deletions(-) delete mode 100644 node_modules/.yarn-integrity delete mode 100644 yarn.lock diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity deleted file mode 100644 index 044a5ddd..00000000 --- a/node_modules/.yarn-integrity +++ /dev/null @@ -1,10 +0,0 @@ -{ - "systemParams": "darwin-arm64-115", - "modulesFolders": [], - "flags": [], - "linkedModules": [], - "topLevelPatterns": [], - "lockfileEntries": {}, - "files": [], - "artifacts": {} -} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index fb57ccd1..00000000 --- a/yarn.lock +++ /dev/null @@ -1,4 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - From 47a43e273982f10912dc552d33b13fca23b7fa5a Mon Sep 17 00:00:00 2001 From: Evan Reynolds Date: Wed, 8 Apr 2026 14:52:16 -0700 Subject: [PATCH 5/5] Reviewing --- .../samples/nexus_messaging/README.md | 31 ++++++++++--------- .../nexus_messaging/caller/CallerStarter.java | 2 +- .../nexus_messaging/caller/CallerWorker.java | 7 +++-- .../caller/CallerWorkflowImpl.java | 6 ++++ .../caller_remote/CallerRemoteStarter.java | 2 +- .../caller_remote/CallerRemoteWorker.java | 7 +++-- .../CallerRemoteWorkflowImpl.java | 10 ++++-- .../handler/GreetingWorkflowImpl.java | 2 +- .../handler/HandlerWorker.java | 6 ++-- .../handler/NexusGreetingServiceImpl.java | 8 +++-- .../NexusRemoteGreetingServiceImpl.java | 5 +++ 11 files changed, 54 insertions(+), 32 deletions(-) diff --git a/core/src/main/java/io/temporal/samples/nexus_messaging/README.md b/core/src/main/java/io/temporal/samples/nexus_messaging/README.md index 15dcf21a..066bc213 100644 --- a/core/src/main/java/io/temporal/samples/nexus_messaging/README.md +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/README.md @@ -20,38 +20,41 @@ There are **two caller patterns** that share the same handler workflow (`Greetin ### Running -Start a Temporal server and create namespaces/endpoint: +Start a Temporal server: ```bash temporal server start-dev +``` +Create the namespaces and Nexus endpoint: -temporal operator namespace create --namespace nexus-sync-operations-handler-namespace -temporal operator namespace create --namespace nexus-sync-operations-caller-namespace +```bash +temporal operator namespace create --namespace nexus-messaging-handler-namespace +temporal operator namespace create --namespace nexus-messaging-caller-namespace temporal operator nexus endpoint create \ - --name nexus-sync-operations-nexus-endpoint \ - --target-namespace nexus-sync-operations-handler-namespace \ - --target-task-queue nexus-sync-operations-handler-task-queue + --name nexus-messaging-nexus-endpoint \ + --target-namespace nexus-messaging-handler-namespace \ + --target-task-queue nexus-messaging-handler-task-queue ``` In one terminal, start the handler worker (shared by both patterns): ```bash -./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_sync_operations.handler.HandlerWorker +./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_messaging.handler.HandlerWorker ``` #### Entity pattern -In a second terminal, run the caller worker: +In the second terminal, run the caller worker: ```bash -./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_sync_operations.caller.CallerWorker +./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_messaging.caller.CallerWorker ``` -In a third terminal, start the caller workflow: +In the third terminal, start the caller workflow: ```bash -./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_sync_operations.caller.CallerStarter +./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_messaging.caller.CallerStarter ``` Expected output: @@ -67,19 +70,19 @@ workflow approved In a second terminal, run the remote caller worker: ```bash -./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_sync_operations.caller_remote.CallerRemoteWorker +./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_messaging.caller_remote.CallerRemoteWorker ``` In a third terminal, start the remote caller workflow: ```bash -./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_sync_operations.caller_remote.CallerRemoteStarter +./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_messaging.caller_remote.CallerRemoteStarter ``` Expected output: ``` -started remote greeting workflow: nexus-sync-operations-remote-greeting-workflow +started remote greeting workflow: nexus-messaging-remote-greeting-workflow supported languages: [CHINESE, ENGLISH] language changed: ENGLISH -> ARABIC workflow approved diff --git a/core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerStarter.java b/core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerStarter.java index bb4163da..95a3d025 100644 --- a/core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerStarter.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerStarter.java @@ -20,7 +20,7 @@ public static void main(String[] args) { client.newWorkflowStub( CallerWorkflow.class, WorkflowOptions.newBuilder() - .setWorkflowId("nexus-sync-operations-caller-" + UUID.randomUUID()) + .setWorkflowId("nexus-messaging-caller-" + UUID.randomUUID()) .setTaskQueue(CallerWorker.TASK_QUEUE) .build()); diff --git a/core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerWorker.java b/core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerWorker.java index 1ac68d76..8194503b 100644 --- a/core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerWorker.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerWorker.java @@ -14,9 +14,9 @@ public class CallerWorker { private static final Logger logger = LoggerFactory.getLogger(CallerWorker.class); - public static final String NAMESPACE = "nexus-sync-operations-caller-namespace"; - public static final String TASK_QUEUE = "nexus-sync-operations-caller-task-queue"; - static final String NEXUS_ENDPOINT = "nexus-sync-operations-nexus-endpoint"; + public static final String NAMESPACE = "nexus-messaging-caller-namespace"; + public static final String TASK_QUEUE = "nexus-messaging-caller-task-queue"; + static final String NEXUS_ENDPOINT = "nexus-messaging-nexus-endpoint"; public static void main(String[] args) throws InterruptedException { WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); @@ -29,6 +29,7 @@ public static void main(String[] args) throws InterruptedException { worker.registerWorkflowImplementationTypes( WorkflowImplementationOptions.newBuilder() .setNexusServiceOptions( + // The key must match the @Service-annotated interface name. Collections.singletonMap( "NexusGreetingService", NexusServiceOptions.newBuilder().setEndpoint(NEXUS_ENDPOINT).build())) diff --git a/core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerWorkflowImpl.java index 5a37caf0..ed07f99e 100644 --- a/core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerWorkflowImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/caller/CallerWorkflowImpl.java @@ -1,6 +1,7 @@ package io.temporal.samples.nexus_messaging.caller; import io.temporal.failure.ApplicationFailure; +import io.temporal.samples.nexus_messaging.caller_remote.CallerRemoteWorkflowImpl; import io.temporal.samples.nexus_messaging.service.Language; import io.temporal.samples.nexus_messaging.service.NexusGreetingService; import io.temporal.workflow.NexusOperationOptions; @@ -9,9 +10,13 @@ import java.time.Duration; import java.util.ArrayList; import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class CallerWorkflowImpl implements CallerWorkflow { + private static final Logger logger = LoggerFactory.getLogger(CallerRemoteWorkflowImpl.class); + // The endpoint is configured at the worker level in CallerWorker; only operation options are // set here. NexusGreetingService greetingService = @@ -36,6 +41,7 @@ public List run() { // 👉 Call a Nexus operation backed by an update against the entity workflow. Language previousLanguage = greetingService.setLanguage(new NexusGreetingService.SetLanguageInput(Language.ARABIC)); + logger.info("Language changed from {} to {}", previousLanguage, Language.ARABIC); // 👉 Call a Nexus operation backed by a query to confirm the language change. Language currentLanguage = diff --git a/core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteStarter.java b/core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteStarter.java index c255f288..ad20ab2a 100644 --- a/core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteStarter.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteStarter.java @@ -20,7 +20,7 @@ public static void main(String[] args) { client.newWorkflowStub( CallerRemoteWorkflow.class, WorkflowOptions.newBuilder() - .setWorkflowId("nexus-sync-operations-remote-caller-" + UUID.randomUUID()) + .setWorkflowId("nexus-messaging-remote-caller-" + UUID.randomUUID()) .setTaskQueue(CallerRemoteWorker.TASK_QUEUE) .build()); diff --git a/core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteWorker.java b/core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteWorker.java index 1e89c59b..4aa850cc 100644 --- a/core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteWorker.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteWorker.java @@ -14,9 +14,9 @@ public class CallerRemoteWorker { private static final Logger logger = LoggerFactory.getLogger(CallerRemoteWorker.class); - public static final String NAMESPACE = "nexus-sync-operations-caller-namespace"; - public static final String TASK_QUEUE = "nexus-sync-operations-caller-remote-task-queue"; - static final String NEXUS_ENDPOINT = "nexus-sync-operations-nexus-endpoint"; + public static final String NAMESPACE = "nexus-messaging-caller-namespace"; + public static final String TASK_QUEUE = "nexus-messaging-caller-remote-task-queue"; + static final String NEXUS_ENDPOINT = "nexus-messaging-nexus-endpoint"; public static void main(String[] args) throws InterruptedException { WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); @@ -29,6 +29,7 @@ public static void main(String[] args) throws InterruptedException { worker.registerWorkflowImplementationTypes( WorkflowImplementationOptions.newBuilder() .setNexusServiceOptions( + // The key must match the @Service-annotated interface name. Collections.singletonMap( "NexusRemoteGreetingService", NexusServiceOptions.newBuilder().setEndpoint(NEXUS_ENDPOINT).build())) diff --git a/core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteWorkflowImpl.java index 8477c10e..16ee4d9c 100644 --- a/core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteWorkflowImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/caller_remote/CallerRemoteWorkflowImpl.java @@ -17,7 +17,7 @@ public class CallerRemoteWorkflowImpl implements CallerRemoteWorkflow { private static final Logger logger = LoggerFactory.getLogger(CallerRemoteWorkflowImpl.class); - private static final String REMOTE_WORKFLOW_ID = "nexus-sync-operations-remote-greeting-workflow"; + private static final String REMOTE_WORKFLOW_ID = "nexus-messaging-remote-greeting-workflow"; NexusRemoteGreetingService greetingRemoteService = Workflow.newNexusServiceStub( @@ -33,7 +33,9 @@ public class CallerRemoteWorkflowImpl implements CallerRemoteWorkflow { public List run() { List log = new ArrayList<>(); - // Start a new GreetingWorkflow on the handler side via Nexus. + // 👉 Async Nexus operation — starts a workflow on the handler and returns a handle. + // Unlike the sync operations below (getLanguages, setLanguage, etc.), this does not block + // until the workflow completes. It is backed by WorkflowRunOperation on the handler side. NexusOperationHandle handle = Workflow.startNexusOperation( greetingRemoteService::runFromRemote, @@ -43,6 +45,8 @@ public List run() { log.add("started remote greeting workflow: " + REMOTE_WORKFLOW_ID); // Query the remote workflow for supported languages. + // Output types (e.g. GetLanguagesOutput) are defined on NexusGreetingService and shared by + // both service interfaces. NexusGreetingService.GetLanguagesOutput languagesOutput = greetingRemoteService.getLanguages( new NexusRemoteGreetingService.GetLanguagesInput(false, REMOTE_WORKFLOW_ID)); @@ -52,7 +56,7 @@ public List run() { Language previousLanguage = greetingRemoteService.setLanguage( new NexusRemoteGreetingService.SetLanguageInput(Language.ARABIC, REMOTE_WORKFLOW_ID)); - logger.info("Language changed from {}", previousLanguage); + logger.info("Language changed from {} to {}", previousLanguage, Language.ARABIC); // Confirm the change by querying. Language currentLanguage = diff --git a/core/src/main/java/io/temporal/samples/nexus_messaging/handler/GreetingWorkflowImpl.java b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/GreetingWorkflowImpl.java index c7140e60..c5ca5596 100644 --- a/core/src/main/java/io/temporal/samples/nexus_messaging/handler/GreetingWorkflowImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/GreetingWorkflowImpl.java @@ -22,7 +22,7 @@ public class GreetingWorkflowImpl implements GreetingWorkflow { private final Map greetings = new EnumMap<>(Language.class); private Language language = Language.ENGLISH; - private static final Logger logger = LoggerFactory.getLogger(HandlerWorker.class); + private static final Logger logger = LoggerFactory.getLogger(GreetingWorkflowImpl.class); // Used to serialize concurrent setLanguageUsingActivity calls so that only one activity runs at // a time per update handler execution. diff --git a/core/src/main/java/io/temporal/samples/nexus_messaging/handler/HandlerWorker.java b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/HandlerWorker.java index ea219b0c..e23fff61 100644 --- a/core/src/main/java/io/temporal/samples/nexus_messaging/handler/HandlerWorker.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/HandlerWorker.java @@ -13,9 +13,9 @@ public class HandlerWorker { private static final Logger logger = LoggerFactory.getLogger(HandlerWorker.class); - public static final String NAMESPACE = "nexus-sync-operations-handler-namespace"; - public static final String TASK_QUEUE = "nexus-sync-operations-handler-task-queue"; - static final String WORKFLOW_ID = "nexus-sync-operations-greeting-workflow"; + public static final String NAMESPACE = "nexus-messaging-handler-namespace"; + public static final String TASK_QUEUE = "nexus-messaging-handler-task-queue"; + static final String WORKFLOW_ID = "nexus-messaging-greeting-workflow"; public static void main(String[] args) throws InterruptedException { WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); diff --git a/core/src/main/java/io/temporal/samples/nexus_messaging/handler/NexusGreetingServiceImpl.java b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/NexusGreetingServiceImpl.java index afe9075e..0926f6ce 100644 --- a/core/src/main/java/io/temporal/samples/nexus_messaging/handler/NexusGreetingServiceImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/NexusGreetingServiceImpl.java @@ -53,9 +53,11 @@ public OperationHandler getLang }); } - // 👉 Backed by an update against the long-running entity workflow. Although updates can run for - // an arbitrarily long time, when exposed via a sync Nexus operation the update should complete - // quickly (sync operations must finish in under 10s). + // 👉 Backed by an update against the long-running entity workflow. Routes to + // setLanguageUsingActivity (not setLanguage) so that new languages not already in the greetings + // map can be fetched via an activity. Although updates can run for an arbitrarily long time, when + // exposed via a sync Nexus operation the update should complete quickly (sync operations must + // finish in under 10s). @OperationImpl public OperationHandler setLanguage() { return OperationHandler.sync( diff --git a/core/src/main/java/io/temporal/samples/nexus_messaging/handler/NexusRemoteGreetingServiceImpl.java b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/NexusRemoteGreetingServiceImpl.java index 1deecf60..42867f58 100644 --- a/core/src/main/java/io/temporal/samples/nexus_messaging/handler/NexusRemoteGreetingServiceImpl.java +++ b/core/src/main/java/io/temporal/samples/nexus_messaging/handler/NexusRemoteGreetingServiceImpl.java @@ -32,6 +32,11 @@ private GreetingWorkflow getWorkflowStub(String workflowId) { // Starts a new GreetingWorkflow with the caller-specified workflow ID. This is an async // Nexus operation backed by WorkflowRunOperation. + // + // fromWorkflowHandle (rather than fromWorkflowMethod) is used here because the Nexus operation + // input (RunFromRemoteInput) differs from the workflow method parameters — run() takes no args. + // The input is consumed to set the workflow ID on the stub; the workflow itself is invoked + // with no arguments via the ::run method reference. @OperationImpl public OperationHandler runFromRemote() { return WorkflowRunOperation.fromWorkflowHandle(