You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2022/04/13 15:29:07 UTC
[camel] 01/02: CAMEL-16834: error handler in DSL model
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 81c32ebe7af5624d0f29722297363a5498b65fb3
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Apr 13 16:42:52 2022 +0200
CAMEL-16834: error handler in DSL model
---
.../camel/catalog/models/routeConfiguration.json | 1 +
.../apache/camel/catalog/schemas/camel-spring.xsd | 1 +
.../org/apache/camel/model/routeConfiguration.json | 1 +
.../camel/model/RouteConfigurationDefinition.java | 40 +++++-
.../apache/camel/model/RouteDefinitionHelper.java | 24 +++-
.../org/apache/camel/model/RoutesDefinition.java | 24 +++-
.../core/xml/AbstractCamelContextFactoryBean.java | 11 +-
.../model/RoutesConfigurationErrorHandlerTest.java | 160 +++++++++++++++++++++
.../java/org/apache/camel/xml/in/ModelParser.java | 1 +
.../modules/ROOT/pages/route-configuration.adoc | 27 ++++
.../RouteConfigurationDefinitionDeserializer.java | 7 +
.../src/generated/resources/camel-yaml-dsl.json | 3 +
.../src/generated/resources/camelYamlDsl.json | 3 +
.../camel/dsl/yaml/RouteConfigurationTest.groovy | 42 ++++++
14 files changed, 334 insertions(+), 11 deletions(-)
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/routeConfiguration.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/routeConfiguration.json
index a1b45b6bb7c..5e560cdfaad 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/routeConfiguration.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/routeConfiguration.json
@@ -12,6 +12,7 @@
"output": false
},
"properties": {
+ "errorHandler": { "kind": "element", "displayName": "Error Handler", "required": false, "type": "object", "javaType": "org.apache.camel.model.ErrorHandlerDefinition", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the error handler to use, for routes that has not already been configured with an error handler." },
"intercept": { "kind": "element", "displayName": "Intercept", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.InterceptDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Adds a route for an interceptor that intercepts every processing step." },
"interceptFrom": { "kind": "element", "displayName": "Intercept From", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.InterceptFromDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Adds a route for an interceptor that intercepts incoming messages on the given endpoint." },
"interceptSendToEndpoint": { "kind": "element", "displayName": "Intercept Send To Endpoint", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.InterceptSendToEndpointDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Applies a route for an interceptor if an exchange is send to the given endpoint" },
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
index 72e6d5fa578..0c731250a98 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
@@ -10535,6 +10535,7 @@ Reference to the route templates in the xml dsl.
<xs:complexContent>
<xs:extension base="tns:optionalIdentifiedDefinition">
<xs:sequence>
+ <xs:element minOccurs="0" ref="tns:errorHandler"/>
<xs:element maxOccurs="unbounded" minOccurs="0" ref="tns:intercept"/>
<xs:element maxOccurs="unbounded" minOccurs="0" ref="tns:interceptFrom"/>
<xs:element maxOccurs="unbounded" minOccurs="0" ref="tns:interceptSendToEndpoint"/>
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/routeConfiguration.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/routeConfiguration.json
index a1b45b6bb7c..5e560cdfaad 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/routeConfiguration.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/routeConfiguration.json
@@ -12,6 +12,7 @@
"output": false
},
"properties": {
+ "errorHandler": { "kind": "element", "displayName": "Error Handler", "required": false, "type": "object", "javaType": "org.apache.camel.model.ErrorHandlerDefinition", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the error handler to use, for routes that has not already been configured with an error handler." },
"intercept": { "kind": "element", "displayName": "Intercept", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.InterceptDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Adds a route for an interceptor that intercepts every processing step." },
"interceptFrom": { "kind": "element", "displayName": "Intercept From", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.InterceptFromDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Adds a route for an interceptor that intercepts incoming messages on the given endpoint." },
"interceptSendToEndpoint": { "kind": "element", "displayName": "Intercept Send To Endpoint", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.InterceptSendToEndpointDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Applies a route for an interceptor if an exchange is send to the given endpoint" },
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteConfigurationDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteConfigurationDefinition.java
index 1a7b65f59a1..0d509cef27d 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteConfigurationDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteConfigurationDefinition.java
@@ -26,6 +26,8 @@ import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
+import org.apache.camel.ErrorHandlerFactory;
+import org.apache.camel.model.errorhandler.ErrorHandlerRefDefinition;
import org.apache.camel.spi.Metadata;
/**
@@ -37,8 +39,8 @@ import org.apache.camel.spi.Metadata;
public class RouteConfigurationDefinition extends OptionalIdentifiedDefinition<RouteConfigurationDefinition>
implements PreconditionContainer {
- // TODO: Model for ErrorHandler (requires to move error handler model from spring-xml, blueprint to core)
-
+ @XmlElement
+ private ErrorHandlerDefinition errorHandler;
@XmlElement(name = "intercept")
private List<InterceptDefinition> intercepts = new ArrayList<>();
@XmlElement(name = "interceptFrom")
@@ -71,6 +73,14 @@ public class RouteConfigurationDefinition extends OptionalIdentifiedDefinition<R
return "RoutesConfiguration " + getId();
}
+ public ErrorHandlerDefinition getErrorHandler() {
+ return errorHandler;
+ }
+
+ public void setErrorHandler(ErrorHandlerDefinition errorHandler) {
+ this.errorHandler = errorHandler;
+ }
+
public List<OnExceptionDefinition> getOnExceptions() {
return onExceptions;
}
@@ -132,6 +142,32 @@ public class RouteConfigurationDefinition extends OptionalIdentifiedDefinition<R
// Fluent API
// -------------------------------------------------------------------------
+ /**
+ * Sets the error handler to use, for routes that has not already been configured with an error handler.
+ *
+ * @param ref reference to existing error handler
+ * @return the builder
+ */
+ public RouteConfigurationDefinition errorHandler(String ref) {
+ ErrorHandlerDefinition def = new ErrorHandlerDefinition();
+ def.setErrorHandlerType(new ErrorHandlerRefDefinition(ref));
+ setErrorHandler(def);
+ return this;
+ }
+
+ /**
+ * Sets the error handler to use, for routes that has not already been configured with an error handler.
+ *
+ * @param errorHandler the error handler
+ * @return the builder
+ */
+ public RouteConfigurationDefinition errorHandler(ErrorHandlerFactory errorHandler) {
+ ErrorHandlerDefinition def = new ErrorHandlerDefinition();
+ def.setErrorHandlerType(errorHandler);
+ setErrorHandler(def);
+ return this;
+ }
+
/**
* Sets the predicate of the precondition in simple language to evaluate in order to determine if this route
* configuration should be included or not.
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinitionHelper.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinitionHelper.java
index 98b9d1923f5..e1d072d2a29 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinitionHelper.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinitionHelper.java
@@ -307,7 +307,7 @@ public final class RouteDefinitionHelper {
* @param route the route
*/
public static void prepareRoute(CamelContext context, RouteDefinition route) {
- prepareRoute(context, route, null, null, null, null, null);
+ prepareRoute(context, route, null, null, null, null, null, null);
}
/**
@@ -317,6 +317,7 @@ public final class RouteDefinitionHelper {
*
* @param context the camel context
* @param route the route
+ * @param errorHandler optional error handler
* @param onExceptions optional list of onExceptions
* @param intercepts optional list of interceptors
* @param interceptFromDefinitions optional list of interceptFroms
@@ -324,13 +325,16 @@ public final class RouteDefinitionHelper {
* @param onCompletions optional list onCompletions
*/
public static void prepareRoute(
- CamelContext context, RouteDefinition route, List<OnExceptionDefinition> onExceptions,
+ CamelContext context, RouteDefinition route,
+ ErrorHandlerDefinition errorHandler,
+ List<OnExceptionDefinition> onExceptions,
List<InterceptDefinition> intercepts,
List<InterceptFromDefinition> interceptFromDefinitions,
List<InterceptSendToEndpointDefinition> interceptSendToEndpointDefinitions,
List<OnCompletionDefinition> onCompletions) {
- prepareRouteImp(context, route, onExceptions, intercepts, interceptFromDefinitions, interceptSendToEndpointDefinitions,
+ prepareRouteImp(context, route, errorHandler, onExceptions, intercepts, interceptFromDefinitions,
+ interceptSendToEndpointDefinitions,
onCompletions);
}
@@ -341,6 +345,7 @@ public final class RouteDefinitionHelper {
*
* @param context the camel context
* @param route the route
+ * @param errorHandler optional error handler
* @param onExceptions optional list of onExceptions
* @param intercepts optional list of interceptors
* @param interceptFromDefinitions optional list of interceptFroms
@@ -348,7 +353,9 @@ public final class RouteDefinitionHelper {
* @param onCompletions optional list onCompletions
*/
private static void prepareRouteImp(
- CamelContext context, RouteDefinition route, List<OnExceptionDefinition> onExceptions,
+ CamelContext context, RouteDefinition route,
+ ErrorHandlerDefinition errorHandler,
+ List<OnExceptionDefinition> onExceptions,
List<InterceptDefinition> intercepts,
List<InterceptFromDefinition> interceptFromDefinitions,
List<InterceptSendToEndpointDefinition> interceptSendToEndpointDefinitions,
@@ -370,7 +377,7 @@ public final class RouteDefinitionHelper {
RouteDefinitionHelper.prepareRouteForInit(route, abstracts, lower);
// parent and error handler builder should be initialized first
- initParentAndErrorHandlerBuilder(context, route, abstracts, onExceptions);
+ initParentAndErrorHandlerBuilder(context, route, errorHandler, abstracts, onExceptions);
// validate top-level violations
validateTopLevel(route.getOutputs());
// then interceptors
@@ -440,10 +447,13 @@ public final class RouteDefinitionHelper {
}
private static void initParentAndErrorHandlerBuilder(
- CamelContext context, RouteDefinition route, List<ProcessorDefinition<?>> abstracts,
+ CamelContext context, RouteDefinition route, ErrorHandlerDefinition errorHandler,
+ List<ProcessorDefinition<?>> abstracts,
List<OnExceptionDefinition> onExceptions) {
- if (context != null) {
+ if (errorHandler != null) {
+ route.setErrorHandlerFactoryIfNull(errorHandler.getErrorHandlerType());
+ } else if (context != null) {
// let the route inherit the error handler builder from camel
// context if none already set
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RoutesDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RoutesDefinition.java
index f53235dc458..7c582c65bfb 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/RoutesDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RoutesDefinition.java
@@ -18,6 +18,7 @@ package org.apache.camel.model;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
@@ -241,6 +242,7 @@ public class RoutesDefinition extends OptionalIdentifiedDefinition<RoutesDefinit
route.setResource(resource);
// merge global and route scoped together
+ final AtomicReference<ErrorHandlerDefinition> gcErrorHandler = new AtomicReference<>();
List<OnExceptionDefinition> oe = new ArrayList<>(onExceptions);
List<InterceptDefinition> icp = new ArrayList<>(intercepts);
List<InterceptFromDefinition> ifrom = new ArrayList<>(interceptFroms);
@@ -250,6 +252,7 @@ public class RoutesDefinition extends OptionalIdentifiedDefinition<RoutesDefinit
List<RouteConfigurationDefinition> globalConfigurations
= getCamelContext().adapt(ModelCamelContext.class).getRouteConfigurationDefinitions();
if (globalConfigurations != null) {
+
// if there are multiple ids configured then we should apply in that same order
String[] ids = route.getRouteConfigurationId() != null
? route.getRouteConfigurationId().split(",") : new String[] { "*" };
@@ -266,6 +269,12 @@ public class RoutesDefinition extends OptionalIdentifiedDefinition<RoutesDefinit
}
})
.forEach(g -> {
+ // there can only be one global error handler, so override previous, meaning
+ // that we will pick the last in the sort (take precedence)
+ if (g.getErrorHandler() != null) {
+ gcErrorHandler.set(g.getErrorHandler());
+ }
+
String aid = g.getId() == null ? "<default>" : g.getId();
// remember the id that was used on the route
route.addAppliedRouteConfigurationId(aid);
@@ -276,11 +285,24 @@ public class RoutesDefinition extends OptionalIdentifiedDefinition<RoutesDefinit
oc.addAll(g.getOnCompletions());
});
}
+
+ // set error handler before prepare
+ if (errorHandlerFactory == null && gcErrorHandler.get() != null) {
+ ErrorHandlerDefinition ehd = gcErrorHandler.get();
+ route.setErrorHandlerFactoryIfNull(ehd.getErrorHandlerType());
+ }
}
}
+ // if the route does not already have an error handler set then use route configured error handler
+ // if one was configured
+ ErrorHandlerDefinition ehd = null;
+ if (errorHandlerFactory == null && gcErrorHandler.get() != null) {
+ ehd = gcErrorHandler.get();
+ }
+
// must prepare the route before we can add it to the routes list
- RouteDefinitionHelper.prepareRoute(getCamelContext(), route, oe, icp, ifrom, ito, oc);
+ RouteDefinitionHelper.prepareRoute(getCamelContext(), route, ehd, oe, icp, ifrom, ito, oc);
if (LOG.isDebugEnabled() && route.getAppliedRouteConfigurationIds() != null) {
LOG.debug("Route: {} is using route configurations ids: {}", route.getId(),
diff --git a/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java b/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java
index adbd809e0c9..b9238c769fa 100644
--- a/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java
+++ b/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java
@@ -25,6 +25,7 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
@@ -57,6 +58,7 @@ import org.apache.camel.impl.engine.DefaultManagementStrategy;
import org.apache.camel.impl.engine.TransformerKey;
import org.apache.camel.impl.engine.ValidatorKey;
import org.apache.camel.model.ContextScanDefinition;
+import org.apache.camel.model.ErrorHandlerDefinition;
import org.apache.camel.model.FaultToleranceConfigurationDefinition;
import org.apache.camel.model.FromDefinition;
import org.apache.camel.model.GlobalOptionsDefinition;
@@ -590,6 +592,7 @@ public abstract class AbstractCamelContextFactoryBean<T extends ModelCamelContex
route.resetPrepare();
// merge global and route scoped together
+ AtomicReference<ErrorHandlerDefinition> errorHandler = new AtomicReference<>();
List<OnExceptionDefinition> oe = new ArrayList<>(getOnExceptions());
List<InterceptDefinition> icp = new ArrayList<>(getIntercepts());
List<InterceptFromDefinition> ifrom = new ArrayList<>(getInterceptFroms());
@@ -615,6 +618,12 @@ public abstract class AbstractCamelContextFactoryBean<T extends ModelCamelContex
}
})
.forEach(g -> {
+ // there can only be one global error handler, so override previous, meaning
+ // that we will pick the last in the sort (take precedence)
+ if (g.getErrorHandler() != null) {
+ errorHandler.set(g.getErrorHandler());
+ }
+
String aid = g.getId() == null ? "<default>" : g.getId();
// remember the id that was used on the route
route.addAppliedRouteConfigurationId(aid);
@@ -629,7 +638,7 @@ public abstract class AbstractCamelContextFactoryBean<T extends ModelCamelContex
}
// must prepare the route before we can add it to the routes list
- RouteDefinitionHelper.prepareRoute(getContext(), route, oe, icp, ifrom, ito, oc);
+ RouteDefinitionHelper.prepareRoute(getContext(), route, errorHandler.get(), oe, icp, ifrom, ito, oc);
if (LOG.isDebugEnabled() && route.getAppliedRouteConfigurationIds() != null) {
LOG.debug("Route: {} is using route configurations ids: {}", route.getId(),
diff --git a/core/camel-core/src/test/java/org/apache/camel/model/RoutesConfigurationErrorHandlerTest.java b/core/camel-core/src/test/java/org/apache/camel/model/RoutesConfigurationErrorHandlerTest.java
new file mode 100644
index 00000000000..0dcc4ec67b7
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/model/RoutesConfigurationErrorHandlerTest.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.model;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.builder.RouteConfigurationBuilder;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Fail.fail;
+
+public class RoutesConfigurationErrorHandlerTest extends ContextTestSupport {
+
+ @Override
+ public boolean isUseRouteBuilder() {
+ return false;
+ }
+
+ @Test
+ public void testGlobal() throws Exception {
+ context.addRoutes(new RouteConfigurationBuilder() {
+ @Override
+ public void configuration() throws Exception {
+ // global routes configuration
+ routeConfiguration().errorHandler(deadLetterChannel("mock:error"));
+
+ }
+ });
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:start")
+ .throwException(new IllegalArgumentException("Foo"));
+
+ from("direct:start2")
+ .throwException(new IllegalArgumentException("Foo2"));
+ }
+ });
+ context.start();
+
+ getMockEndpoint("mock:error").expectedBodiesReceived("Hello World", "Bye World");
+
+ template.sendBody("direct:start", "Hello World");
+ template.sendBody("direct:start2", "Bye World");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Test
+ public void testLocalOverride() throws Exception {
+ context.addRoutes(new RouteConfigurationBuilder() {
+ @Override
+ public void configuration() throws Exception {
+ // global routes configuration
+ routeConfiguration().errorHandler(deadLetterChannel("mock:error"));
+
+ }
+ });
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:start")
+ .throwException(new IllegalArgumentException("Foo"));
+
+ from("direct:start2")
+ .errorHandler(deadLetterChannel("mock:error2"))
+ .throwException(new IllegalArgumentException("Foo2"));
+ }
+ });
+ context.start();
+
+ getMockEndpoint("mock:error").expectedBodiesReceived("Hello World");
+ getMockEndpoint("mock:error2").expectedBodiesReceived("Bye World");
+
+ template.sendBody("direct:start", "Hello World");
+ template.sendBody("direct:start2", "Bye World");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Test
+ public void testLocalConfiguration() throws Exception {
+ context.addRoutes(new RouteConfigurationBuilder() {
+ @Override
+ public void configuration() throws Exception {
+ routeConfiguration("mylocal").errorHandler(deadLetterChannel("mock:error"));
+
+ }
+ });
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:start")
+ .throwException(new IllegalArgumentException("Foo"));
+
+ from("direct:start2").routeConfigurationId("mylocal")
+ .throwException(new IllegalArgumentException("Foo2"));
+ }
+ });
+ context.start();
+
+ getMockEndpoint("mock:error").expectedBodiesReceived("Bye World");
+
+ try {
+ template.sendBody("direct:start", "Hello World");
+ fail("Should throw exception");
+ } catch (Exception e) {
+ // expected
+ }
+ template.sendBody("direct:start2", "Bye World");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Test
+ public void testGlobalAndLocal() throws Exception {
+ context.addRoutes(new RouteConfigurationBuilder() {
+ @Override
+ public void configuration() throws Exception {
+ routeConfiguration().errorHandler(deadLetterChannel("mock:error"));
+ routeConfiguration("mylocal").errorHandler(deadLetterChannel("mock:error2"));
+
+ }
+ });
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:start")
+ .throwException(new IllegalArgumentException("Foo"));
+
+ from("direct:start2").routeConfigurationId("mylocal")
+ .throwException(new IllegalArgumentException("Foo2"));
+ }
+ });
+ context.start();
+
+ getMockEndpoint("mock:error").expectedBodiesReceived("Hello World");
+ getMockEndpoint("mock:error2").expectedBodiesReceived("Bye World");
+
+ template.sendBody("direct:start", "Hello World");
+ template.sendBody("direct:start2", "Bye World");
+
+ assertMockEndpointsSatisfied();
+ }
+
+}
diff --git a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
index 78c3d65df2a..fe6144cae44 100644
--- a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
+++ b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
@@ -978,6 +978,7 @@ public class ModelParser extends BaseParser {
return optionalIdentifiedDefinitionAttributeHandler().accept(def, key, val);
}, (def, key) -> {
switch (key) {
+ case "errorHandler": def.setErrorHandler(doParseErrorHandlerDefinition()); break;
case "interceptFrom": doAdd(doParseInterceptFromDefinition(), def.getInterceptFroms(), def::setInterceptFroms); break;
case "interceptSendToEndpoint": doAdd(doParseInterceptSendToEndpointDefinition(), def.getInterceptSendTos(), def::setInterceptSendTos); break;
case "intercept": doAdd(doParseInterceptDefinition(), def.getIntercepts(), def::setIntercepts); break;
diff --git a/docs/user-manual/modules/ROOT/pages/route-configuration.adoc b/docs/user-manual/modules/ROOT/pages/route-configuration.adoc
index 2215ffb67f6..4a71d7c07c1 100644
--- a/docs/user-manual/modules/ROOT/pages/route-configuration.adoc
+++ b/docs/user-manual/modules/ROOT/pages/route-configuration.adoc
@@ -12,6 +12,7 @@ The route configuration is supported by all DSL's, so useable by: Java, XML, Gro
In the route configuration you can setup common strategies for:
+- xref:error-handler.adoc[Error Handler]
- xref:exception-clause.adoc[OnException]
- xref:oncompletion.adoc[OnCompletion]
- xref:components:eips:intercept.adoc[Intercept]
@@ -176,6 +177,32 @@ then fail and rollback.
If you add more routes, then those routes can also be assigned the _retryError_ configuration
if they should also retry in case of error.
+=== Route Configuration with Error Handler
+
+Each route configuration can also have a specific error handler configured, as shown below:
+
+[source,java]
+----
+public class MyJavaErrorHandler extends RouteConfigurationBuilder {
+
+ @Override
+ public void configuration() throws Exception {
+ routeConfiguration()
+ .errorHandler(deadLetterChannel("mock:dead"));
+
+ routeConfiguration("retryError")
+ .onException(Exception.class).maximumRedeliveries(5);
+ }
+}
+----
+
+In the example above, the _nameless_ configuration has an error handler with a dead letter queue.
+And the route configuration with id _retryError_ does not, and instead it will attempt
+to retry the failing message up till 5 times before giving up (exhausted). Because this
+route configuration does not have any error handler assigned, then Camel will use the default error handler.
+
+IMPORTANT: Routes that have a local error handler defined, will always use this error handler,
+instead of the error handler from route configurations. A route can only have 1 error handler.
== Route Configuration in XML
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteConfigurationDefinitionDeserializer.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteConfigurationDefinitionDeserializer.java
index 5b99d26a339..1123731de60 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteConfigurationDefinitionDeserializer.java
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteConfigurationDefinitionDeserializer.java
@@ -20,6 +20,7 @@ import org.apache.camel.dsl.yaml.common.YamlDeserializationContext;
import org.apache.camel.dsl.yaml.common.YamlDeserializerBase;
import org.apache.camel.dsl.yaml.common.YamlDeserializerResolver;
import org.apache.camel.dsl.yaml.common.exception.UnsupportedFieldException;
+import org.apache.camel.model.ErrorHandlerDefinition;
import org.apache.camel.model.InterceptDefinition;
import org.apache.camel.model.InterceptFromDefinition;
import org.apache.camel.model.InterceptSendToEndpointDefinition;
@@ -41,6 +42,7 @@ import org.snakeyaml.engine.v2.nodes.SequenceNode;
properties = {
@YamlProperty(name = "id", type = "string"),
@YamlProperty(name = "precondition", type = "string"),
+ @YamlProperty(name = "error-handler", type = "object:org.apache.camel.model.ErrorHandlerDefinition.class"),
@YamlProperty(name = "intercept", type = "array:org.apache.camel.model.InterceptDefinition"),
@YamlProperty(name = "intercept-from", type = "array:org.apache.camel.model.InterceptFromDefinition"),
@YamlProperty(name = "intercept-send-to-endpoint",
@@ -79,6 +81,11 @@ public class RouteConfigurationDefinitionDeserializer extends YamlDeserializerBa
case "precondition":
target.setPrecondition(asText(val));
break;
+ case "error-handler":
+ setDeserializationContext(val, dc);
+ ErrorHandlerDefinition ehd = asType(val, ErrorHandlerDefinition.class);
+ target.setErrorHandler(ehd);
+ break;
case "on-exception":
setDeserializationContext(val, dc);
OnExceptionDefinition oed = asType(val, OnExceptionDefinition.class);
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camel-yaml-dsl.json b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camel-yaml-dsl.json
index 24e65a97bef..260ec1996de 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camel-yaml-dsl.json
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camel-yaml-dsl.json
@@ -2324,6 +2324,9 @@
}, {
"type" : "object",
"properties" : {
+ "error-handler" : {
+ "$ref" : "#/items/definitions/org.apache.camel.model.ErrorHandlerDefinition.class"
+ },
"id" : {
"type" : "string"
},
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camelYamlDsl.json b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camelYamlDsl.json
index ac348b5ee5f..62c0864d508 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camelYamlDsl.json
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camelYamlDsl.json
@@ -2228,6 +2228,9 @@
}, {
"type" : "object",
"properties" : {
+ "errorHandler" : {
+ "$ref" : "#/items/definitions/org.apache.camel.model.ErrorHandlerDefinition.class"
+ },
"id" : {
"type" : "string"
},
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RouteConfigurationTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RouteConfigurationTest.groovy
index 04ff381cb6e..bba80b12c80 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RouteConfigurationTest.groovy
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RouteConfigurationTest.groovy
@@ -223,4 +223,46 @@ class RouteConfigurationTest extends YamlTestSupport {
Assertions.assertTrue(out2.isFailed())
}
+ def "route-configuration-error-handler"() {
+ setup:
+ // global configurations
+ loadRoutes """
+ - beans:
+ - name: myFailingProcessor
+ type: ${MyFailingProcessor.name}
+ - route-configuration:
+ - error-handler:
+ dead-letter-channel:
+ dead-letter-uri: "mock:on-error"
+ """
+ // routes
+ loadRoutes """
+ - from:
+ uri: "direct:start"
+ steps:
+ - process:
+ ref: "myFailingProcessor"
+ - from:
+ uri: "direct:start2"
+ steps:
+ - process:
+ ref: "myFailingProcessor"
+ """
+
+ withMock('mock:on-error') {
+ expectedBodiesReceived 'hello', 'hello2'
+ }
+
+ when:
+ context.start()
+
+ withTemplate {
+ to('direct:start').withBody('hello').send()
+ to('direct:start2').withBody('hello2').send()
+ }
+ then:
+ MockEndpoint.assertIsSatisfied(context)
+ }
+
+
}