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 2019/10/18 07:31:15 UTC

[camel] branch master updated: Route collector (#3261)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new f4245fc  Route collector (#3261)
f4245fc is described below

commit f4245fc989cf7800ee088b5afc73485a20e75418
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Fri Oct 18 09:31:02 2019 +0200

    Route collector (#3261)
    
    CAMEL-14050: camel-main - Add logic for automatic RouteBuilder class detection ala camel-spring-boot has
---
 .../src/main/docs/spring-boot.adoc                 |   3 +-
 .../camel/spring/boot/CamelAutoConfiguration.java  |  13 +-
 .../spring/boot/CamelConfigurationProperties.java  | 140 ++++++---------
 ...ava => CamelSpringBootApplicationListener.java} | 167 +++--------------
 .../spring/boot/SpringBootRoutesCollector.java     | 160 +++++++++++++++++
 .../boot/SupervisingRouteControllerTest.java       |  20 ++-
 .../boot/parent/SpringBootRefreshContextTest.java  |   4 +-
 .../java/org/apache/camel/spi/RouteContext.java    |  10 +-
 .../org/apache/camel/main/BaseMainSupport.java     |  35 +++-
 .../camel/main/DefaultConfigurationProperties.java | 199 +++++++++++++++++++++
 .../apache/camel/main/DefaultRoutesCollector.java  | 139 ++++++++++++++
 .../camel/main/MainConfigurationProperties.java    |  24 +++
 .../org/apache/camel/main/RoutesCollector.java     |  61 +++++++
 .../org/apache/camel/main/RoutesConfigurer.java    | 109 +++++++++++
 .../camel-main-configuration-metadata.json         |  39 ++++
 .../main/MainRoutesCollectorPackageScanTest.java   |  50 ++++++
 .../apache/camel/main/MainRoutesCollectorTest.java |  54 ++++++
 .../camel/main/scan/MyDummyRouteBuilder.java       |  27 +++
 .../apache/camel/main/scan/MyScanRouteBuilder.java |  27 +++
 19 files changed, 1032 insertions(+), 249 deletions(-)

diff --git a/components/camel-spring-boot/src/main/docs/spring-boot.adoc b/components/camel-spring-boot/src/main/docs/spring-boot.adoc
index 73a8678..89a5c30 100644
--- a/components/camel-spring-boot/src/main/docs/spring-boot.adoc
+++ b/components/camel-spring-boot/src/main/docs/spring-boot.adoc
@@ -89,7 +89,7 @@ When using Spring Boot make sure to use the following Maven dependency to have s
 ----
 
 
-The component supports 139 options, which are listed below.
+The component supports 140 options, which are listed below.
 
 
 
@@ -182,6 +182,7 @@ The component supports 139 options, which are listed below.
 | *camel.springboot.producer-template-cache-size* | Producer template endpoints cache size. | 1000 | Integer
 | *camel.springboot.route-filter-exclude-pattern* | Used for filtering routes routes matching the given pattern, which follows the following rules: - Match by route id - Match by route input endpoint uri The matching is using exact match, by wildcard and regular expression. For example to only include routes which starts with foo in their route id's, use: include=foo&#42; And to exclude routes which starts from JMS endpoints, use: exclude=jms:&#42; Multiple patterns can be separated by c [...]
 | *camel.springboot.route-filter-include-pattern* | Used for filtering routes routes matching the given pattern, which follows the following rules: - Match by route id - Match by route input endpoint uri The matching is using exact match, by wildcard and regular expression. For example to only include routes which starts with foo in their route id's, use: include=foo&#42; And to exclude routes which starts from JMS endpoints, use: exclude=jms:&#42; Multiple patterns can be separated by c [...]
+| *camel.springboot.routes-collector-enabled* | Whether the routes collector is enabled or not. When enabled Camel will auto-discover routes (RouteBuilder instances from the registry and also load additional XML routes from the file system. The routes collector is default enabled. | true | Boolean
 | *camel.springboot.shutdown-log-inflight-exchanges-on-timeout* | Sets whether to log information about the inflight Exchanges which are still running during a shutdown which didn't complete without the given timeout. | true | Boolean
 | *camel.springboot.shutdown-now-on-timeout* | Sets whether to force shutdown of all consumers when a timeout occurred and thus not all consumers was shutdown within that period. You should have good reasons to set this option to false as it means that the routes keep running and is halted abruptly when CamelContext has been shutdown. | true | Boolean
 | *camel.springboot.shutdown-routes-in-reverse-order* | Sets whether routes should be shutdown in reverse or the same order as they where started. | true | Boolean
diff --git a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelAutoConfiguration.java b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelAutoConfiguration.java
index 6a5bd55..b1c73ea 100644
--- a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelAutoConfiguration.java
+++ b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelAutoConfiguration.java
@@ -29,6 +29,7 @@ import org.apache.camel.ProducerTemplate;
 import org.apache.camel.component.properties.PropertiesComponent;
 import org.apache.camel.component.properties.PropertiesParser;
 import org.apache.camel.main.DefaultConfigurationConfigurer;
+import org.apache.camel.main.RoutesCollector;
 import org.apache.camel.model.Model;
 import org.apache.camel.spi.BeanRepository;
 import org.apache.camel.spring.CamelBeanPostProcessor;
@@ -133,7 +134,6 @@ public class CamelAutoConfiguration {
         // lookup and configure SPI beans
         DefaultConfigurationConfigurer.afterPropertiesSet(camelContext);
 
-
         return camelContext;
     }
 
@@ -144,9 +144,16 @@ public class CamelAutoConfiguration {
 
     @Bean
     @ConditionalOnMissingBean(RoutesCollector.class)
-    RoutesCollector routesCollector(ApplicationContext applicationContext, CamelConfigurationProperties config) {
+    RoutesCollector routesCollector(ApplicationContext applicationContext) {
+        return new SpringBootRoutesCollector(applicationContext);
+    }
+
+    @Bean
+    @ConditionalOnMissingBean(CamelSpringBootApplicationListener.class)
+    CamelSpringBootApplicationListener routesCollectorListener(ApplicationContext applicationContext, CamelConfigurationProperties config,
+                                                               RoutesCollector routesCollector) {
         Collection<CamelContextConfiguration> configurations = applicationContext.getBeansOfType(CamelContextConfiguration.class).values();
-        return new RoutesCollector(applicationContext, new ArrayList(configurations), config);
+        return new CamelSpringBootApplicationListener(applicationContext, new ArrayList(configurations), config, routesCollector);
     }
 
     /**
diff --git a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelConfigurationProperties.java b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelConfigurationProperties.java
index 9f33bac..f1c36fa 100644
--- a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelConfigurationProperties.java
+++ b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelConfigurationProperties.java
@@ -47,56 +47,6 @@ public class CamelConfigurationProperties extends DefaultConfigurationProperties
      */
     private boolean warnOnEarlyShutdown = true;
 
-    /**
-     * Used for inclusive filtering component scanning of RouteBuilder classes with @Component annotation.
-     * The exclusive filtering takes precedence over inclusive filtering.
-     * The pattern is using Ant-path style pattern.
-     *
-     * Multiple patterns can be specified separated by comma.
-     * For example to include all classes starting with Foo use: &#42;&#42;/Foo*
-     * To include all routes form a specific package use: com/mycompany/foo/&#42;
-     * To include all routes form a specific package and its sub-packages use double wildcards: com/mycompany/foo/&#42;&#42;
-     * And to include all routes from two specific packages use: com/mycompany/foo/&#42;,com/mycompany/stuff/&#42;
-     */
-    private String javaRoutesIncludePattern;
-
-    /**
-     * Used for exclusive filtering component scanning of RouteBuilder classes with @Component annotation.
-     * The exclusive filtering takes precedence over inclusive filtering.
-     * The pattern is using Ant-path style pattern.
-     * Multiple patterns can be specified separated by comma.
-     *
-     * For example to exclude all classes starting with Bar use: &#42;&#42;/Bar&#42;
-     * To exclude all routes form a specific package use: com/mycompany/bar/&#42;
-     * To exclude all routes form a specific package and its sub-packages use double wildcards: com/mycompany/bar/&#42;&#42;
-     * And to exclude all routes from two specific packages use: com/mycompany/bar/&#42;,com/mycompany/stuff/&#42;
-     */
-    private String javaRoutesExcludePattern;
-
-    /**
-     * Directory to scan for adding additional XML routes.
-     * You can turn this off by setting the value to false.
-     *
-     * Files can be loaded from either classpath or file by prefixing with classpath: or file:
-     * Wildcards is supported using a ANT pattern style paths, such as classpath:&#42;&#42;/&#42;camel&#42;.xml
-     *
-     * Multiple directories can be specified and separated by comma, such as:
-     * file:/myapp/mycamel/&#42;.xml,file:/myapp/myothercamel/&#42;.xml
-     */
-    private String xmlRoutes = "classpath:camel/*.xml";
-
-    /**
-     * Directory to scan for adding additional XML rests.
-     * You can turn this off by setting the value to false.
-     *
-     * Files can be loaded from either classpath or file by prefixing with classpath: or file:
-     * Wildcards is supported using a ANT pattern style paths, such as classpath:&#42;&#42;/&#42;camel&#42;.xml
-     *
-     * Multiple directories can be specified and separated by comma, such as:
-     * file:/myapp/mycamel/&#42;.xml,file:/myapp/myothercamel/&#42;.xml
-     */
-    private String xmlRests = "classpath:camel-rest/*.xml";
-
     // Default Properties via camel-main
     // ---------------------------------
 
@@ -457,6 +407,65 @@ public class CamelConfigurationProperties extends DefaultConfigurationProperties
      */
     private LoggingLevel beanIntrospectionLoggingLevel;
 
+    /**
+     * Whether the routes collector is enabled or not.
+     *
+     * When enabled Camel will auto-discover routes (RouteBuilder instances from the registry and
+     * also load additional XML routes from the file system.
+     *
+     * The routes collector is default enabled.
+     */
+    private boolean routesCollectorEnabled = true;
+
+    /**
+     * Used for inclusive filtering component scanning of RouteBuilder classes with @Component annotation.
+     * The exclusive filtering takes precedence over inclusive filtering.
+     * The pattern is using Ant-path style pattern.
+     *
+     * Multiple patterns can be specified separated by comma.
+     * For example to include all classes starting with Foo use: &#42;&#42;/Foo*
+     * To include all routes form a specific package use: com/mycompany/foo/&#42;
+     * To include all routes form a specific package and its sub-packages use double wildcards: com/mycompany/foo/&#42;&#42;
+     * And to include all routes from two specific packages use: com/mycompany/foo/&#42;,com/mycompany/stuff/&#42;
+     */
+    private String javaRoutesIncludePattern;
+
+    /**
+     * Used for exclusive filtering component scanning of RouteBuilder classes with @Component annotation.
+     * The exclusive filtering takes precedence over inclusive filtering.
+     * The pattern is using Ant-path style pattern.
+     * Multiple patterns can be specified separated by comma.
+     *
+     * For example to exclude all classes starting with Bar use: &#42;&#42;/Bar&#42;
+     * To exclude all routes form a specific package use: com/mycompany/bar/&#42;
+     * To exclude all routes form a specific package and its sub-packages use double wildcards: com/mycompany/bar/&#42;&#42;
+     * And to exclude all routes from two specific packages use: com/mycompany/bar/&#42;,com/mycompany/stuff/&#42;
+     */
+    private String javaRoutesExcludePattern;
+
+    /**
+     * Directory to scan for adding additional XML routes.
+     * You can turn this off by setting the value to false.
+     *
+     * Files can be loaded from either classpath or file by prefixing with classpath: or file:
+     * Wildcards is supported using a ANT pattern style paths, such as classpath:&#42;&#42;/&#42;camel&#42;.xml
+     *
+     * Multiple directories can be specified and separated by comma, such as:
+     * file:/myapp/mycamel/&#42;.xml,file:/myapp/myothercamel/&#42;.xml
+     */
+    private String xmlRoutes = "classpath:camel/*.xml";
+
+    /**
+     * Directory to scan for adding additional XML rests.
+     * You can turn this off by setting the value to false.
+     *
+     * Files can be loaded from either classpath or file by prefixing with classpath: or file:
+     * Wildcards is supported using a ANT pattern style paths, such as classpath:&#42;&#42;/&#42;camel&#42;.xml
+     *
+     * Multiple directories can be specified and separated by comma, such as:
+     * file:/myapp/mycamel/&#42;.xml,file:/myapp/myothercamel/&#42;.xml
+     */
+    private String xmlRests = "classpath:camel-rest/*.xml";
 
     // Getters & setters
     // -----------------
@@ -485,35 +494,4 @@ public class CamelConfigurationProperties extends DefaultConfigurationProperties
         this.warnOnEarlyShutdown = warnOnEarlyShutdown;
     }
 
-    public String getJavaRoutesIncludePattern() {
-        return javaRoutesIncludePattern;
-    }
-
-    public void setJavaRoutesIncludePattern(String javaRoutesIncludePattern) {
-        this.javaRoutesIncludePattern = javaRoutesIncludePattern;
-    }
-
-    public String getJavaRoutesExcludePattern() {
-        return javaRoutesExcludePattern;
-    }
-
-    public void setJavaRoutesExcludePattern(String javaRoutesExcludePattern) {
-        this.javaRoutesExcludePattern = javaRoutesExcludePattern;
-    }
-
-    public String getXmlRoutes() {
-        return xmlRoutes;
-    }
-
-    public void setXmlRoutes(String xmlRoutes) {
-        this.xmlRoutes = xmlRoutes;
-    }
-
-    public String getXmlRests() {
-        return xmlRests;
-    }
-
-    public void setXmlRests(String xmlRests) {
-        this.xmlRests = xmlRests;
-    }
 }
diff --git a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/RoutesCollector.java b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelSpringBootApplicationListener.java
similarity index 60%
rename from components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/RoutesCollector.java
rename to components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelSpringBootApplicationListener.java
index cb02311..107d749 100644
--- a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/RoutesCollector.java
+++ b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelSpringBootApplicationListener.java
@@ -16,8 +16,6 @@
  */
 package org.apache.camel.spring.boot;
 
-import java.io.FileNotFoundException;
-import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
@@ -27,20 +25,15 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.camel.CamelContext;
-import org.apache.camel.RoutesBuilder;
 import org.apache.camel.StartupListener;
 import org.apache.camel.main.MainDurationEventNotifier;
-import org.apache.camel.model.Model;
-import org.apache.camel.model.ModelHelper;
-import org.apache.camel.model.RoutesDefinition;
-import org.apache.camel.model.rest.RestsDefinition;
+import org.apache.camel.main.RoutesCollector;
+import org.apache.camel.main.RoutesConfigurer;
 import org.apache.camel.spi.CamelEvent;
 import org.apache.camel.spi.CamelEvent.Type;
 import org.apache.camel.spi.EventNotifier;
 import org.apache.camel.support.EventNotifierSupport;
-import org.apache.camel.support.OrderedComparator;
 import org.apache.camel.support.service.ServiceHelper;
-import org.apache.camel.util.ObjectHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.context.ApplicationContext;
@@ -48,34 +41,37 @@ import org.springframework.context.ApplicationListener;
 import org.springframework.context.ConfigurableApplicationContext;
 import org.springframework.context.event.ContextRefreshedEvent;
 import org.springframework.core.Ordered;
-import org.springframework.core.io.Resource;
-import org.springframework.util.AntPathMatcher;
 
 /**
- * Collects routes and rests from the various sources (like Spring application context beans registry or opinionated
+ * A spring application listener that when spring boot is starting (refresh event) will setup Camel by:
+ * <p>
+ * 1. collecting routes and rests from the various sources (like Spring application context beans registry or opinionated
  * classpath locations) and injects these into the Camel context.
+ * 2. setting up Camel main controller if enabled.
+ * 3. setting up run duration if in use.
  */
-public class RoutesCollector implements ApplicationListener<ContextRefreshedEvent>, Ordered {
+public class CamelSpringBootApplicationListener implements ApplicationListener<ContextRefreshedEvent>, Ordered {
 
     // Static collaborators
 
-    private static final Logger LOG = LoggerFactory.getLogger(RoutesCollector.class);
+    private static final Logger LOG = LoggerFactory.getLogger(CamelSpringBootApplicationListener.class);
 
     // Collaborators
 
     private final ApplicationContext applicationContext;
-
     private final List<CamelContextConfiguration> camelContextConfigurations;
-
     private final CamelConfigurationProperties configurationProperties;
+    private final RoutesCollector springBootRoutesCollector;
 
     // Constructors
 
-    public RoutesCollector(ApplicationContext applicationContext, List<CamelContextConfiguration> camelContextConfigurations,
-                           CamelConfigurationProperties configurationProperties) {
+    public CamelSpringBootApplicationListener(ApplicationContext applicationContext, List<CamelContextConfiguration> camelContextConfigurations,
+                                              CamelConfigurationProperties configurationProperties,
+                                              RoutesCollector springBootRoutesCollector) {
         this.applicationContext = applicationContext;
         this.camelContextConfigurations = new ArrayList<>(camelContextConfigurations);
         this.configurationProperties = configurationProperties;
+        this.springBootRoutesCollector = springBootRoutesCollector;
     }
 
     // Overridden
@@ -89,97 +85,15 @@ public class RoutesCollector implements ApplicationListener<ContextRefreshedEven
                 && camelContext.getStatus().isStopped()) {
             LOG.debug("Post-processing CamelContext bean: {}", camelContext.getName());
 
-            final AntPathMatcher matcher = new AntPathMatcher();
-            final List<RoutesBuilder> routes = new ArrayList<>();
-            for (RoutesBuilder routesBuilder : applicationContext.getBeansOfType(RoutesBuilder.class, configurationProperties.isIncludeNonSingletons(), true).values()) {
-                // filter out abstract classes
-                boolean abs = Modifier.isAbstract(routesBuilder.getClass().getModifiers());
-                if (!abs) {
-                    String name = routesBuilder.getClass().getName();
-                    // make name as path so we can use ant path matcher
-                    name = name.replace('.', '/');
-
-                    String exclude = configurationProperties.getJavaRoutesExcludePattern();
-                    String include = configurationProperties.getJavaRoutesIncludePattern();
+            RoutesConfigurer configurer = new RoutesConfigurer(springBootRoutesCollector);
+            configurer.configureRoutes(camelContext, configurationProperties);
 
-                    boolean match = !"false".equals(include);
-                    // exclude take precedence over include
-                    if (match && ObjectHelper.isNotEmpty(exclude)) {
-                        // there may be multiple separated by comma
-                        String[] parts = exclude.split(",");
-                        for (String part : parts) {
-                            // must negate when excluding, and hence !
-                            match = !matcher.match(part, name);
-                            LOG.trace("Java RoutesBuilder: {} exclude filter: {} -> {}", name, part, match);
-                            if (!match) {
-                                break;
-                            }
-                        }
-                    }
-                    // special support for testing with @ExcludeRoutes annotation with camel-test-spring
-                    exclude = System.getProperty("CamelTestSpringExcludeRoutes");
-                    // exclude take precedence over include
-                    if (match && ObjectHelper.isNotEmpty(exclude)) {
-                        // this property is a comma separated list of FQN class names, so we need to make
-                        // name as path so we can use ant patch matcher
-                        exclude = exclude.replace('.', '/');
-                        // there may be multiple separated by comma
-                        String[] parts = exclude.split(",");
-                        for (String part : parts) {
-                            // must negate when excluding, and hence !
-                            match = !matcher.match(part, name);
-                            LOG.trace("Java RoutesBuilder: {} exclude filter: {} -> {}", name, part, match);
-                            if (!match) {
-                                break;
-                            }
-                        }
-                    }
-                    if (match && ObjectHelper.isNotEmpty(include)) {
-                        // there may be multiple separated by comma
-                        String[] parts = include.split(",");
-                        for (String part : parts) {
-                            match = matcher.match(part, name);
-                            LOG.trace("Java RoutesBuilder: {} include filter: {} -> {}", name, part, match);
-                            if (match) {
-                                break;
-                            }
-                        }
-                    }
-                    LOG.debug("Java RoutesBuilder: {} accepted by include/exclude filter: {}", name, match);
-                    if (match) {
-                        routes.add(routesBuilder);
-                    }
-                }
-            }
-
-            // sort routes according to ordered
-            routes.sort(OrderedComparator.get());
-            // then add the routes
-            for (RoutesBuilder routesBuilder : routes) {
-                try {
-                    LOG.debug("Injecting following route into the CamelContext: {}", routesBuilder);
-                    camelContext.addRoutes(routesBuilder);
-                } catch (Exception e) {
-                    throw new CamelSpringBootInitializationException(e);
-                }
+            for (CamelContextConfiguration camelContextConfiguration : camelContextConfigurations) {
+                LOG.debug("CamelContextConfiguration found. Invoking beforeApplicationStart: {}", camelContextConfiguration);
+                camelContextConfiguration.beforeApplicationStart(camelContext);
             }
 
             try {
-                boolean scan = !configurationProperties.getXmlRoutes().equals("false");
-                if (scan) {
-                    loadXmlRoutes(applicationContext, camelContext, configurationProperties.getXmlRoutes());
-                }
-
-                boolean scanRests = !configurationProperties.getXmlRests().equals("false");
-                if (scanRests) {
-                    loadXmlRests(applicationContext, camelContext, configurationProperties.getXmlRests());
-                }
-
-                for (CamelContextConfiguration camelContextConfiguration : camelContextConfigurations) {
-                    LOG.debug("CamelContextConfiguration found. Invoking beforeApplicationStart: {}", camelContextConfiguration);
-                    camelContextConfiguration.beforeApplicationStart(camelContext);
-                }
-
                 if (configurationProperties.isMainRunController()) {
                     CamelMainRunController controller = new CamelMainRunController(applicationContext, camelContext);
 
@@ -192,8 +106,8 @@ public class RoutesCollector implements ApplicationListener<ContextRefreshedEven
                         }
                         // register lifecycle so we can trigger to shutdown the JVM when maximum number of messages has been processed
                         EventNotifier notifier = new MainDurationEventNotifier(camelContext,
-                            configurationProperties.getDurationMaxMessages(), configurationProperties.getDurationMaxIdleSeconds(),
-                            controller.getCompleted(), controller.getLatch(), true);
+                                configurationProperties.getDurationMaxMessages(), configurationProperties.getDurationMaxIdleSeconds(),
+                                controller.getCompleted(), controller.getLatch(), true);
                         // register our event notifier
                         ServiceHelper.startService(notifier);
                         camelContext.getManagementStrategy().addEventNotifier(notifier);
@@ -202,7 +116,7 @@ public class RoutesCollector implements ApplicationListener<ContextRefreshedEven
                     if (configurationProperties.getDurationMaxSeconds() > 0) {
                         LOG.info("CamelSpringBoot will terminate after {} seconds", configurationProperties.getDurationMaxSeconds());
                         terminateMainControllerAfter(camelContext, configurationProperties.getDurationMaxSeconds(),
-                            controller.getCompleted(), controller.getLatch());
+                                controller.getCompleted(), controller.getLatch());
                     }
 
                     camelContext.addStartupListener(new StartupListener() {
@@ -241,8 +155,8 @@ public class RoutesCollector implements ApplicationListener<ContextRefreshedEven
 
                             // register lifecycle so we can trigger to shutdown the JVM when maximum number of messages has been processed
                             EventNotifier notifier = new MainDurationEventNotifier(camelContext,
-                                configurationProperties.getDurationMaxMessages(), configurationProperties.getDurationMaxIdleSeconds(),
-                                completed, latch, false);
+                                    configurationProperties.getDurationMaxMessages(), configurationProperties.getDurationMaxIdleSeconds(),
+                                    completed, latch, false);
                             // register our event notifier
                             ServiceHelper.startService(notifier);
                             camelContext.getManagementStrategy().addEventNotifier(notifier);
@@ -300,39 +214,6 @@ public class RoutesCollector implements ApplicationListener<ContextRefreshedEven
 
     // Helpers
 
-    private void loadXmlRoutes(ApplicationContext applicationContext, CamelContext camelContext, String directory) throws Exception {
-        String[] parts = directory.split(",");
-        for (String part : parts) {
-            LOG.info("Loading additional Camel XML routes from: {}", part);
-            try {
-                Resource[] xmlRoutes = applicationContext.getResources(part);
-                for (Resource xmlRoute : xmlRoutes) {
-                    LOG.debug("Found XML route: {}", xmlRoute);
-                    RoutesDefinition routes = ModelHelper.loadRoutesDefinition(camelContext, xmlRoute.getInputStream());
-                    camelContext.getExtension(Model.class).addRouteDefinitions(routes.getRoutes());
-                }
-            } catch (FileNotFoundException e) {
-                LOG.debug("No XML routes found in {}. Skipping XML routes detection.", part);
-            }
-        }
-    }
-
-    private void loadXmlRests(ApplicationContext applicationContext, CamelContext camelContext, String directory) throws Exception {
-        String[] parts = directory.split(",");
-        for (String part : parts) {
-            LOG.info("Loading additional Camel XML rests from: {}", part);
-            try {
-                final Resource[] xmlRests = applicationContext.getResources(part);
-                for (final Resource xmlRest : xmlRests) {
-                    RestsDefinition rests = ModelHelper.loadRestsDefinition(camelContext, xmlRest.getInputStream());
-                    camelContext.getExtension(Model.class).addRestDefinitions(rests.getRests(), true);
-                }
-            } catch (FileNotFoundException e) {
-                LOG.debug("No XML rests found in {}. Skipping XML rests detection.", part);
-            }
-        }
-    }
-
     private void terminateMainControllerAfter(final CamelContext camelContext, int seconds, final AtomicBoolean completed, final CountDownLatch latch) {
         ScheduledExecutorService executorService = camelContext.getExecutorServiceManager().newSingleThreadScheduledExecutor(this, "CamelSpringBootTerminateTask");
         Runnable task = () -> {
diff --git a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/SpringBootRoutesCollector.java b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/SpringBootRoutesCollector.java
new file mode 100644
index 0000000..335a46c
--- /dev/null
+++ b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/SpringBootRoutesCollector.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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.boot;
+
+import java.io.FileNotFoundException;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.main.DefaultRoutesCollector;
+import org.apache.camel.model.ModelHelper;
+import org.apache.camel.model.RoutesDefinition;
+import org.apache.camel.model.rest.RestsDefinition;
+import org.apache.camel.util.AntPathMatcher;
+import org.apache.camel.util.ObjectHelper;
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.io.Resource;
+
+/**
+ * Spring Boot {@link org.apache.camel.main.RoutesCollector}.
+ */
+public class SpringBootRoutesCollector extends DefaultRoutesCollector {
+
+    private final ApplicationContext applicationContext;
+
+    public SpringBootRoutesCollector(ApplicationContext applicationContext) {
+        this.applicationContext = applicationContext;
+    }
+
+    @Override
+    public List<RoutesBuilder> collectRoutesFromRegistry(CamelContext camelContext, String excludePattern, String includePattern) {
+        final List<RoutesBuilder> routes = new ArrayList<>();
+
+        final AntPathMatcher matcher = new AntPathMatcher();
+        for (RoutesBuilder routesBuilder : applicationContext.getBeansOfType(RoutesBuilder.class, true, true).values()) {
+            // filter out abstract classes
+            boolean abs = Modifier.isAbstract(routesBuilder.getClass().getModifiers());
+            if (!abs) {
+                String name = routesBuilder.getClass().getName();
+                // make name as path so we can use ant path matcher
+                name = name.replace('.', '/');
+
+                boolean match = !"false".equals(includePattern);
+                // exclude take precedence over include
+                if (match && ObjectHelper.isNotEmpty(excludePattern)) {
+                    // there may be multiple separated by comma
+                    String[] parts = excludePattern.split(",");
+                    for (String part : parts) {
+                        // must negate when excluding, and hence !
+                        match = !matcher.match(part, name);
+                        log.trace("Java RoutesBuilder: {} exclude filter: {} -> {}", name, part, match);
+                        if (!match) {
+                            break;
+                        }
+                    }
+                }
+                // special support for testing with @ExcludeRoutes annotation with camel-test-spring
+                excludePattern = System.getProperty("CamelTestSpringExcludeRoutes");
+                // exclude take precedence over include
+                if (match && ObjectHelper.isNotEmpty(excludePattern)) {
+                    // this property is a comma separated list of FQN class names, so we need to make
+                    // name as path so we can use ant patch matcher
+                    excludePattern = excludePattern.replace('.', '/');
+                    // there may be multiple separated by comma
+                    String[] parts = excludePattern.split(",");
+                    for (String part : parts) {
+                        // must negate when excluding, and hence !
+                        match = !matcher.match(part, name);
+                        log.trace("Java RoutesBuilder: {} exclude filter: {} -> {}", name, part, match);
+                        if (!match) {
+                            break;
+                        }
+                    }
+                }
+                if (match && ObjectHelper.isNotEmpty(includePattern)) {
+                    // there may be multiple separated by comma
+                    String[] parts = includePattern.split(",");
+                    for (String part : parts) {
+                        match = matcher.match(part, name);
+                        log.trace("Java RoutesBuilder: {} include filter: {} -> {}", name, part, match);
+                        if (match) {
+                            break;
+                        }
+                    }
+                }
+                log.debug("Java RoutesBuilder: {} accepted by include/exclude filter: {}", name, match);
+                if (match) {
+                    routes.add(routesBuilder);
+                }
+            }
+        }
+
+        return routes;
+    }
+
+    @Override
+    public List<RoutesDefinition> collectXmlRoutesFromDirectory(CamelContext camelContext, String directory) {
+        List<RoutesDefinition> answer = new ArrayList<>();
+
+        String[] parts = directory.split(",");
+        for (String part : parts) {
+            log.info("Loading additional Camel XML routes from: {}", part);
+            try {
+                Resource[] xmlRoutes = applicationContext.getResources(part);
+                for (Resource xmlRoute : xmlRoutes) {
+                    log.debug("Found XML route: {}", xmlRoute);
+                    RoutesDefinition routes = ModelHelper.loadRoutesDefinition(camelContext, xmlRoute.getInputStream());
+                    answer.add(routes);
+                }
+            } catch (FileNotFoundException e) {
+                log.debug("No XML routes found in {}. Skipping XML routes detection.", part);
+            } catch (Exception e) {
+                throw RuntimeCamelException.wrapRuntimeException(e);
+            }
+        }
+
+        return answer;
+    }
+
+    @Override
+    public List<RestsDefinition> collectXmlRestsFromDirectory(CamelContext camelContext, String directory) {
+        List<RestsDefinition> answer = new ArrayList<>();
+
+        String[] parts = directory.split(",");
+        for (String part : parts) {
+            log.info("Loading additional Camel XML rests from: {}", part);
+            try {
+                final Resource[] xmlRests = applicationContext.getResources(part);
+                for (final Resource xmlRest : xmlRests) {
+                    RestsDefinition rests = ModelHelper.loadRestsDefinition(camelContext, xmlRest.getInputStream());
+                    answer.add(rests);
+                }
+            } catch (FileNotFoundException e) {
+                log.debug("No XML rests found in {}. Skipping XML rests detection.", part);
+            } catch (Exception e) {
+                throw RuntimeCamelException.wrapRuntimeException(e);
+            }
+        }
+
+        return answer;
+    }
+
+}
diff --git a/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/SupervisingRouteControllerTest.java b/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/SupervisingRouteControllerTest.java
index 3b2b640..7d9c495 100644
--- a/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/SupervisingRouteControllerTest.java
+++ b/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/SupervisingRouteControllerTest.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.spring.boot;
 
+import java.util.concurrent.TimeUnit;
+
 import org.apache.camel.CamelContext;
 import org.apache.camel.ServiceStatus;
 import org.apache.camel.builder.RouteBuilder;
@@ -32,6 +34,8 @@ import org.springframework.context.annotation.Configuration;
 import org.springframework.test.annotation.DirtiesContext;
 import org.springframework.test.context.junit4.SpringRunner;
 
+import static org.awaitility.Awaitility.await;
+
 @DirtiesContext
 @RunWith(SpringRunner.class)
 @SpringBootTest(
@@ -42,6 +46,7 @@ import org.springframework.test.context.junit4.SpringRunner;
     },
     properties = {
         "camel.springboot.xml-routes = false",
+        "camel.springboot.xml-rests = false",
         "camel.springboot.main-run-controller = true",
         "camel.supervising.controller.enabled = true",
         "camel.supervising.controller.initial-delay = 2s",
@@ -78,13 +83,18 @@ public class SupervisingRouteControllerTest {
         Assert.assertEquals(Long.MAX_VALUE, bar.getMaxDelay().toMillis());
         Assert.assertEquals(3L, bar.getMaxAttempts().longValue());
 
-        Assert.assertEquals(controller, context.getRoute("foo").getRouteContext().getRouteController());
-        Assert.assertEquals(controller, context.getRoute("bar").getRouteContext().getRouteController());
-        Assert.assertNull(context.getRoute("timer-unmanaged").getRouteContext().getRouteController());
-        Assert.assertNull(context.getRoute("timer-no-autostartup").getRouteContext().getRouteController());
-
         Assert.assertEquals(ServiceStatus.Stopped, context.getRouteController().getRouteStatus("foo"));
         Assert.assertEquals(ServiceStatus.Stopped, context.getRouteController().getRouteStatus("bar"));
+        Assert.assertEquals(ServiceStatus.Stopped, context.getRouteController().getRouteStatus("timer-no-autostartup"));
+        Assert.assertEquals(ServiceStatus.Stopped, context.getRouteController().getRouteStatus("jetty"));
+
+        // Wait for the controller to start the routes also unmanaged
+        await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> {
+            Assert.assertEquals(ServiceStatus.Started, context.getRouteController().getRouteStatus("jetty"));
+            Assert.assertEquals(ServiceStatus.Started, context.getRouteController().getRouteStatus("timer-unmanaged"));
+        });
+        Assert.assertEquals(ServiceStatus.Started, context.getRouteController().getRouteStatus("jetty"));
+        Assert.assertEquals(ServiceStatus.Started, context.getRouteController().getRouteStatus("timer-unmanaged"));
     }
 
     // *************************************
diff --git a/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/parent/SpringBootRefreshContextTest.java b/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/parent/SpringBootRefreshContextTest.java
index 141685d..e8ed75d 100644
--- a/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/parent/SpringBootRefreshContextTest.java
+++ b/components/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/parent/SpringBootRefreshContextTest.java
@@ -18,7 +18,7 @@ package org.apache.camel.spring.boot.parent;
 
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.builder.RouteBuilder;
-import org.apache.camel.spring.boot.RoutesCollector;
+import org.apache.camel.spring.boot.CamelSpringBootApplicationListener;
 import org.junit.Test;
 import org.springframework.boot.WebApplicationType;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -36,7 +36,7 @@ public class SpringBootRefreshContextTest {
         parent.refresh();
         ConfigurableApplicationContext context = new SpringApplicationBuilder(Configuration.class).web(WebApplicationType.NONE).parent(parent).run();
         ContextRefreshedEvent refreshEvent = new ContextRefreshedEvent(context);
-        RoutesCollector collector = context.getBean(RoutesCollector.class);
+        CamelSpringBootApplicationListener collector = context.getBean(CamelSpringBootApplicationListener.class);
         collector.onApplicationEvent(refreshEvent); //no changes should happen here
     }
 
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/RouteContext.java b/core/camel-api/src/main/java/org/apache/camel/spi/RouteContext.java
index feb117f..7c5b337 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/RouteContext.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/RouteContext.java
@@ -28,7 +28,6 @@ import org.apache.camel.NamedNode;
 import org.apache.camel.Processor;
 import org.apache.camel.Route;
 import org.apache.camel.RuntimeConfiguration;
-import org.apache.camel.meta.Experimental;
 
 /**
  * The context used to activate new routing rules
@@ -234,19 +233,14 @@ public interface RouteContext extends RuntimeConfiguration, EndpointAware {
      *
      * @return the route controller,
      */
-    @Experimental
-    default RouteController getRouteController() {
-        return null;
-    }
+    RouteController getRouteController();
 
     /**
      * Sets the {@link RouteController} for this route.
      *
      * @param controller the RouteController
      */
-    @Experimental
-    default void setRouteController(RouteController controller) {
-    }
+    void setRouteController(RouteController controller);
 
     Processor getOnCompletion(String onCompletionId);
 
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
index ad8caf5..ee06fdd 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
@@ -28,6 +28,7 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Properties;
+import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -51,7 +52,6 @@ import org.apache.camel.spi.PropertiesComponent;
 import org.apache.camel.spi.PropertyConfigurer;
 import org.apache.camel.spi.RestConfiguration;
 import org.apache.camel.support.LifecycleStrategySupport;
-import org.apache.camel.support.OrderedComparator;
 import org.apache.camel.support.PropertyBindingSupport;
 import org.apache.camel.support.service.ServiceHelper;
 import org.apache.camel.support.service.ServiceSupport;
@@ -81,6 +81,7 @@ public abstract class BaseMainSupport extends ServiceSupport {
 
     protected final List<MainListener> listeners = new ArrayList<>();
     protected final MainConfigurationProperties mainConfigurationProperties = new MainConfigurationProperties();
+    protected RoutesCollector routesCollector = new DefaultRoutesCollector();
     protected List<RoutesBuilder> routeBuilders = new ArrayList<>();
     protected String routeBuilderClasses;
     protected List<Object> configurations = new ArrayList<>();
@@ -210,6 +211,17 @@ public abstract class BaseMainSupport extends ServiceSupport {
         configurations.add(configuration);
     }
 
+    public RoutesCollector getRoutesCollector() {
+        return routesCollector;
+    }
+
+    /**
+     * To use a custom {@link RoutesCollector}.
+     */
+    public void setRoutesCollector(RoutesCollector routesCollector) {
+        this.routesCollector = routesCollector;
+    }
+
     public String getRouteBuilderClasses() {
         return routeBuilderClasses;
     }
@@ -412,6 +424,19 @@ public abstract class BaseMainSupport extends ServiceSupport {
                 }
             }
         }
+
+        if (mainConfigurationProperties.getPackageScanRouteBuilders() != null) {
+            String[] pkgs = mainConfigurationProperties.getPackageScanRouteBuilders().split(",");
+            Set<Class<?>> set = camelContext.getExtension(ExtendedCamelContext.class).getPackageScanClassResolver().findImplementations(RoutesBuilder.class, pkgs);
+            for (Class<?> routeClazz : set) {
+                Object builder = camelContext.getInjector().newInstance(routeClazz);
+                if (builder instanceof RouteBuilder) {
+                    getRoutesBuilders().add((RouteBuilder) builder);
+                } else {
+                    LOG.warn("Class {} is not a RouteBuilder class", routeClazz);
+                }
+            }
+        }
     }
 
     protected void loadConfigurations(CamelContext camelContext) throws Exception {
@@ -519,11 +544,9 @@ public abstract class BaseMainSupport extends ServiceSupport {
 
         // try to load the route builders
         loadRouteBuilders(camelContext);
-        // sort routes according to ordered
-        routeBuilders.sort(OrderedComparator.get());
-        for (RoutesBuilder routeBuilder : routeBuilders) {
-            camelContext.addRoutes(routeBuilder);
-        }
+        // then configure and add the routes
+        RoutesConfigurer configurer = new RoutesConfigurer(routesCollector, routeBuilders);
+        configurer.configureRoutes(camelContext, mainConfigurationProperties);
         // register lifecycle so we are notified in Camel is stopped from JMX or somewhere else
         camelContext.addLifecycleStrategy(new MainLifecycleStrategy(completed, latch));
         // allow to do configuration before its started
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationProperties.java
index 3e92f77..b0c0271 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationProperties.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationProperties.java
@@ -71,6 +71,11 @@ public abstract class DefaultConfigurationProperties<T> {
     private String routeFilterExcludePattern;
     private boolean beanIntrospectionExtendedStatistics;
     private LoggingLevel beanIntrospectionLoggingLevel;
+    private boolean routesCollectorEnabled = true;
+    private String javaRoutesIncludePattern;
+    private String javaRoutesExcludePattern;
+    private String xmlRoutes = "classpath:camel/*.xml";
+    private String xmlRests = "classpath:camel-rest/*.xml";
 
     // getter and setters
     // --------------------------------------------------------------
@@ -704,6 +709,96 @@ public abstract class DefaultConfigurationProperties<T> {
         this.beanIntrospectionLoggingLevel = beanIntrospectionLoggingLevel;
     }
 
+    public boolean isRoutesCollectorEnabled() {
+        return routesCollectorEnabled;
+    }
+
+    /**
+     * Whether the routes collector is enabled or not.
+     * 
+     * When enabled Camel will auto-discover routes (RouteBuilder instances from the registry and
+     * also load additional XML routes from the file system.
+     *
+     * The routes collector is default enabled.
+     */
+    public void setRoutesCollectorEnabled(boolean routesCollectorEnabled) {
+        this.routesCollectorEnabled = routesCollectorEnabled;
+    }
+
+    public String getJavaRoutesIncludePattern() {
+        return javaRoutesIncludePattern;
+    }
+
+    /**
+     * Used for inclusive filtering component scanning of RouteBuilder classes with @Component annotation.
+     * The exclusive filtering takes precedence over inclusive filtering.
+     * The pattern is using Ant-path style pattern.
+     *
+     * Multiple patterns can be specified separated by comma.
+     * For example to include all classes starting with Foo use: &#42;&#42;/Foo*
+     * To include all routes form a specific package use: com/mycompany/foo/&#42;
+     * To include all routes form a specific package and its sub-packages use double wildcards: com/mycompany/foo/&#42;&#42;
+     * And to include all routes from two specific packages use: com/mycompany/foo/&#42;,com/mycompany/stuff/&#42;
+     */
+    public void setJavaRoutesIncludePattern(String javaRoutesIncludePattern) {
+        this.javaRoutesIncludePattern = javaRoutesIncludePattern;
+    }
+
+    public String getJavaRoutesExcludePattern() {
+        return javaRoutesExcludePattern;
+    }
+
+    /**
+     * Used for exclusive filtering component scanning of RouteBuilder classes with @Component annotation.
+     * The exclusive filtering takes precedence over inclusive filtering.
+     * The pattern is using Ant-path style pattern.
+     * Multiple patterns can be specified separated by comma.
+     *
+     * For example to exclude all classes starting with Bar use: &#42;&#42;/Bar&#42;
+     * To exclude all routes form a specific package use: com/mycompany/bar/&#42;
+     * To exclude all routes form a specific package and its sub-packages use double wildcards: com/mycompany/bar/&#42;&#42;
+     * And to exclude all routes from two specific packages use: com/mycompany/bar/&#42;,com/mycompany/stuff/&#42;
+     */
+    public void setJavaRoutesExcludePattern(String javaRoutesExcludePattern) {
+        this.javaRoutesExcludePattern = javaRoutesExcludePattern;
+    }
+
+    public String getXmlRoutes() {
+        return xmlRoutes;
+    }
+
+    /**
+     * Directory to scan for adding additional XML routes.
+     * You can turn this off by setting the value to false.
+     *
+     * Files can be loaded from either classpath or file by prefixing with classpath: or file:
+     * Wildcards is supported using a ANT pattern style paths, such as classpath:&#42;&#42;/&#42;camel&#42;.xml
+     *
+     * Multiple directories can be specified and separated by comma, such as:
+     * file:/myapp/mycamel/&#42;.xml,file:/myapp/myothercamel/&#42;.xml
+     */
+    public void setXmlRoutes(String xmlRoutes) {
+        this.xmlRoutes = xmlRoutes;
+    }
+
+    public String getXmlRests() {
+        return xmlRests;
+    }
+
+    /**
+     * Directory to scan for adding additional XML rests.
+     * You can turn this off by setting the value to false.
+     *
+     * Files can be loaded from either classpath or file by prefixing with classpath: or file:
+     * Wildcards is supported using a ANT pattern style paths, such as classpath:&#42;&#42;/&#42;camel&#42;.xml
+     *
+     * Multiple directories can be specified and separated by comma, such as:
+     * file:/myapp/mycamel/&#42;.xml,file:/myapp/myothercamel/&#42;.xml
+     */
+    public void setXmlRests(String xmlRests) {
+        this.xmlRests = xmlRests;
+    }
+
     // fluent builders
     // --------------------------------------------------------------
 
@@ -1168,4 +1263,108 @@ public abstract class DefaultConfigurationProperties<T> {
         return (T) this;
     }
 
+    /**
+     * Tracing pattern to match which node EIPs to trace.
+     * For example to match all To EIP nodes, use to*.
+     * The pattern matches by node and route id's
+     * Multiple patterns can be separated by comma.
+     */
+    public T withTracingPattern(String tracingPattern) {
+        this.tracingPattern = tracingPattern;
+        return (T) this;
+    }
+
+    /**
+     * Sets the pattern used for determine which custom MDC keys to propagate during message routing when
+     * the routing engine continues routing asynchronously for the given message. Setting this pattern to * will
+     * propagate all custom keys. Or setting the pattern to foo*,bar* will propagate any keys starting with
+     * either foo or bar.
+     * Notice that a set of standard Camel MDC keys are always propagated which starts with camel. as key name.
+     *
+     * The match rules are applied in this order (case insensitive):
+     *
+     * 1. exact match, returns true
+     * 2. wildcard match (pattern ends with a * and the name starts with the pattern), returns true
+     * 3. regular expression match, returns true
+     * 4. otherwise returns false
+     */
+    public T withMdcLoggingKeysPattern(String mdcLoggingKeysPattern) {
+        this.mdcLoggingKeysPattern = mdcLoggingKeysPattern;
+        return (T) this;
+    }
+
+    /**
+     * Whether the routes collector is enabled or not.
+     *
+     * When enabled Camel will auto-discover routes (RouteBuilder instances from the registry and
+     * also load additional XML routes from the file system.
+     *
+     * The routes collector is default enabled.
+     */
+    public T withRoutesCollectorEnabled(boolean routesCollectorEnabled) {
+        this.routesCollectorEnabled = routesCollectorEnabled;
+        return (T) this;
+    }
+
+    /**
+     * Used for inclusive filtering component scanning of RouteBuilder classes with @Component annotation.
+     * The exclusive filtering takes precedence over inclusive filtering.
+     * The pattern is using Ant-path style pattern.
+     *
+     * Multiple patterns can be specified separated by comma.
+     * For example to include all classes starting with Foo use: &#42;&#42;/Foo*
+     * To include all routes form a specific package use: com/mycompany/foo/&#42;
+     * To include all routes form a specific package and its sub-packages use double wildcards: com/mycompany/foo/&#42;&#42;
+     * And to include all routes from two specific packages use: com/mycompany/foo/&#42;,com/mycompany/stuff/&#42;
+     */
+    public T withJavaRoutesIncludePattern(String javaRoutesIncludePattern) {
+        this.javaRoutesIncludePattern = javaRoutesIncludePattern;
+        return (T) this;
+    }
+
+    /**
+     * Used for exclusive filtering component scanning of RouteBuilder classes with @Component annotation.
+     * The exclusive filtering takes precedence over inclusive filtering.
+     * The pattern is using Ant-path style pattern.
+     * Multiple patterns can be specified separated by comma.
+     *
+     * For example to exclude all classes starting with Bar use: &#42;&#42;/Bar&#42;
+     * To exclude all routes form a specific package use: com/mycompany/bar/&#42;
+     * To exclude all routes form a specific package and its sub-packages use double wildcards: com/mycompany/bar/&#42;&#42;
+     * And to exclude all routes from two specific packages use: com/mycompany/bar/&#42;,com/mycompany/stuff/&#42;
+     */
+    public T withJavaRoutesExcludePattern(String javaRoutesExcludePattern) {
+        this.javaRoutesExcludePattern = javaRoutesExcludePattern;
+        return (T) this;
+    }
+
+    /**
+     * Directory to scan for adding additional XML routes.
+     * You can turn this off by setting the value to false.
+     *
+     * Files can be loaded from either classpath or file by prefixing with classpath: or file:
+     * Wildcards is supported using a ANT pattern style paths, such as classpath:&#42;&#42;/&#42;camel&#42;.xml
+     *
+     * Multiple directories can be specified and separated by comma, such as:
+     * file:/myapp/mycamel/&#42;.xml,file:/myapp/myothercamel/&#42;.xml
+     */
+    public T withXmlRoutes(String xmlRoutes) {
+        this.xmlRoutes = xmlRoutes;
+        return (T) this;
+    }
+
+    /**
+     * Directory to scan for adding additional XML rests.
+     * You can turn this off by setting the value to false.
+     *
+     * Files can be loaded from either classpath or file by prefixing with classpath: or file:
+     * Wildcards is supported using a ANT pattern style paths, such as classpath:&#42;&#42;/&#42;camel&#42;.xml
+     *
+     * Multiple directories can be specified and separated by comma, such as:
+     * file:/myapp/mycamel/&#42;.xml,file:/myapp/myothercamel/&#42;.xml
+     */
+    public T withXmlRests(String xmlRests) {
+        this.xmlRests = xmlRests;
+        return (T) this;
+    }
 }
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/DefaultRoutesCollector.java b/core/camel-main/src/main/java/org/apache/camel/main/DefaultRoutesCollector.java
new file mode 100644
index 0000000..3bd10a3
--- /dev/null
+++ b/core/camel-main/src/main/java/org/apache/camel/main/DefaultRoutesCollector.java
@@ -0,0 +1,139 @@
+/*
+ * 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.main;
+
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.model.ModelHelper;
+import org.apache.camel.model.RoutesDefinition;
+import org.apache.camel.model.rest.RestsDefinition;
+import org.apache.camel.support.ResourceHelper;
+import org.apache.camel.util.AntPathMatcher;
+import org.apache.camel.util.ObjectHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A default {@link RoutesCollector}.
+ */
+public class DefaultRoutesCollector implements RoutesCollector {
+
+    protected final Logger log = LoggerFactory.getLogger(getClass());
+
+    @Override
+    public List<RoutesBuilder> collectRoutesFromRegistry(CamelContext camelContext,
+                                                         String excludePattern, String includePattern) {
+        final List<RoutesBuilder> routes = new ArrayList<>();
+
+        final AntPathMatcher matcher = new AntPathMatcher();
+        Set<RoutesBuilder> builders = camelContext.getRegistry().findByType(RoutesBuilder.class);
+        for (RoutesBuilder routesBuilder : builders) {
+            // filter out abstract classes
+            boolean abs = Modifier.isAbstract(routesBuilder.getClass().getModifiers());
+            if (!abs) {
+                String name = routesBuilder.getClass().getName();
+                // make name as path so we can use ant path matcher
+                name = name.replace('.', '/');
+
+                boolean match = !"false".equals(includePattern);
+                // exclude take precedence over include
+                if (match && ObjectHelper.isNotEmpty(excludePattern)) {
+                    // there may be multiple separated by comma
+                    String[] parts = excludePattern.split(",");
+                    for (String part : parts) {
+                        // must negate when excluding, and hence !
+                        match = !matcher.match(part, name);
+                        log.trace("Java RoutesBuilder: {} exclude filter: {} -> {}", name, part, match);
+                        if (!match) {
+                            break;
+                        }
+                    }
+                }
+                if (match && ObjectHelper.isNotEmpty(includePattern)) {
+                    // there may be multiple separated by comma
+                    String[] parts = includePattern.split(",");
+                    for (String part : parts) {
+                        match = matcher.match(part, name);
+                        log.trace("Java RoutesBuilder: {} include filter: {} -> {}", name, part, match);
+                        if (match) {
+                            break;
+                        }
+                    }
+                }
+                log.debug("Java RoutesBuilder: {} accepted by include/exclude filter: {}", name, match);
+                if (match) {
+                    routes.add(routesBuilder);
+                }
+            }
+        }
+
+        return routes;
+    }
+
+    @Override
+    public List<RoutesDefinition> collectXmlRoutesFromDirectory(CamelContext camelContext, String directory) {
+        List<RoutesDefinition> answer = new ArrayList<>();
+
+        String[] parts = directory.split(",");
+        for (String part : parts) {
+            log.info("Loading additional Camel XML routes from: {}", part);
+            try {
+                InputStream is = ResourceHelper.resolveMandatoryResourceAsInputStream(camelContext, part);
+                log.debug("Found XML route: {}", is);
+                RoutesDefinition routes = ModelHelper.loadRoutesDefinition(camelContext, is);
+                answer.add(routes);
+            } catch (FileNotFoundException e) {
+                log.debug("No XML routes found in {}. Skipping XML routes detection.", part);
+            } catch (Exception e) {
+                throw RuntimeCamelException.wrapRuntimeException(e);
+            }
+        }
+
+        return answer;
+    }
+
+    @Override
+    public List<RestsDefinition> collectXmlRestsFromDirectory(CamelContext camelContext, String directory) {
+        List<RestsDefinition> answer = new ArrayList<>();
+
+        String[] parts = directory.split(",");
+        for (String part : parts) {
+            log.info("Loading additional Camel XML rests from: {}", part);
+            try {
+                InputStream is = ResourceHelper.resolveMandatoryResourceAsInputStream(camelContext, part);
+                log.debug("Found XML rest: {}", is);
+                RestsDefinition rests = ModelHelper.loadRestsDefinition(camelContext, is);
+                answer.add(rests);
+            } catch (FileNotFoundException e) {
+                log.debug("No XML rests found in {}. Skipping XML rests detection.", part);
+            } catch (Exception e) {
+                throw RuntimeCamelException.wrapRuntimeException(e);
+            }
+        }
+
+        return answer;
+    }
+
+}
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationProperties.java
index ffdca5d..28ab76d 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationProperties.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationProperties.java
@@ -30,6 +30,7 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties<
     private boolean autowireComponentPropertiesAllowPrivateSetter = true;
     private int durationHitExitCode;
     private boolean hangupInterceptorEnabled = true;
+    private String packageScanRouteBuilders;
 
     // extended configuration
     private final HystrixConfigurationProperties hystrixConfigurationProperties = new HystrixConfigurationProperties(this);
@@ -178,6 +179,19 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties<
         this.hangupInterceptorEnabled = hangupInterceptorEnabled;
     }
 
+    public String getPackageScanRouteBuilders() {
+        return packageScanRouteBuilders;
+    }
+
+    /**
+     * Sets package names for scanning for {@link org.apache.camel.builder.RouteBuilder} classes as candidates to be included.
+     * If you are using Spring Boot then its instead recommended to use Spring Boots component scanning and annotate your route builder
+     * classes with `@Component`. In other words only use this for Camel Main in standalone mode.
+     */
+    public void setPackageScanRouteBuilders(String packageScanRouteBuilders) {
+        this.packageScanRouteBuilders = packageScanRouteBuilders;
+    }
+
     public int getDurationHitExitCode() {
         return durationHitExitCode;
     }
@@ -301,4 +315,14 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties<
         return this;
     }
 
+    /**
+     * Sets package names for scanning for {@link org.apache.camel.builder.RouteBuilder} classes as candidates to be included.
+     * If you are using Spring Boot then its instead recommended to use Spring Boots component scanning and annotate your route builder
+     * classes with `@Component`. In other words only use this for Camel Main in standalone mode.
+     */
+    public MainConfigurationProperties withPackageScanRouteBuilders(String packageScanRouteBuilders) {
+        this.packageScanRouteBuilders = packageScanRouteBuilders;
+        return this;
+    }
+
 }
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/RoutesCollector.java b/core/camel-main/src/main/java/org/apache/camel/main/RoutesCollector.java
new file mode 100644
index 0000000..8d8b2fc
--- /dev/null
+++ b/core/camel-main/src/main/java/org/apache/camel/main/RoutesCollector.java
@@ -0,0 +1,61 @@
+/*
+ * 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.main;
+
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.model.RoutesDefinition;
+import org.apache.camel.model.rest.RestsDefinition;
+
+/**
+ * Collects routes and rests from the various sources (like registry or opinionated
+ * classpath locations) and adds these into the Camel context.
+ */
+public interface RoutesCollector {
+
+    /**
+     * Collects the {@link RoutesBuilder} instances which was discovered from the {@link org.apache.camel.spi.Registry} such as
+     * Spring or CDI bean containers.
+     *
+     * @param camelContext        the Camel Context
+     * @param excludePattern      exclude pattern (see javaRoutesExcludePattern option)
+     * @param includePattern      include pattern  (see javaRoutesIncludePattern option)
+     * @return the discovered routes or an empty list
+     */
+    List<RoutesBuilder> collectRoutesFromRegistry(CamelContext camelContext, String excludePattern, String includePattern);
+
+    /**
+     * Collects all XML routes from the given directory.
+     *
+     * @param camelContext               the Camel Context
+     * @param directory                  the directory (see xmlRoutes option)
+     * @return the discovered routes or an empty list
+     */
+    List<RoutesDefinition> collectXmlRoutesFromDirectory(CamelContext camelContext, String directory) throws Exception;
+
+    /**
+     * Collects all XML rests from the given directory.
+     *
+     * @param camelContext               the Camel Context
+     * @param directory                  the directory (see xmlRests option)
+     * @return the discovered rests or an empty list
+     */
+    List<RestsDefinition> collectXmlRestsFromDirectory(CamelContext camelContext, String directory) throws Exception;
+
+}
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/RoutesConfigurer.java b/core/camel-main/src/main/java/org/apache/camel/main/RoutesConfigurer.java
new file mode 100644
index 0000000..589988e
--- /dev/null
+++ b/core/camel-main/src/main/java/org/apache/camel/main/RoutesConfigurer.java
@@ -0,0 +1,109 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.main;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.model.Model;
+import org.apache.camel.model.RoutesDefinition;
+import org.apache.camel.model.rest.RestsDefinition;
+import org.apache.camel.support.OrderedComparator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * To configure routes using {@link RoutesCollector} which collects the routes from various sources.
+ */
+public class RoutesConfigurer {
+
+    private static final Logger LOG = LoggerFactory.getLogger(RoutesConfigurer.class);
+
+    private final RoutesCollector routesCollector;
+    private final List<RoutesBuilder> routesBuilders;
+
+    /**
+     * Creates a new routes configurer
+     *
+     * @param routesCollector  routes collector
+     */
+    public RoutesConfigurer(RoutesCollector routesCollector) {
+        this(routesCollector, new ArrayList<>());
+    }
+
+    /**
+     * Creates a new routes configurer
+     *
+     * @param routesCollector  routes collector
+     * @param routesBuilders   existing route builders
+     */
+    public RoutesConfigurer(RoutesCollector routesCollector, List<RoutesBuilder> routesBuilders) {
+        this.routesCollector = routesCollector;
+        this.routesBuilders = routesBuilders;
+    }
+
+    /**
+     * Collects routes and rests from the various sources (like registry or opinionated
+     * classpath locations) and injects (adds) these into the Camel context.
+     *
+     * @param camelContext  the Camel context
+     * @param config        the configuration
+     */
+    public void configureRoutes(CamelContext camelContext, DefaultConfigurationProperties config) {
+        if (config.isRoutesCollectorEnabled()) {
+            try {
+                LOG.debug("RoutesCollectorEnabled: {}", routesCollector);
+                final List<RoutesBuilder> routes = routesCollector.collectRoutesFromRegistry(camelContext,
+                        config.getJavaRoutesExcludePattern(),
+                        config.getJavaRoutesIncludePattern());
+
+                // add newly discovered routes
+                routesBuilders.addAll(routes);
+                // sort routes according to ordered
+                routesBuilders.sort(OrderedComparator.get());
+                // then add the routes
+                for (RoutesBuilder builder : routesBuilders) {
+                    LOG.debug("Adding routes into CamelContext from RoutesBuilder: {}", builder);
+                    camelContext.addRoutes(builder);
+                }
+
+                boolean scan = !config.getXmlRoutes().equals("false");
+                if (scan) {
+                    List<RoutesDefinition> defs = routesCollector.collectXmlRoutesFromDirectory(camelContext, config.getXmlRoutes());
+                    for (RoutesDefinition def : defs) {
+                        LOG.debug("Adding routes into CamelContext from XML files: {}", config.getXmlRoutes());
+                        camelContext.getExtension(Model.class).addRouteDefinitions(def.getRoutes());
+                    }
+                }
+
+                boolean scanRests = !config.getXmlRests().equals("false");
+                if (scanRests) {
+                    List<RestsDefinition> defs = routesCollector.collectXmlRestsFromDirectory(camelContext, config.getXmlRests());
+                    for (RestsDefinition def : defs) {
+                        LOG.debug("Adding rests into CamelContext from XML files: {}", config.getXmlRests());
+                        camelContext.getExtension(Model.class).addRestDefinitions(def.getRests(), true);
+                    }
+                }
+            } catch (Exception e) {
+                throw RuntimeCamelException.wrapRuntimeException(e);
+            }
+        }
+    }
+}
diff --git a/core/camel-main/src/main/resources/META-INF/camel-main-configuration-metadata.json b/core/camel-main/src/main/resources/META-INF/camel-main-configuration-metadata.json
index 8548426..bd3d045 100644
--- a/core/camel-main/src/main/resources/META-INF/camel-main-configuration-metadata.json
+++ b/core/camel-main/src/main/resources/META-INF/camel-main-configuration-metadata.json
@@ -147,6 +147,18 @@
 			"defaultValue":"true"
 		},
 		{
+			"name":"camel.main.java-routes-exclude-pattern",
+			"type":"java.lang.String",
+			"sourceType":"org.apache.camel.main.DefaultConfigurationProperties",
+			"description":"Used for exclusive filtering component scanning of RouteBuilder classes with Component annotation. The exclusive filtering takes precedence over inclusive filtering. The pattern is using Ant-path style pattern. Multiple patterns can be specified separated by comma. For example to exclude all classes starting with Bar use: &#42;&#42;\/Bar&#42; To exclude all routes form a specific package use: com\/mycompany\/bar\/&#42; To exclude all routes form a specific package and i [...]
+		},
+		{
+			"name":"camel.main.java-routes-include-pattern",
+			"type":"java.lang.String",
+			"sourceType":"org.apache.camel.main.DefaultConfigurationProperties",
+			"description":"Used for inclusive filtering component scanning of RouteBuilder classes with Component annotation. The exclusive filtering takes precedence over inclusive filtering. The pattern is using Ant-path style pattern. Multiple patterns can be specified separated by comma. For example to include all classes starting with Foo use: &#42;&#42;\/Foo To include all routes form a specific package use: com\/mycompany\/foo\/&#42; To include all routes form a specific package and its su [...]
+		},
+		{
 			"name":"camel.main.jmx-create-connector",
 			"type":"boolean",
 			"sourceType":"org.apache.camel.main.DefaultConfigurationProperties",
@@ -218,6 +230,12 @@
 			"description":"Sets the name of the CamelContext."
 		},
 		{
+			"name":"camel.main.package-scan-route-builders",
+			"type":"java.lang.String",
+			"sourceType":"org.apache.camel.main.MainConfigurationProperties",
+			"description":"Sets package names for scanning for org.apache.camel.builder.RouteBuilder classes as candidates to be included. If you are using Spring Boot then its instead recommended to use Spring Boots component scanning and annotate your route builder classes with Component. In other words only use this for Camel Main in standalone mode."
+		},
+		{
 			"name":"camel.main.producer-template-cache-size",
 			"type":"int",
 			"sourceType":"org.apache.camel.main.DefaultConfigurationProperties",
@@ -237,6 +255,13 @@
 			"description":"Used for filtering routes routes matching the given pattern, which follows the following rules: - Match by route id - Match by route input endpoint uri The matching is using exact match, by wildcard and regular expression as documented by PatternHelper#matchPattern(String,String) . For example to only include routes which starts with foo in their route id's, use: include=foo&#42; And to exclude routes which starts from JMS endpoints, use: exclude=jms:&#42; Multiple patt [...]
 		},
 		{
+			"name":"camel.main.routes-collector-enabled",
+			"type":"boolean",
+			"sourceType":"org.apache.camel.main.DefaultConfigurationProperties",
+			"description":"Whether the routes collector is enabled or not. When enabled Camel will auto-discover routes (RouteBuilder instances from the registry and also load additional XML routes from the file system. The routes collector is default enabled.",
+			"defaultValue":"true"
+		},
+		{
 			"name":"camel.main.shutdown-log-inflight-exchanges-on-timeout",
 			"type":"boolean",
 			"sourceType":"org.apache.camel.main.DefaultConfigurationProperties",
@@ -368,6 +393,20 @@
 			"description":"To turn on MDC logging"
 		},
 		{
+			"name":"camel.main.xml-rests",
+			"type":"java.lang.String",
+			"sourceType":"org.apache.camel.main.DefaultConfigurationProperties",
+			"description":"Directory to scan for adding additional XML rests. You can turn this off by setting the value to false. Files can be loaded from either classpath or file by prefixing with classpath: or file: Wildcards is supported using a ANT pattern style paths, such as classpath:&#42;&#42;\/&#42;camel&#42;.xml Multiple directories can be specified and separated by comma, such as: file:\/myapp\/mycamel\/&#42;.xml,file:\/myapp\/myothercamel\/&#42;.xml",
+			"defaultValue":"classpath:camel-rest\/*.xml"
+		},
+		{
+			"name":"camel.main.xml-routes",
+			"type":"java.lang.String",
+			"sourceType":"org.apache.camel.main.DefaultConfigurationProperties",
+			"description":"Directory to scan for adding additional XML routes. You can turn this off by setting the value to false. Files can be loaded from either classpath or file by prefixing with classpath: or file: Wildcards is supported using a ANT pattern style paths, such as classpath:&#42;&#42;\/&#42;camel&#42;.xml Multiple directories can be specified and separated by comma, such as: file:\/myapp\/mycamel\/&#42;.xml,file:\/myapp\/myothercamel\/&#42;.xml",
+			"defaultValue":"classpath:camel\/*.xml"
+		},
+		{
 			"name":"camel.hystrix.allow-maximum-size-to-diverge-from-core-size",
 			"type":"java.lang.Boolean",
 			"sourceType":"org.apache.camel.main.HystrixConfigurationProperties",
diff --git a/core/camel-main/src/test/java/org/apache/camel/main/MainRoutesCollectorPackageScanTest.java b/core/camel-main/src/test/java/org/apache/camel/main/MainRoutesCollectorPackageScanTest.java
new file mode 100644
index 0000000..06a4b1d
--- /dev/null
+++ b/core/camel-main/src/test/java/org/apache/camel/main/MainRoutesCollectorPackageScanTest.java
@@ -0,0 +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.main;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class MainRoutesCollectorPackageScanTest extends Assert {
+
+    @Test
+    public void testMainRoutesCollector() throws Exception {
+        Main main = new Main();
+        main.configure().withPackageScanRouteBuilders("org.apache.camel.main.scan");
+        main.start();
+
+        CamelContext camelContext = main.getCamelContext();
+        assertNotNull(camelContext);
+        assertEquals(2, camelContext.getRoutes().size());
+
+        MockEndpoint endpoint = camelContext.getEndpoint("mock:scan", MockEndpoint.class);
+        endpoint.expectedBodiesReceived("Hello World");
+        MockEndpoint endpoint2 = camelContext.getEndpoint("mock:dummy", MockEndpoint.class);
+        endpoint2.expectedBodiesReceived("Bye World");
+
+        main.getCamelTemplate().sendBody("direct:scan", "Hello World");
+        main.getCamelTemplate().sendBody("direct:dummy", "Bye World");
+
+        endpoint.assertIsSatisfied();
+        endpoint2.assertIsSatisfied();
+
+        main.stop();
+    }
+
+}
diff --git a/core/camel-main/src/test/java/org/apache/camel/main/MainRoutesCollectorTest.java b/core/camel-main/src/test/java/org/apache/camel/main/MainRoutesCollectorTest.java
new file mode 100644
index 0000000..56ab2ec
--- /dev/null
+++ b/core/camel-main/src/test/java/org/apache/camel/main/MainRoutesCollectorTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.main;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class MainRoutesCollectorTest extends Assert {
+
+    @Test
+    public void testMainRoutesCollector() throws Exception {
+        Main main = new Main();
+        main.bind("myBarRoute", new MyRouteBuilder());
+        main.start();
+
+        CamelContext camelContext = main.getCamelContext();
+        assertNotNull(camelContext);
+        assertEquals(1, camelContext.getRoutes().size());
+
+        MockEndpoint endpoint = camelContext.getEndpoint("mock:results", MockEndpoint.class);
+        endpoint.expectedBodiesReceived("Hello World");
+
+        main.getCamelTemplate().sendBody("direct:start", "Hello World");
+
+        endpoint.assertIsSatisfied();
+
+        main.stop();
+    }
+
+    public static class MyRouteBuilder extends RouteBuilder {
+
+        @Override
+        public void configure() throws Exception {
+            from("direct:start").to("mock:results");
+        }
+    }
+}
diff --git a/core/camel-main/src/test/java/org/apache/camel/main/scan/MyDummyRouteBuilder.java b/core/camel-main/src/test/java/org/apache/camel/main/scan/MyDummyRouteBuilder.java
new file mode 100644
index 0000000..d2a5d78
--- /dev/null
+++ b/core/camel-main/src/test/java/org/apache/camel/main/scan/MyDummyRouteBuilder.java
@@ -0,0 +1,27 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.main.scan;
+
+import org.apache.camel.builder.RouteBuilder;
+
+public class MyDummyRouteBuilder extends RouteBuilder {
+
+    @Override
+    public void configure() throws Exception {
+        from("direct:dummy").to("mock:dummy");
+    }
+}
diff --git a/core/camel-main/src/test/java/org/apache/camel/main/scan/MyScanRouteBuilder.java b/core/camel-main/src/test/java/org/apache/camel/main/scan/MyScanRouteBuilder.java
new file mode 100644
index 0000000..ff258c7
--- /dev/null
+++ b/core/camel-main/src/test/java/org/apache/camel/main/scan/MyScanRouteBuilder.java
@@ -0,0 +1,27 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.main.scan;
+
+import org.apache.camel.builder.RouteBuilder;
+
+public class MyScanRouteBuilder extends RouteBuilder {
+
+    @Override
+    public void configure() throws Exception {
+        from("direct:scan").to("mock:scan");
+    }
+}