You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ac...@apache.org on 2020/07/21 09:12:04 UTC

[camel-spring-boot] branch master updated: "CAMEL-15274:camel-spring-boot - Add back http endpoint for route information"

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

acosentino pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-spring-boot.git


The following commit(s) were added to refs/heads/master by this push:
     new 368c769  "CAMEL-15274:camel-spring-boot - Add back http endpoint for route information"
368c769 is described below

commit 368c76981c3b189a6ef8a9959a8e2136898f6b39
Author: Kodanda Ramu Kakarla <kk...@kkakarla.pnq.csb>
AuthorDate: Sun Jul 19 14:54:04 2020 +0530

    "CAMEL-15274:camel-spring-boot - Add back http endpoint for route information"
---
 .../src/main/docs/spring-boot.adoc                 |  10 +-
 .../endpoint/CamelRouteControllerEndpoint.java     |  54 +++
 ...elRouteControllerEndpointAutoConfiguration.java |  42 ++
 .../boot/actuate/endpoint/CamelRoutesEndpoint.java | 517 +++++++++++++++++++++
 .../CamelRoutesEndpointAutoConfiguration.java      |  52 +++
 .../endpoint/CamelRoutesEndpointProperties.java    |  45 ++
 .../additional-spring-configuration-metadata.json  |  28 ++
 .../boot/actuate/endpoint/ActuatorTestRoute.java   |  30 ++
 .../CamelEndpointsGloballyEnabledTest.java         |  48 ++
 .../CamelRouteControllerEndpointEnabledTest.java   |  55 +++
 ...erEndpointGloballyDisabledAndReenabledTest.java |  62 +++
 .../endpoint/CamelRouteControllerEndpointTest.java |  62 +++
 .../endpoint/CamelRoutesEndpointEnabledTest.java   |  56 +++
 .../actuate/endpoint/CamelRoutesEndpointTest.java  |  85 ++++
 .../CamelRoutesEndpointWriteOperationTest.java     |  65 +++
 15 files changed, 1210 insertions(+), 1 deletion(-)

diff --git a/core/camel-spring-boot/src/main/docs/spring-boot.adoc b/core/camel-spring-boot/src/main/docs/spring-boot.adoc
index 93d00c7..86fb065 100644
--- a/core/camel-spring-boot/src/main/docs/spring-boot.adoc
+++ b/core/camel-spring-boot/src/main/docs/spring-boot.adoc
@@ -92,7 +92,7 @@ When using spring-boot with Spring Boot make sure to use the following Maven dep
 ----
 
 
-The component supports 149 options, which are listed below.
+The component supports 157 options, which are listed below.
 
 
 
@@ -248,6 +248,14 @@ The component supports 149 options, which are listed below.
 | *camel.threadpool.pool-size* | Sets the default core pool size (threads to keep minimum in pool) |  | Integer
 | *camel.threadpool.rejected-policy* | Sets the default handler for tasks which cannot be executed by the thread pool. |  | ThreadPoolRejectedPolicy
 | *camel.threadpool.time-unit* | Sets the default time unit used for keep alive time |  | TimeUnit
+| *management.endpoint.camelroutecontroller.cache.time-to-live* | Maximum time that a response can be cached. | 0ms | Duration
+| *management.endpoint.camelroutecontroller.enabled* | To turn on or off information about Camel Route Controller via actuator endpoint. | true | Boolean
+| *management.endpoint.camelroutes.cache.time-to-live* | Maximum time that a response can be cached. | 0ms | Duration
+| *management.endpoint.camelroutes.enabled* |  | false | Boolean
+| *management.endpoint.camelroutes.enabled* | Whether to enable the camelroutes endpoint. | true | Boolean
+| *management.endpoint.camelroutes.enabled* | To turn on or off information about Camel Routes via actuator endpoint. | true | Boolean
+| *management.endpoint.camelroutes.read-only* | Whether Camel Routes actuator is in read-only mode. If not in read-only mode then operations to start/stop routes would be enabled. | true | Boolean
+| *management.info.camel.enabled* | Whether to enable Camel info. | true | Boolean
 |===
 // spring-boot-auto-configure options: END
 
diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRouteControllerEndpoint.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRouteControllerEndpoint.java
new file mode 100644
index 0000000..c6d3f00
--- /dev/null
+++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRouteControllerEndpoint.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.spring.boot.actuate.endpoint;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Route;
+import org.apache.camel.spi.RouteController;
+import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
+import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
+
+/*
+ * {@link Endpoint} to expose {@link RouteController} information.
+ */
+@Endpoint(id = "camelroutecontroller", enableByDefault = true)
+public class CamelRouteControllerEndpoint {
+
+    private CamelContext camelContext;
+
+    public CamelRouteControllerEndpoint(CamelContext camelContext) {
+        this.camelContext = camelContext;
+    }
+
+    @ReadOperation
+    public List<String> getControlledRoutes() {
+        RouteController controller = camelContext.getRouteController();
+
+        if (controller != null) {
+            return controller.getControlledRoutes().stream()
+                .map(Route::getId)
+                .collect(Collectors.toList());
+        }
+
+        return Collections.emptyList();
+    }
+
+}
diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRouteControllerEndpointAutoConfiguration.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRouteControllerEndpointAutoConfiguration.java
new file mode 100644
index 0000000..0b2cccd
--- /dev/null
+++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRouteControllerEndpointAutoConfiguration.java
@@ -0,0 +1,42 @@
+/*
+ * 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.actuate.endpoint;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/*
+ * Auto configuration for the {@link CamelRouteControllerEndpoint}.
+ */
+@Configuration
+@ConditionalOnClass({CamelRouteControllerEndpoint.class})
+@ConditionalOnBean(CamelAutoConfiguration.class)
+@AutoConfigureAfter(CamelAutoConfiguration.class)
+public class CamelRouteControllerEndpointAutoConfiguration {
+    @Bean
+    @ConditionalOnClass(CamelContext.class)
+    @ConditionalOnMissingBean
+    public CamelRouteControllerEndpoint routeControllerEndpoint(CamelContext camelContext) {
+        return new CamelRouteControllerEndpoint(camelContext);
+    }
+}
diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpoint.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpoint.java
new file mode 100644
index 0000000..1b2c34c
--- /dev/null
+++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpoint.java
@@ -0,0 +1,517 @@
+/*
+ * 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.actuate.endpoint;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.Route;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.StatefulService;
+import org.apache.camel.api.management.ManagedCamelContext;
+import org.apache.camel.api.management.mbean.ManagedRouteMBean;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.spi.RouteError;
+import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
+import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
+import org.springframework.boot.actuate.endpoint.annotation.Selector;
+import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
+import org.springframework.lang.Nullable;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+
+
+/*
+ * {@link Endpoint} to expose {@link org.apache.camel.Route} information.
+ */
+@Endpoint(id = "camelroutes", enableByDefault = true)
+public class CamelRoutesEndpoint {
+
+    private CamelContext camelContext;
+    private static ExtendedCamelContext extendedContext;
+    private static ManagedCamelContext mcontext;
+    private static DefaultCamelContext dcontext; 
+    private CamelRoutesEndpointProperties properties;
+
+    public CamelRoutesEndpoint(CamelContext camelContext, CamelRoutesEndpointProperties properties) {
+        this.camelContext = camelContext;
+        this.extendedContext = camelContext.getExtension(ExtendedCamelContext.class);
+        this.mcontext = camelContext.getExtension(ManagedCamelContext.class);;
+        this.dcontext = camelContext.getExtension(DefaultCamelContext.class);
+        this.properties = properties;
+    }
+
+    @ReadOperation
+    public List<RouteEndpointInfo> readRoutes() {
+        return getRoutesInfo();
+    }
+
+    @ReadOperation
+    public Object doReadAction(@Selector String id, @Selector ReadAction action) {
+        switch (action) {
+        case DETAIL:
+            return getRouteDetailsInfo(id);
+        case INFO:
+            return getRouteInfo(id);
+        default:
+            throw new IllegalArgumentException("Unsupported read action " + action);
+        }
+    }
+
+    @WriteOperation
+    public void doWriteAction(@Selector String id, @Selector WriteAction action, @Nullable TimeInfo timeInfo) {
+        if (this.properties.isReadOnly()) {
+            throw new IllegalArgumentException(String.format("Read only: write action %s is not allowed", action));
+        }
+
+        switch (action) {
+        case STOP:
+            stopRoute(
+                    id,
+                    Optional.ofNullable(timeInfo).flatMap(ti -> Optional.ofNullable(ti.getTimeout())),
+                    Optional.of(TimeUnit.SECONDS),
+                    Optional.ofNullable(timeInfo).flatMap(ti -> Optional.ofNullable(ti.getAbortAfterTimeout())));
+            break;
+        case START:
+            startRoute(id);
+            break;
+        case RESET:
+            resetRoute(id);
+            break;
+        case SUSPEND:
+            suspendRoute(id,
+                    Optional.ofNullable(timeInfo).flatMap(ti -> Optional.ofNullable(ti.getTimeout())),
+                    Optional.of(TimeUnit.SECONDS));
+            break;
+        case RESUME:
+            resumeRoute(id);
+            break;
+        default:
+            throw new IllegalArgumentException("Unsupported write action " + action);
+        }
+    }
+
+    private RouteEndpointInfo getRouteInfo(String id) {
+        Route route = camelContext.getRoute(id);
+        if (route != null) {
+            return new RouteEndpointInfo(route);
+        }
+
+        return null;
+    }
+
+    private List<RouteEndpointInfo> getRoutesInfo() {
+        return camelContext.getRoutes().stream()
+                .map(RouteEndpointInfo::new)
+                .collect(Collectors.toList());
+    }
+
+    private RouteDetailsEndpointInfo getRouteDetailsInfo(String id) {
+        Route route = camelContext.getRoute(id);
+        if (route != null) {
+            return new RouteDetailsEndpointInfo(camelContext, route);
+        }
+
+        return null;
+    }
+
+    private void startRoute(String id) {
+        try {
+            camelContext.getRouteController().startRoute(id);
+        } catch (Exception e) {
+            throw new RuntimeCamelException(e);
+        }
+    }
+
+    private void resetRoute(String id) {
+        try {
+            ManagedRouteMBean managedRouteMBean = mcontext.getManagedRoute(id, ManagedRouteMBean.class);
+            if (managedRouteMBean != null) {
+                managedRouteMBean.reset(true);
+            }
+        } catch (Exception e) {
+            throw new RuntimeCamelException(e);
+        }
+    }
+
+    private void stopRoute(String id, Optional<Long> timeout, Optional<TimeUnit> timeUnit, Optional<Boolean> abortAfterTimeout) {
+        try {
+            if (timeout.isPresent()) {
+                camelContext.getRouteController().stopRoute(id, timeout.get(), timeUnit.orElse(TimeUnit.SECONDS), abortAfterTimeout.orElse(Boolean.TRUE));
+            } else {
+                camelContext.getRouteController().stopRoute(id);
+            }
+        } catch (Exception e) {
+            throw new RuntimeCamelException(e);
+        }
+    }
+
+    private void suspendRoute(String id, Optional<Long> timeout, Optional<TimeUnit> timeUnit) {
+        try {
+            if (timeout.isPresent()) {
+                camelContext.getRouteController().suspendRoute(id, timeout.get(), timeUnit.orElse(TimeUnit.SECONDS));
+            } else {
+                camelContext.getRouteController().suspendRoute(id);
+            }
+        } catch (Exception e) {
+            throw new RuntimeCamelException(e);
+        }
+    }
+
+    private void resumeRoute(String id) {
+        try {
+            camelContext.getRouteController().resumeRoute(id);
+        } catch (Exception e) {
+            throw new RuntimeCamelException(e);
+        }
+    }
+
+    /*
+     * Container for exposing {@link org.apache.camel.Route} information as JSON.
+     */
+    @JsonPropertyOrder({"id", "group", "description", "uptime", "uptimeMillis"})
+    @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    public static class RouteEndpointInfo {
+
+        private final String id;
+        
+        private final String group;
+
+        private final Map<String, Object> properties;
+
+        private final String description;
+
+        private final String uptime;
+
+        private final long uptimeMillis;
+
+        private final String status;
+
+        public RouteEndpointInfo(Route route) {
+            this.id = route.getId();
+            this.group = route.getGroup();
+            this.description = route.getDescription();
+            this.uptime = route.getUptime();
+            this.uptimeMillis = route.getUptimeMillis();
+
+            if (route.getProperties() != null) {
+                this.properties = new HashMap<>(route.getProperties());
+            } else {
+                this.properties = Collections.emptyMap();
+            }
+
+            if (route instanceof StatefulService) {
+                this.status = ((StatefulService) route).getStatus().name();
+            } else {
+                this.status = null;
+            }
+        }
+
+        public String getId() {
+            return id;
+        }
+        
+        public String getGroup() {
+            return group;
+        }
+
+        public Map<String, Object> getProperties() {
+            return properties;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public String getUptime() {
+            return uptime;
+        }
+
+        public long getUptimeMillis() {
+            return uptimeMillis;
+        }
+
+        public String getStatus() {
+            return status;
+        }
+    }
+
+    /*
+     * Container for exposing {@link org.apache.camel.Route} information
+     * with route details as JSON. Route details are retrieved from JMX.
+     */
+    public static class RouteDetailsEndpointInfo extends RouteEndpointInfo {
+
+        @JsonProperty("details")
+        private RouteDetails routeDetails;
+
+        public RouteDetailsEndpointInfo(final CamelContext camelContext, final Route route) {
+            super(route);
+
+            if (camelContext.getManagementStrategy().getManagementAgent() != null) {
+               this.routeDetails = new RouteDetails(mcontext.getManagedRoute(route.getId(), ManagedRouteMBean.class));
+            }
+        }
+
+        @JsonInclude(JsonInclude.Include.NON_EMPTY)
+        static class RouteDetails {
+
+            private long deltaProcessingTime;
+
+            private long exchangesInflight;
+
+            private long exchangesTotal;
+
+            private long externalRedeliveries;
+
+            private long failuresHandled;
+
+            private String firstExchangeCompletedExchangeId;
+
+            private Date firstExchangeCompletedTimestamp;
+
+            private String firstExchangeFailureExchangeId;
+
+            private Date firstExchangeFailureTimestamp;
+
+            private String lastExchangeCompletedExchangeId;
+
+            private Date lastExchangeCompletedTimestamp;
+
+            private String lastExchangeFailureExchangeId;
+
+            private Date lastExchangeFailureTimestamp;
+
+            private long lastProcessingTime;
+
+            private String load01;
+
+            private String load05;
+
+            private String load15;
+
+            private long maxProcessingTime;
+
+            private long meanProcessingTime;
+
+            private long minProcessingTime;
+
+            private Long oldestInflightDuration;
+
+            private String oldestInflightExchangeId;
+
+            private long redeliveries;
+
+            private long totalProcessingTime;
+
+            private RouteError lastError;
+
+            private boolean hasRouteController;
+
+            RouteDetails(ManagedRouteMBean managedRoute) {
+                try {
+                    this.deltaProcessingTime = managedRoute.getDeltaProcessingTime();
+                    this.exchangesInflight = managedRoute.getExchangesInflight();
+                    this.exchangesTotal = managedRoute.getExchangesTotal();
+                    this.externalRedeliveries = managedRoute.getExternalRedeliveries();
+                    this.failuresHandled = managedRoute.getFailuresHandled();
+                    this.firstExchangeCompletedExchangeId = managedRoute.getFirstExchangeCompletedExchangeId();
+                    this.firstExchangeCompletedTimestamp = managedRoute.getFirstExchangeCompletedTimestamp();
+                    this.firstExchangeFailureExchangeId = managedRoute.getFirstExchangeFailureExchangeId();
+                    this.firstExchangeFailureTimestamp = managedRoute.getFirstExchangeFailureTimestamp();
+                    this.lastExchangeCompletedExchangeId = managedRoute.getLastExchangeCompletedExchangeId();
+                    this.lastExchangeCompletedTimestamp = managedRoute.getLastExchangeCompletedTimestamp();
+                    this.lastExchangeFailureExchangeId = managedRoute.getLastExchangeFailureExchangeId();
+                    this.lastExchangeFailureTimestamp = managedRoute.getLastExchangeFailureTimestamp();
+                    this.lastProcessingTime = managedRoute.getLastProcessingTime();
+                    this.load01 = managedRoute.getLoad01();
+                    this.load05 = managedRoute.getLoad05();
+                    this.load15 = managedRoute.getLoad15();
+                    this.maxProcessingTime = managedRoute.getMaxProcessingTime();
+                    this.meanProcessingTime = managedRoute.getMeanProcessingTime();
+                    this.minProcessingTime = managedRoute.getMinProcessingTime();
+                    this.oldestInflightDuration = managedRoute.getOldestInflightDuration();
+                    this.oldestInflightExchangeId = managedRoute.getOldestInflightExchangeId();
+                    this.redeliveries = managedRoute.getRedeliveries();
+                    this.totalProcessingTime = managedRoute.getTotalProcessingTime();
+                    //this.lastError = managedRoute.getLastError();
+                    this.hasRouteController = managedRoute.getHasRouteController();
+                } catch (Exception e) {
+                    // Ignore
+                }
+            }
+
+            public long getDeltaProcessingTime() {
+                return deltaProcessingTime;
+            }
+
+            public long getExchangesInflight() {
+                return exchangesInflight;
+            }
+
+            public long getExchangesTotal() {
+                return exchangesTotal;
+            }
+
+            public long getExternalRedeliveries() {
+                return externalRedeliveries;
+            }
+
+            public long getFailuresHandled() {
+                return failuresHandled;
+            }
+
+            public String getFirstExchangeCompletedExchangeId() {
+                return firstExchangeCompletedExchangeId;
+            }
+
+            public Date getFirstExchangeCompletedTimestamp() {
+                return firstExchangeCompletedTimestamp;
+            }
+
+            public String getFirstExchangeFailureExchangeId() {
+                return firstExchangeFailureExchangeId;
+            }
+
+            public Date getFirstExchangeFailureTimestamp() {
+                return firstExchangeFailureTimestamp;
+            }
+
+            public String getLastExchangeCompletedExchangeId() {
+                return lastExchangeCompletedExchangeId;
+            }
+
+            public Date getLastExchangeCompletedTimestamp() {
+                return lastExchangeCompletedTimestamp;
+            }
+
+            public String getLastExchangeFailureExchangeId() {
+                return lastExchangeFailureExchangeId;
+            }
+
+            public Date getLastExchangeFailureTimestamp() {
+                return lastExchangeFailureTimestamp;
+            }
+
+            public long getLastProcessingTime() {
+                return lastProcessingTime;
+            }
+
+            public String getLoad01() {
+                return load01;
+            }
+
+            public String getLoad05() {
+                return load05;
+            }
+
+            public String getLoad15() {
+                return load15;
+            }
+
+            public long getMaxProcessingTime() {
+                return maxProcessingTime;
+            }
+
+            public long getMeanProcessingTime() {
+                return meanProcessingTime;
+            }
+
+            public long getMinProcessingTime() {
+                return minProcessingTime;
+            }
+
+            public Long getOldestInflightDuration() {
+                return oldestInflightDuration;
+            }
+
+            public String getOldestInflightExchangeId() {
+                return oldestInflightExchangeId;
+            }
+
+            public long getRedeliveries() {
+                return redeliveries;
+            }
+
+            public long getTotalProcessingTime() {
+                return totalProcessingTime;
+            }
+
+            public RouteError getLastError() {
+                return lastError;
+            }
+
+            public boolean getHasRouteController() {
+                return hasRouteController;
+            }
+        }
+    }
+
+    /*
+     * List of write actions available for the endpoint
+     */
+    public enum WriteAction {
+        STOP,
+        START,
+        RESET,
+        SUSPEND,
+        RESUME
+    }
+
+    /*
+     * List of read actions available for the endpoint
+     */
+    public enum ReadAction {
+        DETAIL,
+        INFO
+    }
+
+    /*
+     * Optional time information for the actions
+     */
+    public static class TimeInfo {
+        private Long timeout;
+        private Boolean abortAfterTimeout;
+
+        public Long getTimeout() {
+            return timeout;
+        }
+
+        public void setTimeout(Long timeout) {
+            this.timeout = timeout;
+        }
+
+        public Boolean getAbortAfterTimeout() {
+            return abortAfterTimeout;
+        }
+
+        public void setAbortAfterTimeout(Boolean abortAfterTimeout) {
+            this.abortAfterTimeout = abortAfterTimeout;
+        }
+    }
+
+}
diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointAutoConfiguration.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointAutoConfiguration.java
new file mode 100644
index 0000000..24c9131
--- /dev/null
+++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointAutoConfiguration.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 org.apache.camel.spring.boot.actuate.endpoint;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/*
+ * Auto configuration for the {@link CamelRoutesEndpoint}.
+ */
+@Configuration
+@EnableConfigurationProperties({ CamelRoutesEndpointProperties.class })
+@ConditionalOnClass({CamelRoutesEndpoint.class})
+@ConditionalOnBean(CamelAutoConfiguration.class)
+@AutoConfigureAfter(CamelAutoConfiguration.class)
+public class CamelRoutesEndpointAutoConfiguration {
+
+    private CamelRoutesEndpointProperties properties;
+
+    public CamelRoutesEndpointAutoConfiguration(CamelRoutesEndpointProperties properties) {
+        this.properties = properties;
+    }
+
+    @Bean
+    @ConditionalOnClass(CamelContext.class)
+    @ConditionalOnMissingBean
+    public CamelRoutesEndpoint camelEndpoint(CamelContext camelContext) {
+        return new CamelRoutesEndpoint(camelContext, properties);
+    }
+
+}
diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointProperties.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointProperties.java
new file mode 100644
index 0000000..21d447b
--- /dev/null
+++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointProperties.java
@@ -0,0 +1,45 @@
+/*
+ * 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.actuate.endpoint;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties("management.endpoint.camelroutes")
+public class CamelRoutesEndpointProperties {
+
+    private boolean readOnly = true;
+    private boolean enabled = false;
+
+    public boolean isReadOnly() {
+        return readOnly;
+    }
+
+    public void setReadOnly(boolean readOnly) {
+        this.readOnly = readOnly;
+    }
+    
+    public boolean isEnabled() {
+        return enabled;
+    }
+    
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+    
+    
+
+}
diff --git a/core/camel-spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/core/camel-spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json
new file mode 100644
index 0000000..cf56bd1
--- /dev/null
+++ b/core/camel-spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -0,0 +1,28 @@
+{
+  "properties": [
+    {
+      "name": "management.endpoint.camelroutes.read-only",
+      "type": "java.lang.Boolean",
+      "description": "Whether Camel Routes actuator is in read-only mode. If not in read-only mode then operations to start/stop routes would be enabled.",
+      "defaultValue": true
+    },
+    {
+      "name": "management.endpoint.camelroutes.enabled",
+      "type": "java.lang.Boolean",
+      "description": "To turn on or off information about Camel Routes via actuator endpoint.",
+      "defaultValue": true
+    },
+    {
+      "name": "management.endpoint.camelroutecontroller.enabled",
+      "type": "java.lang.Boolean",
+      "description": "To turn on or off information about Camel Route Controller via actuator endpoint.",
+      "defaultValue": true
+    },
+    {
+      "name": "management.info.camel.enabled",
+      "type": "java.lang.Boolean",
+      "description": "Whether to enable Camel info.",
+      "defaultValue": true
+    }
+  ]
+}
diff --git a/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/ActuatorTestRoute.java b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/ActuatorTestRoute.java
new file mode 100644
index 0000000..b56f1bc
--- /dev/null
+++ b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/ActuatorTestRoute.java
@@ -0,0 +1,30 @@
+/*
+ * 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.actuate.endpoint;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ActuatorTestRoute extends RouteBuilder {
+
+    @Override
+    public void configure() throws Exception {
+        from("timer:foo").routeId("foo-route").routeGroup("foo-route-group").to("log:foo");
+    }
+
+}
diff --git a/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelEndpointsGloballyEnabledTest.java b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelEndpointsGloballyEnabledTest.java
new file mode 100644
index 0000000..729714a
--- /dev/null
+++ b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelEndpointsGloballyEnabledTest.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.camel.spring.boot.actuate.endpoint;
+
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@DirtiesContext
+@RunWith(SpringRunner.class)
+@SpringBootTest(
+    classes = {CamelAutoConfiguration.class, CamelRouteControllerEndpointAutoConfiguration.class,
+               CamelRoutesEndpointAutoConfiguration.class},
+    properties = {"management.endpoints.web.exposure.include=*"})
+                   
+public class CamelEndpointsGloballyEnabledTest {
+    @Autowired
+    private CamelRouteControllerEndpoint routeControllerEndpoint;
+
+    @Autowired
+    private CamelRoutesEndpoint routesEndpoint;
+
+    @Test
+    public void testHealthCheckEndpointPresent() throws Exception {
+        Assert.assertNotNull(routeControllerEndpoint);
+        Assert.assertNotNull(routesEndpoint);
+    }
+}
diff --git a/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRouteControllerEndpointEnabledTest.java b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRouteControllerEndpointEnabledTest.java
new file mode 100644
index 0000000..2a643e1
--- /dev/null
+++ b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRouteControllerEndpointEnabledTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.actuate.endpoint;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/*
+ * Test for the {@link CamelRoutesEndpoint} actuator endpoint.
+ */
+@DirtiesContext
+@RunWith(SpringRunner.class)
+@EnableAutoConfiguration
+@SpringBootApplication
+@SpringBootTest(
+    classes = {CamelAutoConfiguration.class, CamelRouteControllerEndpointAutoConfiguration.class, ActuatorTestRoute.class},
+    properties = {"management.endpoint.camelroutecontroller.enabled=true"}
+)
+public class CamelRouteControllerEndpointEnabledTest extends Assert {
+
+    @Autowired(required = true)
+    CamelRouteControllerEndpoint routeControllerEndpoint;
+
+    @Autowired
+    CamelContext camelContext;
+
+    @Test
+    public void testRoutesEndpointPresent() throws Exception {
+        Assert.assertNotNull(routeControllerEndpoint);
+    }
+
+}
diff --git a/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRouteControllerEndpointGloballyDisabledAndReenabledTest.java b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRouteControllerEndpointGloballyDisabledAndReenabledTest.java
new file mode 100644
index 0000000..03e1e46
--- /dev/null
+++ b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRouteControllerEndpointGloballyDisabledAndReenabledTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.actuate.endpoint;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/*
+ * Test for the {@link CamelRoutesEndpoint} actuator endpoint.
+ */
+@DirtiesContext
+@RunWith(SpringRunner.class)
+@EnableAutoConfiguration
+@SpringBootApplication
+@SpringBootTest(
+    classes = {CamelAutoConfiguration.class, CamelRoutesEndpointAutoConfiguration.class,
+               CamelRouteControllerEndpointAutoConfiguration.class, ActuatorTestRoute.class},
+    properties = {"management.endpoints.enabled-by-default = true",
+                  "management.endpoint.camelroutecontroller.enabled = true"}
+)
+public class CamelRouteControllerEndpointGloballyDisabledAndReenabledTest extends Assert {
+
+    @Autowired
+    CamelRouteControllerEndpoint routeControllerEndpoint;
+
+    @Autowired
+    CamelRoutesEndpoint routesEndpoint;
+
+    @Autowired
+    CamelContext camelContext;
+
+    @Test
+    public void testRoutesEndpointPresent() throws Exception {
+   
+        Assert.assertNotNull(routesEndpoint);
+        Assert.assertNotNull(routeControllerEndpoint);
+    }
+
+}
diff --git a/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRouteControllerEndpointTest.java b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRouteControllerEndpointTest.java
new file mode 100644
index 0000000..74cc589
--- /dev/null
+++ b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRouteControllerEndpointTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.actuate.endpoint;
+
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringRunner;
+
+
+/*
+ * Test for the {@link CamelRouteControllerEndpoint} actuator endpoint.
+ */
+@DirtiesContext
+@RunWith(SpringRunner.class)
+@EnableAutoConfiguration
+@SpringBootApplication
+@SpringBootTest(classes = {CamelAutoConfiguration.class, CamelRouteControllerEndpointAutoConfiguration.class, ActuatorTestRoute.class})
+public class CamelRouteControllerEndpointTest extends Assert {
+
+    @Autowired
+    CamelRouteControllerEndpoint endpoint;
+
+    @Autowired
+    CamelContext camelContext;
+
+    @Test
+    public void testRouteControllerEndpoint() throws Exception {
+        List<String> routesId = endpoint.getControlledRoutes();
+
+        assertNotNull(routesId);
+        /* FIXME DefaultRouteController returns empty list while it does control any route...
+        assertFalse(routesId.isEmpty());
+        assertEquals(routesId.size(), camelContext.getRoutes().size());
+        assertTrue(routesId.stream().anyMatch(r -> "foo-route".equals(r)));
+        */
+    }
+
+}
diff --git a/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointEnabledTest.java b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointEnabledTest.java
new file mode 100644
index 0000000..65a5f58
--- /dev/null
+++ b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointEnabledTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.actuate.endpoint;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringRunner;
+
+
+/*
+ * Test for the {@link CamelRoutesEndpoint} actuator endpoint.
+ */
+@DirtiesContext
+@RunWith(SpringRunner.class)
+@EnableAutoConfiguration
+@SpringBootApplication
+@SpringBootTest(
+    classes = {CamelAutoConfiguration.class, CamelRoutesEndpointAutoConfiguration.class, ActuatorTestRoute.class},
+    properties = {"management.endpoint.camelroutes.enabled = true"}
+)
+public class CamelRoutesEndpointEnabledTest extends Assert {
+
+    @Autowired
+    CamelRoutesEndpoint routesEndpoint;
+
+    @Autowired
+    CamelContext camelContext;
+
+    @Test
+    public void testRoutesEndpointNotPresent() throws Exception {
+        Assert.assertNotNull(routesEndpoint);
+    }
+
+}
diff --git a/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointTest.java b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointTest.java
new file mode 100644
index 0000000..8b8aeca
--- /dev/null
+++ b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.actuate.endpoint;
+
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.spring.boot.actuate.endpoint.CamelRoutesEndpoint.ReadAction;
+import org.apache.camel.spring.boot.actuate.endpoint.CamelRoutesEndpoint.RouteDetailsEndpointInfo;
+import org.apache.camel.spring.boot.actuate.endpoint.CamelRoutesEndpoint.RouteEndpointInfo;
+import org.apache.camel.spring.boot.actuate.endpoint.CamelRoutesEndpoint.TimeInfo;
+import org.apache.camel.spring.boot.actuate.endpoint.CamelRoutesEndpoint.WriteAction;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/*
+ * Test for the {@link CamelRoutesEndpoint} actuator endpoint.
+ */
+@DirtiesContext
+@RunWith(SpringRunner.class)
+@EnableAutoConfiguration
+@SpringBootApplication
+@SpringBootTest(classes = {CamelAutoConfiguration.class, CamelRoutesEndpointAutoConfiguration.class, ActuatorTestRoute.class},
+                           properties = {"management.endpoints.web.exposure.include=*"})
+
+public class CamelRoutesEndpointTest extends Assert {
+
+    @Autowired
+    CamelRoutesEndpoint endpoint;
+
+    @Autowired
+    CamelContext camelContext;
+
+    @Test
+    public void testRoutesEndpoint() throws Exception {
+        List<RouteEndpointInfo> routes = endpoint.readRoutes();
+
+        assertFalse(routes.isEmpty());
+        assertEquals(routes.size(), camelContext.getRoutes().size());
+        assertTrue(routes.stream().anyMatch(r -> "foo-route".equals(r.getId())));
+        assertTrue(routes.stream().anyMatch(r -> "foo-route-group".equals(r.getGroup())));
+        assertTrue(routes.stream().anyMatch(r -> r.getProperties().containsKey("key1") &&  "val1".equals(r.getProperties().get("key1"))));
+        assertTrue(routes.stream().anyMatch(r -> r.getProperties().containsKey("key2") &&  "val2".equals(r.getProperties().get("key2"))));
+    }
+
+    @Test
+    public void testReadOperation() throws Exception {
+        Object answer = endpoint.doReadAction("foo-route", ReadAction.INFO);
+        Assert.assertEquals(RouteEndpointInfo.class, answer.getClass());
+        Assert.assertEquals("foo-route", RouteEndpointInfo.class.cast(answer).getId());
+        answer = endpoint.doReadAction("foo-route", ReadAction.DETAIL);
+        Assert.assertEquals(RouteDetailsEndpointInfo.class, answer.getClass());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testWriteOperationReadOnly() throws Exception {
+        TimeInfo timeInfo = new TimeInfo();
+        timeInfo.setAbortAfterTimeout(true);
+        timeInfo.setTimeout(5L);
+        endpoint.doWriteAction("foo-route", WriteAction.STOP, timeInfo);
+    }
+
+}
diff --git a/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointWriteOperationTest.java b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointWriteOperationTest.java
new file mode 100644
index 0000000..e50df8c
--- /dev/null
+++ b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointWriteOperationTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.actuate.endpoint;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.ServiceStatus;
+import org.apache.camel.impl.engine.AbstractCamelContext;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.spring.boot.actuate.endpoint.CamelRoutesEndpoint.TimeInfo;
+import org.apache.camel.spring.boot.actuate.endpoint.CamelRoutesEndpoint.WriteAction;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/*
+ * Test for the {@link CamelRoutesEndpoint} actuator endpoint.
+ */
+@DirtiesContext
+@RunWith(SpringRunner.class)
+@EnableAutoConfiguration
+@SpringBootApplication
+@SpringBootTest(
+    classes = {CamelAutoConfiguration.class, CamelRoutesEndpointAutoConfiguration.class, ActuatorTestRoute.class},
+    properties = {"management.endpoint.camelroutes.read-only = false"})
+public class CamelRoutesEndpointWriteOperationTest extends Assert {
+
+    @Autowired
+    CamelRoutesEndpoint endpoint;
+
+    @Autowired
+    CamelContext camelContext;
+
+    @Test
+    public void testWriteOperation() throws Exception {
+        AbstractCamelContext acontext = camelContext.getExtension(AbstractCamelContext.class);
+        ServiceStatus status = acontext.getRouteStatus("foo-route");
+        Assert.assertTrue(status.isStarted());
+        TimeInfo timeInfo = new TimeInfo();
+        timeInfo.setAbortAfterTimeout(true);
+        timeInfo.setTimeout(10L);
+        endpoint.doWriteAction("foo-route", WriteAction.STOP, timeInfo);
+        status = acontext.getRouteStatus("foo-route");
+        Assert.assertTrue(status.isStopped());
+    }
+}