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 2022/10/21 06:59:14 UTC

[camel] branch main updated: CAMEL-18617: Some health checks are hidden when running withg supervised controller enabled

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

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


The following commit(s) were added to refs/heads/main by this push:
     new 41e5c11c750 CAMEL-18617: Some health checks are hidden when running withg supervised controller enabled
41e5c11c750 is described below

commit 41e5c11c750302ebfc3d75df43459a2d62d18b35
Author: Luca Burgazzoli <lb...@gmail.com>
AuthorDate: Thu Oct 20 12:11:35 2022 +0200

    CAMEL-18617: Some health checks are hidden when running withg supervised controller enabled
---
 .../main/camel-main-configuration-metadata.json    |   1 +
 .../camel/health-check/camel-kafka-repository      |   2 -
 .../camel/component/kafka/KafkaConsumer.java       |  10 +-
 .../camel/component/kafka/KafkaProducer.java       |  10 +-
 .../CamelMicroProfileHealthCheckRegistry.java      |  52 ++++++---
 .../CamelMicroProfileHealthComponentsTest.java     |  85 ++++++++++++++
 ...MicroProfileHealthSupervisedRoutesMainTest.java |  15 ++-
 .../health/CamelMicroProfileHealthTestHelper.java  | 125 +++++++++++++++++++++
 .../health/CamelMicroProfileHealthTestSupport.java |  18 +++
 ...thCheckRepository.java => HasHealthChecks.java} |  29 +----
 .../apache/camel/health/HealthCheckRepository.java |   8 +-
 .../health/WritableHealthCheckRepository.java      |  33 ++++++
 .../camel/health-check/components-repository       |   2 +
 .../health/ComponentsHealthCheckRepository.java    |  27 +++--
 .../HealthConfigurationPropertiesConfigurer.java   |   6 +
 .../camel-main-configuration-metadata.json         |   1 +
 core/camel-main/src/main/docs/main.adoc            |   3 +-
 .../org/apache/camel/main/BaseMainSupport.java     |  11 ++
 .../camel/main/HealthConfigurationProperties.java  |  13 +++
 19 files changed, 376 insertions(+), 75 deletions(-)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
index da0c1336d03..5a9342a6808 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
@@ -143,6 +143,7 @@
     { "name": "camel.faulttolerance.timeoutEnabled", "description": "Whether timeout is enabled or not on the circuit breaker. Default is false.", "sourceType": "org.apache.camel.main.FaultToleranceConfigurationProperties", "type": "boolean", "javaType": "java.lang.Boolean", "defaultValue": false },
     { "name": "camel.faulttolerance.timeoutPoolSize", "description": "Configures the pool size of the thread pool when timeout is enabled. Default value is 10.", "sourceType": "org.apache.camel.main.FaultToleranceConfigurationProperties", "type": "integer", "javaType": "java.lang.Integer", "defaultValue": 10 },
     { "name": "camel.faulttolerance.timeoutScheduledExecutorService", "description": "References to a custom thread pool to use when timeout is enabled", "sourceType": "org.apache.camel.main.FaultToleranceConfigurationProperties", "type": "string", "javaType": "java.lang.String" },
+    { "name": "camel.health.componentsEnabled", "description": "Whether components health check is enabled", "sourceType": "org.apache.camel.main.HealthConfigurationProperties", "type": "boolean", "javaType": "java.lang.Boolean", "defaultValue": true },
     { "name": "camel.health.consumersEnabled", "description": "Whether consumers health check is enabled", "sourceType": "org.apache.camel.main.HealthConfigurationProperties", "type": "boolean", "javaType": "java.lang.Boolean", "defaultValue": true },
     { "name": "camel.health.enabled", "description": "Whether health check is enabled globally", "sourceType": "org.apache.camel.main.HealthConfigurationProperties", "type": "boolean", "javaType": "java.lang.Boolean", "defaultValue": true },
     { "name": "camel.health.excludePattern", "description": "Pattern to exclude health checks from being invoked by Camel when checking healths. Multiple patterns can be separated by comma.", "sourceType": "org.apache.camel.main.HealthConfigurationProperties", "type": "string", "javaType": "java.lang.String" },
diff --git a/components/camel-kafka/src/generated/resources/META-INF/services/org/apache/camel/health-check/camel-kafka-repository b/components/camel-kafka/src/generated/resources/META-INF/services/org/apache/camel/health-check/camel-kafka-repository
deleted file mode 100644
index 911c92477fe..00000000000
--- a/components/camel-kafka/src/generated/resources/META-INF/services/org/apache/camel/health-check/camel-kafka-repository
+++ /dev/null
@@ -1,2 +0,0 @@
-# Generated by camel build tools - do NOT edit this file!
-class=org.apache.camel.component.kafka.KafkaHealthCheckRepository
diff --git a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaConsumer.java b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaConsumer.java
index 3a6d2860329..e1e29660b5c 100644
--- a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaConsumer.java
+++ b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaConsumer.java
@@ -30,6 +30,7 @@ import org.apache.camel.Suspendable;
 import org.apache.camel.component.kafka.consumer.errorhandler.KafkaConsumerListener;
 import org.apache.camel.health.HealthCheckAware;
 import org.apache.camel.health.HealthCheckHelper;
+import org.apache.camel.health.WritableHealthCheckRepository;
 import org.apache.camel.resume.ConsumerListenerAware;
 import org.apache.camel.resume.ResumeAware;
 import org.apache.camel.resume.ResumeStrategy;
@@ -52,7 +53,7 @@ public class KafkaConsumer extends DefaultConsumer
     protected ExecutorService executor;
     private final KafkaEndpoint endpoint;
     private KafkaConsumerHealthCheck consumerHealthCheck;
