Skip to main content

Introduction

The Cadence Java SDK is the official client library for building workflows and activities in Java and other JVM languages. It provides annotation-based workflow definitions, Spring Boot integration, and comprehensive tooling for enterprise applications.

GitHub Repository

Official Cadence Java Client SDK - Star and contribute on GitHub

Installation

Prerequisites

  • Java 8 or higher (Java 11+ recommended)
  • Maven 3.6+ or Gradle 6.0+
  • Cadence server running (local or remote)

Maven Setup

Add the Cadence Java client dependency to your pom.xml:
pom.xml
<dependencies>
    <dependency>
        <groupId>com.uber.cadence</groupId>
        <artifactId>cadence-client</artifactId>
        <version>3.11.2</version>
    </dependency>
</dependencies>

Gradle Setup

Add the dependency to your build.gradle:
dependencies {
    implementation 'com.uber.cadence:cadence-client:3.11.2'
}

Quick Start

1. Define a Workflow Interface

Workflow interfaces define the contract for workflow execution:
import com.uber.cadence.workflow.WorkflowMethod;
import com.uber.cadence.workflow.SignalMethod;
import com.uber.cadence.workflow.QueryMethod;

public interface HelloWorkflow {
    
    @WorkflowMethod(executionStartToCloseTimeoutSeconds = 600)
    String sayHello(String name);
    
    @SignalMethod
    void updateGreeting(String greeting);
    
    @QueryMethod
    int getCallCount();
}

2. Implement the Workflow

Workflow implementations must be deterministic:
import com.uber.cadence.workflow.Workflow;
import com.uber.cadence.activity.ActivityOptions;
import java.time.Duration;

public class HelloWorkflowImpl implements HelloWorkflow {
    
    private String greeting = "Hello";
    private int callCount = 0;
    
    // Configure activity options
    private final ActivityOptions activityOptions = new ActivityOptions.Builder()
        .setScheduleToStartTimeout(Duration.ofMinutes(1))
        .setStartToCloseTimeout(Duration.ofMinutes(5))
        .setHeartbeatTimeout(Duration.ofSeconds(20))
        .build();
    
    // Create activity stub
    private final GreetingActivities activities = 
        Workflow.newActivityStub(GreetingActivities.class, activityOptions);
    
    @Override
    public String sayHello(String name) {
        callCount++;
        
        // Execute activity
        String result = activities.greet(greeting, name);
        
        // Workflow can also use timers
        Workflow.sleep(Duration.ofSeconds(1));
        
        return result;
    }
    
    @Override
    public void updateGreeting(String greeting) {
        this.greeting = greeting;
    }
    
    @Override
    public int getCallCount() {
        return callCount;
    }
}

3. Define Activity Interface

Activities perform non-deterministic operations:
import com.uber.cadence.activity.ActivityMethod;

public interface GreetingActivities {
    
    @ActivityMethod(scheduleToCloseTimeoutSeconds = 300)
    String greet(String greeting, String name);
    
    @ActivityMethod
    void sendEmail(String to, String message);
}

4. Implement Activities

Activity implementations can call external services:
import com.uber.cadence.activity.Activity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class GreetingActivitiesImpl implements GreetingActivities {
    
    private static final Logger logger = 
        LoggerFactory.getLogger(GreetingActivitiesImpl.class);
    
    @Override
    public String greet(String greeting, String name) {
        // Activities can call external services
        logger.info("Greeting {} with {}", name, greeting);
        
        // Record heartbeat for long-running activities
        Activity.getExecutionContext().heartbeat(null);
        
        return String.format("%s, %s!", greeting, name);
    }
    
    @Override
    public void sendEmail(String to, String message) {
        // Call email service
        logger.info("Sending email to {} with message: {}", to, message);
    }
}

5. Configure and Start Worker

Workers poll for and execute workflows and activities:
import com.uber.cadence.client.WorkflowClient;
import com.uber.cadence.serviceclient.ClientOptions;
import com.uber.cadence.serviceclient.WorkflowServiceTChannel;
import com.uber.cadence.worker.Worker;
import com.uber.cadence.worker.WorkerFactory;
import com.uber.cadence.worker.WorkerOptions;

public class WorkerMain {
    
    private static final String DOMAIN = "my-domain";
    private static final String TASK_LIST = "my-task-list";
    
    public static void main(String[] args) {
        // Create Cadence service client
        WorkflowServiceTChannel service = new WorkflowServiceTChannel(
            ClientOptions.newBuilder()
                .setHost("localhost")
                .setPort(7933)
                .build()
        );
        
        // Create workflow client
        WorkflowClient workflowClient = WorkflowClient.newInstance(
            service,
            WorkflowClient.Options.newBuilder()
                .setDomain(DOMAIN)
                .build()
        );
        
        // Create worker factory
        WorkerFactory factory = WorkerFactory.newInstance(workflowClient);
        
        // Configure worker
        WorkerOptions workerOptions = WorkerOptions.newBuilder()
            .setMaxConcurrentActivityExecutionSize(100)
            .setMaxConcurrentWorkflowExecutionSize(50)
            .build();
        
        Worker worker = factory.newWorker(TASK_LIST, workerOptions);
        
        // Register workflow implementation
        worker.registerWorkflowImplementationTypes(HelloWorkflowImpl.class);
        
        // Register activity implementation
        worker.registerActivitiesImplementations(new GreetingActivitiesImpl());
        
        // Start worker
        factory.start();
        
        System.out.println("Worker started for task list: " + TASK_LIST);
    }
}

