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

davsclaus 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 cacd79f  CAMEL-17626: camel-health - SPI to plugin custom manipulation 
of health check
cacd79f is described below

commit cacd79fa02432df7c39228cce3937314ba5c7c45
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Feb 10 10:31:14 2022 +0100

    CAMEL-17626: camel-health - SPI to plugin custom manipulation of health 
check
---
 .../org/apache/camel/health/HealthCheckHelper.java | 59 ++++---------
 ...kFilter.java => HealthCheckResultStrategy.java} | 18 ++--
 .../health/DefaultHealthCheckRegistryTest.java     | 11 ---
 .../impl/health/HealthCheckResultStrategyTest.java | 86 +++++++++++++++++++
 .../apache/camel/impl/health/HealthCheckTest.java  |  5 ++
 .../impl/health/ReadinessAndLivenessTest.java      | 22 -----
 .../camel/impl/health/AbstractHealthCheck.java     | 99 +++++++++++++---------
 .../impl/health/DefaultHealthCheckRegistry.java    | 14 ++-
 .../impl/health/HealthCheckRegistryRepository.java |  3 +-
 .../modules/ROOT/pages/health-check.adoc           | 18 +++-
 10 files changed, 208 insertions(+), 127 deletions(-)

diff --git 
a/core/camel-api/src/main/java/org/apache/camel/health/HealthCheckHelper.java 
b/core/camel-api/src/main/java/org/apache/camel/health/HealthCheckHelper.java
index 1fe0d56..6336c1e 100644
--- 
a/core/camel-api/src/main/java/org/apache/camel/health/HealthCheckHelper.java
+++ 
b/core/camel-api/src/main/java/org/apache/camel/health/HealthCheckHelper.java
@@ -22,40 +22,27 @@ import java.util.Comparator;
 import java.util.Map;
 import java.util.Optional;
 import java.util.function.Function;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.util.ObjectHelper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * Helper for invoking {@link HealthCheck}'s.
  *
- * The helper will lookup the {@link HealthCheckRegistry} from {@link 
CamelContext} and gather all the registered
- * {@link HealthCheck}s and invoke them and gather their responses.
+ * The helper will look up the {@link HealthCheckRegistry} from {@link 
CamelContext} and gather all the registered
+ * {@link HealthCheck}s and invoke them and gather their results.
  *
