Skip to content

WorkflowCondition Reference ​

Defines conditions for dynamic workflow branching in job execution. WorkflowCondition determines whether a workflow branch should fire based on the outcome of a parent job or batch.

Package: run.ratchet.apiType: record WorkflowCondition(ConditionType type, Serializable expression, int priority) implements Serializable

Overview ​

Workflow conditions are used with JobBuilder.when(), JobBuilder.thenOnSuccess(), and the various BatchBuilder workflow methods. You typically use the static factory methods rather than constructing conditions directly.

java
scheduler.enqueue(() -> analyzeData())
    .branch(WorkflowCondition.success(), () -> archiveResults(), "Archive on success")
    .branch(WorkflowCondition.failure(), () -> notifyAdmins(), "Alert on failure")
    .submit();

Condition Types ​

The ConditionType enum defines 9 evaluation strategies:

Job Conditions ​

TypeDescriptionExpression
SUCCESSJob completed without exceptionnull
FAILUREJob threw an exceptionnull
CUSTOMCustom predicate on full JobResultSerializablePredicate<JobResult<T>>
RESULT_VALUEPredicate on the job's return value onlySerializableFunction<T, Boolean>

Batch Conditions ​

TypeDescriptionExpression
BATCH_SUCCESSAll child jobs completed successfullynull
BATCH_FAILUREOne or more child jobs failednull
BATCH_SUCCESS_RATESuccess rate meets thresholdDouble (0.0 - 1.0)
BATCH_FAILURE_COUNTFailure count within thresholdInteger
BATCH_CUSTOMCustom predicate on BatchContextSerializablePredicate<BatchContext>

Factory Methods ​

success ​

java
public static WorkflowCondition success()

Creates a condition that triggers when the job completes successfully.

java
scheduler.enqueue(() -> processData())
    .thenOnSuccess(() -> sendConfirmation())
    .submit();

// Equivalent using explicit condition:
scheduler.enqueue(() -> processData())
    .branch(WorkflowCondition.success(), () -> sendConfirmation(), "Confirm on success")
    .submit();

failure ​

java
public static WorkflowCondition failure()

Creates a condition that triggers when the job fails.

java
scheduler.enqueue(() -> processPayment())
    .thenOnFailure(() -> refundPayment())
    .submit();

custom ​

java
public static <T> WorkflowCondition custom(SerializablePredicate<JobResult<T>> predicate)

Creates a custom condition evaluated against the full JobResult. The predicate has access to success status, return value, execution time, error details, and metadata.

Type Parameters:

  • T -- the type of the job's return value.
java
// Define condition logic in a CDI bean or static utility class
@ApplicationScoped
public class JobConditions {
    public boolean isSlowSuccess(JobResult<?> result) {
        return result.isSuccess() && result.getExecutionTimeMsOrZero() > 30_000;
    }
}

// Reference the method — do NOT use an inline multi-step lambda
WorkflowCondition slowJob = WorkflowCondition.custom(
    result -> jobConditions.isSlowSuccess(result));

scheduler.enqueue(() -> processData())
    .branch(slowJob, () -> alertSlowExecution(), "Alert when job takes >30s")
    .submit();

custom (with priority) ​

java
public static <T> WorkflowCondition custom(
    SerializablePredicate<JobResult<T>> predicate, int priority)

Same as custom() but with an explicit evaluation priority. Lower values are evaluated first.

java
// In your CDI bean or static utility:
public static boolean isCriticalFailure(JobResult<?> result) {
    return result.isFailure() && result.getError() != null
        && result.getError().contains("CRITICAL");
}

// Priority -1 — evaluated before default branches
WorkflowCondition critical = WorkflowCondition.custom(
    JobConditions::isCriticalFailure, -1);

result ​

java
public static <T> WorkflowCondition result(SerializableFunction<T, Boolean> function)

Creates a condition based on the job's return value only. The function receives the return value (not the full JobResult) and returns a boolean.