6. Execute Workflow

Start workflow execution from your application:
import com.uber.cadence.client.WorkflowClient;
import com.uber.cadence.client.WorkflowOptions;
import com.uber.cadence.serviceclient.ClientOptions;
import com.uber.cadence.serviceclient.WorkflowServiceTChannel;
import java.time.Duration;

public class WorkflowStarter {
    
    public static void main(String[] args) {
        // Create service client
        WorkflowServiceTChannel service = new WorkflowServiceTChannel(
            ClientOptions.newBuilder()
                .setHost("localhost")
                .setPort(7933)
                .build()
        );
        
        // Create workflow client
        WorkflowClient workflowClient = WorkflowClient.newInstance(
            service,
            WorkflowClient.Options.newBuilder()
                .setDomain("my-domain")
                .build()
        );
        
        // Configure workflow options
        WorkflowOptions workflowOptions = new WorkflowOptions.Builder()
            .setWorkflowId("hello-workflow-1")
            .setTaskList("my-task-list")
            .setExecutionStartToCloseTimeout(Duration.ofMinutes(10))
            .build();
        
        // Create workflow stub
        HelloWorkflow workflow = workflowClient.newWorkflowStub(
            HelloWorkflow.class,
            workflowOptions
        );
        
        // Start workflow execution
        String result = workflow.sayHello("World");
        
        System.out.println("Workflow result: " + result);
    }
}

Advanced Patterns

Child Workflows

Compose complex workflows from simpler ones:
import com.uber.cadence.workflow.Workflow;
import com.uber.cadence.workflow.ChildWorkflowOptions;
import java.time.Duration;

public class ParentWorkflowImpl implements ParentWorkflow {
    
    @Override
    public String orchestrate(String input) {
        // Configure child workflow options
        ChildWorkflowOptions childOptions = new ChildWorkflowOptions.Builder()
            .setExecutionStartToCloseTimeout(Duration.ofHours(1))
            .setTaskList("child-task-list")
            .build();
        
        // Create child workflow stub
        ChildWorkflow child = Workflow.newChildWorkflowStub(
            ChildWorkflow.class,
            childOptions
        );
        
        // Execute child workflow
        String childResult = child.processData(input);
        
        return "Parent processed: " + childResult;
    }
}

Saga Pattern

Implement distributed transactions with compensation:
import com.uber.cadence.workflow.Workflow;
import com.uber.cadence.workflow.Saga;
import io.temporal.common.CancellationScope;

public class SagaWorkflowImpl implements SagaWorkflow {
    
    private final BookingActivities activities = 
        Workflow.newActivityStub(BookingActivities.class);
    
    @Override
    public void bookTrip(String userId, String flightId, String hotelId) {
        Saga saga = new Saga(new Saga.Options.Builder()
            .setParallelCompensation(false)
            .build());
        
        try {
            // Book flight
            String flightBookingId = activities.bookFlight(flightId, userId);
            saga.addCompensation(() -> 
                activities.cancelFlight(flightBookingId)
            );
            
            // Book hotel
            String hotelBookingId = activities.bookHotel(hotelId, userId);
            saga.addCompensation(() -> 
                activities.cancelHotel(hotelBookingId)
            );
            
            // Charge payment
            activities.chargePayment(userId, calculateTotal());
            saga.addCompensation(() -> 
                activities.refundPayment(userId)
            );
            
        } catch (Exception e) {
            // Compensate on failure
            saga.compensate();
            throw e;
        }
    }
    
    private double calculateTotal() {
        return 1000.0; // Simplified
    }
}

Signals and Queries

public class OrderWorkflowImpl implements OrderWorkflow {
    
    private String status = "PENDING";
    private boolean approved = false;
    
    @Override
    public void processOrder(String orderId) {
        // Wait for approval signal
        Workflow.await(() -> approved);
        
        status = "APPROVED";
        // Continue processing...
    }
    
    @Override
    public void approveOrder() {
        this.approved = true;
    }
    
    @Override
    public String getStatus() {
        return status;
    }
}

// Send signal from client
OrderWorkflow workflow = workflowClient.newWorkflowStub(
    OrderWorkflow.class,
    "order-workflow-id"
);
workflow.approveOrder();

Retry Policies

Configure automatic retries for activities:
import com.uber.cadence.common.RetryOptions;
import java.time.Duration;

public class RobustWorkflowImpl implements RobustWorkflow {
    
