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 2021/08/03 12:33:49 UTC

[camel] 19/22: CAMEL-16757: route configuration added to classic spring xml (also for cdi xml)

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch CAMEL-16757b
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 33beec2734a530d044f6ce4f15592824ab10e90a
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Tue Aug 3 11:03:12 2021 +0200

    CAMEL-16757: route configuration added to classic spring xml (also for cdi xml)
---
 .../camel/cdi/xml/CamelContextFactoryBean.java     | 14 ++++
 .../camel/cdi/test/XmlRouteConfigurationTest.java  | 77 ++++++++++++++++++++++
 .../resources/camel-context-routeConfiguration.xml | 49 ++++++++++++++
 .../org/apache/camel/spring/xml/camelContext.json  |  1 +
 .../camel/spring/xml/CamelContextFactoryBean.java  | 18 +++++
 .../spring/xml/handler/CamelNamespaceHandler.java  |  1 +
 ...gRoutesConfigurationBuilderIdOrPatternTest.java | 48 ++++++++++++++
 ...ngRoutesConfigurationBuilderIdOrPatternTest.xml | 52 +++++++++++++++
 .../java/org/apache/camel/impl/DefaultModel.java   |  9 +++
 ...ainer.java => RouteConfigurationContainer.java} | 16 ++---
 .../camel/model/RouteConfigurationsDefinition.java |  2 +-
 .../org/apache/camel/model/RouteContainer.java     |  2 +-
 .../apache/camel/model/RouteTemplateContainer.java |  2 +-
 .../core/xml/AbstractCamelContextFactoryBean.java  | 45 +++++++++++--
 .../RoutesConfigurationBuilderIdOrPatternTest.java | 21 ++++++
 15 files changed, 342 insertions(+), 15 deletions(-)

diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/xml/CamelContextFactoryBean.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/xml/CamelContextFactoryBean.java
index 001012d..96b7170 100644
--- a/components/camel-cdi/src/main/java/org/apache/camel/cdi/xml/CamelContextFactoryBean.java
+++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/xml/CamelContextFactoryBean.java
@@ -59,6 +59,7 @@ import org.apache.camel.model.PackageScanDefinition;
 import org.apache.camel.model.Resilience4jConfigurationDefinition;
 import org.apache.camel.model.RestContextRefDefinition;
 import org.apache.camel.model.RouteBuilderDefinition;
+import org.apache.camel.model.RouteConfigurationDefinition;
 import org.apache.camel.model.RouteContextRefDefinition;
 import org.apache.camel.model.RouteDefinition;
 import org.apache.camel.model.RouteTemplateContextRefDefinition;
@@ -296,6 +297,9 @@ public class CamelContextFactoryBean extends AbstractCamelContextFactoryBean<Def
     @XmlElement(name = "rest")
     private List<RestDefinition> rests = new ArrayList<>();
 
+    @XmlElement(name = "routeConfiguration")
+    private List<RouteConfigurationDefinition> routeConfigurations = new ArrayList<>();
+
     @XmlElement(name = "routeTemplate")
     private List<RouteTemplateDefinition> routeTemplates = new ArrayList<>();
 
@@ -492,6 +496,16 @@ public class CamelContextFactoryBean extends AbstractCamelContextFactoryBean<Def
     }
 
     @Override
+    public List<RouteConfigurationDefinition> getRouteConfigurations() {
+        return routeConfigurations;
+    }
+
+    @Override
+    public void setRouteConfigurations(List<RouteConfigurationDefinition> routeConfigurations) {
+        this.routeConfigurations = routeConfigurations;
+    }
+
+    @Override
     public List<RouteTemplateDefinition> getRouteTemplates() {
         return routeTemplates;
     }