Type Parameters:

  • T -- the type of the job's return value.
java
// Define result checks as static methods — comparison operators require a method boundary
public class InventoryConditions {
    public static boolean isHighStock(Object stock) { return ((Integer) stock) > 100; }
    public static boolean isOutOfStock(Object stock) { return ((Integer) stock) == 0; }
}

WorkflowCondition highStock = WorkflowCondition.result(InventoryConditions::isHighStock);

scheduler.enqueue(() -> inventoryService.checkStock(itemId))
    .whenResult(InventoryConditions::isHighStock, () -> placeOrder(itemId))
    .whenResult(InventoryConditions::isOutOfStock, () -> markOutOfStock(itemId))
    .submit();

batchSuccess ​

java
public static WorkflowCondition batchSuccess()

Creates a condition that triggers when all child jobs in a batch complete successfully.

java
scheduler.enqueueBatch("Sync All Accounts")
    .forEach(accounts, acct -> syncService.sync(acct))
    .thenOnBatchSuccess(() -> notifyComplete())
    .submit();

batchFailure ​

java
public static WorkflowCondition batchFailure()

Creates a condition that triggers when one or more child jobs fail.

java
scheduler.enqueueBatch("Process Records")
    .forEach(records, r -> processRecord(r))
    .thenOnBatchFailure(() -> handlePartialFailure())
    .submit();

successRate ​

java
public static WorkflowCondition successRate(double minRate)

Creates a condition based on batch success rate threshold.

Parameters:

  • minRate -- minimum success rate (0.0 to 1.0).

Throws: IllegalArgumentException if minRate is not between 0.0 and 1.0.

java
scheduler.enqueueBatch("Email Campaign")
    .forEach(recipients, r -> sendEmail(r))
    .thenWhenSuccessRate(0.95, () -> log.info("Campaign successful"))
    .submit();

failureCount ​

java
public static WorkflowCondition failureCount(int maxFailures)

Creates a condition based on maximum acceptable batch failures. The condition evaluates to true when failures are at or below the threshold.

Parameters:

  • maxFailures -- maximum number of acceptable failures.

Throws: IllegalArgumentException if maxFailures is negative.

java
scheduler.enqueueBatch("Import Data")
    .forEach(rows, row -> importRow(row))
    .thenWhenFailureCount(5, () -> log.info("Import acceptable (<=5 failures)"))
    .submit();

batchCustom ​

java
public static WorkflowCondition batchCustom(SerializablePredicate<BatchContext> predicate)

Creates a custom batch condition evaluated against the BatchContext.

java
// Multi-step predicates must live in a method — comparison + boolean logic won't analyze inline
public class BatchConditions {
    public static boolean hasCriticalFailures(BatchContext ctx) {
        return ctx.failedItems() > 10 && ctx.isComplete();
    }
}

WorkflowCondition criticalBatch = WorkflowCondition.batchCustom(
    BatchConditions::hasCriticalFailures);

scheduler.enqueueBatch("Critical Process")
    .forEach(items, item -> processItem(item))
    .thenBranch(criticalBatch, () -> escalateToOps(), "Escalate on >10 failures")
    .submit();

batchCustom (with priority) ​

java
public static WorkflowCondition batchCustom(
    SerializablePredicate<BatchContext> predicate, int priority)

Same as batchCustom() but with an explicit evaluation priority.

java
public class BatchConditions {
    public static boolean hasExcessiveFailures(BatchContext ctx) {
        return ctx.failedItems() > 5;
    }
}

WorkflowCondition earlyWarning = WorkflowCondition.batchCustom(
    BatchConditions::hasExcessiveFailures, -1); // evaluated first

Priority System ​

Workflow routing is exclusive. Ratchet evaluates lower priority numbers first and preserves registration order when priorities are equal. The first matching branch runs; every remaining sibling branch is canceled, even if its condition would also evaluate to true.

