Java circuit breaker example with dependency injection
// PaymentActivitiesImpl.java — resilience4j
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
public class PaymentActivitiesImpl implements PaymentActivities {
private final PaymentAPI paymentApi;
private final CircuitBreaker breaker;
public PaymentActivitiesImpl(PaymentAPI paymentApi, CircuitBreaker breaker) {
this.paymentApi = paymentApi;
this.breaker = breaker;
}
@Override
public String chargeCustomer(String orderId, int amount) {
// Throws CallNotPermittedException when the breaker is open.
return breaker.executeSupplier(() -> paymentApi.charge(orderId, amount));
}
}
// PaymentWorker.java
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.temporal.client.WorkflowClient;
import io.temporal.serviceclient.WorkflowServiceStubs;
import io.temporal.worker.Worker;
import io.temporal.worker.WorkerFactory;
import java.time.Duration;
public class PaymentWorker {
public static void main(String[] args) {
WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs();
WorkflowClient client = WorkflowClient.newInstance(service);
WorkerFactory factory = WorkerFactory.newInstance(client);
Worker worker = factory.newWorker("payment");
worker.registerWorkflowImplementationTypes(PaymentWorkflowImpl.class);
// Construct the breaker once at Worker startup so its failure
// counters are shared across all Activity executions.
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.failureRateThreshold(50) // percent
.waitDurationInOpenState(Duration.ofSeconds(30)) // cool-down
.slidingWindowSize(20)
.build();
CircuitBreaker breaker = CircuitBreaker.of("payment-api", config);
worker.registerActivitiesImplementations(
new PaymentActivitiesImpl(new PaymentAPI("https://api.example.com"), breaker)
);
factory.start();
}
}
Java Activity Dependency Injection implementation
In Java, define an @ActivityInterface with Activity method signatures, then create a separate implementation class that implements the interface. Dependencies are passed through the constructor of the implementation class. Register the implementation instance with the Worker. Workflows use Workflow.newActivityStub to create a typed proxy from the Activity interface, and the Temporal runtime routes calls to the registered implementation.
Java Activity Dependency Injection code example
// PaymentActivities.java
@ActivityInterface
public interface PaymentActivities {
String chargeCustomer(String orderId, int amount);
void sendReceipt(String email, String receiptId);
}
// PaymentActivitiesImpl.java
public class PaymentActivitiesImpl implements PaymentActivities {
private final DBClient dbClient;
private final EmailClient emailClient;
public PaymentActivitiesImpl(DBClient dbClient, EmailClient emailClient) {
this.dbClient = dbClient;
this.emailClient = emailClient;
}
@Override
public String chargeCustomer(String orderId, int amount) {
return dbClient.processPayment(orderId, amount);
}
@Override
public void sendReceipt(String email, String receiptId) {
emailClient.send(email, "Payment Receipt", receiptId);
}
}
// PaymentWorkflowImpl.java
public class PaymentWorkflowImpl implements PaymentWorkflow {
private final PaymentActivities activities = Workflow.newActivityStub(
PaymentActivities.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(30))
.build()
);
@Override
public void processPayment(String orderId, int amount, String email) {
String receiptId = activities.chargeCustomer(orderId, amount);
activities.sendReceipt(email, receiptId);
}
}
// PaymentWorker.java
public class PaymentWorker {
public static void main(String[] args) {
WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs();
WorkflowClient client = WorkflowClient.newInstance(service);
WorkerFactory factory = WorkerFactory.newInstance(client);
Worker worker = factory.newWorker("payment");
worker.registerWorkflowImplementationTypes(PaymentWorkflowImpl.class);
// Inject real dependencies at Worker startup
worker.registerActivitiesImplementations(
new PaymentActivitiesImpl(
new PostgresClient("postgres://localhost:5432/payments"),
new SMTPClient("smtp://mail.example.com")
)
);
factory.start();
}
}
Batch Iterator Java implementation
import io.temporal.activity.ActivityOptions;
import io.temporal.workflow.*;
import java.time.Duration;
import java.util.List;
@WorkflowInterface
public interface BatchIteratorWorkflow {
@WorkflowMethod
int run(int offset, int totalProcessed);
}
public class BatchIteratorWorkflowImpl implements BatchIteratorWorkflow {
private final Activities activities = Workflow.newActivityStub(
Activities.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(10))
.build()
);
@Override
public int run(int offset, int totalProcessed) {
List<Record> page = activities.fetchPage(offset, Shared.PAGE_SIZE);
for (Record record : page) {
activities.processRecord(record);
totalProcessed++;
}
Workflow.getLogger(BatchIteratorWorkflowImpl.class).info(
"Processed page at offset " + offset + " (" + page.size() + " records, total: " + totalProcessed + ")"
);
if (page.size() == Shared.PAGE_SIZE) {
BatchIteratorWorkflow next = Workflow.newContinueAsNewStub(BatchIteratorWorkflow.class);
next.run(offset + Shared.PAGE_SIZE, totalProcessed);
}
return totalProcessed;
}
}
Asynchronous child workflow execution in Java
To start a child workflow asynchronously in Java, create options with ChildWorkflowOptions.newBuilder().setParentClosePolicy(ParentClosePolicy.PARENT_CLOSE_POLICY_ABANDON).build(), create a typed stub with Workflow.newChildWorkflowStub(ChildWorkflow.class, options), then call Async.function(child::method, args) to start without blocking. Use Workflow.getWorkflowExecution(child) to get a Promise for the execution.
Synchronous child workflow execution in Java
To execute a child workflow synchronously in Java, create a typed stub with Workflow.newChildWorkflowStub(ChildWorkflow.class) and call a method on it. Calling a method on the stub blocks the parent until the child completes.
Parallel child workflows in Java
To start multiple child workflows in parallel in Java, loop through items creating child stubs and calling Async.function(child::method, args) for each, storing the returned Promises in a list. Then call Promise.allOf(promises).get() to wait for all children to complete, and iterate through the promises to collect results.
Java Continue-As-New implementation
In Java, create a typed stub with `Workflow.newContinueAsNewStub(DataProcessorWorkflow.class)` and call the Workflow method on it. This triggers the Continue-As-New transition. Use `Workflow.getInfo().isContinueAsNewSuggested()` to check if history is approaching the limit.
Java: Workflow configuration with RetryPolicy for Delayed Retry pattern
Example showing how to configure a Workflow with a normal RetryPolicy when using the Delayed Retry pattern. The nextRetryDelay set in the Activity overrides the interval only for the retry following that specific failure:
```java
// ApiWorkflowImpl.java
public class ApiWorkflowImpl implements ApiWorkflow {
private final RateLimitedActivity activities = Workflow.newActivityStub(
RateLimitedActivity.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(10))
.setRetryOptions(RetryOptions.newBuilder()
.setInitialInterval(Duration.ofSeconds(1))
.setBackoffCoefficient(2.0)
.setMaximumAttempts(10)
.build())
.build()
);
@Override
public String run(String endpoint) {
return activities.callApi(endpoint);
}
}
```
Java: ApplicationFailure with nextRetryDelay from HTTP 429 response
Example showing how to extract the Retry-After header from an HTTP 429 response and pass it to ApplicationFailure.newFailureWithCauseAndDelay() to override the retry delay:
```java
// RateLimitedActivityImpl.java
import io.temporal.activity.Activity;
import io.temporal.failure.ApplicationFailure;
import java.time.Duration;
public class RateLimitedActivityImpl implements RateLimitedActivity {
@Override
public String callApi(String endpoint) {
ApiResponse response = httpClient.get(endpoint);
if (response.getStatusCode() == 429) {
int retryAfterSeconds = response.getHeaderInt("Retry-After", 0);
if (retryAfterSeconds > 0) {
throw ApplicationFailure.newFailureWithCauseAndDelay(
"Rate limited — retrying after " + retryAfterSeconds + "s",
"RateLimitError",
null,
Duration.ofSeconds(retryAfterSeconds)
);
}
throw ApplicationFailure.newFailure("Rate limited — retrying per RetryPolicy", "RateLimitError");
}
return response.getBody();
}
}
```
Java: Attempt-proportional delay using nextRetryDelay
Example showing how to set nextRetryDelay dynamically based on the attempt number to implement a custom backoff:
```java
// BackoffActivityImpl.java
import io.temporal.activity.Activity;
import io.temporal.failure.ApplicationFailure;
import java.time.Duration;
public class BackoffActivityImpl implements BackoffActivity {
@Override
public String process(String input) {
int attempt = Activity.getExecutionContext().getInfo().getAttempt();
try {
return downstreamService.call(input);
} catch (ServiceUnavailableException e) {
// Custom delay: 3 seconds × attempt number (3s, 6s, 9s, …)
throw ApplicationFailure.newFailureWithCauseAndDelay(
"Service unavailable on attempt " + attempt,
"ServiceUnavailable",
e,
Duration.ofSeconds(3L * attempt)
);
}
}
}
```
Delayed Start Java API
In Java, use `client.newWorkflowStub()` with `WorkflowOptions.newBuilder()` and call `setStartDelay(Duration.ofSeconds(...))`. Then call `start()` on the workflow stub:
```java
DelayedStartWorkflow workflow = client.newWorkflowStub(
DelayedStartWorkflow.class,
WorkflowOptions.newBuilder()
.setWorkflowId(WORKFLOW_ID)
.setTaskQueue(TASK_QUEUE)
.setStartDelay(Duration.ofSeconds(30))
.build());
workflow.start();
```
Java activity for downstream rate limiting
Example of a simple Java Activity for downstream rate limiting:
```java
// RateLimitedActivities.java
@ActivityInterface
public interface RateLimitedActivities {
@ActivityMethod
String callApi(String input);
}
public class RateLimitedActivitiesImpl implements RateLimitedActivities {
@Override
public String callApi(String input) {
return downstreamApi.call(input);
}
}
```
Java workflow routing to rate-limited queue
Example of a Java Workflow routing Activities to a rate-limited queue:
```java
// MyWorkflowImpl.java
public class MyWorkflowImpl implements MyWorkflow {
private final RateLimitedActivities rateLimitedActivities =
Workflow.newActivityStub(RateLimitedActivities.class,
ActivityOptions.newBuilder()
.setTaskQueue("rate-limited-tq")
.setStartToCloseTimeout(Duration.ofSeconds(30))
.build()
);
@Override
public String run(String input) {
return rateLimitedActivities.callApi(input);
}
}
```
The Workflow specifies an explicit task_queue override in the Activity options to route the throttled Activity to the dedicated queue.
Java worker with rate limiting
Example of a Java Worker configured with downstream rate limiting:
```java
// WorkerSetup.java
WorkerOptions rateLimitedOptions = WorkerOptions.newBuilder()
.setMaxTaskQueueActivitiesPerSecond(5.0)
.build();
Worker rateLimitedWorker = factory.newWorker("rate-limited-tq", rateLimitedOptions);
rateLimitedWorker.registerActivitiesImplementations(new RateLimitedActivitiesImpl());
factory.start();
```
This worker is dedicated to rate-limited activities and requires a separate worker registered on the workflow task queue.
Early Return Java implementation example
Example showing Early Return pattern in Java using Update-with-Start:
```java
// TransactionWorkflowImpl.java
public class TransactionWorkflowImpl implements TransactionWorkflow {
private boolean initDone = false;
private Transaction tx;
private Exception initError = null;
@Override
public TxResult processTransaction(TransactionRequest txRequest) {
this.tx = activities.mintTransactionId(txRequest);
// Phase 1: Fast synchronous initialization
try {
this.tx = activities.initTransaction(this.tx);
} catch (Exception e) {
initError = e;
} finally {
initDone = true; // Signal update handler
}
// Phase 2: Slow asynchronous completion
if (initError != null) {
activities.cancelTransaction(this.tx);
return new TxResult("", "Transaction cancelled.");
} else {
activities.completeTransaction(this.tx);
return new TxResult(this.tx.getId(), "Transaction completed successfully.");
}
}
@Override
public TxResult returnInitResult() {
Workflow.await(() -> initDone); // Wait for initialization
if (initError != null) {
throw Workflow.wrap(initError);
}
return new TxResult(tx.getId(), "Initialization successful");
}
}
// Client.java
TransactionWorkflow workflow = client.newWorkflowStub(
TransactionWorkflow.class,
WorkflowOptions.newBuilder()
.setWorkflowId("transaction-123")
.setTaskQueue("transactions")
.setWorkflowIdConflictPolicy(
WorkflowIdConflictPolicy.WORKFLOW_ID_CONFLICT_POLICY_FAIL)
.build());
WorkflowUpdateHandle<TxResult> updateHandle =
WorkflowClient.startUpdateWithStart(
workflow::returnInitResult,
UpdateOptions.<TxResult>newBuilder().build(),
new WithStartWorkflowOperation<>(workflow::processTransaction, txRequest));
// Get initialization result immediately
TxResult result = updateHandle.getResultAsync().get();
// Use transaction ID immediately while workflow continues
System.out.println("Transaction initialized: " + result.getId());
```
Entity Workflow Java implementation
public record UserAccountInput(String userId, Optional<UserState> state) {}
@WorkflowInterface
public interface UserAccountWorkflow {
@WorkflowMethod
void run(UserAccountInput input);
@UpdateMethod
void updateProfile(ProfileData data);
@UpdateMethod
void changeEmail(String newEmail);
@SignalMethod
void suspend();
@SignalMethod
void reactivate();
@SignalMethod
void delete();
@QueryMethod
UserState getState();
}
public class UserAccountWorkflowImpl implements UserAccountWorkflow {
private String userId;
private UserState state = new UserState();
private boolean deleted = false;
private int operationCount = 0;
private static final int CONTINUE_AS_NEW_THRESHOLD = 1000;
@Override
public void run(UserAccountInput input) {
this.userId = input.userId();
if (input.state().isPresent()) {
this.state = input.state().get();
} else {
state.setStatus("ACTIVE");
state.setCreatedAt(Workflow.currentTimeMillis());
}
Workflow.await(() -> deleted || Workflow.getInfo().isContinueAsNewSuggested());
if (!deleted && Workflow.getInfo().isContinueAsNewSuggested()) {
Workflow.continueAsNew(new UserAccountInput(userId, Optional.of(state)));
}
state.setStatus("DELETED");
state.setDeletedAt(Workflow.currentTimeMillis());
}
@Override
public void updateProfile(ProfileData data) {
validateNotDeleted();
Activities.validateProfile(data);
state.setProfile(data);
state.setUpdatedAt(Workflow.currentTimeMillis());
incrementOperationCount();
}
@Override
public void changeEmail(String newEmail) {
validateNotDeleted();
Activities.sendVerificationEmail(userId, newEmail);
state.setPendingEmail(newEmail);
state.setUpdatedAt(Workflow.currentTimeMillis());
incrementOperationCount();
}
@Override
public void suspend() {
if (!deleted && !"SUSPENDED".equals(state.getStatus())) {
state.setStatus("SUSPENDED");
state.setUpdatedAt(Workflow.currentTimeMillis());
incrementOperationCount();
}
}
@Override
public void reactivate() {
if (!deleted && "SUSPENDED".equals(state.getStatus())) {
state.setStatus("ACTIVE");
state.setUpdatedAt(Workflow.currentTimeMillis());
incrementOperationCount();
}
}
@Override
public void delete() {
deleted = true;
}
@Override
public UserState getState() {
return state;
}
private void validateNotDeleted() {
if (deleted) {
throw new IllegalStateException("User account is deleted");
}
}
private void incrementOperationCount() {
operationCount++;
}
}
Fan-Out Java implementation
```java
// FanOutWorkflow.java
import io.temporal.activity.ActivityOptions;
import io.temporal.workflow.*;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
@WorkflowInterface
public interface FanOutWorkflow {
@WorkflowMethod
int run(int totalRecords, int chunkSize);
}
// FanOutWorkflowImpl.java
public class FanOutWorkflowImpl implements FanOutWorkflow {
@Override
public int run(int totalRecords, int chunkSize) {
if (chunkSize <= 0) chunkSize = Shared.CHUNK_SIZE;
List<Promise<Integer>> promises = new ArrayList<>();
String parentId = Workflow.getInfo().getWorkflowId();
for (int offset = 0; offset < totalRecords; offset += chunkSize) {
int length = Math.min(chunkSize, totalRecords - offset);
ChildWorkflowOptions opts = ChildWorkflowOptions.newBuilder()
.setWorkflowId(parentId + "/batch-" + offset)
.setTaskQueue(Shared.TASK_QUEUE)
.build();
RecordBatchWorkflow child = Workflow.newChildWorkflowStub(RecordBatchWorkflow.class, opts);
promises.add(Async.function(child::run, offset, length));
}
int total = 0;
for (Promise<Integer> p : promises) {
total += p.get();
}
return total;
}
}
// RecordBatchWorkflow.java
@WorkflowInterface
public interface RecordBatchWorkflow {
@WorkflowMethod
int run(int offset, int length);
}
// RecordBatchWorkflowImpl.java
public class RecordBatchWorkflowImpl implements RecordBatchWorkflow {
private final Activities activities = Workflow.newActivityStub(
Activities.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(10))
.build()
);
@Override
public int run(int offset, int length) {
int processed = 0;
for (int i = offset; i < offset + length; i++) {
activities.processRecord(i);
processed++;
}
return processed;
}
}
```
Fast/Slow Retries Java example
```java
import io.temporal.activity.ActivityOptions;
import io.temporal.common.RetryOptions;
import io.temporal.failure.ActivityFailure;
import io.temporal.workflow.Workflow;
import java.time.Duration;
public class FastSlowRetryWorkflowImpl implements FastSlowRetryWorkflow {
@Override
public String run(String request) {
// Phase 1: fast retries
DownstreamActivities fastActivities = Workflow.newActivityStub(
DownstreamActivities.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(30))
.setRetryOptions(RetryOptions.newBuilder()
.setInitialInterval(Duration.ofSeconds(1))
.setBackoffCoefficient(1.5)
.setMaximumInterval(Duration.ofSeconds(30))
.setMaximumAttempts(10)
.build())
.build()
);
try {
return fastActivities.callDownstream(request);
} catch (ActivityFailure e) {
Workflow.getLogger(getClass()).warn(
"Fast retries exhausted — switching to slow retry phase: " + request
);
// Phase 2: slow retries
DownstreamActivities slowActivities = Workflow.newActivityStub(
DownstreamActivities.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(30))
.setRetryOptions(RetryOptions.newBuilder()
.setInitialInterval(Duration.ofMinutes(5))
.setBackoffCoefficient(1.0)
// setMaximumAttempts not set — defaults to unlimited
.build())
.build()
);
return slowActivities.callDownstream(request);
}
}
}
```
This example shows a Workflow implementing fast retries with 1-second initial interval and 10 max attempts, transitioning to slow retries with 5-minute interval and unlimited attempts when fast phase is exhausted.
Java Fixed Count Retries example
import io.temporal.activity.ActivityOptions;
import io.temporal.api.enums.v1.RetryState;
import io.temporal.common.RetryOptions;
import io.temporal.failure.ActivityFailure;
import io.temporal.workflow.Workflow;
import java.time.Duration;
public class PaymentWorkflowImpl implements PaymentWorkflow {
private final PaymentActivities activities = Workflow.newActivityStub(
PaymentActivities.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(10))
.setRetryOptions(RetryOptions.newBuilder()
.setMaximumAttempts(3)
.build())
.build()
);
@Override
public String run(String orderId) {
try {
return activities.chargePaymentApi(orderId);
} catch (ActivityFailure e) {
if (e.getRetryState() == RetryState.RETRY_STATE_MAXIMUM_ATTEMPTS_REACHED) {
Workflow.getLogger(getClass()).error(
"Payment failed: all 3 attempts exhausted: " + orderId, e);
}
throw e;
}
}
}
This example shows how to set MaximumAttempts=3 using RetryOptions for a Java workflow and check the retry state when an ActivityFailure occurs.
Short SLA without per-attempt timeout Java
For a 30-second authorization window, omit StartToCloseTimeout and let ScheduleToCloseTimeout act as the only bound:
```java
ActivityOptions.newBuilder()
.setScheduleToCloseTimeout(Duration.ofSeconds(30))
.setRetryOptions(RetryOptions.newBuilder()
.setInitialInterval(Duration.ofSeconds(3))
.setBackoffCoefficient(1.5)
.build())
.build()
```
Fixed Wall-Time Retries Java implementation
Example of enforcing a 2-minute SLA with per-attempt 30-second timeout:
```java
public class PaymentAuthWorkflowImpl implements PaymentAuthWorkflow {
private final PaymentActivities activities = Workflow.newActivityStub(
PaymentActivities.class,
ActivityOptions.newBuilder()
.setScheduleToCloseTimeout(Duration.ofMinutes(2)) // total budget
.setStartToCloseTimeout(Duration.ofSeconds(30)) // per attempt
.setRetryOptions(RetryOptions.newBuilder()
.setInitialInterval(Duration.ofSeconds(5))
.setBackoffCoefficient(1.5)
.setMaximumInterval(Duration.ofSeconds(30))
.build())
.build()
);
@Override
public String run(String transactionId) {
try {
return activities.authorizeTransaction(transactionId);
} catch (ActivityFailure e) {
if (e.getCause() instanceof TimeoutFailure tf
&& tf.getTimeoutType() == TimeoutType.TIMEOUT_TYPE_SCHEDULE_TO_CLOSE) {
Workflow.getLogger(getClass()).error(
"Authorization failed — 2-minute SLA breached: " + transactionId, e
);
}
throw e;
}
}
}
```
Local Activity Java example
public class Impl implements TransactionWorkflow {
// All activities run as local — no server round-trips.
private final Activities activities = Workflow.newLocalActivityStub(
Activities.class,
LocalActivityOptions.newBuilder()
.setScheduleToCloseTimeout(Duration.ofSeconds(10))
.build()
);
@Override
public Shared.Transaction processTransaction(Shared.TransactionRequest req) {
Shared.Transaction tx = activities.validateTransaction(req);
tx = activities.reserveFunds(tx);
return activities.settleTransaction(tx);
}
}
Local Activity Java API
In Java, use `Workflow.newLocalActivityStub()` with `LocalActivityOptions.newBuilder()` to create a local activity stub. Set `setScheduleToCloseTimeout()` in the options builder.
Java Activity Heartbeat - basic progress tracking
Example in Java showing file processing with heartbeating every 100 lines. Uses Activity.getExecutionContext().getHeartbeatDetails(Integer.class) to retrieve the last checkpoint, and context.heartbeat(currentLine) to store progress.
Java Activity Heartbeat - cancellation handling
In Java, cancellation is delivered when the next heartbeat() call is made. It throws an ActivityCompletionException (specifically ActivityCanceledException for cancellation). The Activity should catch this exception in a try-catch block, perform cleanup via cleanupResources(), and re-throw the error.
Java ApplicationFailure non-retryable method
In Java, use io.temporal.failure.ApplicationFailure.newNonRetryableFailure() to mark an error as non-retryable at the throw site. Pass the error message and type name as parameters. Example: throw ApplicationFailure.newNonRetryableFailure("Order " + orderId + " not found", "OrderNotFoundError")
Java RetryOptions setDoNotRetry method
In Java, use io.temporal.common.RetryOptions.newBuilder().setDoNotRetry() to pass variadic error type name strings. Example: setDoNotRetry("OrderNotFoundError", "ValidationError")
Catching and handling ActivityError in Java workflows
In Java, catch io.temporal.failure.ActivityFailure in a try-except block. Call getCause() and check if it is an instanceof ApplicationFailure. Call getType() and getMessage() to determine the error type and route accordingly.
Frequent polling example in Java
```java
// FrequentPollingActivityImpl.java
@ActivityInterface
public interface PollingActivities {
String doPoll();
}
public class FrequentPollingActivityImpl implements PollingActivities {
@Override
public String doPoll() {
while (true) {
Activity.getExecutionContext().heartbeat(null);
String result = externalService.checkStatus();
if (result.equals("COMPLETED")) {
return result;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw Activity.wrap(e);
}
}
}
}
// FrequentPollingWorkflowImpl.java
public class FrequentPollingWorkflowImpl implements PollingWorkflow {
@Override
public String exec() {
ActivityOptions options = ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(60))
.setHeartbeatTimeout(Duration.ofSeconds(2))
.build();
PollingActivities activities = Workflow.newActivityStub(PollingActivities.class, options);
return activities.doPoll();
}
}
```
This example shows a frequent polling Activity that loops indefinitely with heartbeats every iteration, and a Workflow that executes the Activity with a 60-second start-to-close timeout and 2-second heartbeat timeout.
Infrequent polling example in Java
```java
// InfrequentPollingActivityImpl.java
public class InfrequentPollingActivityImpl implements PollingActivities {
@Override
public String doPoll() {
String result = externalService.checkStatus();
if (!result.equals("COMPLETED")) {
throw new RuntimeException("Service not ready, will retry");
}
return result;
}
}
// InfrequentPollingWorkflowImpl.java
public class InfrequentPollingWorkflowImpl implements PollingWorkflow {
@Override
public String exec() {
ActivityOptions options = ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(2))
.setRetryOptions(
RetryOptions.newBuilder()
.setBackoffCoefficient(1)
.setInitialInterval(Duration.ofSeconds(60))
.build())
.build();
PollingActivities activities = Workflow.newActivityStub(PollingActivities.class, options);
return activities.doPoll();
}
}
```
This example shows an infrequent polling Activity that performs a single poll and throws if the service is not ready, and a Workflow that executes the Activity with a fixed 60-second retry interval (backoff_coefficient=1).
Periodic sequence polling example in Java
```java
// PeriodicPollingChildWorkflowImpl.java
@WorkflowInterface
public interface PollingChildWorkflow {
@WorkflowMethod
String exec(int pollingIntervalInSeconds);
}
public class PeriodicPollingChildWorkflowImpl implements PollingChildWorkflow {
@Override
public String exec(int pollingIntervalInSeconds) {
ActivityOptions options = ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(10))
.build();
PollingActivities activities = Workflow.newActivityStub(PollingActivities.class, options);
int maxAttempts = 10;
for (int i = 0; i < maxAttempts; i++) {
String result = activities.doPoll();
if (result.equals("COMPLETED")) {
return result;
}
Workflow.sleep(Duration.ofSeconds(pollingIntervalInSeconds));
}
// Continue-as-new to prevent unbounded history
PollingChildWorkflow continueAsNew = Workflow.newContinueAsNewStub(PollingChildWorkflow.class);
continueAsNew.exec(pollingIntervalInSeconds);
return null;
}
}
// PeriodicPollingWorkflowImpl.java
public class PeriodicPollingWorkflowImpl implements PollingWorkflow {
@Override
public String exec() {
PollingChildWorkflow childWorkflow = Workflow.newChildWorkflowStub(
PollingChildWorkflow.class,
ChildWorkflowOptions.newBuilder()
.setWorkflowId("ChildWorkflowPoll")
.build());
return childWorkflow.exec(5);
}
}
```
This example shows a Child Workflow that polls up to 10 times with a configurable interval between attempts, then calls Continue-As-New to start a fresh execution. The parent Workflow executes the Child Workflow with a specific workflow ID.
Set Activity priority in Java
```java
ActivityOptions options = ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofMinutes(1))
.setPriority(Priority.newBuilder().setPriorityKey(1).build())
.build();
PaymentActivities activities = Workflow.newActivityStub(PaymentActivities.class, options);
activities.processPayment();
```
This example shows how to override an Activity's priority from the parent Workflow using the Java SDK.
Priority Task Queues example in Java
```java
WorkflowOptions options = WorkflowOptions.newBuilder()
.setWorkflowId("charge-customer-wf")
.setTaskQueue("my-task-queue")
.setPriority(Priority.newBuilder().setPriorityKey(1).build())
.build();
ChargeCustomer workflow = client.newWorkflowStub(ChargeCustomer.class, options);
WorkflowClient.start(workflow::run);
```
This example shows how to set Workflow priority at start time using the Java SDK.
Set Child Workflow priority in Java
```java
ChildWorkflowOptions options = ChildWorkflowOptions.newBuilder()
.setWorkflowId("process-order-child")
.setTaskQueue("my-task-queue")
.setPriority(Priority.newBuilder().setPriorityKey(2).build())
.build();
ProcessOrder child = Workflow.newChildWorkflowStub(ProcessOrder.class, options);
child.run();
```
This example shows how to set a Child Workflow's priority using the Java SDK.
Java task assignment workflow with Updates
```java
// TaskWorkflow.java
@WorkflowInterface
public interface TaskWorkflow {
@WorkflowMethod
void run();
@UpdateMethod
AssignmentResult assignTask(String taskName);
@QueryMethod
List<String> getTasks();
}
public class TaskWorkflowImpl implements TaskWorkflow {
private static final int MAX_TASKS = 10;
private List<String> tasks = new ArrayList<>();
@Override
public void run() {
Workflow.await(() -> false);
}
@UpdateValidatorMethod(updateName = "assignTask")
protected void validateAssignTask(String taskName) {
if (tasks.size() >= MAX_TASKS) {
throw new IllegalStateException("Task limit reached");
}
}
@Override
public AssignmentResult assignTask(String taskName) {
String assignmentId = UUID.randomUUID().toString();
tasks.add(taskName);
return new AssignmentResult(assignmentId, taskName, tasks.size());
}
@Override
public List<String> getTasks() {
return new ArrayList<>(tasks);
}
}
```
Resumable Activity Java implementation
Java Resumable Activity Workflow implementation:
```java
import io.temporal.activity.ActivityOptions;
import io.temporal.common.RetryOptions;
import io.temporal.common.SearchAttributeKey;
import io.temporal.failure.ActivityFailure;
import io.temporal.workflow.SignalMethod;
import io.temporal.workflow.QueryMethod;
import io.temporal.workflow.WorkflowInterface;
import io.temporal.workflow.WorkflowMethod;
import io.temporal.workflow.Workflow;
import java.time.Duration;
@WorkflowInterface
public interface TransferWorkflow {
@WorkflowMethod
String run(TransferInput input);
@SignalMethod
void retryWithCorrection(String correctedAccount);
@SignalMethod
void approve(boolean approved);
@QueryMethod
String getStatus();
}
public class TransferWorkflowImpl implements TransferWorkflow {
private String status = "PENDING";
private String correctedAccount;
private Boolean approval;
private final TransferActivities activities = Workflow.newActivityStub(
TransferActivities.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(30))
.setRetryOptions(RetryOptions.newBuilder()
.setMaximumAttempts(3)
.build())
.build()
);
@Override
public String run(TransferInput input) {
String account = input.getToAccount();
int correctionCount = 0;
while (true) {
status = "TRANSFERRING";
try {
activities.executeTransfer(
new TransferInput(input.getFromAccount(), account, input.getAmount())
);
break;
} catch (ActivityFailure e) {
correctionCount++;
if (correctionCount > 5) {
status = "FAILED";
Workflow.upsertTypedSearchAttributes(
SearchAttributeKey.forKeyword("TransferStatus").valueSet(status)
);
throw e;
}
status = "AWAITING_CORRECTION";
Workflow.upsertTypedSearchAttributes(
SearchAttributeKey.forKeyword("TransferStatus").valueSet(status)
);
Workflow.getLogger(getClass()).warn(
"Transfer failed — waiting for account correction: " + account
);
Workflow.await(() -> correctedAccount != null);
account = correctedAccount;
correctedAccount = null;
}
}
status = "AWAITING_APPROVAL";
Workflow.await(() -> approval != null);
if (approval) {
status = "COMPLETED";
return String.format("Transfer of %.2f to %s completed", input.getAmount(), account);
}
status = "REJECTED";
return "Transfer rejected by client";
}
@Override
public void retryWithCorrection(String account) {
this.correctedAccount = account;
}
@Override
public void approve(boolean decision) {
this.approval = decision;
}
@Override
public String getStatus() {
return status;
}
}
```
Signal with Start Java implementation
In Java, create a BatchRequest using workflowClient.newSignalWithStartRequest(), add the workflow run method with request.add(workflow::run), add the signal method with request.add(workflow::signalName, args), then call workflowClient.signalWithStart(request). Use WorkflowOptions to set the workflow ID and task queue. The request atomically starts the workflow and delivers the signal.
Java Saga pattern implementation
@WorkflowInterface
public interface OpenAccountWorkflow {
@WorkflowMethod
String openAccount(OpenAccountRequest req);
}
public class OpenAccountWorkflowImpl implements OpenAccountWorkflow {
private final Activities activities = Workflow.newActivityStub(
Activities.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(10))
.build());
@Override
public String openAccount(OpenAccountRequest req) {
Saga saga = new Saga(new Saga.Options.Builder()
.setParallelCompensation(false)
.build());
try {
activities.createAccount(req);
saga.addCompensation(activities::clearPostalAddresses, req);
activities.addAddress(req);
saga.addCompensation(activities::removeClient, req);
activities.addClient(req);
saga.addCompensation(activities::disconnectBankAccounts, req);
activities.addBankAccount(req);
return "Account " + req.accountId() + " opened";
} catch (Exception e) {
saga.compensate();
throw e;
}
}
}
This example shows how to implement the Saga pattern in Java using the SDK's Saga helper with addCompensation() and compensate() methods.
Sliding Window Java implementation example
Example Java implementation of Sliding Window pattern:
```java
import io.temporal.activity.ActivityOptions;
import io.temporal.api.enums.v1.ParentClosePolicy;
import io.temporal.workflow.*;
import java.time.Duration;
import java.util.List;
public interface SlidingWindowWorkflow {
@WorkflowInterface
interface Parent {
@WorkflowMethod
int run(Shared.SlidingWindowInput input);
@SignalMethod
void recordCompleted(String recordId);
}
@WorkflowInterface
interface Child {
@WorkflowMethod
void run(String recordId);
}
final class ParentImpl implements Parent {
private int active = 0;
private int totalProcessed = 0;
@Override
public void recordCompleted(String recordId) {
active--;
totalProcessed++;
}
@Override
public int run(Shared.SlidingWindowInput input) {
this.totalProcessed += input.totalProcessed;
this.active += input.active;
int windowSize = input.windowSize > 0 ? input.windowSize : Shared.WINDOW_SIZE;
List<String> recordIds = input.recordIds;
String parentId = Workflow.getInfo().getWorkflowId();
int nextIndex = input.startIndex;
int dispatched = 0;
while (nextIndex < recordIds.size()) {
Workflow.await(() -> active < windowSize);
String recordId = recordIds.get(nextIndex);
ChildWorkflowOptions opts = ChildWorkflowOptions.newBuilder()
.setWorkflowId(parentId + "/record-" + recordId)
.setTaskQueue(Shared.TASK_QUEUE)
.setParentClosePolicy(ParentClosePolicy.PARENT_CLOSE_POLICY_ABANDON)
.build();
Child child = Workflow.newChildWorkflowStub(Child.class, opts);
Async.procedure(child::run, recordId);
Workflow.getWorkflowExecution(child).get();
nextIndex++;
dispatched++;
active++;
if (dispatched >= windowSize) {
Workflow.newContinueAsNewStub(Parent.class)
.run(new Shared.SlidingWindowInput(
recordIds, windowSize, nextIndex, this.totalProcessed, active));
return 0;
}
}
Workflow.await(() -> active == 0);
return this.totalProcessed;
}
}
final class ChildImpl implements Child {
private final Activities activities = Workflow.newActivityStub(
Activities.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(30))
.build());
@Override
public void run(String recordId) {
activities.processRecord(recordId);
String parentWorkflowId = Workflow.getInfo().getParentWorkflowId().orElseThrow();
ExternalWorkflowStub parent = Workflow.newUntypedExternalWorkflowStub(parentWorkflowId);
try {
parent.signal(Shared.COMPLETION_SIGNAL, recordId);
} catch (Exception e) {
String msg = e.getMessage() != null ? e.getMessage() : "";
if (!msg.toLowerCase().contains("not found")) {
throw e;
}
}
}
}
}
```
Updatable Timer Java implementation
```java
// UpdatableTimer.java
public class UpdatableTimer {
private long wakeUpTime;
private boolean wakeUpTimeUpdated;
public void sleepUntil(long wakeUpTime) {
this.wakeUpTime = wakeUpTime;
while (true) {
wakeUpTimeUpdated = false;
Duration sleepInterval = Duration.ofMillis(this.wakeUpTime - Workflow.currentTimeMillis());
if (!Workflow.await(sleepInterval, () -> wakeUpTimeUpdated)) {
break; // Timer expired
}
// Timer was updated, loop to recalculate
}
}
public void updateWakeUpTime(long wakeUpTime) {
this.wakeUpTime = wakeUpTime;
this.wakeUpTimeUpdated = true; // Unblocks await
}
}
```
Updatable Timer pitfall: accumulating uncancelled timers in Java
In the Java SDK, Workflow.await(Duration, condition) does not automatically cancel its internal timer when the condition is met. Repeated calls in a loop accumulate timers. Wrap in a CancellationScope if this is a concern.
Java Worker-Specific Task Queues example activity
```java
// StoreActivitiesImpl.java
public class StoreActivitiesImpl implements StoreActivities {
private final String hostSpecificTaskQueue;
public StoreActivitiesImpl(String hostSpecificTaskQueue) {
this.hostSpecificTaskQueue = hostSpecificTaskQueue;
}
@Override
public TaskQueueFileNamePair download(URL source) {
File localFile = downloadToLocalDisk(source);
return new TaskQueueFileNamePair(
hostSpecificTaskQueue,
localFile.getAbsolutePath());
}
@Override
public String process(String fileName) {
File processed = processLocalFile(new File(fileName));
return processed.getAbsolutePath();
}
@Override
public void upload(String fileName, URL destination) {
uploadFromLocalDisk(new File(fileName), destination);
}
}
```
The download method returns the host-specific Task Queue name alongside the file path. The process and upload methods operate on local files, which are guaranteed to exist because they run on the same host.
Java Worker-Specific Task Queues example workflow
```java
// FileProcessingWorkflowImpl.java
public class FileProcessingWorkflowImpl implements FileProcessingWorkflow {
private final StoreActivities defaultTaskQueueActivities;
public FileProcessingWorkflowImpl() {
ActivityOptions defaultOptions = ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(20))
.build();
this.defaultTaskQueueActivities =
Workflow.newActivityStub(StoreActivities.class, defaultOptions);
}
@Override
public void processFile(URL source, URL destination) {
TaskQueueFileNamePair downloaded =
defaultTaskQueueActivities.download(source);
ActivityOptions hostOptions = ActivityOptions.newBuilder()
.setTaskQueue(downloaded.getHostTaskQueue())
.setScheduleToStartTimeout(Duration.ofSeconds(10))
.setStartToCloseTimeout(Duration.ofSeconds(20))
.build();
StoreActivities hostSpecificActivities =
Workflow.newActivityStub(StoreActivities.class, hostOptions);
String processed = hostSpecificActivities.process(downloaded.getFileName());
hostSpecificActivities.upload(processed, destination);
}
}
```
This example shows a workflow that downloads a file on any worker, then processes and uploads it on the same host-specific worker.
Java Worker-Specific Task Queues example worker setup
```java
// FileProcessingWorker.java
public class FileProcessingWorker {
public static void main(String[] args) {
WorkflowClient client = WorkflowClient.newInstance(service);
String defaultTaskQueue = "FileProcessing";
String hostTaskQueue = "FileProcessing-" + getHostName();
WorkerFactory factory = WorkerFactory.newInstance(client);
Worker defaultWorker = factory.newWorker(defaultTaskQueue);
defaultWorker.registerWorkflowImplementationTypes(
FileProcessingWorkflowImpl.class);
defaultWorker.registerActivitiesImplementations(
new StoreActivitiesImpl(hostTaskQueue));
Worker hostWorker = factory.newWorker(hostTaskQueue);
hostWorker.registerActivitiesImplementations(
new StoreActivitiesImpl(hostTaskQueue));
factory.start();
}
}
```
Each Worker registers with both the default Task Queue and its own host-specific Task Queue. The host-specific queue name includes hostname for uniqueness.
Task Queue constant definition - Java example
In Java, define a Task Queue name constant in a Constants class: public static final String taskQueueName = "my-task-queue-name";. Reference this constant as Constants.taskQueueName in both the workflow client code (in WorkflowOptions.setTaskQueue()) and worker configuration (in factory.newWorker()).
Workflow Definition syntax in Java
A Workflow Definition in Java is implemented using an interface with @WorkflowInterface annotation containing a method with @WorkflowMethod annotation, and a separate implementation class. Example: @WorkflowInterface public interface YourBasicWorkflow { @WorkflowMethod String workflowMethod(Arguments args); }
Eager Workflow Start Java example
public class Starter {
public static void main(String[] args) {
WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs();
WorkflowClient client = WorkflowClient.newInstance(service);
WorkerFactory factory = WorkerFactory.newInstance(client);
io.temporal.worker.Worker worker = factory.newWorker(Shared.TASK_QUEUE);
worker.registerWorkflowImplementationTypes(TransactionWorkflow.Impl.class);
worker.registerActivitiesImplementations(new Activities.Impl());
factory.start();
TransactionWorkflow workflow = client.newWorkflowStub(
TransactionWorkflow.class,
WorkflowOptions.newBuilder()
.setTaskQueue(Shared.TASK_QUEUE)
.setWorkflowId("eager-workflow-start-demo")
.setDisableEagerExecution(false)
.build()
);
Shared.Transaction result = workflow.processTransaction(
new Shared.TransactionRequest(100.00, "USD"));
System.out.printf("Transaction complete: ID=%s Status=%s%n",
result.id(), result.status());
factory.shutdown();
}
}
This example shows starting a Worker in the same process with factory.start() before creating the workflow stub, then using setDisableEagerExecution(false) to enable eager dispatch.
Early Return + Local Activities Java example
```java
// TransactionWorkflow.java
public class Impl implements TransactionWorkflow {
// Phase 1: local activities — zero server round-trips on the hot path.
private final Activities localActivities = Workflow.newLocalActivityStub(
Activities.class,
LocalActivityOptions.newBuilder()
.setScheduleToCloseTimeout(Duration.ofSeconds(5))
.build()
);
// Phase 2: regular activities — background settlement.
private final Activities activities = Workflow.newActivityStub(
Activities.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(30))
.build()
);
private Shared.Transaction tx;
private boolean phase1Done = false;
private RuntimeException phase1Error = null;
@Override
public Shared.Transaction getResult(Shared.TransactionRequest req) {
// Update handler: wait for Phase 1 before returning.
Workflow.await(() -> phase1Done);
if (phase1Error != null) throw phase1Error;
return tx;
}
@Override
public void processTransaction(Shared.TransactionRequest req) {
try {
tx = localActivities.validateTransaction(req);
tx = localActivities.initTransaction(tx);
} catch (RuntimeException e) {
phase1Error = e;
} finally {
phase1Done = true;
}
if (phase1Error != null) {
activities.cancelTransaction(tx);
return;
}
// Phase 2: regular activity in the background.
activities.completeTransaction(tx);
}
}
```
Java Pattern 1 inbound webhook example
```java
@WorkflowInterface
public interface OrderWorkflow {
@WorkflowMethod
String run(Shared.OrderInput order);
@SignalMethod
void paymentReceived(Shared.PaymentPayload payload);
final class Impl implements OrderWorkflow {
private final Activities activities = Workflow.newActivityStub(
Activities.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(30))
.build());
private Shared.PaymentPayload payment = null;
@Override
public String run(Shared.OrderInput order) {
System.out.println("Order " + order.orderId() + ": waiting for payment webhook");
// Block until the inbound webhook signal arrives (or timeout after 24 hours)
boolean received = Workflow.await(Duration.ofHours(24), () -> payment != null);
if (!received) {
return "Order " + order.orderId() + ": timed out waiting for payment";
}
return activities.processPayment(payment);
}
@Override
public void paymentReceived(Shared.PaymentPayload payload) {
System.out.println("Payment signal received: " + payload.paymentId());
this.payment = payload;
}
}
}
```
This example shows a Java OrderWorkflow using Workflow.await to block until a paymentReceived signal arrives, with a 24-hour timeout.
Java Pattern 1 Signal-with-Start starter example
```java
public class Starter {
public static void main(String[] args) throws Exception {
WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs();
WorkflowClient client = WorkflowClient.newInstance(service);
String orderId = "order-" + System.currentTimeMillis();
Shared.PaymentPayload payment = new Shared.PaymentPayload(
"pay-" + System.currentTimeMillis(), 99.99);
OrderWorkflow workflow = client.newWorkflowStub(
OrderWorkflow.class,
WorkflowOptions.newBuilder()
.setTaskQueue(Shared.TASK_QUEUE)
.setWorkflowId("order-" + orderId)
.build());
System.out.println("Sending webhook for order " + orderId);
// Signal-with-Start: atomically starts the workflow (if not running) and
// delivers the payment signal — this is exactly what your HTTP handler would do.
BatchRequest request = client.newSignalWithStartRequest();
request.add(workflow::run, new Shared.OrderInput(orderId, 99.99));
request.add(workflow::paymentReceived, payment);
client.signalWithStart(request);
System.out.println("Webhook signal sent: " + payment.paymentId());
// Wait for the workflow to complete
String result = WorkflowStub.fromTyped(workflow).getResult(String.class);
System.out.println("Order completed: " + result);
System.exit(0);
}
}
```
This example demonstrates Java Signal-with-Start using BatchRequest to atomically create and signal a workflow.
Java Pattern 2 delayed outbound callback example
```java
@WorkflowInterface
public interface DelayedCallbackWorkflow {
@WorkflowMethod
void run(Shared.CallbackInput input);
final class Impl implements DelayedCallbackWorkflow {
private final Activities activities = Workflow.newActivityStub(
Activities.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofMinutes(5))
.build());
@Override
public void run(Shared.CallbackInput input) {
System.out.println("Sleeping " + input.delaySeconds() + "s before calling " + input.callbackUrl());
// Durable sleep — survives worker restarts, server restarts, everything
Workflow.sleep(Duration.ofSeconds(input.delaySeconds()));
// Fire the outbound callback; Temporal retries on HTTP failure
activities.sendWebhookCallback(input);
System.out.println("Callback delivered to " + input.callbackUrl());
}
}
}
```
This Java example demonstrates Workflow.sleep() for a durable delay followed by an activity to send the callback.
Java Pattern 3 async activity completion example
```java
@ActivityInterface
public interface AsyncJobActivity {
@ActivityMethod
String submitJob(Shared.JobInput input);
}
public class AsyncJobActivityImpl implements AsyncJobActivity {
private final ActivityCompletionClient completionClient;
public AsyncJobActivityImpl(ActivityCompletionClient completionClient) {
this.completionClient = completionClient;
}
@Override
public String submitJob(Shared.JobInput input) {
ActivityExecutionContext context = Activity.getExecutionContext();
// Get the task token — this is the claim ticket
byte[] taskToken = context.getTaskToken();
// Submit to external system, persisting the task token so the callback can retrieve it
String jobId = ExternalService.submit(input.payload(), taskToken);
System.out.println("Job " + jobId + " submitted; waiting for async callback");
// Tell Temporal not to mark the activity complete yet
context.doNotCompleteOnReturn();
return null; // ignored
}
}
// In your callback handler:
// completionClient.complete(taskToken, result);
```
This Java example shows Pattern 3 where an activity calls doNotCompleteOnReturn() to signal async completion, then a callback handler uses the task token to complete the activity.
Set fairness key and weight at Workflow start in Java
Set FairnessKey and FairnessWeight in a Priority object passed to WorkflowOptions.setPriority().
Example:
```java
WorkflowOptions options = WorkflowOptions.newBuilder()
.setWorkflowId("process-order-wf")
.setTaskQueue("my-task-queue")
.setPriority(Priority.newBuilder()
.setFairnessKey("tenant-a")
.setFairnessWeight(2.0f)
.build())
.build();
ProcessOrder workflow = client.newWorkflowStub(ProcessOrder.class, options);
WorkflowClient.start(workflow::run);
```
Set fairness key and weight on Activities in Java
Set FairnessKey and FairnessWeight in a Priority object passed to ActivityOptions.setPriority().
Example:
```java
ActivityOptions options = ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofMinutes(1))
.setPriority(Priority.newBuilder()
.setFairnessKey("tenant-a")
.setFairnessWeight(2.0f)
.build())
.build();
TenantActivity activity = Workflow.newActivityStub(TenantActivity.class, options);
activity.processForTenant(request);
```
Use priority and fairness together in Java
Set both PriorityKey and FairnessKey in the Priority object passed to WorkflowOptions.setPriority().
Example:
```java
WorkflowOptions options = WorkflowOptions.newBuilder()
.setWorkflowId("charge-customer-wf")
.setTaskQueue("my-task-queue")
.setPriority(Priority.newBuilder()
.setPriorityKey(1)
.setFairnessKey("tenant-a")
.setFairnessWeight(2.0f)
.build())
.build();
```
Event Accumulator Java implementation
Java implementation uses @SignalMethod annotations and Workflow.await(Duration, Supplier<Boolean>). Workflow.newContinueAsNewStub carries state to the next run.
Event Accumulator Java example workflow
@WorkflowInterface
public interface AccumulatorWorkflow {
@WorkflowMethod
String accumulate(String bucketKey, List<Shared.OrderItem> items, List<String> seenKeys);
@SignalMethod
void addItem(Shared.OrderItem item);
@SignalMethod
void flush();
class Impl implements AccumulatorWorkflow {
private final Activities activities = Workflow.newActivityStub(
Activities.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Shared.MAX_AWAIT_TIME.plusSeconds(10))
.build());
private final ArrayDeque<Shared.OrderItem> unprocessed = new ArrayDeque<>();
private boolean flushRequested = false;
@Override
public String accumulate(String bucketKey, List<Shared.OrderItem> itemsInput, List<String> seenKeysInput) {
List<Shared.OrderItem> items = new ArrayList<>(itemsInput);
Set<String> seenSet = new HashSet<>(seenKeysInput);
do {
// Sliding window: wait for a signal or let the inactivity timer fire
boolean timedOut = !Workflow.await(
Shared.MAX_AWAIT_TIME,
() -> !unprocessed.isEmpty() || flushRequested);
// Drain and deduplicate the signal queue
while (!unprocessed.isEmpty()) {
Shared.OrderItem item = unprocessed.removeFirst();
if (item.orderId.equals(bucketKey) && seenSet.add(item.itemId)) {
items.add(item);
}
}
if (timedOut || flushRequested) {
String result = activities.processItems(bucketKey, items);
if (unprocessed.isEmpty()) return result;
// More signals arrived after timeout/flush — loop to process them
}
} while (!unprocessed.isEmpty() || !Workflow.getInfo().isContinueAsNewSuggested());
// History growing large — continue as new, carrying accumulated state forward
AccumulatorWorkflow continueAsNew = Workflow.newContinueAsNewStub(AccumulatorWorkflow.class);
List<String> seenKeysList = new ArrayList<>(seenSet);
java.util.Collections.sort(seenKeysList); // deterministic order for replay
continueAsNew.accumulate(bucketKey, items, seenKeysList);
return ""; // unreachable
}
@Override
public void addItem(Shared.OrderItem item) {
unprocessed.add(item);
}
@Override
public void flush() {
flushRequested = true;
}
}
}
MapReduce Tree Java implementation
Java implementation with NodeWorkflow interface and NodeWorkflowImpl class. NodeWorkflowImpl maintains collected results list and received counter. Uses @SignalMethod decorated nodeResult handler to receive signals. Checks records.size() against Shared.LEAF_THRESHOLD, starts child workflows with Workflow.newChildWorkflowStub() and Async.procedure/function, waits with Workflow.await(() -> received >= exp), and signals to parent using ExternalWorkflowStub.signal().
Pick First pattern implementation in Java
In Java, the Pick First pattern uses `Async.function()` to start Activities inside a `CancellationScope`. Activities are added to a list of Promise objects. `Promise.anyOf(results).get()` waits for the first Activity to complete. After capturing the result, `scope.cancel()` cancels the remaining Activities. To wait for cancellation cleanup, set `setCancellationType(ActivityCancellationType.WAIT_CANCELLATION_COMPLETED)` in ActivityOptions, then call `get()` on all promises while catching `ActivityFailure` with a `CanceledFailure` cause.