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 2023/08/21 11:55:41 UTC

[camel] 02/14: CAMEL-19765: camel-core - SPI for DumpRouteStrategy.

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

davsclaus pushed a commit to branch ds
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 67563dfacc606da9147a89e7d57f6aab364619d2
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sat Aug 19 13:21:46 2023 +0200

    CAMEL-19765: camel-core - SPI for DumpRouteStrategy.
---
 .../camel/impl/DefaultDumpRoutesStrategy.java      | 237 ++++++++++++++++-----
 .../org/apache/camel/builder/RouteBuilder.java     |   9 +-
 .../camel/model/RouteTemplateDefinition.java       |  14 +-
 .../camel/model/RouteTemplatesDefinition.java      |  18 +-
 .../apache/camel/model/rest/RestDefinition.java    |  15 +-
 .../apache/camel/model/rest/RestsDefinition.java   |  21 +-
 6 files changed, 248 insertions(+), 66 deletions(-)

diff --git a/core/camel-core-engine/src/generated/java/org/apache/camel/impl/DefaultDumpRoutesStrategy.java b/core/camel-core-engine/src/generated/java/org/apache/camel/impl/DefaultDumpRoutesStrategy.java
index f007b6ab0bc..7dc5d6e6a5b 100644
--- a/core/camel-core-engine/src/generated/java/org/apache/camel/impl/DefaultDumpRoutesStrategy.java
+++ b/core/camel-core-engine/src/generated/java/org/apache/camel/impl/DefaultDumpRoutesStrategy.java
@@ -1,20 +1,50 @@
+/*
+ * 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.impl;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.CamelContextAware;
+import org.apache.camel.NamedNode;
 import org.apache.camel.model.Model;
+import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.model.RouteTemplateDefinition;
 import org.apache.camel.model.RouteTemplatesDefinition;
 import org.apache.camel.model.RoutesDefinition;
+import org.apache.camel.model.rest.RestDefinition;
 import org.apache.camel.model.rest.RestsDefinition;
 import org.apache.camel.spi.DumpRoutesStrategy;
 import org.apache.camel.spi.ModelToXMLDumper;
 import org.apache.camel.spi.ModelToYAMLDumper;
+import org.apache.camel.spi.Resource;
 import org.apache.camel.spi.annotations.JdkService;
 import org.apache.camel.support.PluginHelper;
+import org.apache.camel.support.ResourceSupport;
+import org.apache.camel.util.FileUtil;
 import org.apache.camel.util.StringHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import static org.apache.camel.support.LoggerHelper.stripSourceLocationLineNumber;
+
 /**
  * Default {@link DumpRoutesStrategy} that dumps the routes to standard logger.
  */
@@ -22,6 +52,7 @@ import org.slf4j.LoggerFactory;
 public class DefaultDumpRoutesStrategy implements DumpRoutesStrategy, CamelContextAware {
 
     private static final Logger LOG = LoggerFactory.getLogger(DefaultDumpRoutesStrategy.class);
+    private static final String DIVIDER = "--------------------------------------------------------------------------------";
 
     private CamelContext camelContext;
 
@@ -44,111 +75,199 @@ public class DefaultDumpRoutesStrategy implements DumpRoutesStrategy, CamelConte
         }
     }
 