diff --git a/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/XmlRouteConfigurationTest.java b/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/XmlRouteConfigurationTest.java
new file mode 100644
index 0000000..7bbcb0d
--- /dev/null
+++ b/components/camel-cdi/src/test/java/org/apache/camel/cdi/test/XmlRouteConfigurationTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.cdi.test;
+
+import java.nio.file.Paths;
+
+import javax.inject.Inject;
+
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.cdi.CdiCamelExtension;
+import org.apache.camel.cdi.ImportResource;
+import org.apache.camel.cdi.Uri;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.EmptyAsset;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.fail;
+
+@RunWith(Arquillian.class)
+@ImportResource("imported-context.xml")
+public class XmlRouteConfigurationTest {
+
+    @Inject
+    private ProducerTemplate template;
+
+    @Inject
+    @Uri("mock:error")
+    private MockEndpoint mock;
+
+    @Deployment
+    public static Archive<?> deployment() {
+        return ShrinkWrap.create(JavaArchive.class)
+                // Camel CDI
+                .addPackage(CdiCamelExtension.class.getPackage())
+                // Test Camel XML
+                .addAsResource(
+                        Paths.get("src/test/resources/camel-context-routeConfiguration.xml").toFile(),
+                        "imported-context.xml")
+                // Bean archive deployment descriptor
+                .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
+    }
+
+    @Test
+    public void testRoutesConfigurationOnException() throws Exception {
+        mock.expectedBodiesReceived("Bye World");
+
+        try {
+            template.sendBody("direct:start", "Hello World");
+            fail("Should throw exception");
+        } catch (Exception e) {
+            // expected
+        }
+        template.sendBody("direct:start2", "Bye World");
+
+        mock.assertIsSatisfied();
+    }
+}
diff --git a/components/camel-cdi/src/test/resources/camel-context-routeConfiguration.xml b/components/camel-cdi/src/test/resources/camel-context-routeConfiguration.xml
new file mode 100644
index 0000000..a77cae3
--- /dev/null
+++ b/components/camel-cdi/src/test/resources/camel-context-routeConfiguration.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
+
+    <camelContext xmlns="http://camel.apache.org/schema/spring">
+
+        <routeConfiguration id="handleError">
+            <onException>
+                <exception>java.lang.Exception</exception>
+                <handled>
+                    <constant>true</constant>
+                </handled>
+                <to uri="mock:error"/>
+            </onException>
+        </routeConfiguration>
+
+        <route>
+            <from uri="direct:start"/>
+            <throwException exceptionType="java.lang.IllegalArgumentException" message="Foo"/>
+        </route>
+
+        <route routeConfigurationId="handleError">
+            <from uri="direct:start2"/>
+            <throwException exceptionType="java.lang.IllegalArgumentException" message="Foo2"/>
+        </route>
+
+    </camelContext>
+
+</beans>
diff --git a/components/camel-spring-xml/src/generated/resources/org/apache/camel/spring/xml/camelContext.json b/components/camel-spring-xml/src/generated/resources/org/apache/camel/spring/xml/camelContext.json
index fb39212..9a6507f 100644
--- a/components/camel-spring-xml/src/generated/resources/org/apache/camel/spring/xml/camelContext.json
+++ b/components/camel-spring-xml/src/generated/resources/org/apache/camel/spring/xml/camelContext.json
@@ -80,6 +80,7 @@
     "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": "Configuration of interceptors that triggers sending messages to endpoints." },
     "restConfiguration": { "kind": "element", "displayName": "Rest Configuration", "required": false, "type": "object", "javaType": "org.apache.camel.model.rest.RestConfigurationDefinition", "deprecated": false, "autowired": false, "secret": false, "description": "Configuration for rest-dsl" },
     "rest": { "kind": "element", "displayName": "Rest", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.rest.RestDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Contains the rest services defined using the rest-dsl" },
+    "routeConfiguration": { "kind": "element", "displayName": "Route Configuration", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.RouteConfigurationDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Contains the Camel route configurations" },
     "routeTemplate": { "kind": "element", "displayName": "Route Template", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.RouteTemplateDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Contains the Camel route templates" },
     "route": { "kind": "element", "displayName": "Route", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.RouteDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Contains the Camel routes" },
     "id": { "kind": "attribute", "displayName": "Id", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node" }
diff --git a/components/camel-spring-xml/src/main/java/org/apache/camel/spring/xml/CamelContextFactoryBean.java b/components/camel-spring-xml/src/main/java/org/apache/camel/spring/xml/CamelContextFactoryBean.java
index 87a990e..4aa862d 100644
--- a/components/camel-spring-xml/src/main/java/org/apache/camel/spring/xml/CamelContextFactoryBean.java
+++ b/components/camel-spring-xml/src/main/java/org/apache/camel/spring/xml/CamelContextFactoryBean.java
@@ -57,6 +57,7 @@ import org.apache.camel.model.PackageScanDefinition;
 import org.apache.camel.model.Resilience4jConfigurationDefinition;
 import org.apache.camel.model.RestContextRefDefinition;
 import org.apache.camel.model.RouteBuilderDefinition;
+import org.apache.camel.model.RouteConfigurationDefinition;
 import org.apache.camel.model.RouteContextRefDefinition;
 import org.apache.camel.model.RouteDefinition;
 import org.apache.camel.model.RouteTemplateContextRefDefinition;
@@ -263,6 +264,8 @@ public class CamelContextFactoryBean extends AbstractCamelContextFactoryBean<Spr
     private RestConfigurationDefinition restConfiguration;
     @XmlElement(name = "rest")
     private List<RestDefinition> rests = new ArrayList<>();
+    @XmlElement(name = "routeConfiguration")
+    private List<RouteConfigurationDefinition> routeConfigurations = new ArrayList<>();
     @XmlElement(name = "routeTemplate")
     private List<RouteTemplateDefinition> routeTemplates = new ArrayList<>();
     @XmlElement(name = "route")
@@ -557,6 +560,20 @@ public class CamelContextFactoryBean extends AbstractCamelContextFactoryBean<Spr
         this.routes = routes;
     }
 
+    @Override
+    public List<RouteConfigurationDefinition> getRouteConfigurations() {
+        return routeConfigurations;
+    }
+
+    /**
+     * Contains the Camel route configurations
+     */
+    @Override
+    public void setRouteConfigurations(List<RouteConfigurationDefinition> routeConfigurations) {
+        this.routeConfigurations = routeConfigurations;
+    }
+
+    @Override
     public List<RouteTemplateDefinition> getRouteTemplates() {
         return routeTemplates;
     }
@@ -564,6 +581,7 @@ public class CamelContextFactoryBean extends AbstractCamelContextFactoryBean<Spr
     /**
      * Contains the Camel route templates
      */
+    @Override
     public void setRouteTemplates(List<RouteTemplateDefinition> routeTemplates) {
         this.routeTemplates = routeTemplates;
     }
diff --git a/components/camel-spring-xml/src/main/java/org/apache/camel/spring/xml/handler/CamelNamespaceHandler.java b/components/camel-spring-xml/src/main/java/org/apache/camel/spring/xml/handler/CamelNamespaceHandler.java
index d7272be..fed1bba 100644
--- a/components/camel-spring-xml/src/main/java/org/apache/camel/spring/xml/handler/CamelNamespaceHandler.java
+++ b/components/camel-spring-xml/src/main/java/org/apache/camel/spring/xml/handler/CamelNamespaceHandler.java
@@ -390,6 +390,7 @@ public class CamelNamespaceHandler extends NamespaceHandlerSupport {
                 builder.addPropertyValue("implicitId", implicitId);
                 builder.addPropertyValue("restConfiguration", factoryBean.getRestConfiguration());
                 builder.addPropertyValue("rests", factoryBean.getRests());
+                builder.addPropertyValue("routeConfigurations", factoryBean.getRouteConfigurations());
                 builder.addPropertyValue("routeTemplates", factoryBean.getRouteTemplates());
                 builder.addPropertyValue("routes", factoryBean.getRoutes());
                 builder.addPropertyValue("intercepts", factoryBean.getIntercepts());
diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/SpringRoutesConfigurationBuilderIdOrPatternTest.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/SpringRoutesConfigurationBuilderIdOrPatternTest.java
new file mode 100644
index 0000000..5e9a316
--- /dev/null
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/SpringRoutesConfigurationBuilderIdOrPatternTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.spring;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.context.support.AbstractXmlApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class SpringRoutesConfigurationBuilderIdOrPatternTest extends SpringTestSupport {
+
+    @Override
+    protected AbstractXmlApplicationContext createApplicationContext() {
+        return new ClassPathXmlApplicationContext(
+                "org/apache/camel/spring/SpringRoutesConfigurationBuilderIdOrPatternTest.xml");
+    }
+
+    @Test
+    public void testRoutesConfigurationOnException() throws Exception {
+        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();
+    }
+
+}
diff --git a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/SpringRoutesConfigurationBuilderIdOrPatternTest.xml b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/SpringRoutesConfigurationBuilderIdOrPatternTest.xml
new file mode 100644
index 0000000..f8b4d43
--- /dev/null
+++ b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/SpringRoutesConfigurationBuilderIdOrPatternTest.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
+    ">
+
+
+  <camelContext xmlns="http://camel.apache.org/schema/spring">
+
+    <routeConfiguration id="handleError">
+      <onException>
+        <exception>java.lang.Exception</exception>
+        <handled>
+          <constant>true</constant>
+        </handled>
+        <to uri="mock:error"/>
+      </onException>
+    </routeConfiguration>
+
+    <route>
+      <from uri="direct:start"/>
+      <throwException exceptionType="java.lang.IllegalArgumentException" message="Foo"/>
+    </route>
+
+    <route routeConfigurationId="handleError">
+      <from uri="direct:start2"/>
+      <throwException exceptionType="java.lang.IllegalArgumentException" message="Foo2"/>
+    </route>
+
+  </camelContext>
+
+</beans>
diff --git a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
index 475a975..b658b6b 100644
--- a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
+++ b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
@@ -123,6 +123,15 @@ public class DefaultModel implements Model {
         // only add if not already exists (route-loader may let Java DSL add route configuration twice
         // because it extends RouteBuilder as base class)
         if (!this.routesConfigurations.contains(routesConfiguration)) {
+            // check that there is no id clash
+            if (routesConfiguration.getId() != null) {
+                boolean clash = this.routesConfigurations.stream()
+                        .anyMatch(r -> ObjectHelper.equal(r.getId(), routesConfiguration.getId()));
+                if (clash) {
+                    throw new IllegalArgumentException(
+                            "Route configuration already exists with id: " + routesConfiguration.getId());
+                }
+            }
             this.routesConfigurations.add(routesConfiguration);
         }
     }
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteContainer.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteConfigurationContainer.java
similarity index 69%
copy from core/camel-core-model/src/main/java/org/apache/camel/model/RouteContainer.java
copy to core/camel-core-model/src/main/java/org/apache/camel/model/RouteConfigurationContainer.java
index 5e4ba8e..c46aabe 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteContainer.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteConfigurationContainer.java
@@ -21,22 +21,22 @@ import java.util.List;
 import javax.xml.bind.annotation.XmlElementRef;
 
 /**
- * Container to hold {@link org.apache.camel.model.RouteDefinition Route}.
+ * Container to hold {@link RouteConfigurationDefinition route configurations}.
  */
-public interface RouteContainer {
+public interface RouteConfigurationContainer {
 
     /**
-     * Returns the routes
+     * Returns the route configurations
      *
-     * @return the routes
+     * @return the route configurations
      */
     @XmlElementRef
-    List<RouteDefinition> getRoutes();
+    List<RouteConfigurationDefinition> getRouteConfigurations();
 
     /**
-     * Sets the routes to use
+     * Sets the route configurations to use
      *
-     * @param routes the routes
+     * @param routes the route configurations
      */
-    void setRoutes(List<RouteDefinition> routes);
+    void setRouteConfigurations(List<RouteConfigurationDefinition> routes);
 }
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteConfigurationsDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteConfigurationsDefinition.java
index c56874b..92ec02c 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteConfigurationsDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteConfigurationsDefinition.java
@@ -34,7 +34,7 @@ import org.apache.camel.spi.Metadata;
 @Metadata(label = "configuration")
 @XmlRootElement(name = "routeConfigurations")
 @XmlAccessorType(XmlAccessType.FIELD)
-public class RouteConfigurationsDefinition {
+public class RouteConfigurationsDefinition implements RouteConfigurationContainer {
 
     @XmlElementRef
     private List<RouteConfigurationDefinition> routeConfigurations = new ArrayList<>();
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteContainer.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteContainer.java
index 5e4ba8e..ba548f2 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteContainer.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteContainer.java
@@ -21,7 +21,7 @@ import java.util.List;
 import javax.xml.bind.annotation.XmlElementRef;
 
 /**
- * Container to hold {@link org.apache.camel.model.RouteDefinition Route}.
+ * Container to hold {@link org.apache.camel.model.RouteDefinition routes}.
  */
 public interface RouteContainer {
 
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateContainer.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateContainer.java
index ae4e8c8..b2715c8 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateContainer.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateContainer.java
@@ -21,7 +21,7 @@ import java.util.List;
 import javax.xml.bind.annotation.XmlElementRef;
 
 /**
- * Container to hold {@link RouteTemplateDefinition Route}.
+ * Container to hold {@link RouteTemplateDefinition route templates}.
  */
 public interface RouteTemplateContainer {
 
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 2e88712..9b62c51 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
@@ -72,6 +72,8 @@ import org.apache.camel.model.PackageScanDefinition;
 import org.apache.camel.model.Resilience4jConfigurationDefinition;
 import org.apache.camel.model.RestContextRefDefinition;
 import org.apache.camel.model.RouteBuilderDefinition;
+import org.apache.camel.model.RouteConfigurationContainer;
+import org.apache.camel.model.RouteConfigurationDefinition;
 import org.apache.camel.model.RouteContainer;
 import org.apache.camel.model.RouteContextRefDefinition;
 import org.apache.camel.model.RouteDefinition;
@@ -132,6 +134,7 @@ import org.apache.camel.spi.UuidGenerator;
 import org.apache.camel.spi.Validator;
 import org.apache.camel.support.CamelContextHelper;
 import org.apache.camel.support.ObjectHelper;
+import org.apache.camel.support.PatternHelper;
 import org.apache.camel.util.StringHelper;
 import org.apache.camel.util.concurrent.ThreadPoolRejectedPolicy;
 import org.slf4j.Logger;
@@ -143,7 +146,7 @@ import org.slf4j.LoggerFactory;
  */
 @XmlAccessorType(XmlAccessType.FIELD)
 public abstract class AbstractCamelContextFactoryBean<T extends ModelCamelContext> extends IdentifiedType
-        implements RouteTemplateContainer, RouteContainer, RestContainer {
+        implements RouteTemplateContainer, RouteConfigurationContainer, RouteContainer, RestContainer {
 
     private static final Logger LOG = LoggerFactory.getLogger(AbstractCamelContextFactoryBean.class);
 
@@ -444,6 +447,9 @@ public abstract class AbstractCamelContextFactoryBean<T extends ModelCamelContex
             // mark that we are setting up routes
             getContext().adapt(ExtendedCamelContext.class).setupRoutes(false);
 
+            // add route configurations
+            getContext().addRouteConfigurations(getRouteConfigurations());
+
             // init route templates
             initRouteTemplateRefs();
 
@@ -546,9 +552,37 @@ public abstract class AbstractCamelContextFactoryBean<T extends ModelCamelContex
             // sanity check first as the route is created using XML
             RouteDefinitionHelper.sanityCheckRoute(route);
 
-            // leverage logic from route definition helper to prepare the route
-            RouteDefinitionHelper.prepareRoute(getContext(), route, getOnExceptions(), getIntercepts(), getInterceptFroms(),
-                    getInterceptSendToEndpoints(), getOnCompletions());
+            // merge global and route scoped together
+            List<OnExceptionDefinition> oe = new ArrayList<>(getOnExceptions());
+            List<InterceptDefinition> icp = new ArrayList<>(getIntercepts());
+            List<InterceptFromDefinition> ifrom = new ArrayList<>(getInterceptFroms());
+            List<InterceptSendToEndpointDefinition> ito = new ArrayList<>(getInterceptSendToEndpoints());
+            List<OnCompletionDefinition> oc = new ArrayList<>(getOnCompletions());
+            if (getContext() != null) {
+                List<RouteConfigurationDefinition> globalConfigurations
+                        = getContext().adapt(ModelCamelContext.class).getRouteConfigurationDefinitions();
+                if (globalConfigurations != null) {
+                    globalConfigurations.stream()
+                            // global configurations have no id assigned or is a wildcard
+                            // if the route has a route configuration assigned then use pattern matching
+                            .filter(g -> (g.getId() == null || g.getId().equals("*"))
+                                    || (PatternHelper.matchPattern(g.getId(), route.getRouteConfigurationId())))
+                            .forEach(g -> {
+                                oe.addAll(g.getOnExceptions());
+                                icp.addAll(g.getIntercepts());
+                                ifrom.addAll(g.getInterceptFroms());
+                                ito.addAll(g.getInterceptSendTos());
+                                oc.addAll(g.getOnCompletions());
+                            });
+                }
+            }
+
+            // must prepare the route before we can add it to the routes list
+            RouteDefinitionHelper.prepareRoute(getContext(), route, oe, icp, ifrom, ito, oc);
+
+            if (LOG.isDebugEnabled() && route.getRouteConfigurationId() != null) {
+                LOG.debug("Route: {} is using routeConfigurationsId: {}", route.getId(), route.getRouteConfigurationId());
+            }
 
             // mark the route as prepared now
             route.markPrepared();
@@ -857,6 +891,9 @@ public abstract class AbstractCamelContextFactoryBean<T extends ModelCamelContex
     public abstract List<RouteTemplateDefinition> getRouteTemplates();
 
     @Override
+    public abstract List<RouteConfigurationDefinition> getRouteConfigurations();
+
+    @Override
     public abstract List<RouteDefinition> getRoutes();
 
     @Override
diff --git a/core/camel-core/src/test/java/org/apache/camel/model/RoutesConfigurationBuilderIdOrPatternTest.java b/core/camel-core/src/test/java/org/apache/camel/model/RoutesConfigurationBuilderIdOrPatternTest.java
index ebefa1b..d79b4d0 100644
--- a/core/camel-core/src/test/java/org/apache/camel/model/RoutesConfigurationBuilderIdOrPatternTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/model/RoutesConfigurationBuilderIdOrPatternTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.model;
 
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
@@ -28,6 +29,7 @@ import org.apache.camel.builder.RouteConfigurationBuilder;
 import org.apache.camel.support.OrderedComparator;
 import org.junit.jupiter.api.Test;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.fail;
 
 public class RoutesConfigurationBuilderIdOrPatternTest extends ContextTestSupport {
@@ -219,4 +221,23 @@ public class RoutesConfigurationBuilderIdOrPatternTest extends ContextTestSuppor
         assertMockEndpointsSatisfied();
     }
 
+    @Test
+    public void testRoutesConfigurationIdClash() throws Exception {
+        RouteConfigurationBuilder rcb = new RouteConfigurationBuilder() {
+            @Override
+            public void configuration() throws Exception {
+                routeConfiguration().onException(Exception.class).handled(true).to("mock:foo");
+                routeConfiguration("foo").onException(IOException.class).handled(true).to("mock:foo");
+                routeConfiguration("bar").onException(FileNotFoundException.class).handled(true).to("mock:bar");
+                routeConfiguration("foo").onException(IllegalArgumentException.class).handled(true).to("mock:foo");
+            }
+        };
+        try {
+            context.addRoutesConfigurations(rcb);
+            fail("Should throw exception");
+        } catch (IllegalArgumentException e) {
+            assertEquals("Route configuration already exists with id: foo", e.getMessage());
+        }
+    }
+
 }