- * The helper allows filtering out unwanted health checks using {@link 
HealthCheckFilter} or to invoke only readiness or
- * liveness checks.
+ * The helper allows filtering out unwanted health checks using {@link 
Predicate<HealthCheck>} or to invoke only
+ * readiness or liveness checks.
  */
 public final class HealthCheckHelper {
 
-    private static final Logger LOG = 
LoggerFactory.getLogger(HealthCheckHelper.class);
-
     private HealthCheckHelper() {
     }
 
     /**
-     * Get the group of the given check or an empty string if the group is not 
set.
-     *
-     * @param  check the health check
-     * @return       the {@link HealthCheck#getGroup()} or an empty string if 
it is <code>null</code>
-     */
-    public static String getGroup(HealthCheck check) {
-        return ObjectHelper.supplyIfEmpty(check.getGroup(), () -> "");
-    }
-
-    /**
      * Invokes the checks and returns a collection of results.
      */
     public static Collection<HealthCheck.Result> invoke(CamelContext 
camelContext) {
@@ -91,7 +78,7 @@ public final class HealthCheckHelper {
      */
     public static Collection<HealthCheck.Result> invoke(
             CamelContext camelContext,
-            HealthCheckFilter filter) {
+            Predicate<HealthCheck> filter) {
 
         return invoke(camelContext, check -> Collections.emptyMap(), filter);
     }
@@ -106,7 +93,7 @@ public final class HealthCheckHelper {
     public static Collection<HealthCheck.Result> invoke(
             CamelContext camelContext,
             Function<HealthCheck, Map<String, Object>> optionsSupplier,
-            HealthCheckFilter filter) {
+            Predicate<HealthCheck> filter) {
 
         final HealthCheckRegistry registry = 
HealthCheckRegistry.get(camelContext);
 
@@ -115,59 +102,43 @@ public final class HealthCheckHelper {
             // check one by one.
             return registry.stream()
                     
.collect(Collectors.groupingBy(HealthCheckHelper::getGroup))
-                    .entrySet().stream()
-                    .map(Map.Entry::getValue)
+                    .values().stream()
                     .flatMap(Collection::stream)
                     .filter(check -> !registry.isExcluded(check) && 
!filter.test(check))
                     .sorted(Comparator.comparingInt(HealthCheck::getOrder))
                     .distinct()
                     .map(check -> check.call(optionsSupplier.apply(check)))
                     .collect(Collectors.toList());
-        } else {
-            LOG.debug("No health check source found");
         }
 
         return Collections.emptyList();
     }
 
     /**
-     * Query the status of a check by id. Note that this may result in an 
effective invocation of the
-     * {@link HealthCheck}.
+     * Invoke a check by id.
      *
      * @param  camelContext the camel context.
      * @param  id           the check id.
      * @param  options      the check options.
      * @return              an optional {@link HealthCheck.Result}.
      */
-    public static Optional<HealthCheck.Result> query(CamelContext 
camelContext, String id, Map<String, Object> options) {
+    public static Optional<HealthCheck.Result> invoke(CamelContext 
camelContext, String id, Map<String, Object> options) {
         final HealthCheckRegistry registry = 
HealthCheckRegistry.get(camelContext);
 
         if (registry != null) {
             return registry.getCheck(id).map(check -> check.call(options));
-        } else {
-            LOG.debug("No health check source found");
         }
 
         return Optional.empty();
     }
 
     /**
-     * Invoke a check by id.
+     * Get the group of the given check or an empty string if the group is not 
set.
      *
-     * @param  camelContext the camel context.
-     * @param  id           the check id.
-     * @param  options      the check options.
-     * @return              an optional {@link HealthCheck.Result}.
+     * @param  check the health check
+     * @return       the {@link HealthCheck#getGroup()} or an empty string if 
it is <code>null</code>
      */
-    public static Optional<HealthCheck.Result> invoke(CamelContext 
camelContext, String id, Map<String, Object> options) {
-        final HealthCheckRegistry registry = 
HealthCheckRegistry.get(camelContext);
-
-        if (registry != null) {
-            return registry.getCheck(id).map(check -> check.call(options));
-        } else {
-            LOG.debug("No health check source found");
-        }
-
-        return Optional.empty();
+    private static String getGroup(HealthCheck check) {
+        return ObjectHelper.supplyIfEmpty(check.getGroup(), () -> "");
     }
 }
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/health/HealthCheckFilter.java 
b/core/camel-api/src/main/java/org/apache/camel/health/HealthCheckResultStrategy.java
similarity index 57%
rename from 
core/camel-api/src/main/java/org/apache/camel/health/HealthCheckFilter.java
rename to 
core/camel-api/src/main/java/org/apache/camel/health/HealthCheckResultStrategy.java
index 2481104..14bd356 100644
--- 
a/core/camel-api/src/main/java/org/apache/camel/health/HealthCheckFilter.java
+++ 
b/core/camel-api/src/main/java/org/apache/camel/health/HealthCheckResultStrategy.java
@@ -16,17 +16,21 @@
  */
 package org.apache.camel.health;
 
+import java.util.Map;
+
 /**
- * Health check filter.
+ * A strategy that allows SPI to process {@link HealthCheck} results and 
enrich and manipulate the result.
  */
-@FunctionalInterface
-public interface HealthCheckFilter {
+public interface HealthCheckResultStrategy {
 
     /**
-     * Determine if the given {@link HealthCheck} has to be filtered out.
+     * Processes and allows manipulation of the result from the {@link 
HealthCheck} invocation.
      *
-     * @param  check the check to evaluate.
-     * @return       true if the given <dode>check</dode> has to be filtered 
out.
+     * @param check   the invoked health check
+     * @param options optional options when invoked the health check
+     * @param builder the result builder that builds the health check 
response, which can be enriched and manipulated by
+     *                this strategy.
      */
-    boolean test(HealthCheck check);
+    void processResult(HealthCheck check, Map<String, Object> options, 
HealthCheckResultBuilder builder);
+
 }
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/impl/health/DefaultHealthCheckRegistryTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/impl/health/DefaultHealthCheckRegistryTest.java
index 11c805f..59d7cb2 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/impl/health/DefaultHealthCheckRegistryTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/impl/health/DefaultHealthCheckRegistryTest.java
@@ -202,8 +202,6 @@ public class DefaultHealthCheckRegistryTest {
 
     private static class MyHealthCheck extends AbstractHealthCheck implements 
CamelContextAware {
 
-        private CamelContext context;
-
         protected MyHealthCheck(String group, String id) {
             super(group, id);
         }
@@ -213,14 +211,5 @@ public class DefaultHealthCheckRegistryTest {
             builder.up();
         }
 
-        @Override
-        public void setCamelContext(CamelContext camelContext) {
-            this.context = camelContext;
-        }
-
-        @Override
-        public CamelContext getCamelContext() {
-            return context;
-        }
     }
 }
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/impl/health/HealthCheckResultStrategyTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/impl/health/HealthCheckResultStrategyTest.java
new file mode 100644
index 0000000..1afdc31
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/impl/health/HealthCheckResultStrategyTest.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.impl.health;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.health.HealthCheck;
+import org.apache.camel.health.HealthCheckHelper;
+import org.apache.camel.health.HealthCheckRegistry;
+import org.apache.camel.health.HealthCheckResultStrategy;
+import org.apache.camel.health.HealthCheckResultBuilder;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class HealthCheckResultStrategyTest extends ContextTestSupport {
+
+    @Override
+    public boolean isUseRouteBuilder() {
+        return false;
+    }
+
+    @Test
+    public void testMyFoo() throws Exception {
+        context.getRegistry().bind("myStrategy", new MyResultStrategy());
+        context.start();
+
+        HealthCheck hc
+                = 
context.adapt(ExtendedCamelContext.class).getHealthCheckResolver().resolveHealthCheck("myfoo");
+        Assertions.assertNotNull(hc);
+
+        Assertions.assertEquals("acme", hc.getGroup());
+        Assertions.assertEquals("myfoo", hc.getId());
+
+        HealthCheck.Result r = hc.call();
+
+        Assertions.assertEquals(HealthCheck.State.UP, r.getState());
+        Assertions.assertEquals("I changed this", r.getMessage().get());
+    }
+
+    @Test
+    public void testAddToRegistry() throws Exception {
+        context.getRegistry().bind("myStrategy", new MyResultStrategy());
+        context.start();
+
+        HealthCheck hc
+                = 
context.adapt(ExtendedCamelContext.class).getHealthCheckResolver().resolveHealthCheck("myfoo");
+        Assertions.assertNotNull(hc);
+
+        HealthCheckRegistry hcr = 
context.getExtension(HealthCheckRegistry.class);
+        hcr.register(hc);
+
+        Collection<HealthCheck.Result> col = HealthCheckHelper.invoke(context);
+        Assertions.assertEquals(1, col.size());
+
+        HealthCheck.Result r = col.iterator().next();
+        Assertions.assertEquals(HealthCheck.State.UP, r.getState());
+        Assertions.assertEquals("I changed this", r.getMessage().get());
+    }
+
+    private static class MyResultStrategy implements HealthCheckResultStrategy 
{
+
+        @Override
+        public void processResult(HealthCheck check, Map<String, Object> 
options, HealthCheckResultBuilder builder) {
+            builder.up();
+            builder.message("I changed this");
+        }
+    }
+
+}
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/impl/health/HealthCheckTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/impl/health/HealthCheckTest.java
index 4fbeb94..16ad8fc 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/impl/health/HealthCheckTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/impl/health/HealthCheckTest.java
@@ -18,8 +18,10 @@ package org.apache.camel.impl.health;
 
 import java.util.Map;
 
+import org.apache.camel.CamelContext;
 import org.apache.camel.health.HealthCheck;
 import org.apache.camel.health.HealthCheckResultBuilder;
+import org.apache.camel.impl.DefaultCamelContext;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -30,7 +32,10 @@ public class HealthCheckTest {
 
     @Test
     public void testCheck() throws Exception {
+        CamelContext context = new DefaultCamelContext();
+
         MyHealthCheck check = new MyHealthCheck();
+        check.setCamelContext(context);
         check.setState(HealthCheck.State.UP);
         // disable
         check.setEnabled(false);
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/impl/health/ReadinessAndLivenessTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/impl/health/ReadinessAndLivenessTest.java
index cade600..8a44ca1 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/impl/health/ReadinessAndLivenessTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/impl/health/ReadinessAndLivenessTest.java
@@ -71,8 +71,6 @@ public class ReadinessAndLivenessTest {
 
     private static class MyReadyCheck extends AbstractHealthCheck implements 
CamelContextAware {
 
-        private CamelContext context;
-
         protected MyReadyCheck(String group, String id) {
             super(group, id);
         }
@@ -87,21 +85,10 @@ public class ReadinessAndLivenessTest {
             builder.up();
         }
 
-        @Override
-        public void setCamelContext(CamelContext camelContext) {
-            this.context = camelContext;
-        }
-
-        @Override
-        public CamelContext getCamelContext() {
-            return context;
-        }
     }
 
     private static class MyLiveCheck extends AbstractHealthCheck implements 
CamelContextAware {
 
-        private CamelContext context;
-
         protected MyLiveCheck(String group, String id) {
             super(group, id);
         }
@@ -116,14 +103,5 @@ public class ReadinessAndLivenessTest {
             builder.down();
         }
 
-        @Override
-        public void setCamelContext(CamelContext camelContext) {
-            this.context = camelContext;
-        }
-
-        @Override
-        public CamelContext getCamelContext() {
-            return context;
-        }
     }
 }
diff --git 
a/core/camel-health/src/main/java/org/apache/camel/impl/health/AbstractHealthCheck.java
 
b/core/camel-health/src/main/java/org/apache/camel/impl/health/AbstractHealthCheck.java
index 8b7839a..bb29f64 100644
--- 
a/core/camel-health/src/main/java/org/apache/camel/impl/health/AbstractHealthCheck.java
+++ 
b/core/camel-health/src/main/java/org/apache/camel/impl/health/AbstractHealthCheck.java
@@ -20,12 +20,14 @@ import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.Collections;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.CamelContextAware;
 import org.apache.camel.health.HealthCheck;
+import org.apache.camel.health.HealthCheckResultStrategy;
 import org.apache.camel.health.HealthCheckResultBuilder;
 import org.apache.camel.util.ObjectHelper;
 import org.slf4j.Logger;
@@ -36,7 +38,7 @@ import org.slf4j.LoggerFactory;
  */
 public abstract class AbstractHealthCheck implements HealthCheck, 
CamelContextAware {
 
-    private static final Logger LOGGER = 
LoggerFactory.getLogger(AbstractHealthCheck.class);
+    private static final Logger LOG = 
LoggerFactory.getLogger(AbstractHealthCheck.class);
 
     private CamelContext camelContext;
     private boolean enabled = true;
@@ -111,56 +113,68 @@ public abstract class AbstractHealthCheck implements 
HealthCheck, CamelContextAw
 
     @Override
     public Result call(Map<String, Object> options) {
+        HealthCheckResultBuilder builder;
         synchronized (lock) {
-            final HealthCheckResultBuilder builder = 
HealthCheckResultBuilder.on(this);
+            builder = doCall(options);
+        }
+
+        HealthCheckResultStrategy strategy = 
customHealthCheckResponseStrategy();
+        if (strategy != null) {
+            strategy.processResult(this, options, builder);
+        }
 
-            // Extract relevant information from meta data.
-            int invocationCount = (Integer) 
meta.getOrDefault(INVOCATION_COUNT, 0);
-            int failureCount = (Integer) meta.getOrDefault(FAILURE_COUNT, 0);
-            int successCount = (Integer) meta.getOrDefault(SUCCESS_COUNT, 0);
+        return builder.build();
+    }
 
-            String invocationTime = 
ZonedDateTime.now().format(DateTimeFormatter.ISO_ZONED_DATE_TIME);
+    protected HealthCheckResultBuilder doCall(Map<String, Object> options) {
+        final HealthCheckResultBuilder builder = 
HealthCheckResultBuilder.on(this);
 
-            // Set common meta-data
-            meta.put(INVOCATION_ATTEMPT_TIME, invocationTime);
+        // Extract relevant information from meta data.
+        int invocationCount = (Integer) meta.getOrDefault(INVOCATION_COUNT, 0);
+        int failureCount = (Integer) meta.getOrDefault(FAILURE_COUNT, 0);
+        int successCount = (Integer) meta.getOrDefault(SUCCESS_COUNT, 0);
 
-            if (!isEnabled()) {
-                LOGGER.debug("health-check {}/{} disabled", getGroup(), 
getId());
+        String invocationTime = 
ZonedDateTime.now().format(DateTimeFormatter.ISO_ZONED_DATE_TIME);
 
-                builder.message("Disabled");
-                builder.detail(CHECK_ENABLED, false);
+        // Set common meta-data
+        meta.put(INVOCATION_ATTEMPT_TIME, invocationTime);
 
-                return builder.unknown().build();
-            }
+        if (!isEnabled()) {
+            LOG.debug("health-check {}/{} disabled", getGroup(), getId());
+            builder.message("Disabled");
+            builder.detail(CHECK_ENABLED, false);
+            builder.unknown();
+            return builder;
+        }
 
-            LOGGER.debug("Invoke health-check {}/{}", getGroup(), getId());
-            doCall(builder, options);
+        LOG.debug("Invoke health-check {}/{}", getGroup(), getId());
+        doCall(builder, options);
 
-            // State should be set here
-            ObjectHelper.notNull(builder.state(), "Response State");
+        if (builder.state() == null) {
+            builder.unknown();
+        }
 
-            if (builder.state() == State.DOWN) {
-                // reset success since it failed
-                successCount = 0;
-            } else if (builder.state() == State.UP) {
-                // reset failure since it ok
-                failureCount = 0;
-            }
+        if (builder.state() == State.DOWN) {
+            // reset success since it failed
+            successCount = 0;
+        } else if (builder.state() == State.UP) {
+            // reset failure since it ok
+            failureCount = 0;
+        }
 
-            meta.put(INVOCATION_TIME, invocationTime);
-            meta.put(INVOCATION_COUNT, ++invocationCount);
-            meta.put(FAILURE_COUNT, failureCount);
-            meta.put(SUCCESS_COUNT, successCount);
+        meta.put(INVOCATION_TIME, invocationTime);
+        meta.put(INVOCATION_COUNT, ++invocationCount);
+        meta.put(FAILURE_COUNT, failureCount);
+        meta.put(SUCCESS_COUNT, successCount);
 
-            // Copy some meta-data bits to the response attributes so the
-            // response caches the health-check state at the time of the 
invocation.
-            builder.detail(INVOCATION_TIME, meta.get(INVOCATION_TIME));
-            builder.detail(INVOCATION_COUNT, meta.get(INVOCATION_COUNT));
-            builder.detail(FAILURE_COUNT, meta.get(FAILURE_COUNT));
-            builder.detail(SUCCESS_COUNT, meta.get(SUCCESS_COUNT));
+        // Copy some meta-data bits to the response attributes so the
+        // response caches the health-check state at the time of the 
invocation.
+        builder.detail(INVOCATION_TIME, meta.get(INVOCATION_TIME));
+        builder.detail(INVOCATION_COUNT, meta.get(INVOCATION_COUNT));
+        builder.detail(FAILURE_COUNT, meta.get(FAILURE_COUNT));
+        builder.detail(SUCCESS_COUNT, meta.get(SUCCESS_COUNT));
 
-            return builder.build();
-        }
+        return builder;
     }
 
     @Override
@@ -192,4 +206,13 @@ public abstract class AbstractHealthCheck implements 
HealthCheck, CamelContextAw
      * @see HealthCheck#call(Map)
      */
     protected abstract void doCall(HealthCheckResultBuilder builder, 
Map<String, Object> options);
+
+    private HealthCheckResultStrategy customHealthCheckResponseStrategy() {
+        Set<HealthCheckResultStrategy> set = 
camelContext.getRegistry().findByType(HealthCheckResultStrategy.class);
+        if (set.size() == 1) {
+            return set.iterator().next();
+        }
+        return null;
+    }
+
 }
diff --git 
a/core/camel-health/src/main/java/org/apache/camel/impl/health/DefaultHealthCheckRegistry.java
 
b/core/camel-health/src/main/java/org/apache/camel/impl/health/DefaultHealthCheckRegistry.java
index b489d98..0190494 100644
--- 
a/core/camel-health/src/main/java/org/apache/camel/impl/health/DefaultHealthCheckRegistry.java
+++ 
b/core/camel-health/src/main/java/org/apache/camel/impl/health/DefaultHealthCheckRegistry.java
@@ -104,7 +104,7 @@ public class DefaultHealthCheckRegistry extends 
ServiceSupport implements Health
                 .filter(repository -> repository instanceof 
HealthCheckRegistryRepository)
                 .findFirst();
 
-        if (!hcr.isPresent()) {
+        if (hcr.isEmpty()) {
             register(new HealthCheckRegistryRepository());
         }
 
@@ -184,6 +184,9 @@ public class DefaultHealthCheckRegistry extends 
ServiceSupport implements Health
 
         checkIfAccepted(obj);
 
+        // inject context
+        CamelContextAware.trySetCamelContext(obj, camelContext);
+
         if (obj instanceof HealthCheck) {
             HealthCheck healthCheck = (HealthCheck) obj;
             // do we have this already
@@ -208,6 +211,11 @@ public class DefaultHealthCheckRegistry extends 
ServiceSupport implements Health
             }
         }
 
+        // ensure the check is started if we are already started (such as 
added later)
+        if (isStarted()) {
+            ServiceHelper.startService(obj);
+        }
+
         return result;
     }
 
@@ -231,6 +239,10 @@ public class DefaultHealthCheckRegistry extends 
ServiceSupport implements Health
             }
         }
 
+        if (result) {
+            ServiceHelper.stopService(obj);
+        }
+
         return result;
     }
 
diff --git 
a/core/camel-health/src/main/java/org/apache/camel/impl/health/HealthCheckRegistryRepository.java
 
b/core/camel-health/src/main/java/org/apache/camel/impl/health/HealthCheckRegistryRepository.java
index 54210e6..06ff928 100644
--- 
a/core/camel-health/src/main/java/org/apache/camel/impl/health/HealthCheckRegistryRepository.java
+++ 
b/core/camel-health/src/main/java/org/apache/camel/impl/health/HealthCheckRegistryRepository.java
@@ -66,7 +66,8 @@ public class HealthCheckRegistryRepository extends 
ServiceSupport
     public Stream<HealthCheck> stream() {
         if (context != null && enabled) {
             Set<HealthCheck> set = 
this.context.getRegistry().findByType(HealthCheck.class);
-            return set.stream().map(this::toHealthCheck);
+            return set.stream()
+                    .map(this::toHealthCheck);
         } else {
             return Stream.empty();
         }
diff --git a/docs/user-manual/modules/ROOT/pages/health-check.adoc 
b/docs/user-manual/modules/ROOT/pages/health-check.adoc
index 6baa8de..a693b17 100644
--- a/docs/user-manual/modules/ROOT/pages/health-check.adoc
+++ b/docs/user-manual/modules/ROOT/pages/health-check.adoc
@@ -128,6 +128,18 @@ the readiness or liveness checks.
 
 The health checks can also be invoked from JMX.
 
+== Enriching and controlling health check responses
+
+Each health check that is invoked will gather details about the result using 
`HealthCheckResultBuilder`.
+
+To allow enriching and manipulating the result, then you can use 
`HealthCheckResultStrategy` to
+plug in a custom bean that can process the result, and change state, add 
information, remove unwanted information,
+etc. on the result builder.
+
+The custom bean should be registered to the Camel xref:registry.adoc[Registry] 
to be discovered
+by `camel-health`. Only one instance of the bean is allowed.
+
+
 == Writing a custom health check
 
 There are a limited number of health checks provided by Camel out of the box,
@@ -169,7 +181,7 @@ public final class MyHealthCheck extends 
AbstractHealthCheck {
 You can now make _MyHealthCheck_ available to Camel by adding an instance to 
(for example Spring application context)
 or directly to the Camel xref:registry.adoc[Registry].
 
-=== Loading custom health checks
+== Loading custom health checks
 
 Camel can discover and load custom health checks from classpath scanning. This 
requires
 to annotate the custom health checks with `@HealthCheck` annotation on the 
class (see above).
@@ -211,7 +223,7 @@ context.setLoadHealthChecks(true);
 
 TIP: The example `main-health` has a custom health check which is loadable.
 
-==== Loading custom health checks in Camel Quarkus
+=== Loading custom health checks in Camel Quarkus
 
 If you use Camel Quarkus then you can write custom health checks with
 xref:components:others:microprofile-health.adoc[MicroProfile Health], which
@@ -222,7 +234,7 @@ Using Camel's own health check APIs does however allow 
building health checks
 that are usable anywhere you use Camel whether its standalone, spring boot, 
quarkus,
 or something else.
 
-=== Writing custom Health Check for Camel components
+== Writing custom Health Check for Camel components
 
 You can implement custom health checks in Camel components (currently only for 
consumers).
 

Reply via email to