From 2afd33cba816c58960165ac9f957238f16b3c18a Mon Sep 17 00:00:00 2001 From: Evan Reynolds Date: Fri, 28 Aug 2026 20:46:04 -0700 Subject: [PATCH 1/2] Updated some documentation --- .../nexusmessaging/callerpattern/README.md | 19 +++--- .../handler/NexusGreetingServiceImpl.java | 59 ++++++++++++------ .../nexusmessaging/ondemandpattern/README.md | 19 +++--- .../NexusRemoteGreetingServiceImpl.java | 60 +++++++++++++------ 4 files changed, 108 insertions(+), 49 deletions(-) diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/README.md b/core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/README.md index b369a95f..191ec8d0 100644 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/README.md +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/README.md @@ -15,19 +15,24 @@ The caller Workflow: ### Running -Start a Temporal server: +Start a compatible Temporal dev server with Workflow Update callbacks enabled: ```bash -temporal server start-dev +./temporal server start-dev \ + --dynamic-config-value history.enableCHASMCallbacks=true \ + --dynamic-config-value history.enableUpdateCallbacks=true \ + --dynamic-config-value history.enableCHASMSignalBacklinks=true \ + --namespace nexus-messaging-handler-namespace \ + --namespace nexus-messaging-caller-namespace ``` -Create the namespaces and Nexus endpoint: +This sample requires a Temporal dev-server build that supports Workflow Update callbacks. Download the compatible +binary from the [Temporal CLI pre-release instructions](https://docs.temporal.io/standalone-nexus-operation#temporal-cli-support). -```bash -temporal operator namespace create --namespace nexus-messaging-handler-namespace -temporal operator namespace create --namespace nexus-messaging-caller-namespace +Create the Nexus endpoint: -temporal operator nexus endpoint create \ +```bash +./temporal operator nexus endpoint create \ --name nexus-messaging-nexus-endpoint \ --target-namespace nexus-messaging-handler-namespace \ --target-task-queue nexus-messaging-handler-task-queue diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/handler/NexusGreetingServiceImpl.java b/core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/handler/NexusGreetingServiceImpl.java index 2a6d04c6..74bc763f 100644 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/handler/NexusGreetingServiceImpl.java +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/handler/NexusGreetingServiceImpl.java @@ -3,7 +3,11 @@ import io.nexusrpc.handler.OperationHandler; import io.nexusrpc.handler.OperationImpl; import io.nexusrpc.handler.ServiceImpl; -import io.temporal.nexus.Nexus; +import io.temporal.client.UpdateOptions; +import io.temporal.client.WorkflowUpdateStage; +import io.temporal.nexus.TemporalNexusClient; +import io.temporal.nexus.TemporalOperationHandler; +import io.temporal.nexus.TemporalOperationResult; import io.temporal.samples.nexusmessaging.callerpattern.service.Language; import io.temporal.samples.nexusmessaging.callerpattern.service.NexusGreetingService; import org.slf4j.Logger; @@ -11,8 +15,12 @@ /** * Nexus operation handler implementation. Each operation receives a userId, which is mapped to a - * workflow ID using {@link #WORKFLOW_ID_PREFIX}. The operations are synchronous because queries and - * updates against a running workflow complete quickly. + * workflow ID using {@link #WORKFLOW_ID_PREFIX}. + * + *

