This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch errorhandler-in-dsl in repository https://gitbox.apache.org/repos/asf/camel.git
commit 8a2798d74e3582034d34ab6fee73a2348d0f0347 Author: Claus Ibsen <[email protected]> AuthorDate: Tue Apr 5 16:09:07 2022 +0200 CAMEL-16834: error handler in model DSL. WIP --- .../errorhandler/DeadLetterChannelDefinition.java | 21 +- .../DefaultErrorHandlerDefinition.java | 382 ++++++++++++++++++++- 2 files changed, 401 insertions(+), 2 deletions(-) diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/errorhandler/DeadLetterChannelDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/errorhandler/DeadLetterChannelDefinition.java index b541bfa511f..75a01a5804e 100644 --- a/core/camel-core-model/src/main/java/org/apache/camel/model/errorhandler/DeadLetterChannelDefinition.java +++ b/core/camel-core-model/src/main/java/org/apache/camel/model/errorhandler/DeadLetterChannelDefinition.java @@ -32,7 +32,6 @@ import org.apache.camel.spi.Metadata; @XmlAccessorType(XmlAccessType.FIELD) public class DeadLetterChannelDefinition extends DefaultErrorHandlerDefinition { - // TODO: fluent builders // TODO: label, java type, ref @XmlAttribute(required = true) @@ -89,4 +88,24 @@ public class DeadLetterChannelDefinition extends DefaultErrorHandlerDefinition { super.cloneBuilder(other); } + /** + * Whether the dead letter channel should handle (and ignore) any new exception that may been thrown during sending + * the message to the dead letter endpoint. + * <p/> + * The default value is <tt>true</tt> which means any such kind of exception is handled and ignored. Set this to + * <tt>false</tt> to let the exception be propagated back on the {@link org.apache.camel.Exchange}. This can be used + * in situations where you use transactions, and want to use Camel's dead letter channel to deal with exceptions + * during routing, but if the dead letter channel itself fails because of a new exception being thrown, then by + * setting this to <tt>false</tt> the new exceptions is propagated back and set on the + * {@link org.apache.camel.Exchange}, which allows the transaction to detect the exception, and rollback. + * + * @param handleNewException <tt>true</tt> to handle (and ignore), <tt>false</tt> to catch and propagated the + * exception on the {@link org.apache.camel.Exchange} + * @return the builder + */ + public DefaultErrorHandlerDefinition deadLetterHandleNewException(boolean handleNewException) { + setDeadLetterHandleNewException(handleNewException ? "true" : "false"); + return this; + } + } diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/errorhandler/DefaultErrorHandlerDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/errorhandler/DefaultErrorHandlerDefinition.java index 68e2ce03d5f..a2f3d918ed8 100644 --- a/core/camel-core-model/src/main/java/org/apache/camel/model/errorhandler/DefaultErrorHandlerDefinition.java +++ b/core/camel-core-model/src/main/java/org/apache/camel/model/errorhandler/DefaultErrorHandlerDefinition.java @@ -25,14 +25,18 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; +import org.apache.camel.Expression; import org.apache.camel.LoggingLevel; import org.apache.camel.Predicate; import org.apache.camel.Processor; import org.apache.camel.builder.ErrorHandlerBuilder; import org.apache.camel.model.RedeliveryPolicyDefinition; +import org.apache.camel.processor.errorhandler.DefaultErrorHandler; import org.apache.camel.processor.errorhandler.RedeliveryPolicy; import org.apache.camel.spi.CamelLogger; import org.apache.camel.spi.Metadata; +import org.apache.camel.support.ExpressionToPredicateAdapter; +import org.slf4j.LoggerFactory; /** * Default error handler. @@ -42,7 +46,6 @@ import org.apache.camel.spi.Metadata; @XmlAccessorType(XmlAccessType.FIELD) public class DefaultErrorHandlerDefinition extends BaseErrorHandlerDefinition implements ErrorHandlerBuilder { - // TODO: fluent builders // TODO: label, java type, ref @XmlTransient @@ -401,4 +404,381 @@ public class DefaultErrorHandlerDefinition extends BaseErrorHandlerDefinition im public void setRedeliveryPolicy(RedeliveryPolicyDefinition redeliveryPolicy) { this.redeliveryPolicy = redeliveryPolicy; } + + // Builder methods + // ------------------------------------------------------------------------- + public DefaultErrorHandlerDefinition backOffMultiplier(double backOffMultiplier) { + getRedeliveryPolicy().backOffMultiplier(backOffMultiplier); + return this; + } + + public DefaultErrorHandlerDefinition collisionAvoidancePercent(double collisionAvoidancePercent) { + getRedeliveryPolicy().collisionAvoidancePercent(collisionAvoidancePercent); + return this; + } + + public DefaultErrorHandlerDefinition redeliveryDelay(long delay) { + getRedeliveryPolicy().redeliveryDelay(delay); + return this; + } + + public DefaultErrorHandlerDefinition delayPattern(String delayPattern) { + getRedeliveryPolicy().delayPattern(delayPattern); + return this; + } + + public DefaultErrorHandlerDefinition maximumRedeliveries(int maximumRedeliveries) { + getRedeliveryPolicy().maximumRedeliveries(maximumRedeliveries); + return this; + } + + public DefaultErrorHandlerDefinition disableRedelivery() { + getRedeliveryPolicy().maximumRedeliveries(0); + return this; + } + + public DefaultErrorHandlerDefinition maximumRedeliveryDelay(long maximumRedeliveryDelay) { + getRedeliveryPolicy().maximumRedeliveryDelay(maximumRedeliveryDelay); + return this; + } + + public DefaultErrorHandlerDefinition useCollisionAvoidance() { + getRedeliveryPolicy().useCollisionAvoidance(); + return this; + } + + public DefaultErrorHandlerDefinition useExponentialBackOff() { + getRedeliveryPolicy().useExponentialBackOff(); + return this; + } + + public DefaultErrorHandlerDefinition retriesExhaustedLogLevel(LoggingLevel retriesExhaustedLogLevel) { + getRedeliveryPolicy().setRetriesExhaustedLogLevel(retriesExhaustedLogLevel.name()); + return this; + } + + public DefaultErrorHandlerDefinition retryAttemptedLogLevel(LoggingLevel retryAttemptedLogLevel) { + getRedeliveryPolicy().setRetryAttemptedLogLevel(retryAttemptedLogLevel.name()); + return this; + } + + public DefaultErrorHandlerDefinition retryAttemptedLogInterval(int retryAttemptedLogInterval) { + getRedeliveryPolicy().setRetryAttemptedLogInterval(String.valueOf(retryAttemptedLogInterval)); + return this; + } + + public DefaultErrorHandlerDefinition logStackTrace(boolean logStackTrace) { + getRedeliveryPolicy().setLogStackTrace(logStackTrace ? "true" : "false"); + return this; + } + + public DefaultErrorHandlerDefinition logRetryStackTrace(boolean logRetryStackTrace) { + getRedeliveryPolicy().setLogRetryStackTrace(logRetryStackTrace ? "true" : "false"); + return this; + } + + public DefaultErrorHandlerDefinition logHandled(boolean logHandled) { + getRedeliveryPolicy().setLogHandled(logHandled ? "true" : "false"); + return this; + } + + public DefaultErrorHandlerDefinition logNewException(boolean logNewException) { + getRedeliveryPolicy().setLogNewException(logNewException ? "true" : "false"); + return this; + } + + public DefaultErrorHandlerDefinition logExhausted(boolean logExhausted) { + getRedeliveryPolicy().setLogExhausted(logExhausted ? "true" : "false"); + return this; + } + + public DefaultErrorHandlerDefinition logRetryAttempted(boolean logRetryAttempted) { + getRedeliveryPolicy().setLogRetryAttempted(logRetryAttempted ? "true" : "false"); + return this; + } + + public DefaultErrorHandlerDefinition logExhaustedMessageHistory(boolean logExhaustedMessageHistory) { + getRedeliveryPolicy().setLogExhaustedMessageHistory(logExhaustedMessageHistory ? "true" : "false"); + return this; + } + + public DefaultErrorHandlerDefinition logExhaustedMessageBody(boolean logExhaustedMessageBody) { + getRedeliveryPolicy().setLogExhaustedMessageBody(logExhaustedMessageBody ? "true" : "false"); + return this; + } + + public DefaultErrorHandlerDefinition exchangeFormatterRef(String exchangeFormatterRef) { + getRedeliveryPolicy().setExchangeFormatterRef(exchangeFormatterRef); + return this; + } + + /** + * Will allow asynchronous delayed redeliveries. The route, in particular the consumer's component, must support the + * Asynchronous Routing Engine (e.g. seda) + * + * @see RedeliveryPolicy#setAsyncDelayedRedelivery(boolean) + * @return the builder + */ + public DefaultErrorHandlerDefinition asyncDelayedRedelivery() { + getRedeliveryPolicy().setAsyncDelayedRedelivery("true"); + return this; + } + + /** + * Controls whether to allow redelivery while stopping/shutting down a route that uses error handling. + * + * @param allowRedeliveryWhileStopping <tt>true</tt> to allow redelivery, <tt>false</tt> to reject redeliveries + * @return the builder + */ + public DefaultErrorHandlerDefinition allowRedeliveryWhileStopping(boolean allowRedeliveryWhileStopping) { + getRedeliveryPolicy().setAllowRedeliveryWhileStopping(allowRedeliveryWhileStopping ? "true" : "false"); + return this; + } + + /** + * Sets the thread pool to be used for redelivery. + * + * @param executorService the scheduled thread pool to use + * @return the builder. + */ + public DefaultErrorHandlerDefinition executorService(ScheduledExecutorService executorService) { + setExecutorServiceBean(executorService); + return this; + } + + /** + * Sets a reference to a thread pool to be used for redelivery. + * + * @param ref reference to a scheduled thread pool + * @return the builder. + */ + public DefaultErrorHandlerDefinition executorServiceRef(String ref) { + setExecutorServiceRef(ref); + return this; + } + + /** + * Sets the logger used for caught exceptions + * + * @param logger the logger + * @return the builder + */ + public DefaultErrorHandlerDefinition logger(CamelLogger logger) { + setLoggerBean(logger); + return this; + } + + /** + * Sets the logging level of exceptions caught + * + * @param level the logging level + * @return the builder + */ + public DefaultErrorHandlerDefinition loggingLevel(LoggingLevel level) { + setLevel(level); + return this; + } + + /** + * Sets the log used for caught exceptions + * + * @param log the logger + * @return the builder + */ + public DefaultErrorHandlerDefinition log(org.slf4j.Logger log) { + if (loggerBean == null) { + loggerBean = new CamelLogger(LoggerFactory.getLogger(DefaultErrorHandler.class), LoggingLevel.ERROR); + } + loggerBean.setLog(log); + return this; + } + + /** + * Sets the log used for caught exceptions + * + * @param log the log name + * @return the builder + */ + public DefaultErrorHandlerDefinition log(String log) { + return log(LoggerFactory.getLogger(log)); + } + + /** + * Sets the log used for caught exceptions + * + * @param log the log class + * @return the builder + */ + public DefaultErrorHandlerDefinition log(Class<?> log) { + return log(LoggerFactory.getLogger(log)); + } + + /** + * Sets a processor that should be processed <b>before</b> a redelivery attempt. + * <p/> + * Can be used to change the {@link org.apache.camel.Exchange} <b>before</b> its being redelivered. + * + * @param processor the processor + * @return the builder + */ + public DefaultErrorHandlerDefinition onRedelivery(Processor processor) { + setOnRedeliveryProcessor(processor); + return this; + } + + /** + * Sets a reference for the processor to use <b>before</b> a redelivery attempt. + * + * @param onRedeliveryRef the processor's reference + * @return the builder + * @see #onRedelivery(Processor) + */ + public DefaultErrorHandlerDefinition onRedeliveryRef(String onRedeliveryRef) { + setOnRedeliveryRef(onRedeliveryRef); + return this; + } + + /** + * Sets the retry while expression. + * <p/> + * Will continue retrying until expression evaluates to <tt>false</tt>. + * + * @param retryWhile expression that determines when to stop retrying + * @return the builder + */ + public DefaultErrorHandlerDefinition retryWhile(Expression retryWhile) { + setRetryWhilePredicate(ExpressionToPredicateAdapter.toPredicate(retryWhile)); + return this; + } + + public DefaultErrorHandlerDefinition retryWhileRef(String retryWhileRef) { + setRetryWhileRef(retryWhileRef); + return this; + } + + /** + * Will use the original input {@link org.apache.camel.Message} (original body and headers) when an + * {@link org.apache.camel.Exchange} is moved to the dead letter queue. + * <p/> + * <b>Notice:</b> this only applies when all redeliveries attempt have failed and the + * {@link org.apache.camel.Exchange} is doomed for failure. <br/> + * Instead of using the current inprogress {@link org.apache.camel.Exchange} IN message we use the original IN + * message instead. This allows you to store the original input in the dead letter queue instead of the inprogress + * snapshot of the IN message. For instance if you route transform the IN body during routing and then failed. With + * the original exchange store in the dead letter queue it might be easier to manually re submit the + * {@link org.apache.camel.Exchange} again as the IN message is the same as when Camel received it. So you should be + * able to send the {@link org.apache.camel.Exchange} to the same input. + * <p/> + * The difference between useOriginalMessage and useOriginalBody is that the former includes both the original body + * and headers, where as the latter only includes the original body. You can use the latter to enrich the message + * with custom headers and include the original message body. The former wont let you do this, as its using the + * original message body and headers as they are. You cannot enable both useOriginalMessage and useOriginalBody. + * <p/> + * <b>Important:</b> The original input means the input message that are bounded by the current + * {@link org.apache.camel.spi.UnitOfWork}. An unit of work typically spans one route, or multiple routes if they + * are connected using internal endpoints such as direct or seda. When messages is passed via external endpoints + * such as JMS or HTTP then the consumer will create a new unit of work, with the message it received as input as + * the original input. Also some EIP patterns such as splitter, multicast, will create a new unit of work boundary + * for the messages in their sub-route (eg the split message); however these EIPs have an option named + * <tt>shareUnitOfWork</tt> which allows to combine with the parent unit of work in regard to error handling and + * therefore use the parent original message. + * <p/> + * By default this feature is off. + * + * @return the builder + * @see #useOriginalBody() + */ + public DefaultErrorHandlerDefinition useOriginalMessage() { + setUseOriginalMessage("true"); + return this; + } + + /** + * Will use the original input {@link org.apache.camel.Message} body (original body only) when an + * {@link org.apache.camel.Exchange} is moved to the dead letter queue. + * <p/> + * <b>Notice:</b> this only applies when all redeliveries attempt have failed and the + * {@link org.apache.camel.Exchange} is doomed for failure. <br/> + * Instead of using the current inprogress {@link org.apache.camel.Exchange} IN message we use the original IN + * message instead. This allows you to store the original input in the dead letter queue instead of the inprogress + * snapshot of the IN message. For instance if you route transform the IN body during routing and then failed. With + * the original exchange store in the dead letter queue it might be easier to manually re submit the + * {@link org.apache.camel.Exchange} again as the IN message is the same as when Camel received it. So you should be + * able to send the {@link org.apache.camel.Exchange} to the same input. + * <p/> + * The difference between useOriginalMessage and useOriginalBody is that the former includes both the original body + * and headers, where as the latter only includes the original body. You can use the latter to enrich the message + * with custom headers and include the original message body. The former wont let you do this, as its using the + * original message body and headers as they are. You cannot enable both useOriginalMessage and useOriginalBody. + * <p/> + * <b>Important:</b> The original input means the input message that are bounded by the current + * {@link org.apache.camel.spi.UnitOfWork}. An unit of work typically spans one route, or multiple routes if they + * are connected using internal endpoints such as direct or seda. When messages is passed via external endpoints + * such as JMS or HTTP then the consumer will create a new unit of work, with the message it received as input as + * the original input. Also some EIP patterns such as splitter, multicast, will create a new unit of work boundary + * for the messages in their sub-route (eg the split message); however these EIPs have an option named + * <tt>shareUnitOfWork</tt> which allows to combine with the parent unit of work in regard to error handling and + * therefore use the parent original message. + * <p/> + * By default this feature is off. + * + * @return the builder + * @see #useOriginalMessage() + */ + public DefaultErrorHandlerDefinition useOriginalBody() { + setUseOriginalBody("true"); + return this; + } + + /** + * Sets a custom {@link org.apache.camel.Processor} to prepare the {@link org.apache.camel.Exchange} before handled + * by the failure processor / dead letter channel. This allows for example to enrich the message before sending to a + * dead letter queue. + * + * @param processor the processor + * @return the builder + */ + public DefaultErrorHandlerDefinition onPrepareFailure(Processor processor) { + setOnPrepareFailureProcessor(processor); + return this; + } + + /** + * Sets a reference for the processor to use before handled by the failure processor. + * + * @param onPrepareFailureRef the processor's reference + * @return the builder + * @see #onPrepareFailure(Processor) + */ + public DefaultErrorHandlerDefinition onPrepareFailureRef(String onPrepareFailureRef) { + setOnPrepareFailureRef(onPrepareFailureRef); + return this; + } + + /** + * Sets a custom {@link org.apache.camel.Processor} to process the {@link org.apache.camel.Exchange} just after an + * exception was thrown. This allows to execute the processor at the same time the exception was thrown. + * <p/> + * Important: Any exception thrown from this processor will be ignored. + * + * @param processor the processor + * @return the builder + */ + public DefaultErrorHandlerDefinition onExceptionOccurred(Processor processor) { + setOnExceptionOccurredProcessor(processor); + return this; + } + + /** + * Sets a reference for the processor to use just after an exception was thrown. + * + * @param onExceptionOccurredRef the processor's reference + * @return the builder + * @see #onExceptionOccurred(Processor) + */ + public DefaultErrorHandlerDefinition onExceptionOccurredRef(String onExceptionOccurredRef) { + setOnExceptionOccurredRef(onExceptionOccurredRef); + return this; + } + }
