Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Microprofile Health #25186

Draft
wants to merge 31 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
dd7c5e4
Initial implementation
Thihup Oct 13, 2024
a1c4b38
Update version
Thihup Oct 15, 2024
0490f26
Fix TCK tests
Thihup Oct 16, 2024
7be24a9
Fix Checkstyle issue
Thihup Oct 16, 2024
3b9cc17
Ignore Microprofile Config if it is not enabled
Thihup Oct 28, 2024
1ec8dd8
Remove unused system properties
Thihup Oct 28, 2024
f0b52b8
Remove unused method
Thihup Oct 28, 2024
b2842fe
Remove unused Import-Package declarations
Thihup Oct 29, 2024
cfab442
Fix checkstyle
Thihup Oct 29, 2024
84b51ef
Move config file from META-INF to WEB-INF/classes/META-INF
Thihup Oct 29, 2024
6f5fbdf
Remove unused BeansXmlTransformer
Thihup Oct 29, 2024
c21a583
Improve error handling
Thihup Oct 29, 2024
1df0d36
Rename Arquillian extension
Thihup Oct 29, 2024
7d13b7d
Remove unused archive processor
Thihup Oct 29, 2024
2d80b6d
Add license
Thihup Oct 29, 2024
88fcd1e
Rename variables
Thihup Oct 31, 2024
75ad0f6
Remove unused Helidon Health dependency
Thihup Oct 31, 2024
ef217b5
Merge branch 'master' of https://github.com/eclipse-ee4j/glassfish in…
Thihup Nov 5, 2024
2b2e6e8
Update version
Thihup Nov 5, 2024
9bea997
CI
Thihup Nov 5, 2024
4f2fc1e
Remove unused internal dependencies
Thihup Nov 8, 2024
87035d1
Avoid Impl suffix
Thihup Nov 8, 2024
35bb755
Add system property to allow disabling the Health servlet
Thihup Nov 8, 2024
96528c9
Split Health implementation
Thihup Dec 4, 2024
c8ceff3
Merge branch 'master' of https://github.com/eclipse-ee4j/glassfish in…
Thihup Dec 5, 2024
c1355d9
Update version
Thihup Dec 5, 2024
f80f757
Merge branch 'master' of https://github.com/eclipse-ee4j/glassfish in…
Thihup Dec 28, 2024
67e74ef
Fix imports
Thihup Dec 28, 2024
f629daa
Fix imports
Thihup Dec 28, 2024
f3052c6
Fix imports
Thihup Dec 28, 2024
eb5af30
Set content type and encoding for health response body
OndroMih Dec 31, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions appserver/distributions/glassfish/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@
basedir="${patches}/microprofile-rest-client-api" includes="META-INF/MANIFEST.MF"
destfile="${glassfish.modules}/microprofile-rest-client-api.jar"
/>
<jarupdate if:set="java17orGreater"
basedir="${patches}/microprofile-health-api" includes="META-INF/MANIFEST.MF"
destfile="${glassfish.modules}/microprofile-health-api.jar"
/>
<jarupdate
basedir="${patches}/tyrus-client" includes="META-INF/MANIFEST.MF"
destfile="${glassfish.modules}/tyrus-client.jar"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
Manifest-Version: 1.0
Bundle-Description: MicroProfile Health :: API
Bundle-License: Apache License, Version 2.0
Bundle-SymbolicName: org.eclipse.microprofile.health
Bnd-LastModified: 1664892780713
Bundle-ManifestVersion: 2
Bundle-DocURL: https://microprofile.io/project/eclipse/microprofile-he
alth/microprofile-health-api
Bundle-Vendor: Eclipse Foundation
Import-Package: jakarta.enterprise.util;version="[3.0,5)",jakarta.inje
ct;version="[2.0,4)",org.eclipse.microprofile.health;version="[3.0,5)
",org.eclipse.microprofile.health.spi;version="[1.1,2)"
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"
Tool: Bnd-5.2.0.202010142003
Export-Package: org.eclipse.microprofile.health;version="3.0";uses:="j
akarta.enterprise.util,jakarta.inject,org.eclipse.microprofile.health
.spi",org.eclipse.microprofile.health.spi;version="1.1";uses:="org.ec
lipse.microprofile.health"
Bundle-Name: MicroProfile Health Check Bundle
Bundle-Version: 4.0.1
Bundle-SCM: url="https://github.com/eclipse/microprofile-health/microp
rofile-health-api",connection="scm:git:https://github.com/eclipse/mic
roprofile-health.git/microprofile-health-api",developer-connection="s
cm:git:[email protected]:eclipse/microprofile-health.git/microprofile-he
alth-api",tag="4.0.1"
Build-Jdk-Spec: 1.8
Created-By: 1.8.0_292 (AdoptOpenJDK)