-    private KafkaHealthCheckRepository healthCheckRepository;
+    private WritableHealthCheckRepository healthCheckRepository;
     // This list helps to work around the infinite loop of KAFKA-1894
     private final List<KafkaFetchRecords> tasks = new ArrayList<>();
     private volatile boolean stopOffsetRepo;
@@ -123,8 +124,11 @@ public class KafkaConsumer extends DefaultConsumer
         super.doStart();
 
         // health-check is optional so discover and resolve
-        healthCheckRepository = HealthCheckHelper.getHealthCheckRepository(endpoint.getCamelContext(), "camel-kafka",
-                KafkaHealthCheckRepository.class);
+        healthCheckRepository = HealthCheckHelper.getHealthCheckRepository(
+                endpoint.getCamelContext(),
+                "components",
+                WritableHealthCheckRepository.class);
+
         if (healthCheckRepository != null) {
             consumerHealthCheck = new KafkaConsumerHealthCheck(this, getRouteId());
             healthCheckRepository.addHealthCheck(consumerHealthCheck);
diff --git a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaProducer.java b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaProducer.java
index 686c422da57..78b9e062ab2 100755
--- a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaProducer.java
+++ b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaProducer.java
@@ -38,6 +38,7 @@ import org.apache.camel.component.kafka.producer.support.ProducerUtil;
 import org.apache.camel.component.kafka.producer.support.PropagatedHeadersProvider;
 import org.apache.camel.component.kafka.serde.KafkaHeaderSerializer;
 import org.apache.camel.health.HealthCheckHelper;
+import org.apache.camel.health.WritableHealthCheckRepository;
 import org.apache.camel.spi.HeaderFilterStrategy;
 import org.apache.camel.support.DefaultAsyncProducer;
 import org.apache.camel.support.SynchronizationAdapter;
@@ -66,7 +67,7 @@ public class KafkaProducer extends DefaultAsyncProducer {
     @SuppressWarnings("rawtypes")
     private org.apache.kafka.clients.producer.Producer kafkaProducer;
     private KafkaProducerHealthCheck producerHealthCheck;
-    private KafkaHealthCheckRepository healthCheckRepository;
+    private WritableHealthCheckRepository healthCheckRepository;
     private String clientId;
     private String transactionId;
     private final KafkaEndpoint endpoint;
@@ -188,8 +189,11 @@ public class KafkaProducer extends DefaultAsyncProducer {
         }
 
         // health-check is optional so discover and resolve
-        healthCheckRepository = HealthCheckHelper.getHealthCheckRepository(endpoint.getCamelContext(), "camel-kafka",
-                KafkaHealthCheckRepository.class);
+        healthCheckRepository = HealthCheckHelper.getHealthCheckRepository(
+                endpoint.getCamelContext(),
+                "components",
+                WritableHealthCheckRepository.class);
+
         if (healthCheckRepository != null) {
             producerHealthCheck = new KafkaProducerHealthCheck(this, clientId);
             healthCheckRepository.addHealthCheck(producerHealthCheck);
diff --git a/components/camel-microprofile/camel-microprofile-health/src/main/java/org/apache/camel/microprofile/health/CamelMicroProfileHealthCheckRegistry.java b/components/camel-microprofile/camel-microprofile-health/src/main/java/org/apache/camel/microprofile/health/CamelMicroProfileHealthCheckRegistry.java
index b339d57d978..82d158ad561 100644
--- a/components/camel-microprofile/camel-microprofile-health/src/main/java/org/apache/camel/microprofile/health/CamelMicroProfileHealthCheckRegistry.java
+++ b/components/camel-microprofile/camel-microprofile-health/src/main/java/org/apache/camel/microprofile/health/CamelMicroProfileHealthCheckRegistry.java
@@ -27,6 +27,7 @@ import org.apache.camel.StartupListener;
 import org.apache.camel.health.HealthCheck;
 import org.apache.camel.health.HealthCheckRegistry;
 import org.apache.camel.health.HealthCheckRepository;
+import org.apache.camel.impl.health.ComponentsHealthCheckRepository;
 import org.apache.camel.impl.health.ConsumersHealthCheckRepository;
 import org.apache.camel.impl.health.DefaultHealthCheckRegistry;
 import org.apache.camel.impl.health.HealthCheckRegistryRepository;
@@ -89,7 +90,8 @@ public class CamelMicroProfileHealthCheckRegistry extends DefaultHealthCheckRegi
             boolean isAllChecksLiveness = repository.stream().allMatch(HealthCheck::isLiveness);
             boolean isAllChecksReadiness = repository.stream().allMatch(HealthCheck::isReadiness);
 
-            if (!(repository instanceof HealthCheckRegistryRepository) && (isAllChecksLiveness || isAllChecksReadiness)) {
+            if (!(repository instanceof HealthCheckRegistryRepository)
+                    && (isAllChecksLiveness || isAllChecksReadiness)) {
                 try {
                     if (isAllChecksLiveness) {
                         getLivenessRegistry().remove(repository.getId());
@@ -100,7 +102,8 @@ public class CamelMicroProfileHealthCheckRegistry extends DefaultHealthCheckRegi
                     }
                 } catch (IllegalStateException e) {
                     if (LOG.isDebugEnabled()) {
-                        LOG.debug("Failed to remove repository readiness health {} check due to: {}", repository.getId(),
+                        LOG.debug("Failed to remove repository readiness health {} check due to: {}",
+                                repository.getId(),
                                 e.getMessage());
                     }
                 }
@@ -113,12 +116,13 @@ public class CamelMicroProfileHealthCheckRegistry extends DefaultHealthCheckRegi
 
     @Override
     public void onCamelContextStarted(CamelContext context, boolean alreadyStarted) throws Exception {
-        //Noop
+        // Noop
     }
 
     @Override
     public void onCamelContextFullyStarted(CamelContext context, boolean alreadyStarted) throws Exception {
-        // Some repository checks may not be resolvable earlier in the lifecycle, so try one last time on CamelContext started
+        // Some repository checks may not be resolvable earlier in the lifecycle, so try
+        // one last time on CamelContext started
         if (alreadyStarted) {
             repositories.stream()
                     .filter(repository -> repository.stream().findAny().isPresent())
@@ -133,26 +137,32 @@ public class CamelMicroProfileHealthCheckRegistry extends DefaultHealthCheckRegi
             boolean isAllChecksReadiness = repository.stream().allMatch(HealthCheck::isReadiness);
 
             if (repository instanceof HealthCheckRegistryRepository || !isAllChecksLiveness && !isAllChecksReadiness) {
-                // Register each check individually for HealthCheckRegistryRepository or where the repository contains
+                // Register each check individually for HealthCheckRegistryRepository or where
+                // the repository contains
                 // a mix or readiness and liveness checks
                 repository.stream()
                         .filter(HealthCheck::isEnabled)
                         .forEach(this::registerMicroProfileHealthCheck);
             } else {
-                // Since the number of potential checks for consumers / routes etc is non-deterministic
-                // avoid registering each one with SmallRye health and instead aggregate the results so
+                // Since the number of potential checks for consumers / routes etc is
+                // non-deterministic
+                // avoid registering each one with SmallRye health and instead aggregate the
+                // results so
                 // that we avoid highly verbose health output
                 String healthCheckName = repository.getId();
-                if (repository.getClass().getName().startsWith("org.apache.camel") && !healthCheckName.startsWith("camel-")) {
+                if (repository.getClass().getName().startsWith("org.apache.camel")
+                        && !healthCheckName.startsWith("camel-")) {
                     healthCheckName = "camel-" + healthCheckName;
                 }
 
-                CamelMicroProfileRepositoryHealthCheck repositoryHealthCheck
-                        = new CamelMicroProfileRepositoryHealthCheck(getCamelContext(), repository, healthCheckName);
+                CamelMicroProfileRepositoryHealthCheck repositoryHealthCheck = new CamelMicroProfileRepositoryHealthCheck(
+                        getCamelContext(), repository, healthCheckName);
 
-                if (repository instanceof RoutesHealthCheckRepository || repository instanceof ConsumersHealthCheckRepository) {
-                    // Eagerly register routes & consumers HealthCheckRepository since routes may be supervised
-                    // and added with an initial delay. E.g repository.stream() may be empty initially but will eventually
+                if (registerEagerly(repository)) {
+                    // Eagerly register routes, components & consumers HealthCheckRepository since
+                    // routes may be supervised
+                    // and added with an initial delay. E.g repository.stream() may be empty
+                    // initially but will eventually
                     // return some results
                     getReadinessRegistry().register(repository.getId(), repositoryHealthCheck);
                 } else {
@@ -169,8 +179,8 @@ public class CamelMicroProfileHealthCheckRegistry extends DefaultHealthCheckRegi
     }
 
     protected void registerMicroProfileHealthCheck(HealthCheck camelHealthCheck) {
-        org.eclipse.microprofile.health.HealthCheck microProfileHealthCheck
-                = new CamelMicroProfileHealthCheck(getCamelContext(), camelHealthCheck);
+        org.eclipse.microprofile.health.HealthCheck microProfileHealthCheck = new CamelMicroProfileHealthCheck(
+                getCamelContext(), camelHealthCheck);
 
         if (camelHealthCheck.isReadiness()) {
             getReadinessRegistry().register(camelHealthCheck.getId(), microProfileHealthCheck);
@@ -204,8 +214,16 @@ public class CamelMicroProfileHealthCheckRegistry extends DefaultHealthCheckRegi
     }
 
     protected boolean canRegister(HealthCheckRepository repository) {
-        return repository instanceof RoutesHealthCheckRepository || repository instanceof ConsumersHealthCheckRepository
-                || repository.stream().findAny().isPresent();
+        return repository.stream().findAny().isPresent()
+                || repository instanceof RoutesHealthCheckRepository
+                || repository instanceof ConsumersHealthCheckRepository
+                || repository instanceof ComponentsHealthCheckRepository;
+    }
+
+    protected boolean registerEagerly(HealthCheckRepository repository) {
+        return repository instanceof RoutesHealthCheckRepository
+                || repository instanceof ConsumersHealthCheckRepository
+                || repository instanceof ComponentsHealthCheckRepository;
     }
 
     protected HealthRegistry getLivenessRegistry() {
diff --git a/components/camel-microprofile/camel-microprofile-health/src/test/java/org/apache/camel/microprofile/health/CamelMicroProfileHealthComponentsTest.java b/components/camel-microprofile/camel-microprofile-health/src/test/java/org/apache/camel/microprofile/health/CamelMicroProfileHealthComponentsTest.java
new file mode 100644
index 00000000000..2ee2c187555
--- /dev/null
+++ b/components/camel-microprofile/camel-microprofile-health/src/test/java/org/apache/camel/microprofile/health/CamelMicroProfileHealthComponentsTest.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.microprofile.health;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+
+import io.smallrye.health.SmallRyeHealth;
+import org.apache.camel.CamelContext;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.health.HealthCheck;
+import org.apache.camel.health.HealthCheckRegistry;
+import org.eclipse.microprofile.health.HealthCheckResponse.Status;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class CamelMicroProfileHealthComponentsTest extends CamelMicroProfileHealthTestSupport {
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext camelContext = super.createCamelContext();
+        camelContext.addComponent("my", new CamelMicroProfileHealthTestHelper.MyComponent());
+
+        HealthCheckRegistry healthCheckRegistry = HealthCheckRegistry.get(camelContext);
+        // enable consumers health check
+        Object hc = healthCheckRegistry.resolveById("components");
+        healthCheckRegistry.register(hc);
+
+        return camelContext;
+    }
+
+    @Test
+    public void testCamelComponentRepositoryUpStatus() {
+        context.getComponent("my", CamelMicroProfileHealthTestHelper.MyComponent.class).setState(HealthCheck.State.UP);
+
+        SmallRyeHealth health = reporter.getHealth();
+
+        JsonObject healthObject = getHealthJson(health);
+        assertEquals(Status.UP.name(), healthObject.getString("status"));
+
+        JsonArray checks = healthObject.getJsonArray("checks");
+
+        assertHealthCheckOutput("camel-components", Status.UP, checks);
+    }
+
+    @Test
+    public void testCamelComponentRepositoryDownStatus() {
+        context.getComponent("my", CamelMicroProfileHealthTestHelper.MyComponent.class).setState(HealthCheck.State.DOWN);
+
+        SmallRyeHealth health = reporter.getHealth();
+
+        JsonObject healthObject = getHealthJson(health);
+        assertEquals(Status.DOWN.name(), healthObject.getString("status"));
+
+        JsonArray checks = healthObject.getJsonArray("checks");
+
+        assertHealthCheckOutput("camel-components", Status.DOWN, checks);
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("my:start").routeId("healthyRoute")
+                        .setBody(constant("Hello Camel MicroProfile Health"));
+            }
+        };
+    }
+}
diff --git a/components/camel-microprofile/camel-microprofile-health/src/test/java/org/apache/camel/microprofile/health/CamelMicroProfileHealthSupervisedRoutesMainTest.java b/components/camel-microprofile/camel-microprofile-health/src/test/java/org/apache/camel/microprofile/health/CamelMicroProfileHealthSupervisedRoutesMainTest.java
index 9ea95135080..e7c240b48a2 100644
--- a/components/camel-microprofile/camel-microprofile-health/src/test/java/org/apache/camel/microprofile/health/CamelMicroProfileHealthSupervisedRoutesMainTest.java
+++ b/components/camel-microprofile/camel-microprofile-health/src/test/java/org/apache/camel/microprofile/health/CamelMicroProfileHealthSupervisedRoutesMainTest.java
@@ -37,18 +37,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.fail;
 
 public class CamelMicroProfileHealthSupervisedRoutesMainTest {
-    private SmallRyeHealthReporter reporter = new SmallRyeHealthReporter();
+    private final SmallRyeHealthReporter reporter = new SmallRyeHealthReporter();
 
     @Test
     public void testSupervisedRouteHealthChecks() throws Exception {
         CamelContext context = new DefaultCamelContext();
         CamelMicroProfileHealthCheckRegistry registry = new CamelMicroProfileHealthCheckRegistry(context);
+        context.addComponent("my", new CamelMicroProfileHealthTestHelper.MyComponent());
         context.setExtension(HealthCheckRegistry.class, registry);
         context.getRouteController().supervising();
         context.addRoutes(new RouteBuilder() {
             @Override
             public void configure() throws Exception {
-                from("direct:start").routeId("healthyRoute")
+                from("my:start").routeId("healthyRoute")
                         .setBody(constant("Hello Camel MicroProfile Health"));
             }
         });
@@ -56,7 +57,9 @@ public class CamelMicroProfileHealthSupervisedRoutesMainTest {
         SimpleMain main = new SimpleMain(context);
         main.addInitialProperty("camel.health.routes-enabled", "true");
         main.addInitialProperty("camel.health.consumers-enabled", "true");
+        main.addInitialProperty("camel.health.components-enabled", "true");
         main.start();
+
         try {
             SmallRyeHealth health = reporter.getHealth();
 
@@ -64,7 +67,7 @@ public class CamelMicroProfileHealthSupervisedRoutesMainTest {
             assertEquals(Status.UP.name(), healthObject.getString("status"));
 
             JsonArray checks = healthObject.getJsonArray("checks");
-            assertEquals(3, checks.size());
+            assertEquals(4, checks.size());
 
             Optional<JsonObject> camelRoutesCheck = findHealthCheck("camel-routes", checks);
             camelRoutesCheck.ifPresentOrElse(check -> {
@@ -75,6 +78,11 @@ public class CamelMicroProfileHealthSupervisedRoutesMainTest {
             camelConsumersCheck.ifPresentOrElse(check -> {
                 assertEquals(Status.UP.toString(), check.getString("status"));
             }, () -> fail("Expected camel-consumers check not found in health output"));
+
+            Optional<JsonObject> camelComponentsCheck = findHealthCheck("camel-components", checks);
+            camelComponentsCheck.ifPresentOrElse(check -> {
+                assertEquals(Status.UP.toString(), check.getString("status"));
+            }, () -> fail("Expected camel-components check not found in health output"));
         } finally {
             main.stop();
         }
@@ -86,5 +94,4 @@ public class CamelMicroProfileHealthSupervisedRoutesMainTest {
                 .filter(jsonObject -> jsonObject.getString("name").equals(name))
                 .findFirst();
     }
-
 }
diff --git a/components/camel-microprofile/camel-microprofile-health/src/test/java/org/apache/camel/microprofile/health/CamelMicroProfileHealthTestHelper.java b/components/camel-microprofile/camel-microprofile-health/src/test/java/org/apache/camel/microprofile/health/CamelMicroProfileHealthTestHelper.java
index f36562eb71f..70f02eb80d3 100644
--- a/components/camel-microprofile/camel-microprofile-health/src/test/java/org/apache/camel/microprofile/health/CamelMicroProfileHealthTestHelper.java
+++ b/components/camel-microprofile/camel-microprofile-health/src/test/java/org/apache/camel/microprofile/health/CamelMicroProfileHealthTestHelper.java
@@ -19,14 +19,30 @@ package org.apache.camel.microprofile.health;
 import java.io.ByteArrayOutputStream;
 import java.io.StringReader;
 import java.nio.charset.StandardCharsets;
+import java.util.Map;
 import java.util.function.Consumer;
 
 import javax.json.Json;
+import javax.json.JsonArray;
 import javax.json.JsonObject;
 import javax.json.stream.JsonParser;
 
 import io.smallrye.health.SmallRyeHealth;
 import io.smallrye.health.SmallRyeHealthReporter;
+import org.apache.camel.Component;
+import org.apache.camel.Endpoint;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.Producer;
+import org.apache.camel.health.HealthCheck;
+import org.apache.camel.health.HealthCheckHelper;
+import org.apache.camel.health.HealthCheckResultBuilder;
+import org.apache.camel.impl.health.AbstractHealthCheck;
+import org.apache.camel.impl.health.ComponentsHealthCheckRepository;
+import org.apache.camel.support.DefaultComponent;
+import org.apache.camel.support.DefaultConsumer;
+import org.apache.camel.support.DefaultEndpoint;
+import org.apache.camel.support.DefaultProducer;
 import org.eclipse.microprofile.health.HealthCheckResponse;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -45,6 +61,13 @@ public final class CamelMicroProfileHealthTestHelper {
         assertHealthCheckOutput(expectedName, expectedState, healthObject, null);
     }
 
+    public static void assertHealthCheckOutput(
+            String expectedName,
+            HealthCheckResponse.Status expectedState,
+            JsonArray healthObjects) {
+        assertHealthCheckOutput(expectedName, expectedState, healthObjects, null);
+    }
+
     public static void assertHealthCheckOutput(
             String expectedName,
             HealthCheckResponse.Status expectedState,
@@ -59,6 +82,31 @@ public final class CamelMicroProfileHealthTestHelper {
         }
     }
 
+    public static void assertHealthCheckOutput(
+            String expectedName,
+            HealthCheckResponse.Status expectedState,
+            JsonArray healthObjects,
+            Consumer<JsonObject> dataObjectAssertions) {
+
+        boolean match = false;
+
+        for (int i = 0; i < healthObjects.size(); i++) {
+            JsonObject healthObject = healthObjects.getJsonObject(i);
+
+            if (expectedName.equals(healthObject.getString("name"))) {
+                match = true;
+
+                assertEquals(expectedState.name(), healthObject.getString("status"));
+
+                if (dataObjectAssertions != null) {
+                    dataObjectAssertions.accept(healthObject.getJsonObject("data"));
+                }
+            }
+        }
+
+        assertTrue(match, "No elements with name " + expectedName);
+    }
+
     public static JsonObject getHealthJson(SmallRyeHealthReporter reporter, SmallRyeHealth health) {
         JsonParser parser = Json.createParser(new StringReader(getHealthOutput(reporter, health)));
         assertTrue(parser.hasNext(), "Health check content is empty");
@@ -75,4 +123,81 @@ public final class CamelMicroProfileHealthTestHelper {
     public static void dumpHealth(SmallRyeHealthReporter reporter, SmallRyeHealth health) {
         reporter.reportHealth(System.out, health);
     }
+
+    public static class MyComponent extends DefaultComponent {
+        private final MyHealthCheck check = new MyHealthCheck("my-hc");
+
+        @Override
+        protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception {
+            return new MyEndpoint(uri, this, check);
+        }
+
+        public void setState(HealthCheck.State state) {
+            check.setState(state);
+        }
+    }
+
+    public static class MyEndpoint extends DefaultEndpoint {
+        private final MyHealthCheck check;
+
+        public MyEndpoint(String endpointUri, Component component, MyHealthCheck check) {
+            super(endpointUri, component);
+
+            this.check = check;
+        }
+
+        @Override
+        protected void doStart() throws Exception {
+            super.doStart();
+
+            var repo = HealthCheckHelper.getHealthCheckRepository(
+                    getCamelContext(),
+                    ComponentsHealthCheckRepository.REPOSITORY_ID,
+                    ComponentsHealthCheckRepository.class);
+
+            if (repo != null) {
+                repo.addHealthCheck(this.check);
+            }
+        }
+
+        @Override
+        public Producer createProducer() throws Exception {
+            return new DefaultProducer(this) {
+                @Override
+                public void process(Exchange exchange) throws Exception {
+                }
+            };
+        }
+
+        @Override
+        public org.apache.camel.Consumer createConsumer(Processor processor) throws Exception {
+            return new DefaultConsumer(this, processor) {
+            };
+        }
+    }
+
+    public static class MyHealthCheck extends AbstractHealthCheck {
+        private HealthCheck.State state;
+
+        public MyHealthCheck(String id) {
+            super(id);
+
+            this.state = State.UP;
+        }
+
+        @Override
+        protected void doCall(HealthCheckResultBuilder builder, Map<String, Object> options) {
+            builder.state(state);
+        }
+
+        @Override
+        public boolean isLiveness() {
+            return false;
+        }
+
+        public void setState(HealthCheck.State state) {
+            this.state = state;
+        }
+
+    }
 }
diff --git a/components/camel-microprofile/camel-microprofile-health/src/test/java/org/apache/camel/microprofile/health/CamelMicroProfileHealthTestSupport.java b/components/camel-microprofile/camel-microprofile-health/src/test/java/org/apache/camel/microprofile/health/CamelMicroProfileHealthTestSupport.java
index c27765c059c..4d2ac0d67e7 100644
--- a/components/camel-microprofile/camel-microprofile-health/src/test/java/org/apache/camel/microprofile/health/CamelMicroProfileHealthTestSupport.java
+++ b/components/camel-microprofile/camel-microprofile-health/src/test/java/org/apache/camel/microprofile/health/CamelMicroProfileHealthTestSupport.java
@@ -21,6 +21,7 @@ import java.util.Map;
 import java.util.function.Consumer;
 import java.util.stream.Stream;
 
+import javax.json.JsonArray;
 import javax.json.JsonObject;
 
 import io.smallrye.health.SmallRyeHealth;
@@ -78,6 +79,13 @@ public class CamelMicroProfileHealthTestSupport extends CamelTestSupport {
         CamelMicroProfileHealthTestHelper.assertHealthCheckOutput(expectedName, expectedState, healthObject);
     }
 
+    protected void assertHealthCheckOutput(
+            String expectedName,
+            HealthCheckResponse.Status expectedState,
+            JsonArray healthObjects) {
+        CamelMicroProfileHealthTestHelper.assertHealthCheckOutput(expectedName, expectedState, healthObjects);
+    }
+
     protected void assertHealthCheckOutput(
             String expectedName,
             HealthCheckResponse.Status expectedState,
@@ -88,6 +96,16 @@ public class CamelMicroProfileHealthTestSupport extends CamelTestSupport {
                 dataObjectAssertions);
     }
 
+    protected void assertHealthCheckOutput(
+            String expectedName,
+            HealthCheckResponse.Status expectedState,
+            JsonArray healthObjects,
+            Consumer<JsonObject> dataObjectAssertions) {
+
+        CamelMicroProfileHealthTestHelper.assertHealthCheckOutput(expectedName, expectedState, healthObjects,
+                dataObjectAssertions);
+    }
+
     protected JsonObject getHealthJson(SmallRyeHealth health) {
         return CamelMicroProfileHealthTestHelper.getHealthJson(reporter, health);
     }
diff --git a/core/camel-api/src/main/java/org/apache/camel/health/HealthCheckRepository.java b/core/camel-api/src/main/java/org/apache/camel/health/HasHealthChecks.java
similarity index 53%
copy from core/camel-api/src/main/java/org/apache/camel/health/HealthCheckRepository.java
copy to core/camel-api/src/main/java/org/apache/camel/health/HasHealthChecks.java
index 9433e5e986a..9b0a76fdbc2 100644
--- a/core/camel-api/src/main/java/org/apache/camel/health/HealthCheckRepository.java
+++ b/core/camel-api/src/main/java/org/apache/camel/health/HasHealthChecks.java
@@ -16,40 +16,15 @@
  */
 package org.apache.camel.health;
 
-import java.util.Optional;
 import java.util.stream.Stream;
 
-import org.apache.camel.spi.HasId;
-import org.apache.camel.util.ObjectHelper;
-
 /**
- * A repository for health checks.
+ * An interface to represent an object which provides {@link HealthCheck}
  */
-public interface HealthCheckRepository extends HasId {
-
-    /**
-     * Set if the checks associated to this repository is enabled or not.
-     */
-    boolean isEnabled();
-
-    /**
-     * Set if the checks associated to this repository is enabled or not.
-     */
-    void setEnabled(boolean enabled);
-
+public interface HasHealthChecks {
     /**
      * Returns a sequential {@code Stream} with the known {@link HealthCheck} as its source.
      */
     Stream<HealthCheck> stream();
 
-    /**
-     * Returns the check identified by the given <code>id</code> if available.
-     */
-    default Optional<HealthCheck> getCheck(String id) {
-        return stream()
-                .filter(r -> ObjectHelper.equal(r.getId(), id)
-                        || ObjectHelper.equal(r.getId().replace("-health-check", ""), id)
-                        || ObjectHelper.equal(r.getId().replace("route:", ""), id))
-                .findFirst();
-    }
 }
diff --git a/core/camel-api/src/main/java/org/apache/camel/health/HealthCheckRepository.java b/core/camel-api/src/main/java/org/apache/camel/health/HealthCheckRepository.java
index 9433e5e986a..b30e35918ee 100644
--- a/core/camel-api/src/main/java/org/apache/camel/health/HealthCheckRepository.java
+++ b/core/camel-api/src/main/java/org/apache/camel/health/HealthCheckRepository.java
@@ -17,7 +17,6 @@
 package org.apache.camel.health;
 
 import java.util.Optional;
-import java.util.stream.Stream;
 
 import org.apache.camel.spi.HasId;
 import org.apache.camel.util.ObjectHelper;
@@ -25,7 +24,7 @@ import org.apache.camel.util.ObjectHelper;
 /**
  * A repository for health checks.
  */
-public interface HealthCheckRepository extends HasId {
+public interface HealthCheckRepository extends HasId, HasHealthChecks {
 
     /**
      * Set if the checks associated to this repository is enabled or not.
@@ -37,11 +36,6 @@ public interface HealthCheckRepository extends HasId {
      */
     void setEnabled(boolean enabled);
 
-    /**
-     * Returns a sequential {@code Stream} with the known {@link HealthCheck} as its source.
-     */
-    Stream<HealthCheck> stream();
-
     /**
      * Returns the check identified by the given <code>id</code> if available.
      */
diff --git a/core/camel-api/src/main/java/org/apache/camel/health/WritableHealthCheckRepository.java b/core/camel-api/src/main/java/org/apache/camel/health/WritableHealthCheckRepository.java
new file mode 100644
index 00000000000..f3f00ee95f3
--- /dev/null
+++ b/core/camel-api/src/main/java/org/apache/camel/health/WritableHealthCheckRepository.java
@@ -0,0 +1,33 @@
+/*
+ * 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.health;
+
+/**
+ * An interface to represent an object which wishes to be injected with the {@link HealthCheck}
+ */
+public interface WritableHealthCheckRepository extends HealthCheckRepository {
+
+    /**
+     * Adds a {@link HealthCheck} to the repository.
+     */
+    void addHealthCheck(HealthCheck healthCheck);
+
+    /**
+     * Removes a {@link HealthCheck} from the repository.
+     */
+    void removeHealthCheck(HealthCheck healthCheck);
+}
diff --git a/core/camel-health/src/generated/resources/META-INF/services/org/apache/camel/health-check/components-repository b/core/camel-health/src/generated/resources/META-INF/services/org/apache/camel/health-check/components-repository
new file mode 100644
index 00000000000..6dd0dfad13a
--- /dev/null
+++ b/core/camel-health/src/generated/resources/META-INF/services/org/apache/camel/health-check/components-repository
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.health.ComponentsHealthCheckRepository
diff --git a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaHealthCheckRepository.java b/core/camel-health/src/main/java/org/apache/camel/impl/health/ComponentsHealthCheckRepository.java
similarity index 73%
rename from components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaHealthCheckRepository.java
rename to core/camel-health/src/main/java/org/apache/camel/impl/health/ComponentsHealthCheckRepository.java
index 7517605670a..5c80e1ec5f2 100644
--- a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaHealthCheckRepository.java
+++ b/core/camel-health/src/main/java/org/apache/camel/impl/health/ComponentsHealthCheckRepository.java
@@ -14,10 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.component.kafka;
+package org.apache.camel.impl.health;
 
-import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.stream.Stream;
 
 import org.apache.camel.CamelContext;
@@ -26,22 +26,26 @@ import org.apache.camel.DeferredContextBinding;
 import org.apache.camel.NonManagedService;
 import org.apache.camel.StaticService;
 import org.apache.camel.health.HealthCheck;
-import org.apache.camel.health.HealthCheckRepository;
+import org.apache.camel.health.WritableHealthCheckRepository;
 import org.apache.camel.support.service.ServiceSupport;
 
 /**
- * Repository for camel-kafka {@link HealthCheck}s.
+ * Repository for components {@link HealthCheck}s.
  */
-@org.apache.camel.spi.annotations.HealthCheck("camel-kafka-repository")
+@org.apache.camel.spi.annotations.HealthCheck(ComponentsHealthCheckRepository.REPOSITORY_NAME)
 @DeferredContextBinding
-public class KafkaHealthCheckRepository extends ServiceSupport
-        implements CamelContextAware, HealthCheckRepository, StaticService, NonManagedService {
+public class ComponentsHealthCheckRepository extends ServiceSupport
+        implements CamelContextAware, WritableHealthCheckRepository, StaticService, NonManagedService {
 
-    private final List<HealthCheck> checks = new ArrayList<>();
+    public static final String REPOSITORY_ID = "components";
+    public static final String REPOSITORY_NAME = "components-repository";
+
+    private final List<HealthCheck> checks;
     private volatile CamelContext context;
     private boolean enabled = true;
 
-    public KafkaHealthCheckRepository() {
+    public ComponentsHealthCheckRepository() {
+        this.checks = new CopyOnWriteArrayList<>();
     }
 
     @Override
@@ -51,7 +55,7 @@ public class KafkaHealthCheckRepository extends ServiceSupport
 
     @Override
     public String getId() {
-        return "camel-kafka";
+        return REPOSITORY_ID;
     }
 
     @Override
@@ -76,13 +80,14 @@ public class KafkaHealthCheckRepository extends ServiceSupport
                 : Stream.empty();
     }
 
+    @Override
     public void addHealthCheck(HealthCheck healthCheck) {
         CamelContextAware.trySetCamelContext(healthCheck, getCamelContext());
         this.checks.add(healthCheck);
     }
 
+    @Override
     public void removeHealthCheck(HealthCheck healthCheck) {
         this.checks.remove(healthCheck);
     }
-
 }
diff --git a/core/camel-main/src/generated/java/org/apache/camel/main/HealthConfigurationPropertiesConfigurer.java b/core/camel-main/src/generated/java/org/apache/camel/main/HealthConfigurationPropertiesConfigurer.java
index 6b784e8d734..80957c1b00a 100644
--- a/core/camel-main/src/generated/java/org/apache/camel/main/HealthConfigurationPropertiesConfigurer.java
+++ b/core/camel-main/src/generated/java/org/apache/camel/main/HealthConfigurationPropertiesConfigurer.java
@@ -21,6 +21,8 @@ public class HealthConfigurationPropertiesConfigurer extends org.apache.camel.su
     public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) {
         org.apache.camel.main.HealthConfigurationProperties target = (org.apache.camel.main.HealthConfigurationProperties) obj;
         switch (ignoreCase ? name.toLowerCase() : name) {
+        case "componentsenabled":
+        case "ComponentsEnabled": target.setComponentsEnabled(property(camelContext, java.lang.Boolean.class, value)); return true;
         case "consumersenabled":
         case "ConsumersEnabled": target.setConsumersEnabled(property(camelContext, java.lang.Boolean.class, value)); return true;
         case "enabled":
@@ -42,6 +44,8 @@ public class HealthConfigurationPropertiesConfigurer extends org.apache.camel.su
     @Override
     public Class<?> getOptionType(String name, boolean ignoreCase) {
         switch (ignoreCase ? name.toLowerCase() : name) {
+        case "componentsenabled":
+        case "ComponentsEnabled": return java.lang.Boolean.class;
         case "consumersenabled":
         case "ConsumersEnabled": return java.lang.Boolean.class;
         case "enabled":
@@ -64,6 +68,8 @@ public class HealthConfigurationPropertiesConfigurer extends org.apache.camel.su
     public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
         org.apache.camel.main.HealthConfigurationProperties target = (org.apache.camel.main.HealthConfigurationProperties) obj;
         switch (ignoreCase ? name.toLowerCase() : name) {
+        case "componentsenabled":
+        case "ComponentsEnabled": return target.getComponentsEnabled();
         case "consumersenabled":
         case "ConsumersEnabled": return target.getConsumersEnabled();
         case "enabled":
diff --git a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
index da0c1336d03..5a9342a6808 100644
--- a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
+++ b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
@@ -143,6 +143,7 @@
     { "name": "camel.faulttolerance.timeoutEnabled", "description": "Whether timeout is enabled or not on the circuit breaker. Default is false.", "sourceType": "org.apache.camel.main.FaultToleranceConfigurationProperties", "type": "boolean", "javaType": "java.lang.Boolean", "defaultValue": false },
     { "name": "camel.faulttolerance.timeoutPoolSize", "description": "Configures the pool size of the thread pool when timeout is enabled. Default value is 10.", "sourceType": "org.apache.camel.main.FaultToleranceConfigurationProperties", "type": "integer", "javaType": "java.lang.Integer", "defaultValue": 10 },
     { "name": "camel.faulttolerance.timeoutScheduledExecutorService", "description": "References to a custom thread pool to use when timeout is enabled", "sourceType": "org.apache.camel.main.FaultToleranceConfigurationProperties", "type": "string", "javaType": "java.lang.String" },
+    { "name": "camel.health.componentsEnabled", "description": "Whether components health check is enabled", "sourceType": "org.apache.camel.main.HealthConfigurationProperties", "type": "boolean", "javaType": "java.lang.Boolean", "defaultValue": true },
     { "name": "camel.health.consumersEnabled", "description": "Whether consumers health check is enabled", "sourceType": "org.apache.camel.main.HealthConfigurationProperties", "type": "boolean", "javaType": "java.lang.Boolean", "defaultValue": true },
     { "name": "camel.health.enabled", "description": "Whether health check is enabled globally", "sourceType": "org.apache.camel.main.HealthConfigurationProperties", "type": "boolean", "javaType": "java.lang.Boolean", "defaultValue": true },
     { "name": "camel.health.excludePattern", "description": "Pattern to exclude health checks from being invoked by Camel when checking healths. Multiple patterns can be separated by comma.", "sourceType": "org.apache.camel.main.HealthConfigurationProperties", "type": "string", "javaType": "java.lang.String" },
diff --git a/core/camel-main/src/main/docs/main.adoc b/core/camel-main/src/main/docs/main.adoc
index 6d9f53c533a..7a1f658290d 100644
--- a/core/camel-main/src/main/docs/main.adoc
+++ b/core/camel-main/src/main/docs/main.adoc
@@ -160,11 +160,12 @@ The camel.threadpool supports 8 options, which are listed below.
 |===
 
 === Camel Health Check configurations
-The camel.health supports 7 options, which are listed below.
+The camel.health supports 8 options, which are listed below.
 
 [width="100%",cols="2,5,^1,2",options="header"]
 |===
 | Name | Description | Default | Type
+| *camel.health.componentsEnabled* | Whether components health check is enabled | true | Boolean
 | *camel.health.consumersEnabled* | Whether consumers health check is enabled | true | Boolean
 | *camel.health.enabled* | Whether health check is enabled globally | true | Boolean
 | *camel.health.excludePattern* | Pattern to exclude health checks from being invoked by Camel when checking healths. Multiple patterns can be separated by comma. |  | String
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
index fc9e95f18a5..e5d6aa38f0a 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
@@ -1253,6 +1253,17 @@ public abstract class BaseMainSupport extends BaseService {
                 hcr.register(hc);
             }
         }
+        // components are enabled by default
+        if (hcr.isEnabled()) {
+            HealthCheckRepository hc
+                    = hcr.getRepository("components").orElse((HealthCheckRepository) hcr.resolveById("components"));
+            if (hc != null) {
+                if (health.getComponentsEnabled() != null) {
+                    hc.setEnabled(health.getComponentsEnabled());
+                }
+                hcr.register(hc);
+            }
+        }
         // consumers are enabled by default
         if (hcr.isEnabled()) {
             HealthCheckRepository hc
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/HealthConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/HealthConfigurationProperties.java
index a188921c4b1..c8954215f24 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/HealthConfigurationProperties.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/HealthConfigurationProperties.java
@@ -35,6 +35,8 @@ public class HealthConfigurationProperties implements BootstrapCloseable {
     @Metadata(defaultValue = "true")
     private Boolean consumersEnabled;
     @Metadata(defaultValue = "true")
+    private Boolean componentsEnabled;
+    @Metadata(defaultValue = "true")
     private Boolean registryEnabled;
     @Metadata
     private String excludePattern;
@@ -89,6 +91,17 @@ public class HealthConfigurationProperties implements BootstrapCloseable {
         this.consumersEnabled = consumersEnabled;
     }
 
+    public Boolean getComponentsEnabled() {
+        return componentsEnabled;
+    }
+
+    /**
+     * Whether components health check is enabled
+     */
+    public void setComponentsEnabled(Boolean componentsEnabled) {
+        this.componentsEnabled = componentsEnabled;
+    }
+
     public Boolean getRegistryEnabled() {
         return registryEnabled;
     }