You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by lb...@apache.org on 2017/08/11 13:00:53 UTC

[1/2] camel git commit: CAMEL-11659: Create a ClusteredRouteController

Repository: camel
Updated Branches:
  refs/heads/master f8ddd2305 -> d043c7d0a


http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/examples/camel-example-spring-boot-supervising-route-controller/readme.adoc
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot-supervising-route-controller/readme.adoc b/examples/camel-example-spring-boot-supervising-route-controller/readme.adoc
new file mode 100644
index 0000000..8c7bb28
--- /dev/null
+++ b/examples/camel-example-spring-boot-supervising-route-controller/readme.adoc
@@ -0,0 +1,51 @@
+# Camel Supervising Route Controller Example Spring Boot
+
+This example shows how to work with a simple Apache Camel application using Spring Boot and a Supervising Route Controller.
+
+## How to run
+
+You can run this example using
+
+    mvn spring-boot:run
+
+Beside JMX you can use Spring Boot Endpoints to interact with the routes:
+
+* To get info about the routes
++
+[source]
+----
+curl -XGET -s http://localhost:8080/camel/routes
+----
++
++* To get details about a route
+++
++[source]
++----
++curl -XGET -s http://localhost:8080/camel/routes/{id}/detail
++----
+
+* To get info about a route
++
+[source]
+----
+curl -XGET -s http://localhost:8080/camel/routes/{id}/info
+----
+
+* To stop a route
++
+[source]
+----
+curl -XPOST -s http://localhost:8080/camel/routes/{id}/stop
+----
+
+* To start a route
++
+[source]
+----
+curl -XPOST -s http://localhost:8080/camel/routes/{id}/stop
+----
+
+
+## More information
+
+You can find more information about Apache Camel at the website: http://camel.apache.org/

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/examples/camel-example-spring-boot-supervising-route-controller/src/main/java/sample/camel/Application.java
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot-supervising-route-controller/src/main/java/sample/camel/Application.java b/examples/camel-example-spring-boot-supervising-route-controller/src/main/java/sample/camel/Application.java
new file mode 100644
index 0000000..2a97fed
--- /dev/null
+++ b/examples/camel-example-spring-boot-supervising-route-controller/src/main/java/sample/camel/Application.java
@@ -0,0 +1,37 @@
+/**
+ * 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 sample.camel;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+//CHECKSTYLE:OFF
+/**
+ * A sample Spring Boot application that starts the Camel routes.
+ */
+@SpringBootApplication
+public class Application {
+
+    /**
+     * A main method to start this application.
+     */
+    public static void main(String[] args) {
+        SpringApplication.run(Application.class, args);
+    }
+
+}
+//CHECKSTYLE:ON

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/examples/camel-example-spring-boot-supervising-route-controller/src/main/java/sample/camel/ApplicationRoutes.java
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot-supervising-route-controller/src/main/java/sample/camel/ApplicationRoutes.java b/examples/camel-example-spring-boot-supervising-route-controller/src/main/java/sample/camel/ApplicationRoutes.java
new file mode 100644
index 0000000..fb6e2f5
--- /dev/null
+++ b/examples/camel-example-spring-boot-supervising-route-controller/src/main/java/sample/camel/ApplicationRoutes.java
@@ -0,0 +1,52 @@
+/**
+ * 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 sample.camel;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.springframework.stereotype.Component;
+
+/**
+ *
+ */
+@Component
+public class ApplicationRoutes extends RouteBuilder {
+    @Override
+    public void configure() throws Exception {
+        from("timer:foo?period=5s")
+            .id("foo")
+            .startupOrder(2)
+            .log("From timer (foo) ...");
+
+        from("timer:bar?period=5s")
+            .id("bar")
+            .startupOrder(1)
+            .log("From timer (bar) ...");
+
+        from("undertow:http://localhost:9011")
+            .id("undertow")
+            .log("From undertow ...");
+
+        from("undertow:http://localhost:9012")
+            .id("undertow2")
+            .autoStartup(false)
+            .log("From undertow 2...");
+
+        from("undertow:http://localhost:9013")
+            .id("undertow3")
+            .log("From undertow 3...");
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/examples/camel-example-spring-boot-supervising-route-controller/src/main/resources/application.properties
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot-supervising-route-controller/src/main/resources/application.properties b/examples/camel-example-spring-boot-supervising-route-controller/src/main/resources/application.properties
new file mode 100644
index 0000000..8533f6b
--- /dev/null
+++ b/examples/camel-example-spring-boot-supervising-route-controller/src/main/resources/application.properties
@@ -0,0 +1,46 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+debug = false
+
+logging.level.org.springframework = INFO
+logging.level.org.apache.camel.spring.boot = INFO
+logging.level.org.apache.camel.impl = INFO
+logging.level.org.apache.camel.impl.SupervisingRouteController = DEBUG
+logging.level.org.apache.camel.util.backoff = DEBUG
+logging.level.sample.camel = DEBUG
+
+endpoints.enabled = false
+endpoints.jmx.enabled = false
+endpoints.health.enabled = true
+
+# camel routes is by default enabled
+# so you do not have to configure below
+# endpoints.camelroutes.path = /camel/routes
+# endpoints.camelroutes.enabled = true
+
+management.security.enabled = false
+
+camel.springboot.name = SampleSupervisingRouteController
+
+camel.supervising.controller.enabled = true
+camel.supervising.controller.initial-delay = 5s
+camel.supervising.controller.default-back-off.delay = 5s
+camel.supervising.controller.default-back-off.max-attempts = 10
+camel.supervising.controller.routes.undertow.back-off.delay = 10s
+camel.supervising.controller.routes.undertow.back-off.max-attempts = 3
+camel.supervising.controller.routes.undertow3.supervise = false

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/examples/pom.xml
----------------------------------------------------------------------
diff --git a/examples/pom.xml b/examples/pom.xml
index 268733d..7a4441b 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -96,6 +96,7 @@
     <module>camel-example-spring</module>
     <module>camel-example-spring-boot</module>
     <module>camel-example-spring-boot-activemq</module>
+    <module>camel-example-spring-boot-clustered-route-controller</module>
     <module>camel-example-spring-boot-geocoder</module>
     <module>camel-example-spring-boot-grpc</module>
     <module>camel-example-spring-boot-infinispan</module>
@@ -104,8 +105,8 @@
     <module>camel-example-spring-boot-pojo</module>
     <module>camel-example-spring-boot-rest-jpa</module>
     <module>camel-example-spring-boot-rest-swagger</module>
-    <module>camel-example-spring-boot-routecontroller</module>
     <module>camel-example-spring-boot-servicecall</module>
+    <module>camel-example-spring-boot-supervising-route-controller</module>
     <module>camel-example-spring-cloud-servicecall</module>
     <module>camel-example-spring-javaconfig</module>
     <module>camel-example-spring-jms</module>

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/platforms/spring-boot/components-starter/camel-atomix-starter/src/main/java/org/apache/camel/component/atomix/ha/springboot/AtomixClusterServiceAutoConfiguration.java
----------------------------------------------------------------------
diff --git a/platforms/spring-boot/components-starter/camel-atomix-starter/src/main/java/org/apache/camel/component/atomix/ha/springboot/AtomixClusterServiceAutoConfiguration.java b/platforms/spring-boot/components-starter/camel-atomix-starter/src/main/java/org/apache/camel/component/atomix/ha/springboot/AtomixClusterServiceAutoConfiguration.java
new file mode 100644
index 0000000..8bb7ab5
--- /dev/null
+++ b/platforms/spring-boot/components-starter/camel-atomix-starter/src/main/java/org/apache/camel/component/atomix/ha/springboot/AtomixClusterServiceAutoConfiguration.java
@@ -0,0 +1,96 @@
+/**
+ * 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.component.atomix.ha.springboot;
+
+
+import java.util.stream.Collectors;
+
+import io.atomix.catalyst.transport.Address;
+import org.apache.camel.component.atomix.ha.AtomixClusterClientService;
+import org.apache.camel.component.atomix.ha.AtomixClusterService;
+import org.apache.camel.ha.CamelClusterService;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.spring.boot.ha.ClusteredRouteControllerAutoConfiguration;
+import org.apache.camel.util.ObjectHelper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Conditional;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Scope;
+
+@Configuration
+@AutoConfigureBefore({ ClusteredRouteControllerAutoConfiguration.class, CamelAutoConfiguration.class })
+@Conditional(AtomixClusterServiceAutoConfiguration.AutoConfigurationCondition.class)
+@EnableConfigurationProperties(AtomixClusterServiceConfiguration.class)
+public class AtomixClusterServiceAutoConfiguration {
+    @Autowired
+    private AtomixClusterServiceConfiguration configuration;
+
+    @Bean(initMethod = "start", destroyMethod = "stop")
+    @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
+    @ConditionalOnProperty(prefix = "camel.clustered.service.atomix", name = "mode", havingValue = "node")
+    public CamelClusterService clusterNode() {
+        AtomixClusterService service = new AtomixClusterService();
+        service.setNodes(configuration.getNodes().stream().map(Address::new).collect(Collectors.toList()));
+
+        ObjectHelper.ifNotEmpty(configuration.isEphemeral(), service::setEphemeral);
+        ObjectHelper.ifNotEmpty(configuration.getId(), service::setId);
+        ObjectHelper.ifNotEmpty(configuration.getAddress(), service::setAddress);
+        ObjectHelper.ifNotEmpty(configuration.getStoragePath(), service::setStoragePath);
+        ObjectHelper.ifNotEmpty(configuration.getStorageLevel(), service::setStorageLevel);
+        ObjectHelper.ifNotEmpty(configuration.getConfigurationUri(), service::setConfigurationUri);
+
+        return service;
+    }
+
+    @Bean(initMethod = "start", destroyMethod = "stop")
+    @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
+    @ConditionalOnProperty(prefix = "camel.clustered.service.atomix", name = "mode", havingValue = "client")
+    public CamelClusterService clusterClient() {
+        AtomixClusterClientService service = new AtomixClusterClientService();
+        service.setNodes(configuration.getNodes().stream().map(Address::new).collect(Collectors.toList()));
+
+        ObjectHelper.ifNotEmpty(configuration.getId(), service::setId);
+        ObjectHelper.ifNotEmpty(configuration.getConfigurationUri(), service::setConfigurationUri);
+
+        return service;
+    }
+
+    // *****************************************
+    // Conditions
+    // *****************************************
+
+    public static class AutoConfigurationCondition extends AllNestedConditions {
+        public AutoConfigurationCondition() {
+            super(ConfigurationPhase.REGISTER_BEAN);
+        }
+
+        @ConditionalOnProperty(prefix = "camel.clustered.service.atomix", name = "enabled")
+        static class IfEnabled {
+        }
+
+
+        @ConditionalOnProperty(prefix = "camel.clustered.service.atomix", name = "mode")
+        static class WithMode {
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/platforms/spring-boot/components-starter/camel-atomix-starter/src/main/java/org/apache/camel/component/atomix/ha/springboot/AtomixClusterServiceConfiguration.java
----------------------------------------------------------------------
diff --git a/platforms/spring-boot/components-starter/camel-atomix-starter/src/main/java/org/apache/camel/component/atomix/ha/springboot/AtomixClusterServiceConfiguration.java b/platforms/spring-boot/components-starter/camel-atomix-starter/src/main/java/org/apache/camel/component/atomix/ha/springboot/AtomixClusterServiceConfiguration.java
new file mode 100644
index 0000000..27b281a
--- /dev/null
+++ b/platforms/spring-boot/components-starter/camel-atomix-starter/src/main/java/org/apache/camel/component/atomix/ha/springboot/AtomixClusterServiceConfiguration.java
@@ -0,0 +1,151 @@
+/**
+ * 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.component.atomix.ha.springboot;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import io.atomix.copycat.server.storage.StorageLevel;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties(prefix = "camel.clustered.service.atomix")
+public class AtomixClusterServiceConfiguration {
+    enum Mode {
+        node,
+        client
+    }
+
+    /**
+     * Sets if the atomix cluster service should be enabled or not, default is false.
+     */
+    private boolean enabled;
+
+    /**
+     * Sets the cluster mode.
+     */
+    private Mode mode;
+    /**
+     * The address of the nodes composing the cluster.
+     */
+    private Set<String> nodes = new HashSet<>();
+
+    /**
+     * The cluster id.
+     */
+    private String id;
+
+    /**
+     * The address of the node - node only.
+     */
+    private String address;
+
+    /**
+     * Sets if the local member should join groups as PersistentMember or not (node only).
+     */
+    private Boolean ephemeral;
+
+    /**
+     * The storage directory - node only.
+     */
+    private String storagePath;
+
+    /**
+     * The storage mode - node only.
+     */
+    private StorageLevel storageLevel = StorageLevel.MEMORY;
+
+    /**
+     * The Atomix configuration uri.
+     */
+    private String configurationUri;
+
+    // *********************************
+    // Properties
+    // *********************************
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    public Mode getMode() {
+        return mode;
+    }
+
+    public String getAddress() {
+        return address;
+    }
+
+    public void setAddress(String address) {
+        this.address = address;
+    }
+
+    public void setNodes(Set<String> nodes) {
+        this.nodes = nodes;
+    }
+
+    public void setMode(Mode mode) {
+        this.mode = mode;
+    }
+
+    public Set<String> getNodes() {
+        return nodes;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public Boolean isEphemeral() {
+        return ephemeral;
+    }
+
+    public void setEphemeral(Boolean ephemeral) {
+        this.ephemeral = ephemeral;
+    }
+
+    public String getStoragePath() {
+        return storagePath;
+    }
+
+    public void setStoragePath(String storagePath) {
+        this.storagePath = storagePath;
+    }
+
+    public StorageLevel getStorageLevel() {
+        return storageLevel;
+    }
+
+    public void setStorageLevel(StorageLevel storageLevel) {
+        this.storageLevel = storageLevel;
+    }
+
+    public String getConfigurationUri() {
+        return configurationUri;
+    }
+
+    public void setConfigurationUri(String configurationUri) {
+        this.configurationUri = configurationUri;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/platforms/spring-boot/components-starter/camel-atomix-starter/src/main/java/org/apache/camel/component/atomix/springboot/StringToAddressConverter.java
----------------------------------------------------------------------
diff --git a/platforms/spring-boot/components-starter/camel-atomix-starter/src/main/java/org/apache/camel/component/atomix/springboot/StringToAddressConverter.java b/platforms/spring-boot/components-starter/camel-atomix-starter/src/main/java/org/apache/camel/component/atomix/springboot/StringToAddressConverter.java
new file mode 100644
index 0000000..3abf63c
--- /dev/null
+++ b/platforms/spring-boot/components-starter/camel-atomix-starter/src/main/java/org/apache/camel/component/atomix/springboot/StringToAddressConverter.java
@@ -0,0 +1,29 @@
+/**
+ * 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.component.atomix.springboot;
+
+import io.atomix.catalyst.transport.Address;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.stereotype.Component;
+
+@Component
+public class StringToAddressConverter implements Converter<String, Address> {
+    @Override
+    public Address convert(String source) {
+        return new Address(source);
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/platforms/spring-boot/components-starter/camel-atomix-starter/src/main/resources/META-INF/spring.factories
----------------------------------------------------------------------
diff --git a/platforms/spring-boot/components-starter/camel-atomix-starter/src/main/resources/META-INF/spring.factories b/platforms/spring-boot/components-starter/camel-atomix-starter/src/main/resources/META-INF/spring.factories
index c8186fd..ae14d13 100644
--- a/platforms/spring-boot/components-starter/camel-atomix-starter/src/main/resources/META-INF/spring.factories
+++ b/platforms/spring-boot/components-starter/camel-atomix-starter/src/main/resources/META-INF/spring.factories
@@ -16,18 +16,19 @@
 ## ---------------------------------------------------------------------------
 
 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
-org.apache.camel.component.atomix.client.map.springboot.AtomixClientMapComponentAutoConfiguration,\
-org.apache.camel.component.atomix.client.multimap.springboot.AtomixClientMultiMapComponentAutoConfiguration,\
-org.apache.camel.component.atomix.client.set.springboot.AtomixClientSetComponentAutoConfiguration,\
-org.apache.camel.component.atomix.client.value.springboot.AtomixClientValueComponentAutoConfiguration,\
-org.apache.camel.component.atomix.client.queue.springboot.AtomixClientQueueComponentAutoConfiguration,\
-org.apache.camel.component.atomix.client.messaging.springboot.AtomixClientMessagingComponentAutoConfiguration,\
+org.apache.camel.component.atomix.client.map.springboot.AtomixMapComponentAutoConfiguration,\
+org.apache.camel.component.atomix.client.multimap.springboot.AtomixMultiMapComponentAutoConfiguration,\
+org.apache.camel.component.atomix.client.set.springboot.AtomixSetComponentAutoConfiguration,\
+org.apache.camel.component.atomix.client.value.springboot.AtomixValueComponentAutoConfiguration,\
+org.apache.camel.component.atomix.client.queue.springboot.AtomixQueueComponentAutoConfiguration,\
+org.apache.camel.component.atomix.client.messaging.springboot.AtomixMessagingComponentAutoConfiguration,\
 org.apache.camel.component.atomix.client.set.springboot.AtomixSetComponentAutoConfiguration,\
 org.apache.camel.component.atomix.client.queue.springboot.AtomixQueueComponentAutoConfiguration,\
 org.apache.camel.component.atomix.client.map.springboot.AtomixMapComponentAutoConfiguration,\
 org.apache.camel.component.atomix.client.multimap.springboot.AtomixMultiMapComponentAutoConfiguration,\
 org.apache.camel.component.atomix.client.value.springboot.AtomixValueComponentAutoConfiguration,\
-org.apache.camel.component.atomix.client.messaging.springboot.AtomixMessagingComponentAutoConfiguration
+org.apache.camel.component.atomix.client.messaging.springboot.AtomixMessagingComponentAutoConfiguration,\
+org.apache.camel.component.atomix.ha.springboot.AtomixClusterServiceAutoConfiguration
 
 
 


[2/2] camel git commit: CAMEL-11659: Create a ClusteredRouteController

Posted by lb...@apache.org.
CAMEL-11659: Create a ClusteredRouteController


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/d043c7d0
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/d043c7d0
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/d043c7d0

Branch: refs/heads/master
Commit: d043c7d0a7315c6c6a7971894f4574361c93289d
Parents: f8ddd23
Author: lburgazzoli <lb...@gmail.com>
Authored: Thu Aug 10 17:27:49 2017 +0200
Committer: lburgazzoli <lb...@gmail.com>
Committed: Fri Aug 11 14:54:43 2017 +0200

----------------------------------------------------------------------
 .../impl/ha/ClusteredRouteConfiguration.java    |  54 ++++
 .../camel/impl/ha/ClusteredRouteController.java | 314 +++++++++++++++++++
 .../camel/impl/ha/ClusteredRouteFilter.java     |  32 ++
 .../camel/impl/ha/ClusteredRouteFilters.java    |  59 ++++
 .../camel/impl/ha/ClusteredRoutePolicy.java     |  76 ++++-
 .../atomix/ha/AtomixClusterClientService.java   |  10 +
 .../atomix/ha/AtomixClusterService.java         |  10 +
 .../component/atomix/ha/AtomixClusterView.java  |  18 +-
 ...usteredRouteControllerAutoConfiguration.java | 101 ++++++
 .../ClusteredRouteControllerConfiguration.java  | 147 +++++++++
 .../main/resources/META-INF/spring.factories    |   1 +
 examples/README.adoc                            |   6 +-
 .../cluster-bootstrap/pom.xml                   |  77 +++++
 .../examples/cluster/ClusterBootstrap.java      |  41 +++
 .../src/main/resources/log4j2.xml               |  18 ++
 .../cluster-node/pom.xml                        | 100 ++++++
 .../camel/examples/cluster/ClusterNode.java     |  36 +++
 .../cluster/ClusterNodeConfiguration.java       |  68 ++++
 .../src/main/resources/application.properties   |  31 ++
 .../pom.xml                                     |  68 ++++
 .../readme.adoc                                 |  25 ++
 .../pom.xml                                     | 154 ---------
 .../readme.adoc                                 |  51 ---
 .../src/main/java/sample/camel/Application.java |  37 ---
 .../java/sample/camel/ApplicationRoutes.java    |  52 ---
 .../src/main/resources/application.properties   |  46 ---
 .../pom.xml                                     | 154 +++++++++
 .../readme.adoc                                 |  51 +++
 .../src/main/java/sample/camel/Application.java |  37 +++
 .../java/sample/camel/ApplicationRoutes.java    |  52 +++
 .../src/main/resources/application.properties   |  46 +++
 examples/pom.xml                                |   3 +-
 .../AtomixClusterServiceAutoConfiguration.java  |  96 ++++++
 .../AtomixClusterServiceConfiguration.java      | 151 +++++++++
 .../springboot/StringToAddressConverter.java    |  29 ++
 .../main/resources/META-INF/spring.factories    |  15 +-
 36 files changed, 1903 insertions(+), 363 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/camel-core/src/main/java/org/apache/camel/impl/ha/ClusteredRouteConfiguration.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/impl/ha/ClusteredRouteConfiguration.java b/camel-core/src/main/java/org/apache/camel/impl/ha/ClusteredRouteConfiguration.java
new file mode 100644
index 0000000..9cdfaeb
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/impl/ha/ClusteredRouteConfiguration.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.impl.ha;
+
+import java.time.Duration;
+
+import org.apache.camel.util.ObjectHelper;
+
+public class ClusteredRouteConfiguration implements Cloneable {
+    private String namespace;
+    private Duration initialDelay;
+
+    public String getNamespace() {
+        return namespace;
+    }
+
+    public void setNamespace(String namespace) {
+        this.namespace = namespace;
+    }
+
+    public Duration getInitialDelay() {
+        return initialDelay;
+    }
+
+    public void setInitialDelay(Duration initialDelay) {
+        this.initialDelay = initialDelay;
+    }
+
+    // ****************************************
+    // Copy
+    // ****************************************
+
+    public ClusteredRouteConfiguration copy() {
+        try {
+            return (ClusteredRouteConfiguration) super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw ObjectHelper.wrapRuntimeCamelException(e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/camel-core/src/main/java/org/apache/camel/impl/ha/ClusteredRouteController.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/impl/ha/ClusteredRouteController.java b/camel-core/src/main/java/org/apache/camel/impl/ha/ClusteredRouteController.java
new file mode 100644
index 0000000..2a1849e
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/impl/ha/ClusteredRouteController.java
@@ -0,0 +1,314 @@
+/**
+ * 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.ha;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Route;
+import org.apache.camel.ha.CamelClusterService;
+import org.apache.camel.impl.DefaultRouteController;
+import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.spi.RoutePolicy;
+import org.apache.camel.spi.RoutePolicyFactory;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.ServiceHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ClusteredRouteController extends DefaultRouteController {
+    private static final Logger LOGGER = LoggerFactory.getLogger(ClusteredRouteController.class);
+
+    private final Set<String> routes;
+    private final ConcurrentMap<String, ClusteredRouteConfiguration> configurations;
+    private final List<ClusteredRouteFilter> filters;
+    private final PolicyFactory policyFactory;
+    private final ClusteredRouteConfiguration defaultConfiguration;
+    private CamelClusterService clusterService;
+
+    public ClusteredRouteController() {
+        this.routes = new CopyOnWriteArraySet<>();
+        this.configurations = new ConcurrentHashMap<>();
+        this.filters = new ArrayList<>();
+        this.policyFactory = new PolicyFactory();
+
+        this.defaultConfiguration = new ClusteredRouteConfiguration();
+        this.defaultConfiguration.setInitialDelay(Duration.ofMillis(0));
+    }
+
+    // *******************************
+    // Properties.
+    // *******************************
+
+    /**
+     * Add a filter used to to filter cluster aware routes.
+     */
+    public void addFilter(ClusteredRouteFilter filter) {
+        this.filters.add(filter);
+    }
+
+    /**
+     * Sets the filters used to filter cluster aware routes.
+     */
+    public void setFilters(Collection<ClusteredRouteFilter> filters) {
+        this.filters.clear();
+        this.filters.addAll(filters);
+    }
+
+    public Collection<ClusteredRouteFilter> getFilters() {
+        return Collections.unmodifiableList(filters);
+    }
+
+    /**
+     * Add a configuration for the given route.
+     */
+    public void addRouteConfiguration(String routeId, ClusteredRouteConfiguration configuration) {
+        configurations.put(routeId, configuration);
+    }
+
+    /**
+     * Sets the configurations for the routes.
+     */
+    public void setRoutesConfiguration(Map<String, ClusteredRouteConfiguration> configurations) {
+        this.configurations.clear();
+        this.configurations.putAll(configurations);
+    }
+
+    public Map<String, ClusteredRouteConfiguration> getRoutesConfiguration() {
+        return Collections.unmodifiableMap(this.configurations);
+    }
+
+    public Duration getInitialDelay() {
+        return this.defaultConfiguration.getInitialDelay();
+    }
+
+    /**
+     * Set the amount of time the route controller should wait before to start
+     * the routes after the camel context is started.
+     *
+     * @param initialDelay the initial delay.
+     */
+    public void setInitialDelay(Duration initialDelay) {
+        this.defaultConfiguration.setInitialDelay(initialDelay);
+    }
+
+    public String getNamespace() {
+        return this.defaultConfiguration.getNamespace();
+    }
+
+    /**
+     * Set the default namespace.
+     */
+    public void setNamespace(String namespace) {
+        this.defaultConfiguration.setNamespace(namespace);
+    }
+
+    public CamelClusterService getClusterService() {
+        return clusterService;
+    }
+
+    public void setClusterService(CamelClusterService clusterService) {
+        // prevent replacing the service
+        if (this.clusterService != null && this.clusterService != clusterService) {
+            throw new IllegalArgumentException("CamelClusterService is already set");
+        }
+
+        this.clusterService = clusterService;
+    }
+
+    // *******************************
+    //
+    // *******************************
+
+    @Override
+    public Collection<Route> getControlledRoutes() {
+        return this.routes.stream()
+            .map(getCamelContext()::getRoute)
+            .filter(Objects::nonNull)
+            .collect(Collectors.toList());
+    }
+
+    @Override
+    public void doStart() throws Exception {
+        final CamelContext context = getCamelContext();
+
+        // Parameters validation
+        ObjectHelper.notNull(defaultConfiguration.getNamespace(), "Namespace");
+        ObjectHelper.notNull(defaultConfiguration.getInitialDelay(), "initialDelay");
+        ObjectHelper.notNull(context, "camelContext");
+
+        if (clusterService == null) {
+            // Finally try to grab it from the camel context.
+            clusterService = context.hasService(CamelClusterService.class);
+        }
+
+        ObjectHelper.notNull(clusterService, "clusterService");
+
+        if (!ServiceHelper.isStarted(clusterService)) {
+            // Start the cluster service if not yet started.
+            clusterService.start();
+        }
+
+        super.doStart();
+    }
+
+    @Override
+    public void doStop() throws Exception {
+        if (ServiceHelper.isStarted(clusterService)) {
+            // Stop the cluster service.
+            clusterService.stop();
+        }
+    }
+
+    @Override
+    public void setCamelContext(CamelContext camelContext) {
+        if (!camelContext.getRoutePolicyFactories().contains(this.policyFactory)) {
+            camelContext.addRoutePolicyFactory(this.policyFactory);
+        }
+
+        super.setCamelContext(camelContext);
+    }
+
+    // *******************************
+    // Route operations are disabled
+    // *******************************
+
+    @Override
+    public void startRoute(String routeId) throws Exception {
+        failIfClustered(routeId);
+
+        // Delegate to default impl.
+        super.startRoute(routeId);
+    }
+
+    @Override
+    public void stopRoute(String routeId) throws Exception {
+        failIfClustered(routeId);
+
+        // Delegate to default impl.
+        super.stopRoute(routeId);
+    }
+
+    @Override
+    public void stopRoute(String routeId, long timeout, TimeUnit timeUnit) throws Exception {
+        failIfClustered(routeId);
+
+        // Delegate to default impl.
+        super.stopRoute(routeId, timeout, timeUnit);
+    }
+
+    @Override
+    public boolean stopRoute(String routeId, long timeout, TimeUnit timeUnit, boolean abortAfterTimeout) throws Exception {
+        failIfClustered(routeId);
+
+        // Delegate to default impl.
+        return super.stopRoute(routeId, timeout, timeUnit, abortAfterTimeout);
+    }
+
+    @Override
+    public void suspendRoute(String routeId) throws Exception {
+        failIfClustered(routeId);
+
+        // Delegate to default impl.
+        super.suspendRoute(routeId);
+    }
+
+    @Override
+    public void suspendRoute(String routeId, long timeout, TimeUnit timeUnit) throws Exception {
+        failIfClustered(routeId);
+
+        // Delegate to default impl.
+        super.suspendRoute(routeId, timeout, timeUnit);
+    }
+
+    @Override
+    public void resumeRoute(String routeId) throws Exception {
+        failIfClustered(routeId);
+
+        // Delegate to default impl.
+        super.resumeRoute(routeId);
+    }
+
+    // *******************************
+    // Helpers
+    // *******************************
+
+    private void failIfClustered(String routeId) {
+        // Can't perform action on routes managed by this controller as they
+        // are clustered and they may be part of the same view.
+        if (routes.contains(routeId)) {
+            throw new UnsupportedOperationException(
+                "Operation not supported as route " + routeId + " is clustered"
+            );
+        }
+    }
+
+    // *******************************
+    // Factories
+    // *******************************
+
+    private final class PolicyFactory implements RoutePolicyFactory {
+        @Override
+        public RoutePolicy createRoutePolicy(CamelContext camelContext, String routeId, RouteDefinition route) {
+            // All the filter have to be match to include the route in the
+            // clustering set-up
+            if (filters.stream().allMatch(filter -> filter.test(camelContext, routeId, route))) {
+
+                if (ObjectHelper.isNotEmpty(route.getRoutePolicies())) {
+                    // Check if the route is already configured with a clustered
+                    // route policy, in that case exclude it.
+                    if (route.getRoutePolicies().stream().anyMatch(ClusteredRoutePolicy.class::isInstance)) {
+                        LOGGER.debug("Route '{}' has a ClusteredRoutePolicy already set-up", routeId);
+                        return null;
+                    }
+                }
+
+                try {
+                    final ClusteredRouteConfiguration configuration = configurations.getOrDefault(routeId, defaultConfiguration);
+                    final String namespace = ObjectHelper.supplyIfEmpty(configuration.getNamespace(), defaultConfiguration::getNamespace);
+                    final Duration initialDelay = ObjectHelper.supplyIfEmpty(configuration.getInitialDelay(), defaultConfiguration::getInitialDelay);
+
+                    ClusteredRoutePolicy policy = new ClusteredRoutePolicy(clusterService.getView(namespace));
+                    policy.setCamelContext(getCamelContext());
+                    policy.setInitialDelay(initialDelay);
+
+                    LOGGER.debug("Attaching route '{}' to namespace '{}'", routeId, namespace);
+
+                    routes.add(routeId);
+
+                    return policy;
+                } catch (Exception e) {
+                    throw ObjectHelper.wrapRuntimeCamelException(e);
+                }
+            }
+
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/camel-core/src/main/java/org/apache/camel/impl/ha/ClusteredRouteFilter.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/impl/ha/ClusteredRouteFilter.java b/camel-core/src/main/java/org/apache/camel/impl/ha/ClusteredRouteFilter.java
new file mode 100644
index 0000000..a6d3c01
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/impl/ha/ClusteredRouteFilter.java
@@ -0,0 +1,32 @@
+/**
+ * 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.ha;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.model.RouteDefinition;
+
+public interface ClusteredRouteFilter {
+    /**
+     * Test if the route should be clustered or not.
+     *
+     * @param camelContext  the camel context
+     * @param routeId the route id
+     * @param route the route definition
+     * @return true if the route should be included
+     */
+    boolean test(CamelContext camelContext, String routeId, RouteDefinition route);
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/camel-core/src/main/java/org/apache/camel/impl/ha/ClusteredRouteFilters.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/impl/ha/ClusteredRouteFilters.java b/camel-core/src/main/java/org/apache/camel/impl/ha/ClusteredRouteFilters.java
new file mode 100644
index 0000000..ccda0e1
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/impl/ha/ClusteredRouteFilters.java
@@ -0,0 +1,59 @@
+/**
+ * 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.ha;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.util.ObjectHelper;
+
+public final class ClusteredRouteFilters {
+    private ClusteredRouteFilters() {
+    }
+
+    public static final class IsAutoStartup implements ClusteredRouteFilter {
+        @Override
+        public boolean test(CamelContext camelContext, String routeId, RouteDefinition route) {
+            try {
+                return route.isAutoStartup(camelContext);
+            } catch (Exception e) {
+                throw ObjectHelper.wrapRuntimeCamelException(e);
+            }
+        }
+    }
+
+    public static final class BlackList implements ClusteredRouteFilter {
+        private final Set<String> names;
+
+        public BlackList(String name) {
+            this(Collections.singletonList(name));
+        }
+
+        public BlackList(Collection<String> names) {
+            this.names = new HashSet<>(names);
+        }
+
+        @Override
+        public boolean test(CamelContext camelContext, String routeId, RouteDefinition route) {
+            return !names.contains(routeId);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/camel-core/src/main/java/org/apache/camel/impl/ha/ClusteredRoutePolicy.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/impl/ha/ClusteredRoutePolicy.java b/camel-core/src/main/java/org/apache/camel/impl/ha/ClusteredRoutePolicy.java
index 542b15b..e85b3bf 100644
--- a/camel-core/src/main/java/org/apache/camel/impl/ha/ClusteredRoutePolicy.java
+++ b/camel-core/src/main/java/org/apache/camel/impl/ha/ClusteredRoutePolicy.java
@@ -16,14 +16,19 @@
  */
 package org.apache.camel.impl.ha;
 
+import java.time.Duration;
 import java.util.EventObject;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.CamelContextAware;
 import org.apache.camel.Route;
+import org.apache.camel.ServiceStatus;
 import org.apache.camel.StartupListener;
 import org.apache.camel.api.management.ManagedAttribute;
 import org.apache.camel.api.management.ManagedResource;
@@ -39,7 +44,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @ManagedResource(description = "Clustered Route policy using")
-public final class ClusteredRoutePolicy extends RoutePolicySupport implements CamelContextAware {
+public class ClusteredRoutePolicy extends RoutePolicySupport implements CamelContextAware {
     private static final Logger LOGGER = LoggerFactory.getLogger(ClusteredRoutePolicy.class);
 
     private final AtomicBoolean leader;
@@ -50,6 +55,8 @@ public final class ClusteredRoutePolicy extends RoutePolicySupport implements Ca
     private final CamelClusterEventListener.Leadership leadershipEventListener;
     private final CamelContextStartupListener listener;
     private final AtomicBoolean contextStarted;
+    private Duration initialDelay;
+    private ScheduledExecutorService executorService;
 
     private CamelContext camelContext;
 
@@ -61,6 +68,7 @@ public final class ClusteredRoutePolicy extends RoutePolicySupport implements Ca
         this.startedRoutes = new HashSet<>();
         this.leader = new AtomicBoolean(false);
         this.contextStarted = new AtomicBoolean(false);
+        this.initialDelay = Duration.ofMillis(0);
 
         try {
             this.listener = new CamelContextStartupListener();
@@ -74,6 +82,9 @@ public final class ClusteredRoutePolicy extends RoutePolicySupport implements Ca
         this.refCount = ReferenceCount.onRelease(() -> {
             if (camelContext != null) {
                 camelContext.getManagementStrategy().removeEventNotifier(listener);
+                if (executorService != null) {
+                    camelContext.getExecutorServiceManager().shutdownNow(executorService);
+                }
             }
 
             clusterView.removeEventListener(leadershipEventListener);
@@ -102,11 +113,24 @@ public final class ClusteredRoutePolicy extends RoutePolicySupport implements Ca
             this.camelContext = camelContext;
             this.camelContext.addStartupListener(this.listener);
             this.camelContext.getManagementStrategy().addEventNotifier(this.listener);
+            this.executorService = camelContext.getExecutorServiceManager().newSingleThreadScheduledExecutor(this, "ClusteredRoutePolicy");
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
     }
 
+    public Duration getInitialDelay() {
+        return initialDelay;
+    }
+
+    public void setInitialDelay(Duration initialDelay) {
+        this.initialDelay = initialDelay;
+    }
+
+    // ****************************************************
+    // life-cycle
+    // ****************************************************
+
     @Override
     public void onInit(Route route) {
         super.onInit(route);
@@ -121,7 +145,7 @@ public final class ClusteredRoutePolicy extends RoutePolicySupport implements Ca
     }
 
     @Override
-    public void doShutdown() {
+    public void doShutdown() throws Exception {
         this.refCount.release();
     }
 
@@ -159,11 +183,19 @@ public final class ClusteredRoutePolicy extends RoutePolicySupport implements Ca
     }
 
     private void doStartManagedRoutes() {
+        if (!isRunAllowed()) {
+            return;
+        }
+
         try {
             for (Route route : stoppedRoutes) {
-                LOGGER.debug("Starting route {}", route.getId());
-                camelContext.startRoute(route.getId());
-                startedRoutes.add(route);
+                ServiceStatus status = route.getRouteContext().getRoute().getStatus(getCamelContext());
+                if (status.isStartable()) {
+                    LOGGER.debug("Starting route '{}'", route.getId());
+                    camelContext.startRoute(route.getId());
+
+                    startedRoutes.add(route);
+                }
             }
 
             stoppedRoutes.removeAll(startedRoutes);
@@ -183,11 +215,19 @@ public final class ClusteredRoutePolicy extends RoutePolicySupport implements Ca
     }
 
     private void doStopManagedRoutes() {
+        if (!isRunAllowed()) {
+            return;
+        }
+
         try {
             for (Route route : startedRoutes) {
-                LOGGER.debug("Stopping route {}", route.getId());
-                stopRoute(route);
-                stoppedRoutes.add(route);
+                ServiceStatus status = route.getRouteContext().getRoute().getStatus(getCamelContext());
+                if (status.isStoppable()) {
+                    LOGGER.debug("Stopping route '{}'", route.getId());
+                    stopRoute(route);
+
+                    stoppedRoutes.add(route);
+                }
             }
 
             startedRoutes.removeAll(stoppedRoutes);
@@ -196,6 +236,16 @@ public final class ClusteredRoutePolicy extends RoutePolicySupport implements Ca
         }
     }
 
+    private void onCamelContextStarted() {
+        LOGGER.debug("Apply cluster policy (stopped-routes='{}', started-routes='{}')",
+            stoppedRoutes.stream().map(Route::getId).collect(Collectors.joining(",")),
+            startedRoutes.stream().map(Route::getId).collect(Collectors.joining(","))
+        );
+
+        clusterView.addEventListener(leadershipEventListener);
+        setLeader(clusterView.getLocalMember().isMaster());
+    }
+
     // ****************************************************
     // Event handling
     // ****************************************************
@@ -240,8 +290,14 @@ public final class ClusteredRoutePolicy extends RoutePolicySupport implements Ca
             // so start/stop of managed routes do not clash with CamelContext
             // startup
             if (contextStarted.compareAndSet(false, true)) {
-                clusterView.addEventListener(leadershipEventListener);
-                setLeader(clusterView.getLocalMember().isMaster());
+
+                // Eventually delay the startup of the routes a later time
+                if (initialDelay.toMillis() > 0) {
+                    LOGGER.debug("Policy will be effective in {}", initialDelay);
+                    executorService.schedule(ClusteredRoutePolicy.this::onCamelContextStarted, initialDelay.toMillis(), TimeUnit.MILLISECONDS);
+                } else {
+                    ClusteredRoutePolicy.this.onCamelContextStarted();
+                }
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/components/camel-atomix/src/main/java/org/apache/camel/component/atomix/ha/AtomixClusterClientService.java
----------------------------------------------------------------------
diff --git a/components/camel-atomix/src/main/java/org/apache/camel/component/atomix/ha/AtomixClusterClientService.java b/components/camel-atomix/src/main/java/org/apache/camel/component/atomix/ha/AtomixClusterClientService.java
index 6194f81..6df320f 100644
--- a/components/camel-atomix/src/main/java/org/apache/camel/component/atomix/ha/AtomixClusterClientService.java
+++ b/components/camel-atomix/src/main/java/org/apache/camel/component/atomix/ha/AtomixClusterClientService.java
@@ -113,6 +113,16 @@ public final class AtomixClusterClientService extends AbstractCamelClusterServic
     }
 
     @Override
+    protected void doStop() throws Exception {
+        super.doStop();
+
+        if (atomix != null) {
+            LOGGER.debug("Shutdown atomix client {}", atomix);
+            atomix.close().join();
+        }
+    }
+
+    @Override
     protected AtomixClusterView createView(String namespace) throws Exception {
         return new AtomixClusterView(this, namespace, getOrCreateClient(), configuration);
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/components/camel-atomix/src/main/java/org/apache/camel/component/atomix/ha/AtomixClusterService.java
----------------------------------------------------------------------
diff --git a/components/camel-atomix/src/main/java/org/apache/camel/component/atomix/ha/AtomixClusterService.java b/components/camel-atomix/src/main/java/org/apache/camel/component/atomix/ha/AtomixClusterService.java
index 3faf79e..b2315ae 100644
--- a/components/camel-atomix/src/main/java/org/apache/camel/component/atomix/ha/AtomixClusterService.java
+++ b/components/camel-atomix/src/main/java/org/apache/camel/component/atomix/ha/AtomixClusterService.java
@@ -143,6 +143,16 @@ public final class AtomixClusterService extends AbstractCamelClusterService<Atom
     }
 
     @Override
+    protected void doStop() throws Exception {
+        super.doStop();
+
+        if (atomix != null) {
+            LOGGER.debug("Shutdown atomix replica {}", atomix);
+            atomix.shutdown().join();
+        }
+    }
+
+    @Override
     protected AtomixClusterView createView(String namespace) throws Exception {
         return new AtomixClusterView(this, namespace, getOrCreateReplica(), configuration);
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/components/camel-atomix/src/main/java/org/apache/camel/component/atomix/ha/AtomixClusterView.java
----------------------------------------------------------------------
diff --git a/components/camel-atomix/src/main/java/org/apache/camel/component/atomix/ha/AtomixClusterView.java b/components/camel-atomix/src/main/java/org/apache/camel/component/atomix/ha/AtomixClusterView.java
index 4f02a85..4be80a1 100644
--- a/components/camel-atomix/src/main/java/org/apache/camel/component/atomix/ha/AtomixClusterView.java
+++ b/components/camel-atomix/src/main/java/org/apache/camel/component/atomix/ha/AtomixClusterView.java
@@ -93,13 +93,25 @@ final class AtomixClusterView extends AbstractCamelClusterView {
             ).get();
 
             LOGGER.debug("Listen election events");
-            group.election().onElection(term -> fireLeadershipChangedEvent(new AtomixClusterMember(term.leader())));
+            group.election().onElection(term -> {
+                if (isRunAllowed()) {
+                    fireLeadershipChangedEvent(new AtomixClusterMember(term.leader()));
+                }
+            });
 
             LOGGER.debug("Listen join events");
-            group.onJoin(member -> fireMemberAddedEvent(new AtomixClusterMember(member)));
+            group.onJoin(member -> {
+                if (isRunAllowed()) {
+                    fireMemberAddedEvent(new AtomixClusterMember(member));
+                }
+            });
 
             LOGGER.debug("Listen leave events");
-            group.onLeave(member -> fireMemberRemovedEvent(new AtomixClusterMember(member)));
+            group.onLeave(member -> {
+                if (isRunAllowed()) {
+                    fireMemberRemovedEvent(new AtomixClusterMember(member));
+                }
+            });
 
             LOGGER.debug("Join group {}", getNamespace());
             localMember.join();

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/ha/ClusteredRouteControllerAutoConfiguration.java
----------------------------------------------------------------------
diff --git a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/ha/ClusteredRouteControllerAutoConfiguration.java b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/ha/ClusteredRouteControllerAutoConfiguration.java
new file mode 100644
index 0000000..29be993
--- /dev/null
+++ b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/ha/ClusteredRouteControllerAutoConfiguration.java
@@ -0,0 +1,101 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.spring.boot.ha;
+
+import java.time.Duration;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.camel.converter.TimePatternConverter;
+import org.apache.camel.ha.CamelClusterService;
+import org.apache.camel.impl.ha.ClusteredRouteConfiguration;
+import org.apache.camel.impl.ha.ClusteredRouteController;
+import org.apache.camel.impl.ha.ClusteredRouteFilter;
+import org.apache.camel.impl.ha.ClusteredRouteFilters;
+import org.apache.camel.spi.RouteController;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.util.ObjectHelper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Scope;
+
+@Configuration
+@AutoConfigureBefore(CamelAutoConfiguration.class)
+@ConditionalOnProperty(prefix = "camel.clustered.controller", name = "enabled")
+@EnableConfigurationProperties(ClusteredRouteControllerConfiguration.class)
+public class ClusteredRouteControllerAutoConfiguration {
+    @Autowired
+    private ClusteredRouteControllerConfiguration configuration;
+    @Autowired(required = false)
+    private List<ClusteredRouteFilter> filters = Collections.emptyList();
+
+    @Bean
+    @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
+    @ConditionalOnMissingBean
+    @ConditionalOnBean(CamelClusterService.class)
+    public RouteController routeController() {
+        ClusteredRouteController controller = new ClusteredRouteController();
+        controller.setNamespace(configuration.getNamespace());
+
+        Optional.ofNullable(configuration.getInitialDelay())
+            .map(TimePatternConverter::toMilliSeconds)
+            .map(Duration::ofMillis)
+            .ifPresent(controller::setInitialDelay);
+
+        controller.setFilters(filters);
+        controller.addFilter(new ClusteredRouteFilters.IsAutoStartup());
+
+        if (ObjectHelper.isEmpty(configuration.getClusterService())) {
+            controller.setClusterService(configuration.getClusterService());
+        }
+
+        for (Map.Entry<String, ClusteredRouteControllerConfiguration.RouteConfiguration> entry: configuration.getRoutes().entrySet()) {
+            final String routeId = entry.getKey();
+            final ClusteredRouteControllerConfiguration.RouteConfiguration conf = entry.getValue();
+
+            if (conf.isClustered()) {
+                ClusteredRouteConfiguration routeConfiguration = new ClusteredRouteConfiguration();
+
+                routeConfiguration.setNamespace(
+                    Optional.ofNullable(conf.getNamespace())
+                        .orElseGet(controller::getNamespace)
+                );
+                routeConfiguration.setInitialDelay(
+                    Optional.ofNullable(conf.getInitialDelay())
+                        .map(TimePatternConverter::toMilliSeconds)
+                        .map(Duration::ofMillis)
+                        .orElseGet(controller::getInitialDelay)
+                );
+
+                controller.addRouteConfiguration(routeId, routeConfiguration);
+            } else {
+                controller.addFilter(new ClusteredRouteFilters.BlackList(routeId));
+            }
+        }
+
+        return controller;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/ha/ClusteredRouteControllerConfiguration.java
----------------------------------------------------------------------
diff --git a/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/ha/ClusteredRouteControllerConfiguration.java b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/ha/ClusteredRouteControllerConfiguration.java
new file mode 100644
index 0000000..946c20d
--- /dev/null
+++ b/components/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/ha/ClusteredRouteControllerConfiguration.java
@@ -0,0 +1,147 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.camel.spring.boot.ha;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.camel.ha.CamelClusterService;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties(prefix = "camel.clustered.controller")
+public class ClusteredRouteControllerConfiguration {
+    /**
+     * Global option to enable/disable this ${@link org.apache.camel.spi.RouteController}, default is false.
+     */
+    private boolean enabled;
+
+    /**
+     * Set the amount of time the route controller should wait before to start
+     * the routes after the camel context is started or after the route is
+     * initialized if the route is created after the camel context is started.
+     */
+    private String initialDelay;
+
+    /**
+     * The default namespace.
+     */
+    private String namespace;
+
+    /**
+     * The reference to a cluster service.
+     */
+    private String clusterServiceRef;
+
+    /**
+     * The cluster service.
+     */
+    private CamelClusterService clusterService;
+
+    /**
+     * Routes configuration.
+     */
+    private Map<String, RouteConfiguration> routes = new HashMap<>();
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    public String getInitialDelay() {
+        return initialDelay;
+    }
+
+    public void setInitialDelay(String initialDelay) {
+        this.initialDelay = initialDelay;
+    }
+
+    public String getNamespace() {
+        return namespace;
+    }
+
+    public void setNamespace(String namespace) {
+        this.namespace = namespace;
+    }
+
+    public Map<String, RouteConfiguration> getRoutes() {
+        return routes;
+    }
+
+    public void setRoutes(Map<String, RouteConfiguration> routes) {
+        this.routes = routes;
+    }
+
+    public CamelClusterService getClusterService() {
+        return clusterService;
+    }
+
+    public void setClusterService(CamelClusterService clusterService) {
+        this.clusterService = clusterService;
+    }
+
+    // *****************************************
+    // Configuration Classes
+    // *****************************************
+
+    public static class RouteConfiguration {
+        /**
+         * Control if the route should be clustered or not, default is true.
+         */
+        private boolean clustered = true;
+
+        /**
+         * Set the amount of time the route controller should wait before to start
+         * the routes after the camel context is started or after the route is
+         * initialized if the route is created after the camel context is started.
+         */
+        private String initialDelay;
+
+        /**
+         * The default namespace.
+         */
+        private String namespace;
+
+
+        public boolean isClustered() {
+            return clustered;
+        }
+
+        public void setClustered(boolean clustered) {
+            this.clustered = clustered;
+        }
+
+        public String getInitialDelay() {
+            return initialDelay;
+        }
+
+        public void setInitialDelay(String initialDelay) {
+            this.initialDelay = initialDelay;
+        }
+
+        public String getNamespace() {
+            return namespace;
+        }
+
+        public void setNamespace(String namespace) {
+            this.namespace = namespace;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/components/camel-spring-boot/src/main/resources/META-INF/spring.factories
----------------------------------------------------------------------
diff --git a/components/camel-spring-boot/src/main/resources/META-INF/spring.factories b/components/camel-spring-boot/src/main/resources/META-INF/spring.factories
index ac8c58e..9686cc3 100644
--- a/components/camel-spring-boot/src/main/resources/META-INF/spring.factories
+++ b/components/camel-spring-boot/src/main/resources/META-INF/spring.factories
@@ -26,4 +26,5 @@ org.apache.camel.spring.boot.cloud.CamelCloudServiceCallConfigurationAutoConfigu
 org.apache.camel.spring.boot.cloud.CamelCloudServiceDiscoveryAutoConfiguration,\
 org.apache.camel.spring.boot.cloud.CamelCloudServiceFilterAutoConfiguration,\
 org.apache.camel.spring.boot.cloud.CamelCloudServiceChooserAutoConfiguration,\
+org.apache.camel.spring.boot.ha.ClusteredRouteControllerAutoConfiguration,\
 org.apache.camel.spring.boot.security.CamelSSLAutoConfiguration

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/examples/README.adoc
----------------------------------------------------------------------
diff --git a/examples/README.adoc b/examples/README.adoc
index bb2568c..1f10378 100644
--- a/examples/README.adoc
+++ b/examples/README.adoc
@@ -11,7 +11,7 @@ View the individual example READMEs for details.
 ### Examples
 
 // examples: START
-Number of Examples: 96 (8 deprecated)
+Number of Examples: 97 (8 deprecated)
 
 [width="100%",cols="4,2,4",options="header"]
 |=======================================================================
@@ -44,11 +44,13 @@ Number of Examples: 96 (8 deprecated)
 
 | link:camel-example-spring-boot/readme.adoc[Spring Boot] (camel-example-spring-boot) | Beginner | An example showing how to work with Camel and Spring Boot
 
+| link:camel-example-spring-boot-clustered-route-controller/readme.adoc[Spring Boot Clustered Route Controller] (camel-example-spring-boot-clustered-route-controller) | Beginner | An example showing how to work with Camel's Clustered Route Controller and Spring Boot
+
 | link:camel-example-spring-boot-live-reload/readme.adoc[Spring Boot Live Reload] (camel-example-spring-boot-live-reload) | Beginner | An example showing how to use the live reload feature of Spring Boot with Camel
 
 | link:camel-example-spring-boot-pojo/README.adoc[Spring Boot Pojo] (camel-example-spring-boot-pojo) | Beginner | An example showing how to work with Camel POJO routing with Spring Boot
 
-| link:camel-example-spring-boot-routecontroller/readme.adoc[Spring Boot Routecontroller] (camel-example-spring-boot-routecontroller) | Beginner | An example showing how to work with Camel Route Controller and Spring Boot
+| link:camel-example-spring-boot-supervising-route-controller/readme.adoc[Spring Boot Supervising Route Controller] (camel-example-spring-boot-supervising-route-controller) | Beginner | An example showing how to work with Camel's Supervising Route Controller and Spring Boot
 
 | link:camel-example-spring-javaconfig/README.md[Spring Java Config] (camel-example-spring-javaconfig) | Beginner | An example showing how to work with Camel and Spring Java Config
 

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/examples/camel-example-spring-boot-clustered-route-controller/cluster-bootstrap/pom.xml
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot-clustered-route-controller/cluster-bootstrap/pom.xml b/examples/camel-example-spring-boot-clustered-route-controller/cluster-bootstrap/pom.xml
new file mode 100644
index 0000000..f86fb3b
--- /dev/null
+++ b/examples/camel-example-spring-boot-clustered-route-controller/cluster-bootstrap/pom.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.camel.example</groupId>
+    <artifactId>camel-example-spring-boot-clustered-route-controller</artifactId>
+    <version>2.20.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>camel-example-spring-boot-clustered-route-controller-cluster-bootstrap</artifactId>
+  <name>Camel :: Example :: Spring Boot :: Clustered Route Controller :: Cluster Bootstrap</name>
+  <description>Bootstrap an Atomix Cluster (single node)</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>io.atomix</groupId>
+      <artifactId>atomix-all</artifactId>
+      <version>${atomix-version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-slf4j-impl</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>exec-maven-plugin</artifactId>
+        <version>1.6.0</version>
+        <executions>
+          <execution>
+            <goals>
+              <goal>java</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <includeProjectDependencies>true</includeProjectDependencies>
+          <mainClass>org.apache.camel.examples.cluster.ClusterBootstrap</mainClass>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/examples/camel-example-spring-boot-clustered-route-controller/cluster-bootstrap/src/main/java/org/apache/camel/examples/cluster/ClusterBootstrap.java
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot-clustered-route-controller/cluster-bootstrap/src/main/java/org/apache/camel/examples/cluster/ClusterBootstrap.java b/examples/camel-example-spring-boot-clustered-route-controller/cluster-bootstrap/src/main/java/org/apache/camel/examples/cluster/ClusterBootstrap.java
new file mode 100644
index 0000000..1115524
--- /dev/null
+++ b/examples/camel-example-spring-boot-clustered-route-controller/cluster-bootstrap/src/main/java/org/apache/camel/examples/cluster/ClusterBootstrap.java
@@ -0,0 +1,41 @@
+/**
+ * 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.examples.cluster;
+
+import io.atomix.AtomixReplica;
+import io.atomix.catalyst.transport.Address;
+import io.atomix.catalyst.transport.netty.NettyTransport;
+import io.atomix.copycat.server.storage.Storage;
+import io.atomix.copycat.server.storage.StorageLevel;
+
+public final class ClusterBootstrap {
+    private ClusterBootstrap() {
+    }
+
+    public static void main(String[] args) {
+        String address = System.getProperty("cluster.address", "127.0.0.1:8700");
+
+        AtomixReplica.builder(new Address(address))
+            .withTransport(new NettyTransport())
+            .withStorage(Storage.builder()
+                .withStorageLevel(StorageLevel.MEMORY)
+                .build())
+            .build()
+            .bootstrap()
+            .join();
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/examples/camel-example-spring-boot-clustered-route-controller/cluster-bootstrap/src/main/resources/log4j2.xml
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot-clustered-route-controller/cluster-bootstrap/src/main/resources/log4j2.xml b/examples/camel-example-spring-boot-clustered-route-controller/cluster-bootstrap/src/main/resources/log4j2.xml
new file mode 100644
index 0000000..06e8664
--- /dev/null
+++ b/examples/camel-example-spring-boot-clustered-route-controller/cluster-bootstrap/src/main/resources/log4j2.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="INFO">
+  <Appenders>
+    <Console name="Console" target="SYSTEM_OUT">
+      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
+    </Console>
+    <File name="File" fileName="target/cluster-bootstrap.log" immediateFlush="false" append="false">
+      <PatternLayout pattern="%d{yyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
+    </File>
+  </Appenders>
+  <Loggers>
+    <Logger name="io.atomix" level="DEBUG"/>
+
+    <Root level="INFO">
+      <AppenderRef ref="Console" />
+    </Root>
+  </Loggers>
+</Configuration>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/examples/camel-example-spring-boot-clustered-route-controller/cluster-node/pom.xml
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot-clustered-route-controller/cluster-node/pom.xml b/examples/camel-example-spring-boot-clustered-route-controller/cluster-node/pom.xml
new file mode 100644
index 0000000..c137031
--- /dev/null
+++ b/examples/camel-example-spring-boot-clustered-route-controller/cluster-node/pom.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.camel.example</groupId>
+    <artifactId>camel-example-spring-boot-clustered-route-controller</artifactId>
+    <version>2.20.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>camel-example-spring-boot-clustered-route-controller-cluster-node</artifactId>
+  <name>Camel :: Example :: Spring Boot :: Clustered Route Controller :: Cluster Node</name>
+  <description>Bootstrap an Camel Cluster Node</description>
+
+  <dependencyManagement>
+    <dependencies>
+      <!-- Spring Boot BOM -->
+      <dependency>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-dependencies</artifactId>
+        <version>${spring.boot-version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+      <!-- Camel BOM -->
+      <dependency>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-spring-boot-dependencies</artifactId>
+        <version>${project.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-undertow</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-actuator</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-spring-boot-starter</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-atomix-starter</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-maven-plugin</artifactId>
+        <version>${spring-boot-version}</version>
+        <executions>
+          <execution>
+            <goals>
+              <goal>repackage</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/examples/camel-example-spring-boot-clustered-route-controller/cluster-node/src/main/java/org/apache/camel/examples/cluster/ClusterNode.java
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot-clustered-route-controller/cluster-node/src/main/java/org/apache/camel/examples/cluster/ClusterNode.java b/examples/camel-example-spring-boot-clustered-route-controller/cluster-node/src/main/java/org/apache/camel/examples/cluster/ClusterNode.java
new file mode 100644
index 0000000..7228dc3
--- /dev/null
+++ b/examples/camel-example-spring-boot-clustered-route-controller/cluster-node/src/main/java/org/apache/camel/examples/cluster/ClusterNode.java
@@ -0,0 +1,36 @@
+/**
+ * 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.examples.cluster;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+//CHECKSTYLE:OFF
+/**
+ * A sample Spring Boot application that starts the Camel routes.
+ */
+@SpringBootApplication
+public class ClusterNode {
+
+    /**
+     * A main method to start this application.
+     */
+    public static void main(String[] args) {
+        SpringApplication.run(ClusterNode.class, args);
+    }
+}
+//CHECKSTYLE:ON

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/examples/camel-example-spring-boot-clustered-route-controller/cluster-node/src/main/java/org/apache/camel/examples/cluster/ClusterNodeConfiguration.java
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot-clustered-route-controller/cluster-node/src/main/java/org/apache/camel/examples/cluster/ClusterNodeConfiguration.java b/examples/camel-example-spring-boot-clustered-route-controller/cluster-node/src/main/java/org/apache/camel/examples/cluster/ClusterNodeConfiguration.java
new file mode 100644
index 0000000..e7df7be
--- /dev/null
+++ b/examples/camel-example-spring-boot-clustered-route-controller/cluster-node/src/main/java/org/apache/camel/examples/cluster/ClusterNodeConfiguration.java
@@ -0,0 +1,68 @@
+/**
+ * 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.examples.cluster;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class ClusterNodeConfiguration {
+    private static final Logger LOGGER = LoggerFactory.getLogger(ClusterNodeConfiguration.class);
+
+    @Bean
+    public RouteBuilder routeBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                // This route is configured to be local (see application.properties)
+                // so it will be started regardless of the leadership status if
+                // this node.
+                from("timer:heartbeat?period=10s")
+                    .routeId("heartbeat")
+                    .log("HeartBeat route (timer) {{node.id}} ...");
+
+                // This route is configured to be clustered so it will be started
+                // by the controller only when this node is leader
+                from("timer:clustered?period=5s")
+                    .routeId("clustered")
+                    .log("Clustered route (timer) {{node.id}} ...");
+            }
+        };
+    }
+
+    /**
+     * A small hack to find out the a free port so you can run multiple instances
+     * of the example without having to manually set the property: server.port
+     */
+    @Bean
+    public EmbeddedServletContainerCustomizer containerCustomizer() {
+        return container -> {
+            try (ServerSocket socket = new ServerSocket(0)) {
+                LOGGER.debug("server.port: {}", socket.getLocalPort());
+                container.setPort(socket.getLocalPort());
+            } catch (IOException ignored) {
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/examples/camel-example-spring-boot-clustered-route-controller/cluster-node/src/main/resources/application.properties
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot-clustered-route-controller/cluster-node/src/main/resources/application.properties b/examples/camel-example-spring-boot-clustered-route-controller/cluster-node/src/main/resources/application.properties
new file mode 100644
index 0000000..e364efa
--- /dev/null
+++ b/examples/camel-example-spring-boot-clustered-route-controller/cluster-node/src/main/resources/application.properties
@@ -0,0 +1,31 @@
+
+debug = false
+
+logging.level.org.springframework = INFO
+logging.level.io.atomix = DEBUG
+logging.level.org.apache.camel.ha = DEBUG
+logging.level.org.apache.camel.impl.ha = DEBUG
+logging.level.org.apache.camel.component.atomix = DEBUG
+logging.level.org.apache.camel.examples.cluster = DEBUG
+
+endpoints.enabled = false
+endpoints.jmx.enabled = false
+endpoints.health.enabled = true
+
+management.port = -1
+
+node.id = ${random.uuid}
+
+camel.springboot.name = SampleClusteredRouteController
+camel.springboot.jmx-enabled = false
+
+camel.clustered.controller.enabled = true
+camel.clustered.controller.namespace = camel
+camel.clustered.controller.initial-delay = 5s
+
+camel.clustered.controller.routes.heartbeat.clustered = false
+
+camel.clustered.service.atomix.enabled = true
+camel.clustered.service.atomix.mode = client
+camel.clustered.service.atomix.nodes = localhost:8700
+camel.clustered.service.atomix.id = ${node.id}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/examples/camel-example-spring-boot-clustered-route-controller/pom.xml
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot-clustered-route-controller/pom.xml b/examples/camel-example-spring-boot-clustered-route-controller/pom.xml
new file mode 100644
index 0000000..c27d554
--- /dev/null
+++ b/examples/camel-example-spring-boot-clustered-route-controller/pom.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.camel.example</groupId>
+    <artifactId>examples</artifactId>
+    <version>2.20.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>camel-example-spring-boot-clustered-route-controller</artifactId>
+  <name>Camel :: Example :: Spring Boot :: Clustered Route Controller</name>
+  <description>An example showing how to work with Camel's Clustered Route Controller and Spring Boot</description>
+  <packaging>pom</packaging>
+
+  <modules>
+    <module>cluster-bootstrap</module>
+    <module>cluster-node</module>
+  </modules>
+  <properties>
+    <category>Beginner</category>
+
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    <spring.boot-version>${spring-boot-version}</spring.boot-version>
+  </properties>
+
+
+
+  <profiles>
+    <profile>
+      <id>jdk9-build</id>
+      <activation>
+        <jdk>9</jdk>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <configuration>
+              <argLine>--add-modules java.xml.bind --add-opens java.base/java.lang=ALL-UNNAMED</argLine>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/examples/camel-example-spring-boot-clustered-route-controller/readme.adoc
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot-clustered-route-controller/readme.adoc b/examples/camel-example-spring-boot-clustered-route-controller/readme.adoc
new file mode 100644
index 0000000..38cc96f
--- /dev/null
+++ b/examples/camel-example-spring-boot-clustered-route-controller/readme.adoc
@@ -0,0 +1,25 @@
+# Camel Clustered Route Controller Example Spring Boot
+
+This example shows how to work with a simple Apache Camel application using Spring Boot and a Clustered Route Controller.
+
+## How to run
+
+1. build the project:
++
+    mvn clean package
+
+2. in a shell, run the cluster node
++
+    mvn -pl cluster-bootstrap exec:java
+
+3. in a separate shell, run the first camel node
++
+    mvn -pl cluster-node spring-boot:run
+
+4. in a separate shell, run the second camel node
++
+    mvn -pl cluster-node spring-boot:run
+
+## More information
+
+You can find more information about Apache Camel at the website: http://camel.apache.org/

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/examples/camel-example-spring-boot-routecontroller/pom.xml
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot-routecontroller/pom.xml b/examples/camel-example-spring-boot-routecontroller/pom.xml
deleted file mode 100644
index b30b67e..0000000
--- a/examples/camel-example-spring-boot-routecontroller/pom.xml
+++ /dev/null
@@ -1,154 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-    Licensed to the Apache Software Foundation (ASF) under one or more
-    contributor license agreements.  See the NOTICE file distributed with
-    this work for additional information regarding copyright ownership.
-    The ASF licenses this file to You under the Apache License, Version 2.0
-    (the "License"); you may not use this file except in compliance with
-    the License.  You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
-
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>org.apache.camel.example</groupId>
-    <artifactId>examples</artifactId>
-    <version>2.20.0-SNAPSHOT</version>
-  </parent>
-
-  <artifactId>camel-example-spring-boot-routecontroller</artifactId>
-  <name>Camel :: Example :: Spring Boot :: Route Controller</name>
-  <description>An example showing how to work with Camel Route Controller and Spring Boot</description>
-
-  <properties>
-    <category>Beginner</category>
-
-    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
-    <spring.boot-version>${spring-boot-version}</spring.boot-version>
-  </properties>
-
-  <dependencyManagement>
-    <dependencies>
-      <!-- Spring Boot BOM -->
-      <dependency>
-        <groupId>org.springframework.boot</groupId>
-        <artifactId>spring-boot-dependencies</artifactId>
-        <version>${spring.boot-version}</version>
-        <type>pom</type>
-        <scope>import</scope>
-      </dependency>
-      <!-- Camel BOM -->
-      <dependency>
-        <groupId>org.apache.camel</groupId>
-        <artifactId>camel-spring-boot-dependencies</artifactId>
-        <version>${project.version}</version>
-        <type>pom</type>
-        <scope>import</scope>
-      </dependency>
-    </dependencies>
-  </dependencyManagement>
-
-  <dependencies>
-
-    <dependency>
-      <groupId>org.jolokia</groupId>
-      <artifactId>jolokia-core</artifactId>
-    </dependency>
-
-    <!-- Spring Boot -->
-    <dependency>
-      <groupId>org.springframework.boot</groupId>
-      <artifactId>spring-boot-starter-web</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework.boot</groupId>
-      <artifactId>spring-boot-starter-undertow</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework.boot</groupId>
-      <artifactId>spring-boot-actuator</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework.boot</groupId>
-      <artifactId>spring-boot-configuration-processor</artifactId>
-      <optional>true</optional>
-      <version>${spring-boot-version}</version>
-    </dependency>
-
-    <!-- Camel -->
-    <dependency>
-      <groupId>org.apache.camel</groupId>
-      <artifactId>camel-spring-boot-starter</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.camel</groupId>
-      <artifactId>camel-undertow-starter</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.camel</groupId>
-      <artifactId>camel-stream-starter</artifactId>
-    </dependency>
-
-    <!-- test -->
-    <dependency>
-      <groupId>org.springframework.boot</groupId>
-      <artifactId>spring-boot-starter-test</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.camel</groupId>
-      <artifactId>camel-test-spring</artifactId>
-      <scope>test</scope>
-    </dependency>
-
-  </dependencies>
-
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.springframework.boot</groupId>
-        <artifactId>spring-boot-maven-plugin</artifactId>
-        <version>${spring-boot-version}</version>
-        <executions>
-          <execution>
-            <goals>
-              <goal>repackage</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-    </plugins>
-  </build>
-
-  <profiles>
-    <profile>
-      <id>jdk9-build</id>
-      <activation>
-        <jdk>9</jdk>
-      </activation>
-      <build>
-        <plugins>
-          <plugin>
-            <artifactId>maven-surefire-plugin</artifactId>
-            <configuration>
-              <argLine>--add-modules java.xml.bind --add-opens java.base/java.lang=ALL-UNNAMED</argLine>
-            </configuration>
-          </plugin>
-        </plugins>
-      </build>
-    </profile>
-  </profiles>
-</project>

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/examples/camel-example-spring-boot-routecontroller/readme.adoc
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot-routecontroller/readme.adoc b/examples/camel-example-spring-boot-routecontroller/readme.adoc
deleted file mode 100644
index f94221d..0000000
--- a/examples/camel-example-spring-boot-routecontroller/readme.adoc
+++ /dev/null
@@ -1,51 +0,0 @@
-# Camel Route Controller Example Spring Boot
-
-This example shows how to work with a simple Apache Camel application using Spring Boot and a Route Controller.
-
-## How to run
-
-You can run this example using
-
-    mvn spring-boot:run
-
-Beside JMX you can use Spring Boot Endpoints to interact with the routes:
-
-* To get info about the routes
-+
-[source]
-----
-curl -XGET -s http://localhost:8080/camel/routes
-----
-+
-+* To get details about a route
-++
-+[source]
-+----
-+curl -XGET -s http://localhost:8080/camel/routes/{id}/detail
-+----
-
-* To get info about a route
-+
-[source]
-----
-curl -XGET -s http://localhost:8080/camel/routes/{id}/info
-----
-
-* To stop a route
-+
-[source]
-----
-curl -XPOST -s http://localhost:8080/camel/routes/{id}/stop
-----
-
-* To start a route
-+
-[source]
-----
-curl -XPOST -s http://localhost:8080/camel/routes/{id}/stop
-----
-
-
-## More information
-
-You can find more information about Apache Camel at the website: http://camel.apache.org/

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/examples/camel-example-spring-boot-routecontroller/src/main/java/sample/camel/Application.java
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot-routecontroller/src/main/java/sample/camel/Application.java b/examples/camel-example-spring-boot-routecontroller/src/main/java/sample/camel/Application.java
deleted file mode 100644
index 2a97fed..0000000
--- a/examples/camel-example-spring-boot-routecontroller/src/main/java/sample/camel/Application.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * 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 sample.camel;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-
-//CHECKSTYLE:OFF
-/**
- * A sample Spring Boot application that starts the Camel routes.
- */
-@SpringBootApplication
-public class Application {
-
-    /**
-     * A main method to start this application.
-     */
-    public static void main(String[] args) {
-        SpringApplication.run(Application.class, args);
-    }
-
-}
-//CHECKSTYLE:ON

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/examples/camel-example-spring-boot-routecontroller/src/main/java/sample/camel/ApplicationRoutes.java
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot-routecontroller/src/main/java/sample/camel/ApplicationRoutes.java b/examples/camel-example-spring-boot-routecontroller/src/main/java/sample/camel/ApplicationRoutes.java
deleted file mode 100644
index fb6e2f5..0000000
--- a/examples/camel-example-spring-boot-routecontroller/src/main/java/sample/camel/ApplicationRoutes.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * 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 sample.camel;
-
-import org.apache.camel.builder.RouteBuilder;
-import org.springframework.stereotype.Component;
-
-/**
- *
- */
-@Component
-public class ApplicationRoutes extends RouteBuilder {
-    @Override
-    public void configure() throws Exception {
-        from("timer:foo?period=5s")
-            .id("foo")
-            .startupOrder(2)
-            .log("From timer (foo) ...");
-
-        from("timer:bar?period=5s")
-            .id("bar")
-            .startupOrder(1)
-            .log("From timer (bar) ...");
-
-        from("undertow:http://localhost:9011")
-            .id("undertow")
-            .log("From undertow ...");
-
-        from("undertow:http://localhost:9012")
-            .id("undertow2")
-            .autoStartup(false)
-            .log("From undertow 2...");
-
-        from("undertow:http://localhost:9013")
-            .id("undertow3")
-            .log("From undertow 3...");
-    }
-}

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/examples/camel-example-spring-boot-routecontroller/src/main/resources/application.properties
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot-routecontroller/src/main/resources/application.properties b/examples/camel-example-spring-boot-routecontroller/src/main/resources/application.properties
deleted file mode 100644
index 8533f6b..0000000
--- a/examples/camel-example-spring-boot-routecontroller/src/main/resources/application.properties
+++ /dev/null
@@ -1,46 +0,0 @@
-## ---------------------------------------------------------------------------
-## 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.
-## ---------------------------------------------------------------------------
-
-debug = false
-
-logging.level.org.springframework = INFO
-logging.level.org.apache.camel.spring.boot = INFO
-logging.level.org.apache.camel.impl = INFO
-logging.level.org.apache.camel.impl.SupervisingRouteController = DEBUG
-logging.level.org.apache.camel.util.backoff = DEBUG
-logging.level.sample.camel = DEBUG
-
-endpoints.enabled = false
-endpoints.jmx.enabled = false
-endpoints.health.enabled = true
-
-# camel routes is by default enabled
-# so you do not have to configure below
-# endpoints.camelroutes.path = /camel/routes
-# endpoints.camelroutes.enabled = true
-
-management.security.enabled = false
-
-camel.springboot.name = SampleSupervisingRouteController
-
-camel.supervising.controller.enabled = true
-camel.supervising.controller.initial-delay = 5s
-camel.supervising.controller.default-back-off.delay = 5s
-camel.supervising.controller.default-back-off.max-attempts = 10
-camel.supervising.controller.routes.undertow.back-off.delay = 10s
-camel.supervising.controller.routes.undertow.back-off.max-attempts = 3
-camel.supervising.controller.routes.undertow3.supervise = false

http://git-wip-us.apache.org/repos/asf/camel/blob/d043c7d0/examples/camel-example-spring-boot-supervising-route-controller/pom.xml
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot-supervising-route-controller/pom.xml b/examples/camel-example-spring-boot-supervising-route-controller/pom.xml
new file mode 100644
index 0000000..6b231fe
--- /dev/null
+++ b/examples/camel-example-spring-boot-supervising-route-controller/pom.xml
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.camel.example</groupId>
+    <artifactId>examples</artifactId>
+    <version>2.20.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>camel-example-spring-boot-supervising-route-controller</artifactId>
+  <name>Camel :: Example :: Spring Boot :: Supervising Route Controller</name>
+  <description>An example showing how to work with Camel's Supervising Route Controller and Spring Boot</description>
+
+  <properties>
+    <category>Beginner</category>
+
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    <spring.boot-version>${spring-boot-version}</spring.boot-version>
+  </properties>
+
+  <dependencyManagement>
+    <dependencies>
+      <!-- Spring Boot BOM -->
+      <dependency>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-dependencies</artifactId>
+        <version>${spring.boot-version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+      <!-- Camel BOM -->
+      <dependency>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-spring-boot-dependencies</artifactId>
+        <version>${project.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.jolokia</groupId>
+      <artifactId>jolokia-core</artifactId>
+    </dependency>
+
+    <!-- Spring Boot -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-undertow</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-actuator</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-configuration-processor</artifactId>
+      <optional>true</optional>
+      <version>${spring-boot-version}</version>
+    </dependency>
+
+    <!-- Camel -->
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-spring-boot-starter</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-undertow-starter</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-stream-starter</artifactId>
+    </dependency>
+
+    <!-- test -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-test-spring</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-maven-plugin</artifactId>
+        <version>${spring-boot-version}</version>
+        <executions>
+          <execution>
+            <goals>
+              <goal>repackage</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <profiles>
+    <profile>
+      <id>jdk9-build</id>
+      <activation>
+        <jdk>9</jdk>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <configuration>
+              <argLine>--add-modules java.xml.bind --add-opens java.base/java.lang=ALL-UNNAMED</argLine>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>