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 <lburgazz...@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; }