Temporal Workflow Java SDK - A framework for authoring Workflows and Activities in Java
Comprehensive exception hierarchy for handling workflow and activity failures, with support for retries, timeouts, cancellation, and custom application failures.
Foundation classes for the Temporal exception hierarchy.
/**
* Base class for all Temporal-related exceptions.
*/
public class TemporalException extends RuntimeException {
/**
* Creates TemporalException with message.
* @param message exception message
*/
public TemporalException(String message);
/**
* Creates TemporalException with message and cause.
* @param message exception message
* @param cause underlying cause
*/
public TemporalException(String message, Throwable cause);
/**
* Creates TemporalException with cause.
* @param cause underlying cause
*/
public TemporalException(Throwable cause);
}
/**
* Base for failures that can cross workflow boundaries.
*/
public abstract class TemporalFailure extends TemporalException {
/**
* Creates TemporalFailure with message and cause.
* @param message failure message
* @param cause original cause
* @param dataConverter data converter for details
*/
protected TemporalFailure(String message, Throwable cause, DataConverter dataConverter);
/**
* Gets original failure that was converted.
* @return original failure or null
*/
public Throwable getOriginalFailure();
/**
* Gets failure details as Optional.
* @param detailsClass class of details
* @param detailsType generic type of details
* @return optional failure details
*/
public <T> Optional<T> getDetails(Class<T> detailsClass, Type detailsType);
/**
* Gets failure details as Optional.
* @param detailsClass class of details
* @return optional failure details
*/
public <T> Optional<T> getDetails(Class<T> detailsClass);
/**
* Gets raw failure details.
* @return optional payloads
*/
public Optional<Payloads> getDetailsPayloads();
/**
* Gets data converter used for details.
* @return data converter
*/
public DataConverter getDataConverter();
}Application-level failures with custom error types and retry control.
/**
* Application-level failures with custom error types.
*/
public final class ApplicationFailure extends TemporalFailure {
/**
* Creates new application failure.
* @param message error message
* @param type error type
* @param nonRetryable whether failure should not be retried
* @param details additional details
* @return ApplicationFailure instance
*/
public static ApplicationFailure newFailure(String message, String type, boolean nonRetryable, Object... details);
/**
* Creates retryable application failure.
* @param message error message
* @param type error type
* @param details additional details
* @return ApplicationFailure instance
*/
public static ApplicationFailure newFailure(String message, String type, Object... details);
/**
* Creates non-retryable application failure.
* @param message error message
* @param type error type
* @param details additional details
* @return ApplicationFailure instance
*/
public static ApplicationFailure newNonRetryableFailure(String message, String type, Object... details);
/**
* Creates application failure from throwable.
* @param message error message
* @param type error type
* @param nonRetryable whether failure should not be retried
* @param cause original cause
* @param details additional details
* @return ApplicationFailure instance
*/
public static ApplicationFailure newFailureWithCause(String message, String type, boolean nonRetryable, Throwable cause, Object... details);
/**
* Creates retryable failure with cause.
* @param message error message
* @param type error type
* @param cause original cause
* @param details additional details
* @return ApplicationFailure instance
*/
public static ApplicationFailure newFailureWithCause(String message, String type, Throwable cause, Object... details);
/**
* Creates non-retryable failure with cause.
* @param message error message
* @param type error type
* @param cause original cause
* @param details additional details
* @return ApplicationFailure instance
*/
public static ApplicationFailure newNonRetryableFailureWithCause(String message, String type, Throwable cause, Object... details);
/**
* Gets application error type.
* @return error type
*/
public String getType();
/**
* Checks if failure is non-retryable.
* @return true if non-retryable
*/
public boolean isNonRetryable();
}Usage Examples:
public class PaymentActivitiesImpl implements PaymentActivities {
@Override
public PaymentResult processPayment(PaymentInfo paymentInfo) {
try {
return paymentService.charge(paymentInfo);
} catch (InsufficientFundsException e) {
// Non-retryable business error
throw ApplicationFailure.newNonRetryableFailure(
"Insufficient funds",
"INSUFFICIENT_FUNDS",
paymentInfo.getAmount(),
paymentInfo.getAccountId()
);
} catch (PaymentGatewayException e) {
// Retryable technical error
throw ApplicationFailure.newFailure(
"Payment gateway temporarily unavailable",
"GATEWAY_ERROR",
e.getGatewayCode()
);
}
}
}
// Catching application failures in workflows
public class OrderWorkflowImpl implements OrderWorkflow {
@Override
public OrderResult processOrder(OrderRequest request) {
try {
PaymentResult payment = activities.processPayment(request.getPaymentInfo());
return new OrderResult(true, payment.getTransactionId());
} catch (ApplicationFailure e) {
if ("INSUFFICIENT_FUNDS".equals(e.getType())) {
// Handle insufficient funds
return new OrderResult(false, "Payment declined: insufficient funds");
} else if (e.isNonRetryable()) {
// Handle other non-retryable errors
return new OrderResult(false, "Payment failed: " + e.getMessage());
} else {
// Let retryable errors propagate
throw e;
}
}
}
}Failures that wrap exceptions from activity executions.
/**
* Wraps exceptions thrown by activity executions.
*/
public final class ActivityFailure extends TemporalFailure {
/**
* Gets scheduled event ID.
* @return scheduled event ID
*/
public long getScheduledEventId();
/**
* Gets started event ID.
* @return started event ID
*/
public long getStartedEventId();
/**
* Gets activity type.
* @return activity type
*/
public String getActivityType();
/**
* Gets activity ID.
* @return activity ID
*/
public String getActivityId();
/**
* Gets retry state.
* @return retry state
*/
public RetryState getRetryState();
}Failures from child workflow executions.
/**
* Wraps failures from child workflow executions.
*/
public final class ChildWorkflowFailure extends TemporalFailure {
/**
* Gets namespace of child workflow.
* @return namespace
*/
public String getNamespace();
/**
* Gets workflow execution of child.
* @return workflow execution
*/
public WorkflowExecution getExecution();
/**
* Gets workflow type of child.
* @return workflow type
*/
public String getWorkflowType();
/**
* Gets initiated event ID.
* @return initiated event ID
*/
public long getInitiatedEventId();
/**
* Gets started event ID.
* @return started event ID
*/
public long getStartedEventId();
/**
* Gets retry state.
* @return retry state
*/
public RetryState getRetryState();
}Failures indicating timeout occurred during execution.
/**
* Indicates timeout occurred during execution.
*/
public final class TimeoutFailure extends TemporalFailure {
/**
* Gets timeout type.
* @return timeout type
*/
public TimeoutType getTimeoutType();
/**
* Gets last heartbeat details if available.
* @param detailsClass class of details
* @param detailsType generic type of details
* @return optional heartbeat details
*/
public <T> Optional<T> getLastHeartbeatDetails(Class<T> detailsClass, Type detailsType);
/**
* Gets last heartbeat details if available.
* @param detailsClass class of details
* @return optional heartbeat details
*/
public <T> Optional<T> getLastHeartbeatDetails(Class<T> detailsClass);
}
/**
* Types of timeouts that can occur.
*/
public enum TimeoutType {
START_TO_CLOSE,
SCHEDULE_TO_START,
SCHEDULE_TO_CLOSE,
HEARTBEAT
}Usage Examples:
public class TimeoutHandlingWorkflowImpl implements TimeoutHandlingWorkflow {
@Override
public String processWithTimeouts(ProcessingRequest request) {
try {
// Activity with timeout
ProcessingActivities activities = Workflow.newActivityStub(
ProcessingActivities.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofMinutes(5))
.setHeartbeatTimeout(Duration.ofMinutes(1))
.build()
);
return activities.processData(request);
} catch (ActivityFailure e) {
if (e.getCause() instanceof TimeoutFailure) {
TimeoutFailure timeout = (TimeoutFailure) e.getCause();
switch (timeout.getTimeoutType()) {
case START_TO_CLOSE:
return handleStartToCloseTimeout(request, timeout);
case HEARTBEAT:
return handleHeartbeatTimeout(request, timeout);
default:
return "Processing failed due to timeout: " + timeout.getTimeoutType();
}
}
throw e;
}
}
private String handleHeartbeatTimeout(ProcessingRequest request, TimeoutFailure timeout) {
// Get last heartbeat details to resume processing
Optional<ProcessingProgress> progress = timeout.getLastHeartbeatDetails(ProcessingProgress.class);
if (progress.isPresent()) {
// Resume from last known progress
request.setResumeFromIndex(progress.get().getProcessedCount());
return "Resuming processing from index: " + progress.get().getProcessedCount();
}
return "Processing timed out without progress information";
}
}Failures indicating workflow or activity was canceled.
/**
* Indicates workflow or activity was canceled.
*/
public final class CanceledFailure extends TemporalFailure {
/**
* Creates CanceledFailure with details.
* @param details cancellation details
* @param dataConverter data converter
* @return CanceledFailure instance
*/
public static CanceledFailure newFailure(Object details, DataConverter dataConverter);
/**
* Creates CanceledFailure with message and details.
* @param message cancellation message
* @param details cancellation details
* @param dataConverter data converter
* @return CanceledFailure instance
*/
public static CanceledFailure newFailure(String message, Object details, DataConverter dataConverter);
}Usage Examples:
public class CancellationHandlingWorkflowImpl implements CancellationHandlingWorkflow {
@Override
public String processWithCancellation(ProcessingRequest request) {
try {
// Long running activity
ProcessingActivities activities = Workflow.newActivityStub(ProcessingActivities.class);
return activities.processLargeDataset(request);
} catch (CanceledFailure e) {
// Handle cancellation gracefully
Optional<String> cancellationReason = e.getDetails(String.class);
String reason = cancellationReason.orElse("No reason provided");
// Perform cleanup
activities.cleanupPartialProcessing(request.getId());
return "Processing was canceled: " + reason;
}
}
}
public class CancellableActivityImpl implements CancellableActivity {
@Override
public String processData(String data) {
ActivityExecutionContext context = Activity.getExecutionContext();
try {
for (int i = 0; i < 1000; i++) {
// Check for cancellation via heartbeat
context.heartbeat("Processing item " + i);
// Process item
Thread.sleep(1000);
}
return "Processing completed";
} catch (ActivityCompletionException e) {
if (e.getCause() instanceof CanceledFailure) {
// Activity was canceled
return "Processing canceled at item " + getCurrentIndex();
}
throw Activity.wrap(e);
}
}
}Failures indicating workflow was terminated.
/**
* Indicates workflow was terminated.
*/
public final class TerminatedFailure extends TemporalFailure {
/**
* Creates TerminatedFailure.
* @param reason termination reason
* @param details termination details
* @param dataConverter data converter
* @return TerminatedFailure instance
*/
public static TerminatedFailure newFailure(String reason, Object details, DataConverter dataConverter);
}Failures originating from the Temporal server.
/**
* Failures originating from the Temporal server.
*/
public final class ServerFailure extends TemporalFailure {
/**
* Gets server error type.
* @return server error type
*/
public String getType();
/**
* Checks if failure is non-retryable.
* @return true if non-retryable
*/
public boolean isNonRetryable();
}Failures from Nexus operation executions.
/**
* Failures from Nexus operation executions.
*/
public final class NexusOperationFailure extends TemporalFailure {
/**
* Gets scheduled event ID.
* @return scheduled event ID
*/
public long getScheduledEventId();
/**
* Gets endpoint name.
* @return endpoint name
*/
public String getEndpoint();
/**
* Gets service name.
* @return service name
*/
public String getService();
/**
* Gets operation name.
* @return operation name
*/
public String getOperation();
/**
* Gets operation ID.
* @return operation ID
*/
public String getOperationId();
}Default implementation for converting between exceptions and Temporal failures.
/**
* Default implementation for converting between exceptions and Temporal failures.
*/
public class DefaultFailureConverter implements FailureConverter {
/**
* Gets default instance.
* @return default failure converter
*/
public static DefaultFailureConverter INSTANCE;
/**
* Converts exception to failure.
* @param exception exception to convert
* @param dataConverter data converter for details
* @return temporal failure
*/
@Override
public TemporalFailure exceptionToFailure(Throwable exception, DataConverter dataConverter);
/**
* Converts failure to exception.
* @param failure failure to convert
* @param dataConverter data converter for details
* @return exception
*/
@Override
public Throwable failureToException(FailureInfo failure, DataConverter dataConverter);
}
/**
* Interface for converting between exceptions and failures.
*/
public interface FailureConverter {
/**
* Converts exception to failure for transmission.
* @param exception exception to convert
* @param dataConverter data converter
* @return temporal failure
*/
TemporalFailure exceptionToFailure(Throwable exception, DataConverter dataConverter);
/**
* Converts failure back to exception.
* @param failure failure info from server
* @param dataConverter data converter
* @return exception
*/
Throwable failureToException(FailureInfo failure, DataConverter dataConverter);
}Usage Examples:
public class CustomFailureConverter implements FailureConverter {
@Override
public TemporalFailure exceptionToFailure(Throwable exception, DataConverter dataConverter) {
if (exception instanceof CustomBusinessException) {
CustomBusinessException cbe = (CustomBusinessException) exception;
return ApplicationFailure.newFailure(
cbe.getMessage(),
cbe.getErrorCode(),
cbe.isRetryable() == false, // nonRetryable
cbe.getContext()
);
}
// Delegate to default converter
return DefaultFailureConverter.INSTANCE.exceptionToFailure(exception, dataConverter);
}
@Override
public Throwable failureToException(FailureInfo failure, DataConverter dataConverter) {
// Convert back to custom exception if needed
if (failure.getType().equals("CustomBusinessException")) {
return new CustomBusinessException(
failure.getMessage(),
failure.getSource(),
failure.getNonRetryableFlag()
);
}
// Delegate to default converter
return DefaultFailureConverter.INSTANCE.failureToException(failure, dataConverter);
}
}
public class ErrorHandlingExample {
public void demonstrateErrorHandling() {
try {
// Call activity that might fail
String result = activities.riskyOperation();
} catch (ActivityFailure activityFailure) {
Throwable cause = activityFailure.getCause();
if (cause instanceof ApplicationFailure) {
ApplicationFailure appFailure = (ApplicationFailure) cause;
switch (appFailure.getType()) {
case "VALIDATION_ERROR":
// Handle validation errors
handleValidationError(appFailure);
break;
case "BUSINESS_RULE_VIOLATION":
// Handle business rule violations
handleBusinessRuleViolation(appFailure);
break;
default:
// Handle other application failures
handleGenericApplicationFailure(appFailure);
}
} else if (cause instanceof TimeoutFailure) {
TimeoutFailure timeoutFailure = (TimeoutFailure) cause;
handleTimeout(timeoutFailure);
}
} catch (ChildWorkflowFailure childFailure) {
// Handle child workflow failures
System.err.println("Child workflow failed: " + childFailure.getWorkflowType());
}
}
private void handleValidationError(ApplicationFailure failure) {
Optional<ValidationErrorDetails> details = failure.getDetails(ValidationErrorDetails.class);
if (details.isPresent()) {
System.err.println("Validation failed: " + details.get().getFieldErrors());
}
}
}Indicates the current retry state of a failed execution.
/**
* Indicates the current retry state of a failed execution.
*/
public enum RetryState {
/**
* Execution is being retried.
*/
IN_PROGRESS,
/**
* Execution failed non-retryably or retry policy exhausted.
*/
NON_RETRYABLE_FAILURE,
/**
* Timeout exceeded while retrying.
*/
TIMEOUT,
/**
* Maximum number of attempts reached.
*/
MAXIMUM_ATTEMPTS_REACHED,
/**
* Retry policy not set.
*/
RETRY_POLICY_NOT_SET,
/**
* Internal service error during retries.
*/
INTERNAL_SERVICE_ERROR,
/**
* Execution was canceled while retrying.
*/
CANCEL_REQUESTED
}Install with Tessl CLI
npx tessl i tessl/maven-io-temporal--temporal-sdk