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

jlmonteiro pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tomee.git


The following commit(s) were added to refs/heads/master by this push:
     new 51278e8620 TOMEE-3897 Basic Smallrye Health integration
51278e8620 is described below

commit 51278e862004c08ca3343c073ef80e3fb0b91f6b
Author: Jean-Louis Monteiro <[email protected]>
AuthorDate: Tue Apr 12 13:38:20 2022 +0200

    TOMEE-3897 Basic Smallrye Health integration
---
 tck/microprofile-tck/health/pom.xml                |   1 -
 .../health/src/test/resources/arquillian.xml       |   1 +
 tck/microprofile-tck/health/tck-suite.xml          |  28 ++
 .../microprofile/health/MPHealthCDIExtension.java  | 185 +++++++++++++
 .../health/MicroProfileHealthChecksEndpoint.java   |  64 +++++
 .../health/MicroProfileHealthReporter.java         | 298 +++++++++++++++++++++
 .../health/MicroProfileHealthReporterProducer.java |  62 +++++
 .../jakarta.enterprise.inject.spi.Extension        |   1 +
 8 files changed, 639 insertions(+), 1 deletion(-)

diff --git a/tck/microprofile-tck/health/pom.xml 
b/tck/microprofile-tck/health/pom.xml
index 776bea6bf1..2b9d74edad 100644
--- a/tck/microprofile-tck/health/pom.xml
+++ b/tck/microprofile-tck/health/pom.xml
@@ -32,7 +32,6 @@
     <plugins>
       <plugin>
         <artifactId>maven-surefire-plugin</artifactId>
-        <!-- Updated version to fix hanging text -->
         <version>3.0.0-M6</version>
         <configuration>
           <dependenciesToScan>
diff --git a/tck/microprofile-tck/health/src/test/resources/arquillian.xml 
b/tck/microprofile-tck/health/src/test/resources/arquillian.xml
index 9fc654b381..44965b76e3 100644
--- a/tck/microprofile-tck/health/src/test/resources/arquillian.xml
+++ b/tck/microprofile-tck/health/src/test/resources/arquillian.xml
@@ -27,6 +27,7 @@
       <property name="ajpPort">-1</property>
       <property name="stopPort">-1</property>
       <property name="classifier">microprofile</property>
+      <property name="debug">false</property>
       <property name="conf">src/test/conf</property>
       <property name="dir">target/tomee</property>
       <property name="appWorkingDir">target/workdir</property>
diff --git a/tck/microprofile-tck/health/tck-suite.xml 
b/tck/microprofile-tck/health/tck-suite.xml
new file mode 100644
index 0000000000..63884741f2
--- /dev/null
+++ b/tck/microprofile-tck/health/tck-suite.xml
@@ -0,0 +1,28 @@
+<!--
+  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.
+  -->
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"; >
+
+<suite name="microprofile-health-TCK" verbose="2" 
configfailurepolicy="continue" >
+
+  <test name="microprofile-health TCK">
+    <packages>
+      <package name="org.eclipse.microprofile.health.tck.*">
+      </package>
+    </packages>
+  </test>
+
+</suite>
\ No newline at end of file
diff --git 
a/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/health/MPHealthCDIExtension.java
 
b/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/health/MPHealthCDIExtension.java
new file mode 100644
index 0000000000..d449227676
--- /dev/null
+++ 
b/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/health/MPHealthCDIExtension.java
@@ -0,0 +1,185 @@
+/*
+ * 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.tomee.microprofile.health;
+
+import io.smallrye.health.SmallRyeHealthReporter;
+import jakarta.enterprise.event.Observes;
+import jakarta.enterprise.inject.Instance;
+import jakarta.enterprise.inject.spi.AfterDeploymentValidation;
+import jakarta.enterprise.inject.spi.BeanManager;
+import jakarta.enterprise.inject.spi.BeforeBeanDiscovery;
+import jakarta.enterprise.inject.spi.BeforeShutdown;
+import jakarta.enterprise.inject.spi.Extension;
+import jakarta.enterprise.inject.spi.ProcessAnnotatedType;
+import jakarta.enterprise.util.AnnotationLiteral;
+import org.eclipse.microprofile.config.Config;
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.eclipse.microprofile.health.HealthCheck;
+import org.eclipse.microprofile.health.HealthCheckResponse;
+import org.eclipse.microprofile.health.Liveness;
+import org.eclipse.microprofile.health.Readiness;
+import org.eclipse.microprofile.health.Startup;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+public class MPHealthCDIExtension implements Extension {
+
+    private final String MP_HEALTH_DISABLE_DEFAULT_PROCEDURES = 
"mp.health.disable-default-procedures";
+
+
+
+    // Use a single Jakarta Contexts and Dependency Injection instance to 
select and destroy all HealthCheck probes instances
+    private Instance<Object> instance;
+    private final List<HealthCheck> livenessChecks = new ArrayList<>();
+    private final List<HealthCheck> readinessChecks = new ArrayList<>();
+    private final List<HealthCheck> startupChecks = new ArrayList<>();
+    private HealthCheck defaultReadinessCheck;
+    private HealthCheck defaultStartupCheck;
+
+    private MicroProfileHealthReporter reporter;
+
+    public MPHealthCDIExtension() {
+    }
+
+    /**
+     * Get some beans registered like the reporter
+     * @param bbd
+     * @param beanManager
+     */
+    public void observeBeforeBeanDiscovery(@Observes final BeforeBeanDiscovery 
bbd, final BeanManager beanManager) {
+        
bbd.addAnnotatedType(beanManager.createAnnotatedType(MicroProfileHealthReporterProducer.class));
+    }
+
+    /**
+     * Get Jakarta Contexts and Dependency Injection <em>instances</em> of 
HealthCheck and
+     * add them to the {@link MicroProfileHealthReporter}.
+     */
+    private void afterDeploymentValidation(@Observes final 
AfterDeploymentValidation avd, BeanManager bm) {
+        instance = bm.createInstance();
+
+        final Instance<MicroProfileHealthReporter> reporters = 
instance.select(MicroProfileHealthReporter.class);
+        final Optional<MicroProfileHealthReporter> microProfileHealthReporter 
= reporters.stream().findFirst();
+        if (microProfileHealthReporter.isEmpty()) {
+            throw new IllegalStateException("Most likely a bug. No reporter 
found in the bean manager");
+        }
+        this.reporter = microProfileHealthReporter.get();
+
+        addHealthChecks(Liveness.Literal.INSTANCE, reporter::addLivenessCheck, 
livenessChecks);
+        addHealthChecks(Readiness.Literal.INSTANCE, 
reporter::addReadinessCheck, readinessChecks);
+        addHealthChecks(Startup.Literal.INSTANCE, reporter::addStartupCheck, 
startupChecks);
+        reporter.setUserChecksProcessed(true);
+        if (readinessChecks.isEmpty()) {
+            final Config config = 
ConfigProvider.getConfig(MPHealthCDIExtension.class.getClassLoader());
+            boolean disableDefaultprocedure = 
config.getOptionalValue(MP_HEALTH_DISABLE_DEFAULT_PROCEDURES, 
Boolean.class).orElse(false);
+            if (!disableDefaultprocedure) {
+                // no readiness probe are present in the deployment. register 
a readiness check so that the deployment is considered ready
+                defaultReadinessCheck = new 
DefaultReadinessHealthCheck("Apache TomEE Server");
+                reporter.addReadinessCheck(defaultReadinessCheck, 
MPHealthCDIExtension.class.getClassLoader());
+            }
+        }
+        if (startupChecks.isEmpty()) {
+            Config config = 
ConfigProvider.getConfig(MPHealthCDIExtension.class.getClassLoader());
+            boolean disableDefaultprocedure = 
config.getOptionalValue(MP_HEALTH_DISABLE_DEFAULT_PROCEDURES, 
Boolean.class).orElse(false);
+            if (!disableDefaultprocedure) {
+                // no startup probes are present in the deployment. register a 
startup check so that the deployment is considered started
+                defaultStartupCheck = new DefaultStartupHealthCheck("Apache 
TomEE Server");
+                reporter.addStartupCheck(defaultStartupCheck, 
MPHealthCDIExtension.class.getClassLoader());
+            }
+        }
+    }
+
+    private void addHealthChecks(
+        AnnotationLiteral qualifier,
+        BiConsumer<HealthCheck, ClassLoader> healthFunction, List<HealthCheck> 
healthChecks) {
+        for (HealthCheck healthCheck : instance.select(HealthCheck.class, 
qualifier)) {
+            healthFunction.accept(healthCheck, 
MPHealthCDIExtension.class.getClassLoader());
+            healthChecks.add(healthCheck);
+        }
+    }
+
+    /**
+     * Called when the deployment is undeployed.
+     * <p>
+     * Remove all the instances of {@link HealthCheck} from the {@link 
MicroProfileHealthReporter}.
+     */
+    public void beforeShutdown(@Observes final BeforeShutdown bs) {
+        removeHealthCheck(livenessChecks, reporter::removeLivenessCheck);
+        removeHealthCheck(readinessChecks, reporter::removeReadinessCheck);
+        removeHealthCheck(startupChecks, reporter::removeStartupCheck);
+
+        if (defaultReadinessCheck != null) {
+            reporter.removeReadinessCheck(defaultReadinessCheck);
+            defaultReadinessCheck = null;
+        }
+
+        if (defaultStartupCheck != null) {
+            reporter.removeStartupCheck(defaultStartupCheck);
+            defaultStartupCheck = null;
+        }
+
+        instance = null;
+    }
+
+    private void removeHealthCheck(List<HealthCheck> healthChecks,
+                                   Consumer<HealthCheck> healthFunction) {
+        for (HealthCheck healthCheck : healthChecks) {
+            healthFunction.accept(healthCheck);
+            instance.destroy(healthCheck);
+        }
+        healthChecks.clear();
+    }
+
+    public void vetoSmallryeHealthReporter(@Observes 
ProcessAnnotatedType<SmallRyeHealthReporter> pat) {
+        pat.veto();
+    }
+
+    private static final class DefaultReadinessHealthCheck implements 
HealthCheck {
+
+        private final String deploymentName;
+
+        DefaultReadinessHealthCheck(String deploymentName) {
+            this.deploymentName = deploymentName;
+        }
+
+        @Override
+        public HealthCheckResponse call() {
+            return HealthCheckResponse.named("ready-" + deploymentName)
+                    .up()
+                    .build();
+        }
+    }
+
+    private static final class DefaultStartupHealthCheck implements 
HealthCheck {
+
+        private final String deploymentName;
+
+        DefaultStartupHealthCheck(String deploymentName) {
+            this.deploymentName = deploymentName;
+        }
+
+        @Override
+        public HealthCheckResponse call() {
+            return HealthCheckResponse.named("started-" + deploymentName)
+                .up()
+                .build();
+        }
+    }
+}
\ No newline at end of file
diff --git 
a/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/health/MicroProfileHealthChecksEndpoint.java
 
b/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/health/MicroProfileHealthChecksEndpoint.java
new file mode 100644
index 0000000000..173836477c
--- /dev/null
+++ 
b/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/health/MicroProfileHealthChecksEndpoint.java
@@ -0,0 +1,64 @@
+/**
+ * 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.tomee.microprofile.health;
+
+import io.smallrye.health.SmallRyeHealth;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+
+import java.util.function.Supplier;
+
+@Path("health")
+@ApplicationScoped
+public class MicroProfileHealthChecksEndpoint {
+
+    @Inject
+    private MicroProfileHealthReporter reporter;
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getChecks() {
+        return toResponse(reporter::getHealth);
+    }
+
+    @GET
+    @Path("live")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getLiveChecks() {
+        return toResponse(reporter::getLiveness);
+    }
+
+    @GET
+    @Path("ready")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getReadyChecks() {
+        return toResponse(reporter::getReadiness);
+    }
+
+
+    private Response toResponse(final Supplier<SmallRyeHealth> health) {
+        return Response
+            .status(health.get().isDown() ? 
Response.Status.SERVICE_UNAVAILABLE : Response.Status.OK)
+            .entity(health.get().getPayload())
+            .build();
+    }
+}
diff --git 
a/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/health/MicroProfileHealthReporter.java
 
b/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/health/MicroProfileHealthReporter.java
new file mode 100644
index 0000000000..9374c01fcc
--- /dev/null
+++ 
b/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/health/MicroProfileHealthReporter.java
@@ -0,0 +1,298 @@
+/*
+ * 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.tomee.microprofile.health;
+
+import io.smallrye.health.SmallRyeHealth;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.context.Dependent;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonObjectBuilder;
+import org.apache.openejb.util.LogCategory;
+import org.apache.openejb.util.Logger;
+import org.eclipse.microprofile.health.HealthCheck;
+import org.eclipse.microprofile.health.HealthCheckResponse;
+import org.eclipse.microprofile.health.HealthCheckResponseBuilder;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+@ApplicationScoped
+public class MicroProfileHealthReporter {
+
+    public static final String DOWN = "DOWN";
+    public static final String UP = "UP";
+    private final boolean defaultServerProceduresDisabled;
+    private final String defaultReadinessEmptyResponse;
+    private final String defaultStartupEmptyResponse;
+    private Map<HealthCheck, ClassLoader> healthChecks = new HashMap<>();
+    private Map<HealthCheck, ClassLoader> livenessChecks = new HashMap<>();
+    private Map<HealthCheck, ClassLoader> readinessChecks = new HashMap<>();
+    private Map<HealthCheck, ClassLoader> startupChecks = new HashMap<>();
+    private Map<HealthCheck, ClassLoader> serverReadinessChecks = new 
HashMap<>();
+
+    private final HealthCheck emptyDeploymentLivenessCheck;
+    private final HealthCheck emptyDeploymentReadinessCheck;
+    private final HealthCheck emptyDeploymentStartupCheck;
+
+    private boolean userChecksProcessed = false;
+
+    private static class EmptyDeploymentCheckStatus implements HealthCheck {
+        private final String name;
+        private final String status;
+
+        EmptyDeploymentCheckStatus(String name, String status) {
+            this.name = name;
+            this.status = status;
+        }
+
+        @Override
+        public HealthCheckResponse call() {
+            return HealthCheckResponse.named(name)
+                    .status(status.equals("UP"))
+                    .build();
+        }
+    }
+
+    public MicroProfileHealthReporter() {
+        this.emptyDeploymentLivenessCheck  = new 
EmptyDeploymentCheckStatus("empty-liveness-checks", UP);
+        this.emptyDeploymentReadinessCheck  = new 
EmptyDeploymentCheckStatus("empty-readiness-checks", UP);
+        this.emptyDeploymentStartupCheck  = new 
EmptyDeploymentCheckStatus("empty-startup-checks", UP);
+        this.defaultServerProceduresDisabled = false;
+        this.defaultReadinessEmptyResponse = DOWN;
+        this.defaultStartupEmptyResponse = DOWN;
+    }
+
+    public MicroProfileHealthReporter(String emptyLivenessChecksStatus, String 
emptyReadinessChecksStatus,
+                                      String emptyStartupChecksStatus, boolean 
defaultServerProceduresDisabled,
+                                      String defaultReadinessEmptyResponse, 
String defaultStartupEmptyResponse) {
+
+        this.emptyDeploymentLivenessCheck  = new 
EmptyDeploymentCheckStatus("empty-liveness-checks", emptyLivenessChecksStatus);
+        this.emptyDeploymentReadinessCheck  = new 
EmptyDeploymentCheckStatus("empty-readiness-checks", 
emptyReadinessChecksStatus);
+        this.emptyDeploymentStartupCheck  = new 
EmptyDeploymentCheckStatus("empty-startup-checks", emptyStartupChecksStatus);
+        this.defaultServerProceduresDisabled = defaultServerProceduresDisabled;
+        this.defaultReadinessEmptyResponse = defaultReadinessEmptyResponse;
+        this.defaultStartupEmptyResponse = defaultStartupEmptyResponse;
+    }
+
+    public SmallRyeHealth getHealth() {
+        HashMap<HealthCheck, ClassLoader> deploymentChecks = new HashMap<>();
+        deploymentChecks.putAll(healthChecks);
+        deploymentChecks.putAll(livenessChecks);
+        deploymentChecks.putAll(readinessChecks);
+        deploymentChecks.putAll(startupChecks);
+
+        HashMap<HealthCheck, ClassLoader> serverChecks= new HashMap<>();
+        serverChecks.putAll(serverReadinessChecks);
+        if (deploymentChecks.size() == 0 && !defaultServerProceduresDisabled) {
+            serverChecks.put(emptyDeploymentLivenessCheck, 
Thread.currentThread().getContextClassLoader());
+            serverChecks.put(emptyDeploymentReadinessCheck, 
Thread.currentThread().getContextClassLoader());
+            serverChecks.put(emptyDeploymentStartupCheck, 
Thread.currentThread().getContextClassLoader());
+        }
+
+        return getHealth(serverChecks, deploymentChecks);
+    }
+
+    public SmallRyeHealth getLiveness() {
+        final Map<HealthCheck, ClassLoader> serverChecks;
+        if (livenessChecks.size() == 0 && !defaultServerProceduresDisabled) {
+            serverChecks = 
Collections.singletonMap(emptyDeploymentLivenessCheck, 
Thread.currentThread().getContextClassLoader());
+        } else {
+            serverChecks = Collections.emptyMap();
+        }
+        return getHealth(serverChecks, livenessChecks);
+    }
+
+    public SmallRyeHealth getReadiness() {
+        final Map<HealthCheck, ClassLoader> serverChecks = new HashMap<>();
+        serverChecks.putAll(serverReadinessChecks);
+        if (readinessChecks.size() == 0) {
+            if (defaultServerProceduresDisabled) {
+                return getHealth(serverChecks, readinessChecks,
+                    userChecksProcessed ? HealthCheckResponse.Status.UP :
+                        
HealthCheckResponse.Status.valueOf(defaultReadinessEmptyResponse));
+            } else {
+                serverChecks.put(emptyDeploymentReadinessCheck, 
Thread.currentThread().getContextClassLoader());
+                return getHealth(serverChecks, readinessChecks);
+            }
+        }
+        return getHealth(serverChecks, readinessChecks);
+    }
+
+    public SmallRyeHealth getStartup() {
+        Map<HealthCheck, ClassLoader> serverChecks = Collections.emptyMap();
+        if (startupChecks.size() == 0) {
+            if (defaultServerProceduresDisabled) {
+                return getHealth(serverChecks, startupChecks,
+                    userChecksProcessed ? HealthCheckResponse.Status.UP :
+                        
HealthCheckResponse.Status.valueOf(defaultStartupEmptyResponse));
+            } else {
+                serverChecks = 
Collections.singletonMap(emptyDeploymentStartupCheck, 
Thread.currentThread().getContextClassLoader());
+                return getHealth(serverChecks, startupChecks);
+            }
+        }
+        return getHealth(serverChecks, startupChecks);
+    }
+
+    private SmallRyeHealth getHealth(Map<HealthCheck, ClassLoader> 
serverChecks, Map<HealthCheck, ClassLoader> deploymentChecks) {
+        return getHealth(serverChecks, deploymentChecks, 
HealthCheckResponse.Status.UP);
+    }
+
+    private SmallRyeHealth getHealth(Map<HealthCheck, ClassLoader> 
serverChecks, Map<HealthCheck,
+        ClassLoader> deploymentChecks, HealthCheckResponse.Status 
defaultStatus) {
+        JsonArrayBuilder results = Json.createArrayBuilder();
+        HealthCheckResponse.Status status = defaultStatus;
+
+        status = processChecks(serverChecks, results, status);
+
+        status = processChecks(deploymentChecks, results, status);
+
+        JsonObjectBuilder builder = Json.createObjectBuilder();
+
+        JsonArray checkResults = results.build();
+
+        builder.add("status", status.toString());
+        builder.add("checks", checkResults);
+
+        JsonObject build = builder.build();
+
+        if (status.equals(HealthCheckResponse.Status.DOWN)) {
+            Logger.getInstance(LogCategory.OPENEJB, 
MicroProfileHealthReporter.class).error("Reporting DOWN status: " + 
build.toString());
+        }
+
+        return new SmallRyeHealth(build);
+    }
+
+    private HealthCheckResponse.Status processChecks(Map<HealthCheck, 
ClassLoader> checks, JsonArrayBuilder results, HealthCheckResponse.Status 
status) {
+        if (checks != null) {
+            for (Map.Entry<HealthCheck, ClassLoader> entry : 
checks.entrySet()) {
+                // use the classloader of the deployment's module instead of 
the TCCL (which is the server's ModuleClassLoader
+                // to ensure that any resources that checks the TCCL (such as 
MP Config) will use the correct one
+                // when the health checks are called.
+                final ClassLoader oldTCCL = 
Thread.currentThread().getContextClassLoader();
+                try {
+                    
Thread.currentThread().setContextClassLoader(entry.getValue());
+                    status = fillCheck(entry.getKey(), results, status);
+                } finally {
+                    Thread.currentThread().setContextClassLoader(oldTCCL);
+                }
+            }
+        }
+
+        return status;
+    }
+
+    private HealthCheckResponse.Status fillCheck(HealthCheck check, 
JsonArrayBuilder results, HealthCheckResponse.Status globalOutcome) {
+        JsonObject each = jsonObject(check);
+        results.add(each);
+        if (globalOutcome == HealthCheckResponse.Status.UP) {
+            String status = each.getString("status");
+            if (status.equals(DOWN)) {
+                return HealthCheckResponse.Status.DOWN;
+            }
+        }
+        return globalOutcome;
+    }
+
+    private JsonObject jsonObject(HealthCheck check) {
+        try {
+            return jsonObject(check.call());
+        } catch (RuntimeException e) {
+            // Log Stacktrace to server log so an error is not just in Health 
Check response
+            Logger.getInstance(LogCategory.OPENEJB, 
MicroProfileHealthReporter.class).error("Error processing Health Checks", e);
+
+            HealthCheckResponseBuilder response = 
HealthCheckResponse.named(check.getClass().getName()).down();
+
+            return jsonObject(response.build());
+        }
+    }
+
+    private JsonObject jsonObject(HealthCheckResponse response) {
+        JsonObjectBuilder builder = Json.createObjectBuilder();
+        builder.add("name", response.getName());
+        builder.add("status", response.getStatus().toString());
+        response.getData().ifPresent(d -> {
+            JsonObjectBuilder data = Json.createObjectBuilder();
+            for (Map.Entry<String, Object> entry : d.entrySet()) {
+                Object value = entry.getValue();
+                if (value instanceof String) {
+                    data.add(entry.getKey(), (String) value);
+                } else if (value instanceof Long) {
+                    data.add(entry.getKey(), (Long) value);
+                } else if (value instanceof Boolean) {
+                    data.add(entry.getKey(), (Boolean) value);
+                }
+            }
+            builder.add("data", data.build());
+        });
+
+        return builder.build();
+    }
+
+    public void addHealthCheck(HealthCheck check, ClassLoader 
moduleClassLoader) {
+        if (check != null) {
+            healthChecks.put(check, moduleClassLoader);
+        }
+    }
+
+    public void removeHealthCheck(HealthCheck check) {
+        healthChecks.remove(check);
+    }
+
+    public void addReadinessCheck(HealthCheck check, ClassLoader 
moduleClassLoader) {
+        if (check != null) {
+            readinessChecks.put(check, moduleClassLoader);
+        }
+    }
+
+    public void addServerReadinessCheck(HealthCheck check, ClassLoader 
moduleClassLoader) {
+        if (check != null) {
+            serverReadinessChecks.put(check, moduleClassLoader);
+        }
+    }
+
+    public void removeReadinessCheck(HealthCheck check) {
+        readinessChecks.remove(check);
+    }
+
+    public void addLivenessCheck(HealthCheck check, ClassLoader 
moduleClassLoader) {
+        if (check != null) {
+            livenessChecks.put(check, moduleClassLoader);
+        }
+    }
+
+    public void removeLivenessCheck(HealthCheck check) {
+        livenessChecks.remove(check);
+    }
+
+    public void addStartupCheck(HealthCheck check, ClassLoader 
moduleClassLoader) {
+        if (check != null) {
+            startupChecks.put(check, moduleClassLoader);
+        }
+    }
+
+    public void removeStartupCheck(HealthCheck check) {
+        startupChecks.remove(check);
+    }
+
+    public void setUserChecksProcessed(boolean userChecksProcessed) {
+        this.userChecksProcessed = userChecksProcessed;
+    }
+}
\ No newline at end of file
diff --git 
a/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/health/MicroProfileHealthReporterProducer.java
 
b/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/health/MicroProfileHealthReporterProducer.java
new file mode 100644
index 0000000000..ffaedc5bab
--- /dev/null
+++ 
b/tomee/tomee-microprofile/mp-common/src/main/java/org/apache/tomee/microprofile/health/MicroProfileHealthReporterProducer.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomee.microprofile.health;
+
+import io.smallrye.health.ResponseProvider;
+import jakarta.annotation.PreDestroy;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.context.Dependent;
+import jakarta.enterprise.inject.Produces;
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.eclipse.microprofile.health.HealthCheckResponse;
+
+@Dependent
+public class MicroProfileHealthReporterProducer {
+
+    @Produces
+    @ApplicationScoped
+    public MicroProfileHealthReporter reporter() {
+        final String emptyLivenessChecksStatus = "UP";
+        final String emptyReadinessChecksStatus = "UP";
+        final String emptyStartupChecksStatus = "UP";
+
+        // MicroProfile Health supports the 
mp.health.disable-default-procedures to let users disable any vendor procedures
+        final boolean defaultServerProceduresDisabled = 
ConfigProvider.getConfig().getOptionalValue("mp.health.disable-default-procedures",
 Boolean.class).orElse(false);
+        // MicroProfile Health supports the 
mp.health.default.readiness.empty.response to let users specify default empty 
readiness responses
+        final String defaultReadinessEmptyResponse = 
ConfigProvider.getConfig().getOptionalValue("mp.health.default.readiness.empty.response",
 String.class).orElse("DOWN");
+        // MicroProfile Health supports the 
mp.health.default.startup.empty.response to let users specify default empty 
startup responses
+        final String defaultStartupEmptyResponse = 
ConfigProvider.getConfig().getOptionalValue("mp.health.default.startup.empty.response",
 String.class).orElse("DOWN");
+
+        MicroProfileHealthReporter healthReporter = new 
MicroProfileHealthReporter(emptyLivenessChecksStatus, 
emptyReadinessChecksStatus,
+                                                        
emptyStartupChecksStatus, defaultServerProceduresDisabled,
+                                                        
defaultReadinessEmptyResponse, defaultStartupEmptyResponse);
+
+        if (!defaultServerProceduresDisabled) {
+            ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+            // todo add our own server checks
+        }
+
+        HealthCheckResponse.setResponseProvider(new ResponseProvider());
+        return healthReporter;
+    }
+
+    @PreDestroy
+    public void removeResponseProvider() {
+        HealthCheckResponse.setResponseProvider(null);
+    }
+
+}
\ No newline at end of file
diff --git 
a/tomee/tomee-microprofile/mp-common/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension
 
b/tomee/tomee-microprofile/mp-common/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension
new file mode 100644
index 0000000000..c2a778fd52
--- /dev/null
+++ 
b/tomee/tomee-microprofile/mp-common/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension
@@ -0,0 +1 @@
+org.apache.tomee.microprofile.health.MPHealthCDIExtension

Reply via email to