Workshop 4: HelloWorkflow
Your First Temporal Workflow & Activity
Create your first complete Temporal workflow - the "Hello World" for distributed workflows!
What we want to build
Create your first complete Temporal workflow that demonstrates the core concepts: a workflow that orchestrates an activity to generate a personalized greeting.
This is the classic "Hello World" for distributed workflows!
Expecting Result
By the end of this lesson, you'll have:
- ✅ Working HelloWorkflow that takes a name and returns a greeting
- ✅ GreetingActivity that generates the actual greeting message
- ✅ Complete Temporal configuration that registers both components
- ✅ Runner that executes the workflow automatically
- ✅ Console output showing successful workflow execution
- ✅ Your first workflow visible in the Temporal Web UI
Code Steps
Step 1: Create the Workflow Interface
Open src/workshop/lesson_4/workflow/HelloWorkflow.kt
and replace the TODO comments:
package com.temporal.bootcamp.lesson4.workflow
import io.temporal.workflow.WorkflowInterface
import io.temporal.workflow.WorkflowMethod
@WorkflowInterface
interface HelloWorkflow {
@WorkflowMethod
fun sayHello(name: String): String
}
Workflow Interface Key Points
Important Annotations:
@WorkflowInterface
marks this as a workflow contract@WorkflowMethod
marks the main entry point for the workflow
Design Principles:
- Keep it simple: one input parameter, one return value
- This defines what your workflow does, not how it does it
Step 2: Create the Activity Interface
Open src/workshop/lesson_4/activity/GreetingActivity.kt
:
package com.temporal.bootcamp.lesson4.activity
import io.temporal.activity.ActivityInterface
import io.temporal.activity.ActivityMethod
@ActivityInterface
interface GreetingActivity {
@ActivityMethod
fun generateGreeting(name: String): String
}
Activity Interface Key Points
Important Annotations:
@ActivityInterface
marks this as an activity contract@ActivityMethod
marks the method that will be executed by workers
Design Principles:
- Activities do the actual work (business logic)
- Can make external calls, access databases, etc.
Step 3: Implement the Activity
Open src/workshop/lesson_4/activity/GreetingActivityImpl.kt
:
package com.temporal.bootcamp.lesson4.activity
import mu.KotlinLogging
import org.springframework.stereotype.Component
@Component
class GreetingActivityImpl : GreetingActivity {
private val logger = KotlinLogging.logger {}
override fun generateGreeting(name: String): String {
logger.info { "Generating greeting for: $name" }
// Simulate some processing
Thread.sleep(100)
val greeting = "Hello, $name! Welcome to Temporal workflows!"
logger.info { "Generated greeting: $greeting" }
return greeting
}
}
Activity Implementation Key Points
Important Features:
@Component
makes this a Spring-managed bean- Add logging to see when activities execute
- Simulate processing with Thread.sleep()
Real-World Usage:
- This is where real business logic would go
- Database calls, API calls, file processing, etc.
Step 4: Implement the Workflow
Open src/workshop/lesson_4/workflow/HelloWorkflowImpl.kt
:
package com.temporal.bootcamp.lesson4.workflow
import com.temporal.answer.lesson_4.activity.GreetingActivity
import io.temporal.activity.ActivityOptions
import io.temporal.workflow.Workflow
import java.time.Duration
class HelloWorkflowImpl : HelloWorkflow {
private val greetingActivity = Workflow.newActivityStub(
GreetingActivity::class.java,
ActivityOptions.newBuilder()
.setScheduleToCloseTimeout(Duration.ofMinutes(1))
.setStartToCloseTimeout(Duration.ofSeconds(30))
.build()
)
// Continued on next slide...
Workflow Implementation Continued
override fun sayHello(name: String): String {
val logger = Workflow.getLogger(this::class.java)
logger.info("HelloWorkflow started for: $name")
// Call the activity
val greeting = greetingActivity.generateGreeting(name)
logger.info("HelloWorkflow completed for: $name")
return greeting
}
}
Workflow Implementation Key Points
Important Concepts:
- Create activity stub with timeout configuration
- Use
Workflow.getLogger()
for workflow logging - Keep workflow logic simple - just orchestration
Activity Stub Configuration:
- ScheduleToCloseTimeout: Total time for activity (including retries)
- StartToCloseTimeout: Time for single activity execution
Step 5: Configure Temporal (Part 1)
Open src/workshop/lesson_4/config/TemporalConfig.kt
and start building the configuration:
package com.temporal.bootcamp.lesson4.config
import com.temporal.answer.lesson_4.activity.GreetingActivityImpl
import com.temporal.answer.lesson_4.workflow.HelloWorkflowImpl
import io.temporal.client.WorkflowClient
import io.temporal.serviceclient.WorkflowServiceStubs
import io.temporal.worker.Worker
import io.temporal.worker.WorkerFactory
import mu.KotlinLogging
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import javax.annotation.PostConstruct
import javax.annotation.PreDestroy
@Configuration
class TemporalConfig {
companion object {
const val TASK_QUEUE = "lesson4-hello-queue"
}
private val logger = KotlinLogging.logger {}
private lateinit var workerFactory: WorkerFactory
// Continued on next slide...
Step 5: Configure Temporal (Part 2)
@Bean
fun workflowServiceStubs(): WorkflowServiceStubs {
return WorkflowServiceStubs.newLocalServiceStubs()
}
@Bean
fun workflowClient(workflowServiceStubs: WorkflowServiceStubs): WorkflowClient {
return WorkflowClient.newInstance(workflowServiceStubs)
}
@Bean
fun workerFactory(workflowClient: WorkflowClient): WorkerFactory {
workerFactory = WorkerFactory.newInstance(workflowClient)
return workerFactory
}
@Bean
fun greetingActivity(): GreetingActivityImpl {
return GreetingActivityImpl()
}
// Continued on next slide...
Step 5: Configure Temporal (Part 3)
@Observed
@EventListener(ApplicationReadyEvent::class)
fun onApplicationReady(event: ApplicationReadyEvent) {
val worker: Worker = workerFactory.newWorker(TASK_QUEUE)
// Register workflow and activity
worker.registerWorkflowImplementationTypes(HelloWorkflowImpl::class.java)
worker.registerActivitiesImplementations(greetingActivity())
workerFactory.start()
logger.info { "✅ Temporal worker started for HelloWorkflow!" }
}
@PreDestroy
fun shutdown() {
workerFactory.shutdown()
}
}
Configuration Key Points
Important Elements:
- TASK_QUEUE constant - keeps queue name consistent
- Register both workflow and activity implementations
- Start worker factory after registration
- Graceful shutdown in @PreDestroy
Critical Order:
- Create worker
- Register implementations
- Start worker factory
Step 6: Create the Workflow Runner
Open src/workshop/lesson_4/runner/HelloWorkflowRunner.kt
:
package com.temporal.bootcamp.lesson4.runner
import com.temporal.answer.lesson_4.config.TemporalConfig
import com.temporal.answer.lesson_4.workflow.HelloWorkflow
import io.temporal.client.WorkflowClient
import io.temporal.client.WorkflowOptions
import mu.KotlinLogging
import org.springframework.boot.CommandLineRunner
import org.springframework.stereotype.Component
@Component
class HelloWorkflowRunner(
private val workflowClient: WorkflowClient
) : CommandLineRunner {
private val logger = KotlinLogging.logger {}
// Continued on next slide...
Workflow Runner Implementation
override fun run(vararg args: String?) {
logger.info { "🚀 Running HelloWorkflow..." }
val workflow = workflowClient.newWorkflowStub(
HelloWorkflow::class.java,
WorkflowOptions.newBuilder()
.setTaskQueue(TemporalConfig.TASK_QUEUE)
.setWorkflowId("hello-workflow-${System.currentTimeMillis()}")
.build()
)
WorkflowClient.start {
val result = workflow.sayHello("Temporal Learner")
// Print the result
logger.info { "✅ Workflow completed!" }
logger.info { " Result: $result" }
// Give some guidance to the user
logger.info { "" }
logger.info { "🌐 Check the Temporal Web UI at http://localhost:8233" }
logger.info { " You should see your workflow execution in the 'Workflows' tab!" }
}
}
}
Runner Key Points
Important Features:
- Implements CommandLineRunner - runs automatically on startup
- Creates workflow stub with options
- Unique workflow ID using timestamp
- Calls workflow method and gets result
Workflow Options:
- Task Queue: Must match worker configuration
- Workflow ID: Unique identifier for this execution
How to Run
Prerequisites:
1. Temporal server running:
temporal server start-dev
2. Verify Web UI:
http://localhost:8233 should load
Make sure both are working before proceeding!
Expected Output
✅ Temporal worker started for HelloWorkflow!
🚀 Running HelloWorkflow...
Generating greeting for: Temporal Learner
Generated greeting: Hello, Temporal Learner! Welcome to Temporal workflows!
✅ Workflow completed! Result: Hello, Temporal Learner! Welcome to Temporal workflows!
🌐 Check http://localhost:8233 to see your workflow!
🎉 If you see this, your first workflow is working!
Verify in Web UI
Steps to verify:
- Go to http://localhost:8233
- Click "Workflows" tab
- You should see:
hello-workflow-{timestamp}
with status "Completed" - Click on the workflow to see execution details
- Check the timeline - you'll see both workflow and activity execution
Visual confirmation of your workflow execution!
Troubleshooting
"Connection refused"
- Make sure
temporal server start-dev
is running - Check that you see "Started Temporal Server" message
"Workflow not found" in Web UI
- Make sure your application ran successfully
- Check console for error messages
- Verify the task queue name matches in config and runner
More Troubleshooting
"Activity not registered"
- Check that
GreetingActivityImpl
is annotated with@Component
- Verify the activity is registered in
TemporalConfig.startWorker()
- Make sure imports are correct
Application won't start
- Check that all imports are added correctly
- Verify Kotlin syntax (especially lambda expressions)
- Make sure Spring can find all classes with
@Component
and@Configuration
What You've Accomplished
✅ Major Achievements:
- ✅ Created your first workflow interface and implementation
- ✅ Created your first activity interface and implementation
- ✅ Configured Temporal to register and run your components
- ✅ Successfully executed a workflow and saw the results
- ✅ Verified execution in the Temporal Web UI
- ✅ Understood the basic workflow → activity pattern