36 changes: 35 additions & 1 deletion appserver/featuresets/glassfish/pom.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2022, 2023 Contributors to Eclipse Foundation.
Copyright (c) 2022, 2024 Contributors to Eclipse Foundation.
Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.

This program and the accompanying materials are made available under the
Expand Down Expand Up @@ -840,6 +840,40 @@
</exclusions>
</dependency>

<!-- MicroProfile Health -->
<dependency>
<groupId>org.eclipse.microprofile.health</groupId>
<artifactId>microprofile-health-api</artifactId>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.glassfish.main.microprofile</groupId>
<artifactId>microprofile-health</artifactId>
<version>${project.version}</version>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.glassfish.main.microprofile</groupId>
<artifactId>health-glassfish</artifactId>
<version>${project.version}</version>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>

<!-- MicroProfile JWT -->
<dependency>
<groupId>org.eclipse.microprofile.jwt</groupId>
Expand Down
89 changes: 89 additions & 0 deletions appserver/microprofile/health-glassfish/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.glassfish.main.microprofile</groupId>
<artifactId>microprofile-parent</artifactId>
<version>7.0.21-SNAPSHOT</version>
</parent>

<artifactId>health-glassfish</artifactId>
<packaging>glassfish-jar</packaging>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>17</maven.compiler.release>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove maven.compiler.release setting, it's ignored.

</properties>

<dependencies>
<dependency>
<groupId>org.glassfish.main.microprofile</groupId>
<artifactId>microprofile-health</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.json.bind</groupId>
<artifactId>jakarta.json.bind-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>17</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<supportedProjectTypes>
<supportedProjectType>glassfish-jar</supportedProjectType>
</supportedProjectTypes>
<instructions>
<!-- A list of packages that are exposed to classes outside the bundle -->
<Export-Package>
org.glassfish.microprofile.impl;
</Export-Package>
<Import-Package>
jakarta.enterprise.context.spi;
jakarta.enterprise.event;
jakarta.enterprise.inject;
jakarta.enterprise.inject.spi;
jakarta.json.bind;
jakarta.servlet;
jakarta.servlet.http;
org.eclipse.microprofile.health;
org.glassfish.internal.api;
org.glassfish.microprofile.health;
org.glassfish.hk2.api;
org.glassfish.hk2.utilities;
</Import-Package>
Comment on lines +60 to +73
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The <Import-Package> block can be removed. By default, all imported packages are added automatically: https://felix.apache.org/documentation/subprojects/apache-felix-maven-bundle-plugin-bnd.html#_import_package

Suggested change
<Import-Package>
jakarta.enterprise.context.spi;
jakarta.enterprise.event;
jakarta.enterprise.inject;
jakarta.enterprise.inject.spi;
jakarta.json.bind;
jakarta.servlet;
jakarta.servlet.http;
org.eclipse.microprofile.health;
org.glassfish.internal.api;
org.glassfish.microprofile.health;
org.glassfish.hk2.api;
org.glassfish.hk2.utilities;
</Import-Package>

</instructions>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hk2-locator resources need to be included, otherwise HK2 will not find the HealthService service.

Suggested change
</instructions>
<Include-Resource>
{maven-resources},
META-INF/hk2-locator/=target/classes/META-INF/hk2-locator/,
</Include-Resource>
</instructions>

</configuration>
<executions>
<execution>
<id>osgi-bundle</id>
<phase>package</phase>
<goals>
<goal>bundle</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2024 Contributors to Eclipse Foundation.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.glassfish.microprofile.impl;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rename this package. It doesn't even mention "health". For example:

Suggested change
package org.glassfish.microprofile.impl;
package org.glassfish.microprofile.health.service;

If the package name is not "impl", then it can even be removed from <Export-Package> in pom.xml.


import jakarta.enterprise.context.spi.CreationalContext;
import jakarta.enterprise.event.Observes;
import jakarta.enterprise.inject.spi.AfterDeploymentValidation;
import jakarta.enterprise.inject.spi.Annotated;
import jakarta.enterprise.inject.spi.Bean;
import jakarta.enterprise.inject.spi.BeanManager;
import jakarta.enterprise.inject.spi.Extension;
import jakarta.enterprise.inject.spi.ProcessBean;

import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.Liveness;
import org.eclipse.microprofile.health.Readiness;
import org.eclipse.microprofile.health.Startup;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.utilities.ServiceLocatorUtilities;
import org.glassfish.internal.api.Globals;
import org.glassfish.microprofile.health.HealthReporter;