-    protected void onDump(String dump) {
-        LOG.info("\n\n{}\n", dump);
+    protected void onDump(Resource resource, String dump) {
+        String loc = null;
+        if (resource != null) {
+            loc = extractLocationName(resource.getLocation());
+        }
+        if (loc != null) {
+            LOG.info("\nSource: {}\n{}\n{}\n", loc, DIVIDER, dump);
+        } else {
+            LOG.info("\n\n{}\n", dump);
+        }
     }
 
     protected void doDumpRoutesAsYaml(CamelContext camelContext) {
         final ModelToYAMLDumper dumper = PluginHelper.getModelToYAMLDumper(camelContext);
         final Model model = camelContext.getCamelContextExtension().getContextPlugin(Model.class);
+        final DummyResource dummy = new DummyResource(null, null);
 
         int size = model.getRouteDefinitions().size();
         if (size > 0) {
             LOG.info("Dumping {} routes as YAML", size);
-            RoutesDefinition def = new RoutesDefinition();
-            def.setRoutes(model.getRouteDefinitions());
-            try {
-                String dump = dumper.dumpModelAsYaml(camelContext, def, true, false);
-                onDump(dump);
-            } catch (Exception e) {
-                LOG.warn("Error dumping routes to YAML due to {}. This exception is ignored.", e.getMessage(), e);
+            Map<Resource, RoutesDefinition> groups = new LinkedHashMap<>();
+            for (RouteDefinition route : model.getRouteDefinitions()) {
+                Resource res = route.getResource();
+                if (res == null) {
+                    res = dummy;
+                }
+                RoutesDefinition routes = groups.computeIfAbsent(res, resource -> new RoutesDefinition());
+                routes.getRoutes().add(route);
+            }
+            for (Map.Entry<Resource, RoutesDefinition> entry : groups.entrySet()) {
+                RoutesDefinition def = entry.getValue();
+                Resource resource = entry.getKey();
+                doDumpYaml(camelContext, def, resource == dummy ? null : resource, dumper, "routes");
             }
         }
 
         size = model.getRestDefinitions().size();
         if (size > 0) {
             LOG.info("Dumping {} rests as YAML", size);
-            RestsDefinition def = new RestsDefinition();
-            def.setRests(model.getRestDefinitions());
-            try {
-                String dump = dumper.dumpModelAsYaml(camelContext, def, true, false);
-                onDump(dump);
-            } catch (Exception e) {
-                LOG.warn("Error dumping rests to YAML due to {}. This exception is ignored.", e.getMessage(), e);
+            Map<Resource, RestsDefinition> groups = new LinkedHashMap<>();
+            for (RestDefinition rest : model.getRestDefinitions()) {
+                Resource res = rest.getResource();
+                if (res == null) {
+                    res = dummy;
+                }
+                RestsDefinition rests = groups.computeIfAbsent(res, resource -> new RestsDefinition());
+                rests.getRests().add(rest);
+            }
+            for (Map.Entry<Resource, RestsDefinition> entry : groups.entrySet()) {
+                RestsDefinition def = entry.getValue();
+                Resource resource = entry.getKey();
+                doDumpYaml(camelContext, def, resource == dummy ? null : resource, dumper, "rests");
             }
         }
 
         size = model.getRouteTemplateDefinitions().size();
         if (size > 0) {
             LOG.info("Dumping {} route templates as YAML", size);
-            RouteTemplatesDefinition def = new RouteTemplatesDefinition();
-            def.setRouteTemplates(model.getRouteTemplateDefinitions());
-            try {
-                String dump = dumper.dumpModelAsYaml(camelContext, def, true, false);
-                onDump(dump);
-            } catch (Exception e) {
-                LOG.warn("Error dumping route-templates to YAML due to {}. This exception is ignored.", e.getMessage(), e);
+            Map<Resource, RouteTemplatesDefinition> groups = new LinkedHashMap<>();
+            for (RouteTemplateDefinition rt : model.getRouteTemplateDefinitions()) {
+                Resource res = rt.getResource();
+                if (res == null) {
+                    res = dummy;
+                }
+                RouteTemplatesDefinition rests = groups.computeIfAbsent(res, resource -> new RouteTemplatesDefinition());
+                rests.getRouteTemplates().add(rt);
+            }
+            for (Map.Entry<Resource, RouteTemplatesDefinition> entry : groups.entrySet()) {
+                RouteTemplatesDefinition def = entry.getValue();
+                Resource resource = entry.getKey();
+                doDumpYaml(camelContext, def, resource == dummy ? null : resource, dumper, "route-templates");
             }
         }
     }
 
+    protected void doDumpYaml(CamelContext camelContext, NamedNode def, Resource resource,
+                              ModelToYAMLDumper dumper, String kind) {
+        try {
+            String dump = dumper.dumpModelAsYaml(camelContext, def, true, false);
+            onDump(resource, dump);
+        } catch (Exception e) {
+            LOG.warn("Error dumping {}} to YAML due to {}. This exception is ignored.", kind, e.getMessage(), e);
+        }
+    }
+
     protected void doDumpRoutesAsXml(CamelContext camelContext) {
         final ModelToXMLDumper dumper = PluginHelper.getModelToXMLDumper(camelContext);
         final Model model = camelContext.getCamelContextExtension().getContextPlugin(Model.class);
+        final DummyResource dummy = new DummyResource(null, null);
 
         int size = model.getRouteDefinitions().size();
         if (size > 0) {
             LOG.info("Dumping {} routes as XML", size);
-            // for XML to output nicely all routes in one XML then lets put them into <routes>
-            RoutesDefinition def = new RoutesDefinition();
-            def.setRoutes(model.getRouteDefinitions());
-            try {
-                String xml = dumper.dumpModelAsXml(camelContext, def, true);
-                // lets separate routes with empty line
-                xml = StringHelper.replaceFirst(xml, "xmlns=\"http://camel.apache.org/schema/spring\">",
-                        "xmlns=\"http://camel.apache.org/schema/spring\">\n");
-                xml = xml.replace("</route>", "</route>\n");
-                onDump(xml);
-            } catch (Exception e) {
-                LOG.warn("Error dumping routes to XML due to {}. This exception is ignored.", e.getMessage(), e);
+            Map<Resource, RoutesDefinition> groups = new LinkedHashMap<>();
+            for (RouteDefinition route : model.getRouteDefinitions()) {
+                Resource res = route.getResource();
+                if (res == null) {
+                    res = dummy;
+                }
+                RoutesDefinition routes = groups.computeIfAbsent(res, resource -> new RoutesDefinition());
+                routes.getRoutes().add(route);
+            }
+            for (Map.Entry<Resource, RoutesDefinition> entry : groups.entrySet()) {
+                RoutesDefinition def = entry.getValue();
+                Resource resource = entry.getKey();
+                doDumpXml(camelContext, def, resource == dummy ? null : resource, dumper, "route", "routes");
             }
         }
 
         size = model.getRestDefinitions().size();
         if (size > 0) {
             LOG.info("Dumping {} rests as XML", size);
-            // for XML to output nicely all routes in one XML then lets put them into <routes>
-            RestsDefinition def = new RestsDefinition();
-            def.setRests(model.getRestDefinitions());
-            try {
-                String xml = dumper.dumpModelAsXml(camelContext, def, true);
-                // lets separate rests with empty line
-                xml = StringHelper.replaceFirst(xml, "xmlns=\"http://camel.apache.org/schema/spring\">",
-                        "xmlns=\"http://camel.apache.org/schema/spring\">\n");
-                xml = xml.replace("</rest>", "</rest>\n");
-                onDump(xml);
-            } catch (Exception e) {
-                LOG.warn("Error dumping rests to XML due to {}. This exception is ignored.", e.getMessage(), e);
+            Map<Resource, RestsDefinition> groups = new LinkedHashMap<>();
+            for (RestDefinition rest : model.getRestDefinitions()) {
+                Resource res = rest.getResource();
+                if (res == null) {
+                    res = dummy;
+                }
+                RestsDefinition routes = groups.computeIfAbsent(res, resource -> new RestsDefinition());
+                routes.getRests().add(rest);
+            }
+            for (Map.Entry<Resource, RestsDefinition> entry : groups.entrySet()) {
+                RestsDefinition def = entry.getValue();
+                Resource resource = entry.getKey();
+                doDumpXml(camelContext, def, resource == dummy ? null : resource, dumper, "rest", "rests");
             }
         }
 
         size = model.getRouteTemplateDefinitions().size();
         if (size > 0) {
             LOG.info("Dumping {} route templates as XML", size);
-            // for XML to output nicely all routes in one XML then lets put them into <routes>
-            RouteTemplatesDefinition def = new RouteTemplatesDefinition();
-            def.setRouteTemplates(model.getRouteTemplateDefinitions());
-            try {
-                String xml = dumper.dumpModelAsXml(camelContext, def, true);
-                // lets separate rests with empty line
-                xml = StringHelper.replaceFirst(xml, "xmlns=\"http://camel.apache.org/schema/spring\">",
-                        "xmlns=\"http://camel.apache.org/schema/spring\">\n");
-                xml = xml.replace("</routeTemplate>", "</routeTemplate>\n");
-                onDump(xml);
-            } catch (Exception e) {
-                LOG.warn("Error dumping route-templates to XML due to {}. This exception is ignored.", e.getMessage(), e);
+            Map<Resource, RouteTemplatesDefinition> groups = new LinkedHashMap<>();
+            for (RouteTemplateDefinition rt : model.getRouteTemplateDefinitions()) {
+                Resource res = rt.getResource();
+                if (res == null) {
+                    res = dummy;
+                }
+                RouteTemplatesDefinition routes = groups.computeIfAbsent(res, resource -> new RouteTemplatesDefinition());
+                routes.getRouteTemplates().add(rt);
+            }
+            for (Map.Entry<Resource, RouteTemplatesDefinition> entry : groups.entrySet()) {
+                RouteTemplatesDefinition def = entry.getValue();
+                Resource resource = entry.getKey();
+                doDumpXml(camelContext, def, resource == dummy ? null : resource, dumper, "routeTemplate", "route-templates");
+            }
+        }
+    }
+
+    protected void doDumpXml(CamelContext camelContext, NamedNode def, Resource resource,
+                              ModelToXMLDumper dumper, String replace, String kind) {
+        try {
+            String xml = dumper.dumpModelAsXml(camelContext, def, true);
+            // lets separate with empty line
+            xml = StringHelper.replaceFirst(xml, "xmlns=\"http://camel.apache.org/schema/spring\">",
+                    "xmlns=\"http://camel.apache.org/schema/spring\">\n");
+            xml = xml.replace("</" + replace + ">", "</" + replace + ">\n");
+            onDump(resource, xml);
+        } catch (Exception e) {
+            LOG.warn("Error dumping {}} to XML due to {}. This exception is ignored.", kind, e.getMessage(), e);
+        }
+    }
+
+    private static final class DummyResource extends ResourceSupport {
+
+        private DummyResource(String scheme, String location) {
+            super(scheme, location);
+        }
+
+        @Override
+        public boolean exists() {
+            return true;
+        }
+
+        @Override
+        public InputStream getInputStream() throws IOException {
+            return null; // not in use
+        }
+    }
+
+    private static String extractLocationName(String loc) {
+        if (loc == null) {
+            return null;
+        }
+        loc = stripSourceLocationLineNumber(loc);
+        if (loc != null) {
+            if (loc.contains(":")) {
+                // strip prefix
+                loc = loc.substring(loc.indexOf(':') + 1);
+                // file based such as xml and yaml
+                loc = FileUtil.stripPath(loc);
             }
         }
+        return loc;
     }
 
 }
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java b/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
index aef44851022..3c99c0c385a 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
@@ -273,7 +273,6 @@ public abstract class RouteBuilder extends BuilderSupport implements RoutesBuild
         if (restConfiguration == null) {
             restConfiguration = new RestConfigurationDefinition();
         }
-
         return restConfiguration;
     }
 
@@ -308,6 +307,9 @@ public abstract class RouteBuilder extends BuilderSupport implements RoutesBuild
      */
     public RestDefinition rest() {
         getRestCollection().setCamelContext(getContext());
+        if (resource != null) {
+            getRestCollection().setResource(resource);
+        }
         RestDefinition answer = getRestCollection().rest();
         configureRest(answer);
         return answer;
@@ -321,6 +323,9 @@ public abstract class RouteBuilder extends BuilderSupport implements RoutesBuild
      */
     public RestDefinition rest(String path) {
         getRestCollection().setCamelContext(getContext());
+        if (resource != null) {
+            getRestCollection().setResource(resource);
+        }
         RestDefinition answer = getRestCollection().rest(path);
         configureRest(answer);
         return answer;
@@ -724,6 +729,8 @@ public abstract class RouteBuilder extends BuilderSupport implements RoutesBuild
 
             // remember the source resource
             getRouteCollection().setResource(getResource());
+            getRestCollection().setResource(getResource());
+            getRouteTemplateCollection().setResource(getResource());
 
             for (RouteDefinition route : getRouteCollection().getRoutes()) {
                 // ensure the route is prepared after configure method is complete
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java
index 0addb6dc7f4..320b3a11267 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java
@@ -34,6 +34,8 @@ import org.apache.camel.RouteTemplateContext;
 import org.apache.camel.builder.EndpointConsumerBuilder;
 import org.apache.camel.spi.AsEndpointUri;
 import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.Resource;
+import org.apache.camel.spi.ResourceAware;
 
 /**
  * Defines a route template (parameterized routes)
@@ -42,7 +44,7 @@ import org.apache.camel.spi.Metadata;
 @XmlRootElement(name = "routeTemplate")
 @XmlType(propOrder = { "templateParameters", "templateBeans", "route" })
 @XmlAccessorType(XmlAccessType.FIELD)
-public class RouteTemplateDefinition extends OptionalIdentifiedDefinition<RouteTemplateDefinition> {
+public class RouteTemplateDefinition extends OptionalIdentifiedDefinition<RouteTemplateDefinition> implements ResourceAware {
 
     @XmlTransient
     private Consumer<RouteTemplateContext> configurer;
@@ -55,6 +57,8 @@ public class RouteTemplateDefinition extends OptionalIdentifiedDefinition<RouteT
     private List<RouteTemplateBeanDefinition> templateBeans;
     @XmlElement(name = "route", required = true)
     private RouteDefinition route = new RouteDefinition();
+    @XmlTransient
+    private Resource resource;
 
     public List<RouteTemplateParameterDefinition> getTemplateParameters() {
         return templateParameters;
@@ -88,6 +92,14 @@ public class RouteTemplateDefinition extends OptionalIdentifiedDefinition<RouteT
         return configurer;
     }
 
+    public Resource getResource() {
+        return resource;
+    }
+
+    public void setResource(Resource resource) {
+        this.resource = resource;
+    }
+
     // Fluent API
     // -------------------------------------------------------------------------
 
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplatesDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplatesDefinition.java
index 2ef67a3d694..a50d583b3eb 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplatesDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplatesDefinition.java
@@ -29,6 +29,8 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.CamelContextAware;
 import org.apache.camel.ErrorHandlerFactory;
 import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.Resource;
+import org.apache.camel.spi.ResourceAware;
 
 /**
  * A series of route templates
@@ -37,12 +39,14 @@ import org.apache.camel.spi.Metadata;
 @XmlRootElement(name = "routeTemplates")
 @XmlAccessorType(XmlAccessType.FIELD)
 public class RouteTemplatesDefinition extends OptionalIdentifiedDefinition<RouteTemplatesDefinition>
-        implements RouteTemplateContainer, CamelContextAware {
+        implements RouteTemplateContainer, CamelContextAware, ResourceAware {
 
     @XmlTransient
     private CamelContext camelContext;
     @XmlTransient
     private ErrorHandlerFactory errorHandlerFactory;
+    @XmlTransient
+    private Resource resource;
 
     @XmlElementRef
     private List<RouteTemplateDefinition> routeTemplates = new ArrayList<>();
@@ -98,6 +102,14 @@ public class RouteTemplatesDefinition extends OptionalIdentifiedDefinition<Route
         this.errorHandlerFactory = errorHandlerFactory;
     }
 
+    public Resource getResource() {
+        return resource;
+    }
+
+    public void setResource(Resource resource) {
+        this.resource = resource;
+    }
+
     // Fluent API
     // -------------------------------------------------------------------------
 
@@ -129,7 +141,9 @@ public class RouteTemplatesDefinition extends OptionalIdentifiedDefinition<Route
         if (handler != null) {
             template.getRoute().setErrorHandlerFactoryIfNull(handler);
         }
-
+        if (resource != null) {
+            template.setResource(resource);
+        }
         return template;
     }
 
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/rest/RestDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/rest/RestDefinition.java
index ae020ff6614..50f62993466 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/rest/RestDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/rest/RestDefinition.java
@@ -31,6 +31,7 @@ import jakarta.xml.bind.annotation.XmlAttribute;
 import jakarta.xml.bind.annotation.XmlElement;
 import jakarta.xml.bind.annotation.XmlElementRef;
 import jakarta.xml.bind.annotation.XmlRootElement;
+import jakarta.xml.bind.annotation.XmlTransient;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.RuntimeCamelException;
@@ -39,6 +40,8 @@ import org.apache.camel.model.RouteDefinition;
 import org.apache.camel.model.ToDefinition;
 import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.NodeIdFactory;
+import org.apache.camel.spi.Resource;
+import org.apache.camel.spi.ResourceAware;
 import org.apache.camel.spi.RestConfiguration;
 import org.apache.camel.support.CamelContextHelper;
 import org.apache.camel.util.FileUtil;
@@ -52,7 +55,7 @@ import org.apache.camel.util.URISupport;
 @Metadata(label = "rest")
 @XmlRootElement(name = "rest")
 @XmlAccessorType(XmlAccessType.FIELD)
-public class RestDefinition extends OptionalIdentifiedDefinition<RestDefinition> {
+public class RestDefinition extends OptionalIdentifiedDefinition<RestDefinition> implements ResourceAware {
 
     @XmlAttribute
     private String path;
@@ -89,6 +92,8 @@ public class RestDefinition extends OptionalIdentifiedDefinition<RestDefinition>
     private List<SecurityDefinition> securityRequirements = new ArrayList<>();
     @XmlElementRef
     private List<VerbDefinition> verbs = new ArrayList<>();
+    @XmlTransient
+    private Resource resource;
 
     @Override
     public String getShortName() {
@@ -261,6 +266,14 @@ public class RestDefinition extends OptionalIdentifiedDefinition<RestDefinition>
         this.apiDocs = apiDocs;
     }
 
+    public Resource getResource() {
+        return resource;
+    }
+
+    public void setResource(Resource resource) {
+        this.resource = resource;
+    }
+
     // Fluent API
     // -------------------------------------------------------------------------
 
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/rest/RestsDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/rest/RestsDefinition.java
index 3e4a4a39523..96fd21ca7d1 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/rest/RestsDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/rest/RestsDefinition.java
@@ -23,9 +23,12 @@ import jakarta.xml.bind.annotation.XmlAccessType;
 import jakarta.xml.bind.annotation.XmlAccessorType;
 import jakarta.xml.bind.annotation.XmlElementRef;
 import jakarta.xml.bind.annotation.XmlRootElement;
+import jakarta.xml.bind.annotation.XmlTransient;
 
 import org.apache.camel.model.OptionalIdentifiedDefinition;
 import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.Resource;
+import org.apache.camel.spi.ResourceAware;
 
 /**
  * A series of rest services defined using the rest-dsl
@@ -33,10 +36,12 @@ import org.apache.camel.spi.Metadata;
 @Metadata(label = "rest")
 @XmlRootElement(name = "rests")
 @XmlAccessorType(XmlAccessType.FIELD)
-public class RestsDefinition extends OptionalIdentifiedDefinition<RestsDefinition> implements RestContainer {
+public class RestsDefinition extends OptionalIdentifiedDefinition<RestsDefinition> implements RestContainer, ResourceAware {
 
     @XmlElementRef
     private List<RestDefinition> rests = new ArrayList<>();
+    @XmlTransient
+    private Resource resource;
 
     public RestsDefinition() {
     }
@@ -72,6 +77,14 @@ public class RestsDefinition extends OptionalIdentifiedDefinition<RestsDefinitio
         this.rests = rests;
     }
 
+    public Resource getResource() {
+        return resource;
+    }
+
+    public void setResource(Resource resource) {
+        this.resource = resource;
+    }
+
     // Fluent API
     // -------------------------------------------------------------------------
 
@@ -106,7 +119,11 @@ public class RestsDefinition extends OptionalIdentifiedDefinition<RestsDefinitio
     // -------------------------------------------------------------------------
 
     protected RestDefinition createRest() {
-        return new RestDefinition();
+        RestDefinition rest = new RestDefinition();
+        if (resource != null) {
+            rest.setResource(resource);
+        }
+        return rest;
     }
 
 }