Every operation is a {@link TemporalOperationHandler}: the start handler receives a {@link + * TemporalNexusClient} scoped to the invocation and returns a {@link TemporalOperationResult}. The + * queries and the signal complete inline and return a sync result; {@code setLanguage} starts the + * update through the Nexus client, which makes it an asynchronous operation. */ @ServiceImpl(service = NexusGreetingService.class) public class NexusGreetingServiceImpl { @@ -29,8 +37,8 @@ public static String getWorkflowId(String userId) { return WORKFLOW_ID_PREFIX + userId; } - private GreetingWorkflow getWorkflowStub(String userId) { - return Nexus.getOperationContext() + private GreetingWorkflow getWorkflowStub(TemporalNexusClient client, String userId) { + return client .getWorkflowClient() .newWorkflowStub(GreetingWorkflow.class, getWorkflowId(userId)); } @@ -39,41 +47,56 @@ private GreetingWorkflow getWorkflowStub(String userId) { public OperationHandler< NexusGreetingService.GetLanguagesInput, NexusGreetingService.GetLanguagesOutput> getLanguages() { - return OperationHandler.sync( - (ctx, details, input) -> { + return TemporalOperationHandler.create( + (ctx, client, input) -> { logger.info("Query for GetLanguages was received for user {}", input.getUserId()); - return getWorkflowStub(input.getUserId()).getLanguages(input); + return TemporalOperationResult.sync( + getWorkflowStub(client, input.getUserId()).getLanguages(input)); }); } @OperationImpl public OperationHandler getLanguage() { - return OperationHandler.sync( - (ctx, details, input) -> { + return TemporalOperationHandler.create( + (ctx, client, input) -> { logger.info("Query for GetLanguage was received for user {}", input.getUserId()); - return getWorkflowStub(input.getUserId()).getLanguage(); + return TemporalOperationResult.sync( + getWorkflowStub(client, input.getUserId()).getLanguage()); }); } // Routes to setLanguageUsingActivity (not setLanguage) so that new languages not already in the // greetings map can be fetched via an activity. + // + // Starting the update through the Nexus client makes this an asynchronous Nexus operation: the + // caller receives an operation token and the update result is delivered later over the Nexus + // completion callback. If the update is already complete when the start call returns, the result + // comes back synchronously instead. @OperationImpl public OperationHandler setLanguage() { - return OperationHandler.sync( - (ctx, details, input) -> { + return TemporalOperationHandler.create( + (ctx, client, input) -> { logger.info("Update for SetLanguage was received for user {}", input.getUserId()); - return getWorkflowStub(input.getUserId()).setLanguageUsingActivity(input); + return client.startWorkflowUpdate( + GreetingWorkflow.class, + getWorkflowId(input.getUserId()), + GreetingWorkflow::setLanguageUsingActivity, + input, + UpdateOptions.newBuilder(Language.class) + .setUpdateName("setLanguageUsingActivity") + .setWaitForStage(WorkflowUpdateStage.ACCEPTED) + .build()); }); } @OperationImpl public OperationHandler approve() { - return OperationHandler.sync( - (ctx, details, input) -> { + return TemporalOperationHandler.create( + (ctx, client, input) -> { logger.info("Signal for Approve was received for user {}", input.getUserId()); - getWorkflowStub(input.getUserId()).approve(input); - return new NexusGreetingService.ApproveOutput(); + getWorkflowStub(client, input.getUserId()).approve(input); + return TemporalOperationResult.sync(new NexusGreetingService.ApproveOutput()); }); } } diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/README.md b/core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/README.md index c987e85f..e179eeba 100644 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/README.md +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/README.md @@ -15,19 +15,24 @@ The caller Workflow: ### Running -Start a Temporal server: +Start a compatible Temporal dev server with Workflow Update callbacks enabled: ```bash -temporal server start-dev +./temporal server start-dev \ + --dynamic-config-value history.enableCHASMCallbacks=true \ + --dynamic-config-value history.enableUpdateCallbacks=true \ + --dynamic-config-value history.enableCHASMSignalBacklinks=true \ + --namespace nexus-messaging-handler-namespace \ + --namespace nexus-messaging-caller-namespace ``` -Create the namespaces and Nexus endpoint: +This sample requires a Temporal dev-server build that supports Workflow Update callbacks. Download the compatible +binary from the [Temporal CLI pre-release instructions](https://docs.temporal.io/standalone-nexus-operation#temporal-cli-support). -```bash -temporal operator namespace create --namespace nexus-messaging-handler-namespace -temporal operator namespace create --namespace nexus-messaging-caller-namespace +Create the Nexus endpoint: -temporal operator nexus endpoint create \ +```bash +./temporal operator nexus endpoint create \ --name nexus-messaging-nexus-endpoint \ --target-namespace nexus-messaging-handler-namespace \ --target-task-queue nexus-messaging-handler-task-queue diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/handler/NexusRemoteGreetingServiceImpl.java b/core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/handler/NexusRemoteGreetingServiceImpl.java index b7397987..b373959a 100644 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/handler/NexusRemoteGreetingServiceImpl.java +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/handler/NexusRemoteGreetingServiceImpl.java @@ -3,8 +3,13 @@ import io.nexusrpc.handler.OperationHandler; import io.nexusrpc.handler.OperationImpl; import io.nexusrpc.handler.ServiceImpl; +import io.temporal.client.UpdateOptions; import io.temporal.client.WorkflowOptions; +import io.temporal.client.WorkflowUpdateStage; import io.temporal.nexus.Nexus; +import io.temporal.nexus.TemporalNexusClient; +import io.temporal.nexus.TemporalOperationHandler; +import io.temporal.nexus.TemporalOperationResult; import io.temporal.nexus.WorkflowHandle; import io.temporal.nexus.WorkflowRunOperation; import io.temporal.samples.nexusmessaging.ondemandpattern.service.Language; @@ -15,6 +20,12 @@ /** * Nexus operation handler for the on-demand pattern. Each operation receives the target workflow ID * in its input, and {@code runFromRemote} starts a brand-new GreetingWorkflow. + * + *

The messaging operations are {@link TemporalOperationHandler}s: the start handler receives a + * {@link TemporalNexusClient} scoped to the invocation and returns a {@link + * TemporalOperationResult}. The queries and the signal complete inline and return a sync result; + * {@code setLanguage} starts the update through the Nexus client, which makes it an asynchronous + * operation. */ @ServiceImpl(service = NexusRemoteGreetingService.class) public class NexusRemoteGreetingServiceImpl { @@ -32,8 +43,8 @@ public static String getWorkflowId(String userId) { return WORKFLOW_ID_PREFIX + userId; } - private GreetingWorkflow getWorkflowStub(String userId) { - return Nexus.getOperationContext() + private GreetingWorkflow getWorkflowStub(TemporalNexusClient client, String userId) { + return client .getWorkflowClient() .newWorkflowStub(GreetingWorkflow.class, getWorkflowId(userId)); } @@ -63,31 +74,46 @@ public OperationHandler r NexusRemoteGreetingService.GetLanguagesInput, NexusRemoteGreetingService.GetLanguagesOutput> getLanguages() { - return OperationHandler.sync( - (ctx, details, input) -> { + return TemporalOperationHandler.create( + (ctx, client, input) -> { logger.info("Query for GetLanguages was received for userId {}", input.getUserId()); - return getWorkflowStub(input.getUserId()) - .getLanguages(new GreetingWorkflow.GetLanguagesInput(input.isIncludeUnsupported())); + return TemporalOperationResult.sync( + getWorkflowStub(client, input.getUserId()) + .getLanguages( + new GreetingWorkflow.GetLanguagesInput(input.isIncludeUnsupported()))); }); } @OperationImpl public OperationHandler getLanguage() { - return OperationHandler.sync( - (ctx, details, input) -> { + return TemporalOperationHandler.create( + (ctx, client, input) -> { logger.info("Query for GetLanguage was received for userId {}", input.getUserId()); - return getWorkflowStub(input.getUserId()).getLanguage(); + return TemporalOperationResult.sync( + getWorkflowStub(client, input.getUserId()).getLanguage()); }); } // Uses setLanguageUsingActivity so that new languages are fetched via an activity. + // + // Starting the update through the Nexus client makes this an asynchronous Nexus operation: the + // caller receives an operation token and the update result is delivered later over the Nexus + // completion callback. If the update is already complete when the start call returns, the result + // comes back synchronously instead. @OperationImpl public OperationHandler setLanguage() { - return OperationHandler.sync( - (ctx, details, input) -> { + return TemporalOperationHandler.create( + (ctx, client, input) -> { logger.info("Update for SetLanguage was received for userId {}", input.getUserId()); - return getWorkflowStub(input.getUserId()) - .setLanguageUsingActivity(new GreetingWorkflow.SetLanguageInput(input.getLanguage())); + return client.startWorkflowUpdate( + GreetingWorkflow.class, + getWorkflowId(input.getUserId()), + GreetingWorkflow::setLanguageUsingActivity, + new GreetingWorkflow.SetLanguageInput(input.getLanguage()), + UpdateOptions.newBuilder(Language.class) + .setUpdateName("setLanguageUsingActivity") + .setWaitForStage(WorkflowUpdateStage.ACCEPTED) + .build()); }); } @@ -95,12 +121,12 @@ public OperationHandler s public OperationHandler< NexusRemoteGreetingService.ApproveInput, NexusRemoteGreetingService.ApproveOutput> approve() { - return OperationHandler.sync( - (ctx, details, input) -> { + return TemporalOperationHandler.create( + (ctx, client, input) -> { logger.info("Signal for Approve was received for userId {}", input.getUserId()); - getWorkflowStub(input.getUserId()) + getWorkflowStub(client, input.getUserId()) .approve(new GreetingWorkflow.ApproveInput(input.getName())); - return new NexusRemoteGreetingService.ApproveOutput(); + return TemporalOperationResult.sync(new NexusRemoteGreetingService.ApproveOutput()); }); } } From b9d49e32246249b9841b7547885a21cefcc98f6c Mon Sep 17 00:00:00 2001 From: Evan Reynolds Date: Fri, 4 Sep 2026 17:00:44 -0700 Subject: [PATCH 2/2] Update the readme files --- .../nexusmessaging/callerpattern/README.md | 21 ++++++++++--------- .../nexusmessaging/ondemandpattern/README.md | 21 ++++++++++--------- .../samples/nexusstandalone/README.MD | 1 + .../samples/nexusstandaloneactivity/README.md | 1 + 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/README.md b/core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/README.md index 191ec8d0..56949142 100644 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/README.md +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/README.md @@ -15,24 +15,25 @@ The caller Workflow: ### Running -Start a compatible Temporal dev server with Workflow Update callbacks enabled: +Start a Temporal server with Workflow Update callbacks enabled: ```bash -./temporal server start-dev \ - --dynamic-config-value history.enableCHASMCallbacks=true \ +temporal server start-dev \ --dynamic-config-value history.enableUpdateCallbacks=true \ - --dynamic-config-value history.enableCHASMSignalBacklinks=true \ - --namespace nexus-messaging-handler-namespace \ - --namespace nexus-messaging-caller-namespace + --dynamic-config-value history.enableCHASMSignalBacklinks=true ``` -This sample requires a Temporal dev-server build that supports Workflow Update callbacks. Download the compatible -binary from the [Temporal CLI pre-release instructions](https://docs.temporal.io/standalone-nexus-operation#temporal-cli-support). +`history.enableUpdateCallbacks` defaults to `false`. The `setLanguage` operation starts its Update +through the Nexus client, so its result is delivered over a Nexus completion callback; without this +flag the callback is never registered and the operation never completes. -Create the Nexus endpoint: +Create the namespaces and Nexus endpoint: ```bash -./temporal operator nexus endpoint create \ +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-messaging-nexus-endpoint \ --target-namespace nexus-messaging-handler-namespace \ --target-task-queue nexus-messaging-handler-task-queue diff --git a/core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/README.md b/core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/README.md index e179eeba..cd734102 100644 --- a/core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/README.md +++ b/core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/README.md @@ -15,24 +15,25 @@ The caller Workflow: ### Running -Start a compatible Temporal dev server with Workflow Update callbacks enabled: +Start a Temporal server with Workflow Update callbacks enabled: ```bash -./temporal server start-dev \ - --dynamic-config-value history.enableCHASMCallbacks=true \ +temporal server start-dev \ --dynamic-config-value history.enableUpdateCallbacks=true \ - --dynamic-config-value history.enableCHASMSignalBacklinks=true \ - --namespace nexus-messaging-handler-namespace \ - --namespace nexus-messaging-caller-namespace + --dynamic-config-value history.enableCHASMSignalBacklinks=true ``` -This sample requires a Temporal dev-server build that supports Workflow Update callbacks. Download the compatible -binary from the [Temporal CLI pre-release instructions](https://docs.temporal.io/standalone-nexus-operation#temporal-cli-support). +`history.enableUpdateCallbacks` defaults to `false`. The `setLanguage` operation starts its Update +through the Nexus client, so its result is delivered over a Nexus completion callback; without this +flag the callback is never registered and the operation never completes. -Create the Nexus endpoint: +Create the namespaces and Nexus endpoint: ```bash -./temporal operator nexus endpoint create \ +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-messaging-nexus-endpoint \ --target-namespace nexus-messaging-handler-namespace \ --target-task-queue nexus-messaging-handler-task-queue diff --git a/core/src/main/java/io/temporal/samples/nexusstandalone/README.MD b/core/src/main/java/io/temporal/samples/nexusstandalone/README.MD index eee6d684..a1de93a7 100644 --- a/core/src/main/java/io/temporal/samples/nexusstandalone/README.MD +++ b/core/src/main/java/io/temporal/samples/nexusstandalone/README.MD @@ -30,6 +30,7 @@ from the environment (and optionally a profile from `temporal.toml`). ```bash ./temporal server start-dev \ + --dynamic-config-value nexusoperation.enableStandalone=true \ --namespace my-caller-namespace \ --namespace my-handler-namespace ``` diff --git a/core/src/main/java/io/temporal/samples/nexusstandaloneactivity/README.md b/core/src/main/java/io/temporal/samples/nexusstandaloneactivity/README.md index fb84bd6a..2d9f23a2 100644 --- a/core/src/main/java/io/temporal/samples/nexusstandaloneactivity/README.md +++ b/core/src/main/java/io/temporal/samples/nexusstandaloneactivity/README.md @@ -33,6 +33,7 @@ from the environment (and optionally a profile from `temporal.toml`). ```bash ./temporal server start-dev \ + --dynamic-config-value nexusoperation.enableStandalone=true \ --dynamic-config-value activity.enableCallbacks=true \ --namespace my-caller-namespace \ --namespace my-handler-namespace