public class CollectHealthChecksExtension implements Extension {

private final HealthReporter service;
private final Set<Bean<HealthCheck>> healthChecks = Collections.newSetFromMap(new ConcurrentHashMap<>());

public CollectHealthChecksExtension() {
ServiceLocator defaultBaseServiceLocator = Globals.getDefaultBaseServiceLocator();
HealthReporter healthReporterService = defaultBaseServiceLocator.getService(HealthReporter.class);
if (healthReporterService == null) {
ServiceLocatorUtilities.addClasses(defaultBaseServiceLocator, true, HealthReporter.class);
healthReporterService = defaultBaseServiceLocator.getService(HealthReporter.class);
}
service = healthReporterService;
}
public <T> void processBeans(@Observes ProcessBean<T> beans) {
Annotated annotated = beans.getAnnotated();
if (annotated.isAnnotationPresent(Liveness.class) ||
annotated.isAnnotationPresent(Readiness.class) ||
annotated.isAnnotationPresent(Startup.class)) {
if (beans.getBean().getTypes().contains(HealthCheck.class)) {
healthChecks.add((Bean<HealthCheck>) beans.getBean());
}
}
}

public void afterDeploymentValidation(@Observes AfterDeploymentValidation adv, BeanManager beanManager) {
healthChecks.forEach(bean -> {
CreationalContext<HealthCheck> creationalContext = beanManager.createCreationalContext(bean);
service.addHealthCheck("", bean.create(creationalContext));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should bind the health checks to application name, which has to be unique. When undeployed, HealthService.event should unbind health checks for the same application name.

The problem with the current approach is that when 2 applications are deployed, they deploy all health checks under the same context name. When one of the applications is undeployed, it would remove health checks also from other applications. We need to add and remove health checks for a single application under a unique context.

});
}



}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.glassfish.microprofile.impl;


import org.glassfish.api.StartupRunLevel;
import org.glassfish.api.event.EventListener;
import org.glassfish.hk2.runlevel.RunLevel;
import org.glassfish.internal.api.Globals;
import org.glassfish.internal.data.ApplicationInfo;
import org.glassfish.internal.deployment.Deployment;
import org.glassfish.microprofile.health.HealthReporter;
import org.jvnet.hk2.annotations.Service;

@Service(name = "healthcheck-service")
@RunLevel(StartupRunLevel.VAL)
public class HealthService implements EventListener {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's required to register the event listener, otherwise the event method won't be called:

import org.glassfish.api.event.Events;
import org.glassfish.hk2.api.PostConstruct;

public class HealthService implements PostConstruct, EventListener {

    @Inject
    Events events;

    @Override
    public void postConstruct() {
        events.register(this);
    }



@Override
public void event(Event<?> event) {

HealthReporter service = Globals.getDefaultHabitat().getService(HealthReporter.class);

if (event.is(Deployment.APPLICATION_UNLOADED) && event.hook() instanceof ApplicationInfo appInfo) {
service.removeAllHealthChecksFrom(appInfo.getName());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The healthchecks are registered with "" context root but here we want to remove them by appInfo.getName(), which is not "". As a result, the health checks are not removed on undeployment, remain leaking, and even remain in the output of the /health endpoint.

}

if (event.is(Deployment.APPLICATION_STARTED) && event.hook() instanceof ApplicationInfo appInfo) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either remove this block, or use it to find out the application name to be used in the CDI extension for the application context name


}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2024 Contributors to Eclipse Foundation.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.glassfish.microprofile.impl;

import jakarta.json.bind.Jsonb;
import jakarta.json.bind.JsonbBuilder;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.eclipse.microprofile.health.HealthCheckResponse;
import org.glassfish.internal.api.Globals;
import org.glassfish.microprofile.health.HealthReport;
import org.glassfish.microprofile.health.HealthReporter;

public class HealthServlet extends HttpServlet {

private static final Logger LOGGER = Logger.getLogger(HealthServlet.class.getName());

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
HealthReport healthReport;
try {
healthReport = Globals.getDefaultHabitat().getService(HealthReporter.class)
.getReport(getReportKind(req.getRequestURI()));

int httpStatus = switch (healthReport.status()) {
case UP -> HttpServletResponse.SC_OK;
case DOWN -> HttpServletResponse.SC_SERVICE_UNAVAILABLE;
};
resp.setStatus(httpStatus);
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Unable to fetch health check status", e);
healthReport = new HealthReport(HealthCheckResponse.Status.DOWN, List.of());
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}

Jsonb jsonb = JsonbBuilder.create();
String json = jsonb.toJson(healthReport);
resp.setCharacterEncoding("UTF-8");
resp.setContentType("application/json");
resp.getWriter().println(json);
OndroMih marked this conversation as resolved.
Show resolved Hide resolved
}

private static HealthReporter.ReportKind getReportKind(String path) {
if (path.endsWith("health/live")) {
return HealthReporter.ReportKind.LIVE;
} else if (path.endsWith("health/ready")) {
return HealthReporter.ReportKind.READY;
} else if (path.endsWith("health/started")) {
return HealthReporter.ReportKind.STARTED;
} else {
return HealthReporter.ReportKind.ALL;
}
}
}
Loading