Skip to content
Closed
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
154 changes: 154 additions & 0 deletions core/src/main/java/io/temporal/samples/hello/HelloAwait.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved
*
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Modifications copyright (C) 2017 Uber Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
* use this file except in compliance with the License. A copy of the License is
* located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package io.temporal.samples.hello

import io.temporal.client.WorkflowClient
import io.temporal.client.WorkflowOptions
import io.temporal.failure.ApplicationFailure
import io.temporal.serviceclient.WorkflowServiceStubs
import io.temporal.worker.WorkerFactory
import io.temporal.workflow.SignalMethod
import io.temporal.workflow.Workflow
import io.temporal.workflow.WorkflowInterface
import io.temporal.workflow.WorkflowMethod
import java.time.Duration

/**
* Sample Temporal workflow that demonstrates how to use workflow await methods to wait up to a
* specified timeout for a condition updated from a signal handler.
*/
object HelloAwait {
// Define the task queue name
const val TASK_QUEUE: String = "HelloAwaitTaskQueue"

// Define the workflow unique id
const val WORKFLOW_ID: String = "HelloAwaitWorkflow"

/**
* With the Workflow and Activities defined, we can now start execution. The main method starts
* the worker and then the workflow.
*/
@Throws(
Exception::class) @JvmStatic fun main(args: Array<String>) {
// Get a Workflow service stub.

val service = WorkflowServiceStubs.newLocalServiceStubs()

/*
* Get a Workflow service client which can be used to start, Await, and Query Workflow Executions.
*/
val client = WorkflowClient.newInstance(service)

/*
* Define the workflow factory. It is used to create workflow workers for a specific task queue.
*/
val factory = WorkerFactory.newInstance(client)

/*
* Define the workflow worker. Workflow workers listen to a defined task queue and process
* workflows and activities.
*/
val worker = factory.newWorker(TASK_QUEUE)

/*
* Register the workflow implementation with the worker.
* Workflow implementations must be known to the worker at runtime in
* order to dispatch workflow tasks.
*/
worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl::class.java)

/*
* Start all the workers registered for a specific task queue.
* The started workers then start polling for workflows and activities.
*/
factory.start()

// Create the workflow options
val workflowOptions =
WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).setWorkflowId(WORKFLOW_ID).build()

// Create the workflow client stub. It is used to start the workflow execution.
val workflow = client.newWorkflowStub(GreetingWorkflow::class.java, workflowOptions)

// Start workflow asynchronously and call its getGreeting workflow method
WorkflowClient.start<String?> { workflow.getGreeting(HelloAwait.GreetingPayload("foobar")) }

// After start for getGreeting returns, the workflow is guaranteed to be started.
// Send WaitForName signal.
workflow.waitForName("World")

/*
* Here we create a new untyped workflow stub using the same workflow id.
* The untyped stub is a convenient way to wait for a workflow result.
*/
val workflowById = client.newUntypedWorkflowStub(WORKFLOW_ID)

val greeting = workflowById.getResult(String::class.java)

println(greeting)
System.exit(0)
}

data class GreetingPayload(
var idempotenceToken: String
)

/**
* The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.
*
*
* Workflow Definitions should not contain any heavyweight computations, non-deterministic
* code, network calls, database operations, etc. Those things should be handled by the
* Activities.
*
* @see WorkflowInterface
*
* @see WorkflowMethod
*/
@WorkflowInterface
interface GreetingWorkflow {
@WorkflowMethod fun getGreeting(greetingPayload: GreetingPayload): String?

// Define the workflow waitForName signal method. This method is executed when the workflow
// receives a "WaitForName" signal.
@SignalMethod fun waitForName(name: String?)
}

// Define the workflow implementation which implements the getGreetings workflow method.
class GreetingWorkflowImpl : GreetingWorkflow {
private var name: String? = null

override fun getGreeting(greetingPayload: GreetingPayload): String? {
val ok = Workflow.await(Duration.ofSeconds(10)
) { name != null }
if (ok) {
return "Hello $name!"
} else {
// To fail workflow use ApplicationFailure. Any other exception would cause workflow to
// stall, not to fail.
throw ApplicationFailure.newFailure(
"WaitForName signal is not received within 10 seconds.", "signal-timeout")
}
}

override fun waitForName(name: String?) {
this.name = name
}
}
}
98 changes: 98 additions & 0 deletions core/src/test/java/io/temporal/samples/hello/HelloAwaitTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved
*
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Modifications copyright (C) 2017 Uber Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
* use this file except in compliance with the License. A copy of the License is
* located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package io.temporal.samples.hello

import com.squareup.payrollrunner.HelloAwait
import io.temporal.client.WorkflowClient
import io.temporal.client.WorkflowException
import io.temporal.client.WorkflowOptions
import io.temporal.failure.ApplicationFailure
import com.squareup.payrollrunner.HelloAwait.GreetingWorkflow
import io.temporal.testing.TestWorkflowRule
import java.time.Duration
import org.junit.Assert
import org.junit.Rule
import org.junit.Test

/** Unit test for [HelloAwait]. Doesn't use an external Temporal service. */
class HelloAwaitTest {
private val WORKFLOW_ID = "WORKFLOW1"

@get:Rule
var testWorkflowRule: TestWorkflowRule = TestWorkflowRule.newBuilder().setWorkflowTypes(
HelloAwait.GreetingWorkflowImpl::class.java).build()

@Test fun testAwaitSignal() {
// Get a workflow stub using the same task queue the worker uses.
val workflowOptions =
WorkflowOptions.newBuilder()
.setTaskQueue(testWorkflowRule.taskQueue)
.setWorkflowId(WORKFLOW_ID)
.build()

val workflow: GreetingWorkflow =
testWorkflowRule
.workflowClient
.newWorkflowStub(GreetingWorkflow::class.java, workflowOptions)

// Start workflow asynchronously to not use another thread to await.
WorkflowClient.start(workflow::getGreeting, HelloAwait.GreetingPayload("foobar"))
workflow.waitForName("World")

// So we can send a await to it using workflow stub immediately.
// But just to demonstrate the unit testing of a long running workflow adding a long sleep here.
// testWorkflowRule.getTestEnvironment().sleep(Duration.ofSeconds(30));
val workflowById =
testWorkflowRule.workflowClient.newUntypedWorkflowStub(WORKFLOW_ID)

val greeting = workflowById.getResult(String::class.java)
Assert.assertEquals("Hello World!", greeting)
}

@Test fun testAwaitTimeout() {
// Get a workflow stub using the same task queue the worker uses.
val workflowOptions =
WorkflowOptions.newBuilder()
.setTaskQueue(testWorkflowRule.taskQueue)
.setWorkflowId(WORKFLOW_ID)
.build()

val workflow: GreetingWorkflow =
testWorkflowRule
.workflowClient
.newWorkflowStub(GreetingWorkflow::class.java, workflowOptions)

// Start workflow asynchronously to not use another thread to wait.
WorkflowClient.start(workflow::getGreeting, HelloAwait.GreetingPayload("foobar"))

// Skip time to force Await timeout
testWorkflowRule.testEnvironment.sleep(Duration.ofSeconds(30))

val workflowById =
testWorkflowRule.workflowClient.newUntypedWorkflowStub(WORKFLOW_ID)

try {
workflowById.getResult(String::class.java)
Assert.fail("not reachable")
} catch (e: WorkflowException) {
val applicationFailure = e.cause as ApplicationFailure?
Assert.assertEquals("signal-timeout", applicationFailure!!.type)
}
}
}