Communicate between Java and Go workers

We have a java backend and a Go backend. How can I start a workflow written in Java that is registered with the java worker from Go?

I also want to do the reverse.

I saw in the docs that we import a workflow in a worker and register the workflow and activities. So I can’t figure out how in Go I can get the java code and register in the worker.

Also, does a worker only listen for one task queue name? and any workflow that is started with a task queue name either in Go or java is executed by a worker that listens for that task queue name? Is my understanding correct?

I’m a frontend developer who just started learning Go and all this is a bit overwhelming. So please help me out… if possible with an example.

The simplest option is to use a workflow type name when starting a workflow.

	run, err := ts.client.ExecuteWorkflow(ctx, options, "<JavaWorkflowTypeName>", args...)

By default (unless overridden through @WorkflowMethod.name) the workflow type name is the short name of the workflow interface.

I also want to do the reverse.

    WorkflowStub workflow =
        workflowClient.newUntypedWorkflowStub("<GoWorkflowType>", workflowOptions);
    workflow.start(args);

I saw in the docs that we import a workflow in a worker and register the workflow and activities. So I can’t figure out how in Go I can get the java code and register in the worker.

Only activities and workflows that a given worker implements have to be registered. So if a workflow starts activities that its worker doesn’t implement they don’t need to be registered.

Also, does a worker only listen for one task queue name? and any workflow that is started with a task queue name either in Go or java is executed by a worker that listens for that task queue name? Is my understanding correct?

Yes, this is correct. One important implication is that workers that implement different sets of workflows and activities must listen to different task queues. It also implies that when calling workflows and activities a correct task queue must be always specified.

I’m a frontend developer who just started learning Go and all this is a bit overwhelming. So please help me out… if possible with an example.

Welcome to Temporal, feel free to ask more questions when in doubt. We do plan to add the cross-language example.

FWIW this is how I’m starting workflows from a Kotlin Ktor server using the Temporal java-sdk. The workflows are all hosted by workers running on separate services in our cloud, and are currently written in Go.

So to call one of the golang Workflows (from the Temporal samples) with a function definition:

func TransferMoney(ctx workflow.Context, transferDetails TransferDetails) error {

And the TransferDetails struct:

type TransferDetails struct {
    Amount      float32
    FromAccount string
    ToAccount   string
    ReferenceID string
}

This is the simplified Kotlin code I use:

// Set up the connection to the temporal server
val hostPort = "my-temporal-service-frontend.default.svc.cluster.local:7233"
val workflowServiceStubsOptions = WorkflowServiceStubsOptions
    .newBuilder()
    .setTarget(hostPort)
    .build()
val serviceStubs = WorkflowServiceStubs.newInstance(workflowServiceStubsOptions)

// Get a client instance (one per Temporal namespace)
val namespace = "examples"
val workflowClient = WorkflowClient.newInstance(
        serviceStubs,
        WorkflowClientOptions
            .newBuilder()
            .setNamespace(namespace)
            .build()
)

// Create a stub we'll use to start the workflow
val workflowType = "TransferMoney"
val taskQueue = "TRANSFER_MONEY_TASK_QUEUE"
val workflowOptions = WorkflowOptions
        .newBuilder()
        .setTaskQueue(taskQueue)
        .build()
val workflowStub = workflowClient.newUntypedWorkflowStub(workflowType, workflowOptions)

// Temporal expects the workflow params in a specific format, the SDK will use a JSON encoder
// to serialize the args to that format.  Since you're kicking off a workflow that was written in Go 
// you likely don't have access to the `TransferDetails` struct within your code, so you have to either
// model up a POJO or data class, or mock up a string template like the 'jsonString' above.  It's far
// easier IMO to give the _start()_ method something the JSON parser can encode than trying to send 
// it a String - unless the workflow param is an actual golang `string`.
val jsonString = "{\n" +
        "    \"Amount\":22.19, \n" +
        "    \"FromAccount\":\"acct1\",\n" +
        "    \"ToAccount\":\"acct2\",\n" +
        "    \"ReferenceID\":\"my-test-3\"\n" +
        "}"
val workflowArgs: JsonNode = jacksonObjectMapper().readTree(jsonString)
val execution = workflowStub.start(workflowArgs)

I did have a few issues passing workflow arguments which is why I wanted to post this. There isn’t a lot in the documentation about how to send an argument payload that the Temporal service will understand, so maybe this will save someone a lot of trial and error.

To start a workflow running in another application in your cloud you need to know:

  • The namespace in which the Workflow belongs
  • The taskQueue to which the Workflow implementation has subscribed
  • The workflowType which is generally the class name
  • The workflow parameters (args) and types.

Hope this helps.

Thank you, @Fredric_Doddridge for the code sample. I personally think that passing raw JSON is not a ideal in this case. I think having POJO that matches the Go structure is a much cleaner way to pass arguments across languages. Another option is to use Protobufs which have code generation to these languages.

Yeah I agree, using raw JSON is messy. For example only :slight_smile:

For most of our apps we use a schema repository so we can use strongly typed structures. I’d like to make more use of protobufs. The code above is from a service we expose to a third-party that sends us a JSON payload to start one of an approved list of workflows in our system, based on auth:

data class StartWorkflowRequest(val namespace: String = "default", val workflowType: String, val taskQueue: String, val workflowArgs: Iterable<JsonNode>?)

I made it generic to have a single endpoint, but that also means I don’t know the parameter types ahead of time. The client sends a payload that looks like this:

{ 
  "namespace": "examples", 
  "workflowType": "TransferMoney", 
  "taskQueue": "TRANSFER_MONEY_TASK_QUEUE", 
  "workflowArgs": [
    {
      "Amount":22.19, 
      "FromAccount":"acct1",
      "ToAccount":"acct2",
      "ReferenceID":"my-test-3"
    }
  ]
}

@maxim thanks for replying.

How can I register a workflow in Go if there’s only one worker that is started by java code?

How can I register a workflow in Go if there’s only one worker that is started by java code?

I don’t understand the question. What are you trying to achieve from the business logic point of view?