java
// Conditions with multi-step logic live in a helper — the framework stores method refs as JSON
public class JobConditions {
    public static boolean isSlowSuccess(JobResult<?> r) {
        return r.isSuccess() && r.getExecutionTimeMsOrZero() > 30_000;
    }
    public static boolean isCriticalFailure(JobResult<?> r) {
        return r.isFailure() && r.getError() != null && r.getError().contains("CRITICAL");
    }
}

scheduler.enqueue(() -> processData())
    // Priority 0 (default) — single method ref on context, no helper needed
    .when(JobResult::isSuccess, () -> archiveResults())
    // Priority 1 — evaluated after default
    .when(JobConditions::isSlowSuccess, () -> logSlowExecution(), 1)
    // Priority -1 — evaluated before default
    .when(JobConditions::isCriticalFailure, () -> escalateCritical(), -1)
    .submit();

WorkflowBranch ​

WorkflowBranch pairs a WorkflowCondition with a task and optional description.

java
public record WorkflowBranch(
    WorkflowCondition condition,
    Serializable task,
    String description
) implements Serializable

Record Components ​

ComponentTypeDescription
conditionWorkflowConditionWhen this branch should fire
taskSerializableThe serialized task to execute
descriptionStringOptional human-readable label for monitoring

Constructors ​

With description ​

java
public WorkflowBranch(
    WorkflowCondition condition, Serializable task, String description)

Creates a branch with a descriptive label. condition must not be null.

Without description ​

java
public WorkflowBranch(WorkflowCondition condition, Serializable task)

Creates a branch without a description.

getPriority ​

java
public int getPriority()

Returns the priority from the underlying condition. Ratchet uses it to decide evaluation order before applying registration order as the tie-break.

Usage ​

WorkflowBranch is typically created implicitly by JobBuilder and BatchBuilder methods. You rarely construct them directly:

java
// These builder methods create WorkflowBranch instances internally:
scheduler.enqueue(() -> processData())
    .thenOnSuccess(() -> archive())        // creates WorkflowBranch with SUCCESS condition
    .thenOnFailure(() -> alert())          // creates WorkflowBranch with FAILURE condition
    .when(result -> ..., () -> action())   // creates WorkflowBranch with CUSTOM condition
    .submit();

// Access branches via the builder (advanced):
List<WorkflowBranch> branches = builder.workflowBranches();

Predicate Serialization Contract ​

Custom predicates (CUSTOM, BATCH_CUSTOM, RESULT_VALUE) are stored in the database as JobPayload JSON, using the same Class/Method/Args format as job task lambdas. The predicate is analyzed at scheduling time using ASM bytecode inspection and must resolve to a single public method call.

Supported shapes ​

Method reference on the context argument:

java
// JobResult::isSuccess — isSuccess() called on the JobResult passed at evaluation time
WorkflowCondition.custom(JobResult::isSuccess)

Static method reference:

java
// MyConditions.check(BatchContext) — static method, no CDI bean needed
WorkflowCondition.batchCustom(MyConditions::allSucceeded)

Instance method reference on a CDI bean:

java
// myService is injected; evaluate(JobResult) is called on the CDI-resolved instance
WorkflowCondition.custom(result -> myService.evaluate(result))

Constraint: single method call ​

Predicates must contain exactly one method invocation. Multi-step inline logic fails at scheduling time with an IllegalArgumentException:

java
// FAILS at scheduling — two operations (isSuccess + getValue) not reducible to one call
WorkflowCondition.custom(result -> result.isSuccess() && result.getValue() != null)

// FIX — wrap in a CDI bean method or a static helper
WorkflowCondition.custom(result -> myConditions.isSuccessAndNonNull(result))

Why this matters ​

Because predicates are stored as JSON, they are stable across recompilations and deployments. Unlike JDK-serialized lambda blobs (which break when class files change), a stored JobPayload like {"target":"com.example.MyConditions","method":"allSucceeded",...} remains valid as long as the method exists with the same signature.

See Also ​