Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ See the README.md file in each main sample directory for cut/paste Gradle comman

- [**Getting Started**](/core/src/main/java/io/temporal/samples/nexus): Demonstrates how to get started with Temporal and Nexus.

- [**Mapping Multiple Arguments**](/core/src/main/java/io/temporal/samples/nexus): Demonstrates how map a Nexus operation to a Workflow that takes multiple arguments.

- [**Cancellation**](/core/src/main/java/io/temporal/samples/nexuscancellation): Demonstrates how to cancel an async Nexus operation.

- [**Context/Header Propagation**](/core/src/main/java/io/temporal/samples/nexuscontextpropagation): Demonstrates how to propagate context through Nexus operation headers.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Nexus Multiple Arguments Sample

This sample shows how to map a Nexus operation to a caller workflow that takes multiple input arguments using [WorkflowRunOperation.fromWorkflowHandle](https://javadoc.io/doc/io.temporal/temporal-sdk/latest/io/temporal/nexus/WorkflowRunOperation.html#fromWorkflowHandle(io.temporal.nexus.WorkflowHandleFactory)).

To run this sample, set up your environment following the instructions in the main [Nexus Sample](../nexus/README.md).

In separate terminal windows:

### Nexus handler worker

```
./gradlew -q execute -PmainClass=io.temporal.samples.nexusmultipleargs.handler.HandlerWorker \
--args="-target-host localhost:7233 -namespace my-target-namespace"
```

### Nexus caller worker

```
./gradlew -q execute -PmainClass=io.temporal.samples.nexusmultipleargs.caller.CallerWorker \
--args="-target-host localhost:7233 -namespace my-caller-namespace"
```

### Start caller workflow

```
./gradlew -q execute -PmainClass=io.temporal.samples.nexusmultipleargs.caller.CallerStarter \
--args="-target-host localhost:7233 -namespace my-caller-namespace"
```

### Output

which should result in:
```
[main] INFO i.t.s.nexus.caller.CallerStarter - Workflow result: Nexus Echo 👋
[main] INFO i.t.s.nexus.caller.CallerStarter - Workflow result: ¡Hola! Nexus 👋
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package io.temporal.samples.nexusmultipleargs.caller;

import io.temporal.api.common.v1.WorkflowExecution;
import io.temporal.client.WorkflowClient;
import io.temporal.client.WorkflowOptions;
import io.temporal.samples.nexus.caller.CallerWorker;
import io.temporal.samples.nexus.caller.EchoCallerWorkflow;
import io.temporal.samples.nexus.caller.HelloCallerWorkflow;
import io.temporal.samples.nexus.options.ClientOptions;
import io.temporal.samples.nexus.service.NexusService;
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();
EchoCallerWorkflow echoWorkflow =
client.newWorkflowStub(EchoCallerWorkflow.class, workflowOptions);
WorkflowExecution execution = WorkflowClient.start(echoWorkflow::echo, "Nexus Echo 👋");
logger.info(
"Started EchoCallerWorkflow workflowId: {} runId: {}",
execution.getWorkflowId(),
execution.getRunId());
logger.info("Workflow result: {}", echoWorkflow.echo("Nexus Echo 👋"));
HelloCallerWorkflow helloWorkflow =
client.newWorkflowStub(HelloCallerWorkflow.class, workflowOptions);
execution = WorkflowClient.start(helloWorkflow::hello, "Nexus", NexusService.Language.EN);
logger.info(
"Started HelloCallerWorkflow workflowId: {} runId: {}",
execution.getWorkflowId(),
execution.getRunId());
logger.info("Workflow result: {}", helloWorkflow.hello("Nexus", NexusService.Language.ES));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package io.temporal.samples.nexusmultipleargs.caller;

import io.temporal.client.WorkflowClient;
import io.temporal.samples.nexus.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(
"NexusService",
NexusServiceOptions.newBuilder().setEndpoint("my-nexus-endpoint-name").build()))
.build(),
EchoCallerWorkflowImpl.class,
HelloCallerWorkflowImpl.class);

factory.start();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.temporal.samples.nexusmultipleargs.caller;

import io.temporal.workflow.WorkflowInterface;
import io.temporal.workflow.WorkflowMethod;

@WorkflowInterface
public interface EchoCallerWorkflow {
@WorkflowMethod
String echo(String message);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.temporal.samples.nexusmultipleargs.caller;

import io.temporal.samples.nexus.caller.EchoCallerWorkflow;
import io.temporal.samples.nexus.service.NexusService;
import io.temporal.workflow.NexusOperationOptions;
import io.temporal.workflow.NexusServiceOptions;
import io.temporal.workflow.Workflow;
import java.time.Duration;

public class EchoCallerWorkflowImpl implements EchoCallerWorkflow {
NexusService nexusService =
Workflow.newNexusServiceStub(
NexusService.class,
NexusServiceOptions.newBuilder()
.setOperationOptions(
NexusOperationOptions.newBuilder()
.setScheduleToCloseTimeout(Duration.ofSeconds(10))
.build())
.build());

@Override
public String echo(String message) {
return nexusService.echo(new NexusService.EchoInput(message)).getMessage();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.temporal.samples.nexusmultipleargs.caller;

import io.temporal.samples.nexus.service.NexusService;
import io.temporal.workflow.WorkflowInterface;
import io.temporal.workflow.WorkflowMethod;

@WorkflowInterface
public interface HelloCallerWorkflow {
@WorkflowMethod
String hello(String message, NexusService.Language language);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package io.temporal.samples.nexusmultipleargs.caller;

import io.temporal.samples.nexus.caller.HelloCallerWorkflow;
import io.temporal.samples.nexus.service.NexusService;
import io.temporal.workflow.NexusOperationHandle;
import io.temporal.workflow.NexusOperationOptions;
import io.temporal.workflow.NexusServiceOptions;
import io.temporal.workflow.Workflow;
import java.time.Duration;

public class HelloCallerWorkflowImpl implements HelloCallerWorkflow {
NexusService nexusService =
Workflow.newNexusServiceStub(
NexusService.class,
NexusServiceOptions.newBuilder()
.setOperationOptions(
NexusOperationOptions.newBuilder()
.setScheduleToCloseTimeout(Duration.ofSeconds(10))
.build())
.build());

@Override
public String hello(String message, NexusService.Language language) {
NexusOperationHandle<NexusService.HelloOutput> handle =
Workflow.startNexusOperation(
nexusService::hello, new NexusService.HelloInput(message, language));
// Optionally wait for the operation to be started. NexusOperationExecution will contain the
// operation token in case this operation is asynchronous.
handle.getExecution().get();
return handle.getResult().get().getMessage();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.temporal.samples.nexusmultipleargs.handler;

import io.temporal.client.WorkflowClient;
import io.temporal.samples.nexus.options.ClientOptions;
import io.temporal.worker.Worker;
import io.temporal.worker.WorkerFactory;

public class HandlerWorker {
public static final String DEFAULT_TASK_QUEUE_NAME = "my-handler-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(HelloHandlerWorkflowImpl.class);
worker.registerNexusServiceImplementation(new NexusServiceImpl());

factory.start();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.temporal.samples.nexusmultipleargs.handler;

import io.temporal.samples.nexus.service.NexusService;
import io.temporal.workflow.WorkflowInterface;
import io.temporal.workflow.WorkflowMethod;

@WorkflowInterface
public interface HelloHandlerWorkflow {
@WorkflowMethod
NexusService.HelloOutput hello(String name, NexusService.Language language);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.temporal.samples.nexusmultipleargs.handler;

import io.temporal.failure.ApplicationFailure;
import io.temporal.samples.nexus.service.NexusService;

public class HelloHandlerWorkflowImpl implements HelloHandlerWorkflow {
@Override
public NexusService.HelloOutput hello(String name, NexusService.Language language) {
switch (language) {
case EN:
return new NexusService.HelloOutput("Hello " + name + " 👋");
case FR:
return new NexusService.HelloOutput("Bonjour " + name + " 👋");
case DE:
return new NexusService.HelloOutput("Hallo " + name + " 👋");
case ES:
return new NexusService.HelloOutput("¡Hola! " + name + " 👋");
case TR:
return new NexusService.HelloOutput("Merhaba " + name + " 👋");
}
throw ApplicationFailure.newFailure(
"Unsupported language: " + language, "UNSUPPORTED_LANGUAGE");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package io.temporal.samples.nexusmultipleargs.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.service.NexusService;

// To create a service implementation, annotate the class with @ServiceImpl and provide the
// interface that the service implements. The service implementation class should have methods that
// return OperationHandler that correspond to the operations defined in the service interface.
@ServiceImpl(service = NexusService.class)
public class NexusServiceImpl {
@OperationImpl
public OperationHandler<NexusService.EchoInput, NexusService.EchoOutput> echo() {
// OperationHandler.sync is a meant for exposing simple RPC handlers.
return OperationHandler.sync(
// The method is for making arbitrary short calls to other services or databases, or
// perform simple computations such as this one. Users can also access a workflow client by
// calling
// Nexus.getOperationContext().getWorkflowClient(ctx) to make arbitrary calls such as
// signaling, querying, or listing workflows.
(ctx, details, input) -> new NexusService.EchoOutput(input.getMessage()));
}

@OperationImpl
public OperationHandler<NexusService.HelloInput, NexusService.HelloOutput> hello() {
// If the operation input parameters are different from the workflow input parameters,
// use the WorkflowRunOperation.fromWorkflowHandler constructor and the appropriate constructor
// method on WorkflowHandle to map the Nexus input to the workflow parameters.
return WorkflowRunOperation.fromWorkflowHandle(
(ctx, details, input) ->
WorkflowHandle.fromWorkflowMethod(
Nexus.getOperationContext()
.getWorkflowClient()
.newWorkflowStub(
HelloHandlerWorkflow.class,
// Workflow IDs should typically be business meaningful IDs and are used
// to
// dedupe workflow starts.
// For this example, we're using the request ID allocated by Temporal
// when
// the
// caller workflow schedules
// the operation, this ID is guaranteed to be stable across retries of
// this
// operation.
//
// Task queue defaults to the task queue this operation is handled on.
WorkflowOptions.newBuilder()
.setWorkflowId(details.getRequestId())
.build())
::hello,
input.getName(),
input.getLanguage()));
}
}
Loading