    private final RetryOptions retryOptions = new RetryOptions.Builder()
        .setInitialInterval(Duration.ofSeconds(1))
        .setMaximumInterval(Duration.ofMinutes(1))
        .setBackoffCoefficient(2.0)
        .setMaximumAttempts(5)
        .setDoNotRetry(IllegalArgumentException.class)
        .build();
    
    private final ActivityOptions activityOptions = new ActivityOptions.Builder()
        .setStartToCloseTimeout(Duration.ofMinutes(5))
        .setRetryOptions(retryOptions)
        .build();
    
    private final RiskyActivities activities = 
        Workflow.newActivityStub(RiskyActivities.class, activityOptions);
    
    @Override
    public void executeRiskyOperation() {
        // Activity will be automatically retried on failure
        activities.riskyCall();
    }
}

Spring Boot Integration

Spring Configuration

import com.uber.cadence.client.WorkflowClient;
import com.uber.cadence.serviceclient.ClientOptions;
import com.uber.cadence.serviceclient.WorkflowServiceTChannel;
import com.uber.cadence.worker.WorkerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CadenceConfiguration {
    
    @Value("${cadence.service.host:localhost}")
    private String cadenceHost;
    
    @Value("${cadence.service.port:7933}")
    private int cadencePort;
    
    @Value("${cadence.domain}")
    private String domain;
    
    @Bean
    public WorkflowServiceTChannel workflowService() {
        return new WorkflowServiceTChannel(
            ClientOptions.newBuilder()
                .setHost(cadenceHost)
                .setPort(cadencePort)
                .build()
        );
    }
    
    @Bean
    public WorkflowClient workflowClient(WorkflowServiceTChannel service) {
        return WorkflowClient.newInstance(
            service,
            WorkflowClient.Options.newBuilder()
                .setDomain(domain)
                .build()
        );
    }
    
    @Bean
    public WorkerFactory workerFactory(WorkflowClient workflowClient) {
        return WorkerFactory.newInstance(workflowClient);
    }
}

Spring Worker Component

import com.uber.cadence.worker.Worker;
import com.uber.cadence.worker.WorkerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class CadenceWorker implements CommandLineRunner {
    
    @Autowired
    private WorkerFactory workerFactory;
    
    @Autowired
    private GreetingActivitiesImpl activities;
    
    @Override
    public void run(String... args) {
        Worker worker = workerFactory.newWorker("my-task-list");
        
        worker.registerWorkflowImplementationTypes(HelloWorkflowImpl.class);
        worker.registerActivitiesImplementations(activities);
        
        workerFactory.start();
    }
}

Testing

Unit Testing Workflows

import com.uber.cadence.testing.TestWorkflowEnvironment;
import com.uber.cadence.worker.Worker;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;

public class HelloWorkflowTest {
    
    private TestWorkflowEnvironment testEnv;
    private Worker worker;
    
    @Before
    public void setUp() {
        testEnv = TestWorkflowEnvironment.newInstance();
        worker = testEnv.newWorker("test-task-list");
        
        // Register workflow
        worker.registerWorkflowImplementationTypes(HelloWorkflowImpl.class);
    }
    
    @After
    public void tearDown() {
        testEnv.close();
    }
    
    @Test
    public void testWorkflow() {
        // Mock activities
        GreetingActivities activities = mock(GreetingActivities.class);
        when(activities.greet("Hello", "World"))
            .thenReturn("Hello, World!");
        worker.registerActivitiesImplementations(activities);
        
        // Start test environment
        testEnv.start();
        
        // Create workflow stub
        HelloWorkflow workflow = testEnv.newWorkflowStub(HelloWorkflow.class);
        
        // Execute workflow
        String result = workflow.sayHello("World");
        
        // Verify result
        assertEquals("Hello, World!", result);
        verify(activities).greet("Hello", "World");
    }
}

Sample Repository

Cadence Java Samples

Enterprise examples including:
  • Spring Boot integration
  • Saga pattern implementation
  • Complex orchestration scenarios
  • Testing strategies
  • Production configurations

Best Practices

  • Keep workflow code deterministic
  • Use Workflow.getVersion() for versioning
  • Avoid direct instantiation of random or time
  • Use workflow methods for all non-deterministic operations
  • Make activities idempotent when possible
  • Implement heartbeats for long-running activities
  • Use dependency injection for testability
  • Handle exceptions appropriately
  • Use Spring’s dependency injection for activities
  • Configure workers as Spring components
  • Externalize configuration to application.properties
  • Implement health checks for workers
  • Adjust worker pool sizes based on load
  • Use local activities for fast operations
  • Enable sticky execution for better performance
  • Monitor metrics and adjust accordingly

Next Steps

Core Concepts

Understand workflows, activities, and task lists

Go Client

Explore the Go SDK for cloud-native apps

CLI Reference

Manage workflows with the Cadence CLI

Operations

Monitor and operate Cadence in production

Build docs developers (and LLMs) love