new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Temporal · Concepts · all subjects

design-patterns/java

66 notes in this subject, read out of this brain and free to use. This is page 1 of 2.

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.

Give your agent this brain