All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.digdag.spi.TaskExecutionException Maven / Gradle / Ivy

package io.digdag.spi;

import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import io.digdag.client.config.Config;
import io.digdag.client.config.ConfigFactory;
import io.digdag.client.config.ConfigElement;

import static com.google.common.base.Strings.isNullOrEmpty;
import static java.util.Locale.ENGLISH;

/**
 * An exception thrown when an expected exception happens.
 *
 * 

* When an error happens in an operator, it should be wrapped with TaskExecutionException if * the cause is expected so that Digdag shows a message for users without verbose stacktrace. * The other exception classes are regarded as unexpected and Digdag shows stacktrace for operator * developers. *

* *

* TaskExecutionException is also used to let Digdag retry the task later. Use ofNextPolling * (if task retries for simple polling) or ofNextPollingWithCause (if task retries to recover * from an error) method to retry. *

*/ public class TaskExecutionException extends RuntimeException { public static ConfigElement buildExceptionErrorConfig(Throwable ex) { return buildExceptionErrorConfig(formatExceptionMessage(ex), ex); } public static ConfigElement buildExceptionErrorConfig(String message, Throwable ex) { Map map = ImmutableMap.of( "message", message, "stacktrace", Arrays.asList(ex.getStackTrace()) .stream() .map(it -> it.toString()) .collect(Collectors.joining(", "))); return ConfigElement.ofMap(map); } private static String formatExceptionMessage(Throwable ex) { return firstNonEmptyMessage(ex) .transform(message -> { return String.format(ENGLISH, "%s (%s)", message, ex.getClass().getSimpleName() .replaceFirst("(?:Exception|Error)$", "") .replaceAll("([A-Z]+)([A-Z][a-z])", "$1 $2") .replaceAll("([a-z])([A-Z])", "$1 $2") .toLowerCase()); }) .or(() -> ex.toString()); } private static Optional firstNonEmptyMessage(Throwable ex) { String message = ex.getMessage(); if (!isNullOrEmpty(message)) { return Optional.of(message); } Throwable cause = ex.getCause(); if (cause == null) { return Optional.absent(); } return firstNonEmptyMessage(cause); } public static TaskExecutionException ofNextPolling(int interval, ConfigElement nextStateParams) { return new TaskExecutionException(interval, nextStateParams); } public static TaskExecutionException ofNextPollingWithCause(Throwable cause, int interval, ConfigElement nextStateParams) { return new TaskExecutionException(cause, buildExceptionErrorConfig(cause), interval, nextStateParams); } private TaskExecutionException(int retryInterval, ConfigElement stateParams) { super("Retrying this task after "+retryInterval+" seconds"); this.error = Optional.absent(); this.retryInterval = Optional.of(retryInterval); this.stateParams = Optional.of(stateParams); } private TaskExecutionException(Throwable cause, ConfigElement error, int retryInterval, ConfigElement stateParams) { super(cause); this.error = Optional.of(error); this.retryInterval = Optional.of(retryInterval); this.stateParams = Optional.of(stateParams); } private final Optional error; private final Optional retryInterval; private final Optional stateParams; /** * Wrap an expected exception to make the task failed. */ public TaskExecutionException(Throwable cause) { this(formatExceptionMessage(cause), cause); } /** * Wrap an expected exception with a custom well-formatted message to make the task failed. */ public TaskExecutionException(String customMessage, Throwable cause) { super(customMessage, cause); this.error = Optional.of(buildExceptionErrorConfig(customMessage, cause)); this.retryInterval = Optional.absent(); this.stateParams = Optional.absent(); } /** * Build an expected exception with a simple message to make the task failed. */ public TaskExecutionException(String message) { this(message, ImmutableMap.of()); } /** * Build an expected exception with a simple message and properties to make the task failed. */ public TaskExecutionException(String message, Map errorProperties) { super(message); this.error = Optional.of(buildPropertiesErrorConfig(message, errorProperties)); this.retryInterval = Optional.absent(); this.stateParams = Optional.absent(); } private static ConfigElement buildPropertiesErrorConfig(String message, Map errorProperties) { ImmutableMap.Builder builder = ImmutableMap.builder(); builder.putAll(errorProperties); builder.put("message", message); return ConfigElement.ofMap(builder.build()); } @Deprecated public TaskExecutionException(String message, ConfigElement error) { super(message); this.error = Optional.of(error); this.retryInterval = Optional.absent(); this.stateParams = Optional.absent(); } @Deprecated public TaskExecutionException(Throwable cause, ConfigElement error) { super(formatExceptionMessage(cause), cause); this.error = Optional.of(error); this.retryInterval = Optional.absent(); this.stateParams = Optional.absent(); } public Optional getError(ConfigFactory cf) { return error.transform(it -> it.toConfig(cf)); } public boolean isError() { return error.isPresent(); } public Optional getRetryInterval() { return retryInterval; } public Optional getStateParams(ConfigFactory cf) { return stateParams.transform(it -> it.toConfig(cf)); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy