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