From eaf70cbf711d476582703f51c2f3ff20db984dbb Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Sun, 6 Oct 2019 13:08:57 +0530 Subject: [PATCH 1/3] PAYARA-3829 MicroProfile Healthcheck 2.1 implementation --- .../healthcheck/HealthCheckService.java | 96 +++++++++++++++--- .../healthcheck/HealthCheckType.java | 48 +++++++++ .../servlet/HealthCheckServlet.java | 18 +++- ...ealthCheckServletContainerInitializer.java | 99 ++++++++++++------- appserver/pom.xml | 2 +- 5 files changed, 210 insertions(+), 53 deletions(-) create mode 100644 appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/HealthCheckType.java diff --git a/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/HealthCheckService.java b/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/HealthCheckService.java index d0b6704c832..f59d6df0c85 100644 --- a/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/HealthCheckService.java +++ b/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/HealthCheckService.java @@ -55,10 +55,10 @@ import javax.json.JsonArrayBuilder; import javax.json.JsonObjectBuilder; import javax.servlet.http.HttpServletResponse; - import fish.payara.microprofile.healthcheck.config.MetricsHealthCheckConfiguration; - +import java.util.HashMap; import static java.util.logging.Level.WARNING; +import org.eclipse.microprofile.config.ConfigProvider; import org.eclipse.microprofile.health.HealthCheck; import org.eclipse.microprofile.health.HealthCheckResponse; import org.glassfish.api.StartupRunLevel; @@ -92,9 +92,14 @@ public class HealthCheckService implements EventListener, ConfigListener { @Inject MetricsHealthCheckConfiguration configuration; + private boolean backwardCompatibilityEnabled; + private static final Logger LOG = Logger.getLogger(HealthCheckService.class.getName()); - private final Map> healthChecks = new ConcurrentHashMap<>(); + private final Map> health = new ConcurrentHashMap<>(); + private final Map> readiness = new ConcurrentHashMap<>(); + private final Map> liveness = new ConcurrentHashMap<>(); + private final Map applicationClassLoaders = new ConcurrentHashMap<>(); private final List applicationsLoaded = new CopyOnWriteArrayList<>(); @@ -104,6 +109,9 @@ public void postConstruct() { events = Globals.getDefaultBaseServiceLocator().getService(Events.class); } events.register(this); + this.backwardCompatibilityEnabled = ConfigProvider.getConfig() + .getOptionalValue("mp.health.enable-backward-compatibility", Boolean.class) + .orElse(false); } @Override @@ -112,7 +120,9 @@ public void event(Event event) { if (event.is(Deployment.APPLICATION_UNLOADED)) { ApplicationInfo appInfo = Deployment.APPLICATION_UNLOADED.getHook(event); if (appInfo != null) { - healthChecks.remove(appInfo.getName()); + readiness.remove(appInfo.getName()); + liveness.remove(appInfo.getName()); + health.remove(appInfo.getName()); applicationClassLoaders.remove(appInfo.getName()); applicationsLoaded.remove(appInfo.getName()); } @@ -134,15 +144,48 @@ public boolean isEnabled() { public boolean isSecurityEnabled() { return Boolean.parseBoolean(configuration.getSecurityEnabled()); } - + /** - * Register a HealthCheck to the Set of HealthChecks to execute when performHealthChecks is called. + * Register a Readiness to the Set of HealthChecks to execute when + * performHealthChecks is called. * * @param appName The name of the application being deployed * @param healthCheck The HealthCheck to register */ - public void registerHealthCheck(String appName, HealthCheck healthCheck) { - // If we don't already have the app registered, we need to create a new Set for it + public void registerReadiness(String appName, HealthCheck healthCheck) { + registerHealthCheck(appName, healthCheck, readiness); + } + + /** + * Register a Liveness to the Set of HealthChecks to execute when + * performHealthChecks is called. + * + * @param appName The name of the application being deployed + * @param healthCheck The HealthCheck to register + */ + public void registerLiveness(String appName, HealthCheck healthCheck) { + registerHealthCheck(appName, healthCheck, liveness); + } + + /** + * Register a Health to the Set of HealthChecks to execute when + * performHealthChecks is called. + * + * @param appName The name of the application being deployed + * @param healthCheck The HealthCheck to register + */ + public void registerHealth(String appName, HealthCheck healthCheck) { + registerHealthCheck(appName, healthCheck, health); + } + + /** + * Register a HealthCheck to the Set of HealthChecks based on appName. + * + * @param appName The name of the application being deployed + * @param healthCheck The HealthCheck to register + */ + private void registerHealthCheck(String appName, HealthCheck healthCheck, Map> healthChecks) { + // If we don't already have the app registered, we need to create a new Set for it if (!healthChecks.containsKey(appName)) { // Sync so that we don't get clashes synchronized (this) { @@ -184,9 +227,26 @@ public void registerClassLoader(String appName, ClassLoader classloader) { * @param response The response to return * @throws IOException If there's an issue writing the response */ - public void performHealthChecks(HttpServletResponse response) throws IOException { + public void performHealthChecks(HttpServletResponse response, HealthCheckType type) throws IOException { Set healthCheckResponses = new HashSet<>(); + final Map> healthChecks; + if (type == HealthCheckType.READINESS) { + healthChecks = readiness; + } else if (type == HealthCheckType.LIVENESS) { + healthChecks = liveness; + } else { + healthChecks = new HashMap<>(health); + readiness.forEach((k, v) -> healthChecks.merge(k, v, (oldValue, newValue) -> { + oldValue.addAll(newValue); + return oldValue; + })); + liveness.forEach((k, v) -> healthChecks.merge(k, v, (oldValue, newValue) -> { + oldValue.addAll(newValue); + return oldValue; + })); + } + // Iterate over every HealthCheck stored in the Map for (Map.Entry> healthChecksEntry : healthChecks.entrySet()) { for (HealthCheck healthCheck : healthChecksEntry.getValue()) { @@ -218,12 +278,13 @@ public void performHealthChecks(HttpServletResponse response) throws IOException // If we haven't encountered an exception, construct the JSON response if (response.getStatus() != 500) { - constructResponse(response, healthCheckResponses); + constructResponse(response, healthCheckResponses, type); } } private void constructResponse(HttpServletResponse httpResponse, - Set healthCheckResponses) throws IOException { + Set healthCheckResponses, + HealthCheckType type) throws IOException { httpResponse.setContentType("application/json"); // For each HealthCheckResponse we got from executing the health checks... @@ -233,7 +294,11 @@ private void constructResponse(HttpServletResponse httpResponse, // Add the name and state healthCheckObject.add("name", healthCheckResponse.getName()); - healthCheckObject.add("state", healthCheckResponse.getState().toString()); + if(backwardCompatibilityEnabled && type == HealthCheckType.HEALTH) { + healthCheckObject.add("state", healthCheckResponse.getState().toString()); + } else { + healthCheckObject.add("status", healthCheckResponse.getState().toString()); + } // Add data if present JsonObjectBuilder healthCheckData = Json.createObjectBuilder(); @@ -258,10 +323,11 @@ private void constructResponse(HttpServletResponse httpResponse, JsonObjectBuilder responseObject = Json.createObjectBuilder(); // Set the aggregate outcome - if (httpResponse.getStatus() == 200) { - responseObject.add("outcome", "UP"); + String status = httpResponse.getStatus() == 200 ? "UP" : "DOWN"; + if (backwardCompatibilityEnabled && type == HealthCheckType.HEALTH) { + responseObject.add("outcome", status); } else { - responseObject.add("outcome", "DOWN"); + responseObject.add("status", status); } // Add all of the checks diff --git a/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/HealthCheckType.java b/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/HealthCheckType.java new file mode 100644 index 00000000000..0ae98653c03 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/HealthCheckType.java @@ -0,0 +1,48 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2019] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package fish.payara.microprofile.healthcheck; + +public enum HealthCheckType { + + READINESS, + LIVENESS, + HEALTH; + +} diff --git a/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/servlet/HealthCheckServlet.java b/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/servlet/HealthCheckServlet.java index 8b6b0329548..05f74f3197d 100644 --- a/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/servlet/HealthCheckServlet.java +++ b/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/servlet/HealthCheckServlet.java @@ -40,6 +40,7 @@ package fish.payara.microprofile.healthcheck.servlet; import fish.payara.microprofile.healthcheck.HealthCheckService; +import fish.payara.microprofile.healthcheck.HealthCheckType; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -53,7 +54,10 @@ * @author Andrew Pielage */ public class HealthCheckServlet extends HttpServlet { - + + private static final String READINESS_ENDPOINT = "/ready"; + private static final String LIVENESS_ENDPOINT = "/live"; + /** * Processes requests for both HTTP GET and POST methods. * @@ -75,7 +79,15 @@ protected void processRequest(HttpServletRequest request, HttpServletResponse re response.sendError(SC_FORBIDDEN, "MicroProfile Health Check Service is disabled"); return; } - healthCheckService.performHealthChecks(response); + + if (READINESS_ENDPOINT.equals(request.getPathInfo())) { + healthCheckService.performHealthChecks(response, HealthCheckType.READINESS); + } else if (LIVENESS_ENDPOINT.equals(request.getPathInfo())) { + healthCheckService.performHealthChecks(response, HealthCheckType.LIVENESS); + } else { + healthCheckService.performHealthChecks(response, HealthCheckType.HEALTH); + } + } // @@ -114,7 +126,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) */ @Override public String getServletInfo() { - return "Short description"; + return "HealthCheck Endpoint"; }// } diff --git a/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/servlet/HealthCheckServletContainerInitializer.java b/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/servlet/HealthCheckServletContainerInitializer.java index baca6d14933..c14b080f897 100644 --- a/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/servlet/HealthCheckServletContainerInitializer.java +++ b/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/servlet/HealthCheckServletContainerInitializer.java @@ -39,14 +39,16 @@ */ package fish.payara.microprofile.healthcheck.servlet; -import static fish.payara.microprofile.Constants.DEFAULT_GROUP_NAME; import fish.payara.microprofile.healthcheck.HealthCheckService; import fish.payara.microprofile.healthcheck.config.MetricsHealthCheckConfiguration; import static java.util.Arrays.asList; +import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.logging.Level; +import static java.util.logging.Level.FINE; +import static java.util.logging.Level.INFO; import java.util.logging.Logger; +import javax.enterprise.inject.Any; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.CDI; @@ -60,8 +62,11 @@ import static javax.servlet.annotation.ServletSecurity.TransportGuarantee.CONFIDENTIAL; import org.eclipse.microprofile.health.Health; import org.eclipse.microprofile.health.HealthCheck; +import org.eclipse.microprofile.health.Liveness; +import org.eclipse.microprofile.health.Readiness; import org.glassfish.api.invocation.InvocationManager; import static org.glassfish.common.util.StringHelper.isEmpty; +import org.glassfish.hk2.api.ServiceLocator; import org.glassfish.internal.api.Globals; /** @@ -71,7 +76,16 @@ * @author Andrew Pielage */ public class HealthCheckServletContainerInitializer implements ServletContainerInitializer { - + + private static final Logger LOGGER = Logger.getLogger(HealthCheckServletContainerInitializer.class.getName()); + + private static final AnnotationLiteral READINESS = new AnnotationLiteral() { + }; + private static final AnnotationLiteral LIVENESS = new AnnotationLiteral() { + }; + private static final AnnotationLiteral HEALTH = new AnnotationLiteral() { + }; + @Override public void onStartup(Set> c, ServletContext ctx) throws ServletException { // Check if this context is the root one ("/") @@ -79,11 +93,11 @@ public void onStartup(Set> c, ServletContext ctx) throws ServletExcepti // Check if there is already a servlet for healthcheck Map registrations = ctx.getServletRegistrations(); MetricsHealthCheckConfiguration configuration = Globals.getDefaultHabitat().getService(MetricsHealthCheckConfiguration.class); - - if (!Boolean.parseBoolean(configuration.getEnabled())){ + + if (!Boolean.parseBoolean(configuration.getEnabled())) { return; //MP Healthcheck disabled } - + for (ServletRegistration reg : registrations.values()) { if (reg.getClass().equals(HealthCheckServlet.class) || reg.getMappings().contains("/" + configuration.getEndpoint())) { return; @@ -95,52 +109,69 @@ public void onStartup(Set> c, ServletContext ctx) throws ServletExcepti && !asList(virtualServers.split(",")).contains(ctx.getVirtualServerName())) { return; } - + // Register servlet ServletRegistration.Dynamic reg = ctx.addServlet("microprofile-healthcheck-servlet", HealthCheckServlet.class); - reg.addMapping("/" + configuration.getEndpoint()); + reg.addMapping("/" + configuration.getEndpoint() + "/*"); if (Boolean.parseBoolean(configuration.getSecurityEnabled())) { String[] roles = configuration.getRoles().split(","); reg.setServletSecurity(new ServletSecurityElement(new HttpConstraintElement(CONFIDENTIAL, roles))); ctx.declareRoles(roles); } } - + // Get the BeanManager BeanManager beanManager = null; try { beanManager = CDI.current().getBeanManager(); } catch (Exception ex) { - Logger.getLogger(HealthCheckServletContainerInitializer.class.getName()).log(Level.FINE, - "Exception getting BeanManager; this probably isn't a CDI application. " - + "No HealthChecks will be registered", ex); + LOGGER.log(FINE, "Exception getting BeanManager; this probably isn't a CDI application. " + + "No HealthChecks will be registered", ex); } - - // Check for any Beans annotated with @Health + + // Check for any Beans annotated with @Readiness, @Liveness or @Health if (beanManager != null) { - HealthCheckService healthCheckService = Globals.getDefaultBaseServiceLocator().getService( - HealthCheckService.class); - InvocationManager invocationManager = Globals.getDefaultBaseServiceLocator().getService( - InvocationManager.class); - - // For each bean annotated with @Health and implementing the HealthCheck interface, + ServiceLocator serviceLocator = Globals.getDefaultBaseServiceLocator(); + HealthCheckService healthCheckService = serviceLocator.getService(HealthCheckService.class); + InvocationManager invocationManager = serviceLocator.getService(InvocationManager.class); + String appName = invocationManager.getCurrentInvocation().getAppName(); + + // For each bean annotated with @Readiness, @Liveness or @Health + // and implementing the HealthCheck interface, // register it to the HealthCheckService along with the application name - Set> beans = beanManager.getBeans(HealthCheck.class, new AnnotationLiteral() {}); + Set> beans = new HashSet<>(); + + beans.addAll(beanManager.getBeans(HealthCheck.class, READINESS)); + beans.addAll(beanManager.getBeans(HealthCheck.class, LIVENESS)); + beans.addAll(beanManager.getBeans(HealthCheck.class, HEALTH)); + for (Bean bean : beans) { - HealthCheck healthCheck = (HealthCheck) beanManager.getReference(bean, bean.getBeanClass(), - beanManager.createCreationalContext(bean)); - - healthCheckService.registerHealthCheck(invocationManager.getCurrentInvocation().getAppName(), - healthCheck); - healthCheckService.registerClassLoader(invocationManager.getCurrentInvocation().getAppName(), - healthCheck.getClass().getClassLoader()); - - Logger.getLogger(HealthCheckServletContainerInitializer.class.getName()).log(Level.INFO, - "Registered {0} as a HealthCheck for app: {1}", - new Object[]{bean.getBeanClass().getCanonicalName(), - invocationManager.getCurrentInvocation().getAppName()}); + HealthCheck healthCheck = (HealthCheck) beanManager.getReference( + bean, + HealthCheck.class, + beanManager.createCreationalContext(bean) + ); + + if (bean.getQualifiers().contains(READINESS)) { + healthCheckService.registerReadiness(appName, healthCheck); + } + if (bean.getQualifiers().contains(LIVENESS)) { + healthCheckService.registerLiveness(appName, healthCheck); + } + if (bean.getQualifiers().contains(HEALTH)) { + healthCheckService.registerHealth(appName, healthCheck); + } + healthCheckService.registerClassLoader( + appName, + healthCheck.getClass().getClassLoader() + ); + + LOGGER.log(INFO, + "Registered {0} as a HealthCheck for app: {1}", + new Object[]{bean.getBeanClass().getCanonicalName(), appName} + ); } } } - + } diff --git a/appserver/pom.xml b/appserver/pom.xml index 5ab94ad87fb..8b8f09e0710 100644 --- a/appserver/pom.xml +++ b/appserver/pom.xml @@ -205,7 +205,7 @@ 2.0.1 1.1.payara-p1 - 1.0.payara-p1 + 2.1 2.0.1.payara-p1 1.2.1.payara-p1 1.1.2 From 6af77e0e4e7e22516a136e9b0100b225e17a6a3e Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Wed, 9 Oct 2019 08:16:08 +0530 Subject: [PATCH 2/3] PAYARA-3829 MicroProfile Healthcheck 2.1 implementation - PR Review changes --- .../healthcheck/HealthCheckService.java | 137 +++++++++--------- .../healthcheck/HealthCheckType.java | 46 +++++- .../servlet/HealthCheckServlet.java | 11 +- ...ealthCheckServletContainerInitializer.java | 36 ++--- 4 files changed, 121 insertions(+), 109 deletions(-) diff --git a/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/HealthCheckService.java b/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/HealthCheckService.java index f59d6df0c85..7a6bc881ab6 100644 --- a/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/HealthCheckService.java +++ b/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/HealthCheckService.java @@ -39,6 +39,9 @@ */ package fish.payara.microprofile.healthcheck; +import static fish.payara.microprofile.healthcheck.HealthCheckType.HEALTH; +import static fish.payara.microprofile.healthcheck.HealthCheckType.LIVENESS; +import static fish.payara.microprofile.healthcheck.HealthCheckType.READINESS; import java.beans.PropertyChangeEvent; import java.io.IOException; import java.util.ArrayList; @@ -57,6 +60,8 @@ import javax.servlet.http.HttpServletResponse; import fish.payara.microprofile.healthcheck.config.MetricsHealthCheckConfiguration; import java.util.HashMap; +import java.util.Map.Entry; +import java.util.function.BiConsumer; import static java.util.logging.Level.WARNING; import org.eclipse.microprofile.config.ConfigProvider; import org.eclipse.microprofile.health.HealthCheck; @@ -92,7 +97,7 @@ public class HealthCheckService implements EventListener, ConfigListener { @Inject MetricsHealthCheckConfiguration configuration; - private boolean backwardCompatibilityEnabled; + private boolean backwardCompEnabled; private static final Logger LOG = Logger.getLogger(HealthCheckService.class.getName()); @@ -109,7 +114,7 @@ public void postConstruct() { events = Globals.getDefaultBaseServiceLocator().getService(Events.class); } events.register(this); - this.backwardCompatibilityEnabled = ConfigProvider.getConfig() + this.backwardCompEnabled = ConfigProvider.getConfig() .getOptionalValue("mp.health.enable-backward-compatibility", Boolean.class) .orElse(false); } @@ -120,11 +125,12 @@ public void event(Event event) { if (event.is(Deployment.APPLICATION_UNLOADED)) { ApplicationInfo appInfo = Deployment.APPLICATION_UNLOADED.getHook(event); if (appInfo != null) { - readiness.remove(appInfo.getName()); - liveness.remove(appInfo.getName()); - health.remove(appInfo.getName()); - applicationClassLoaders.remove(appInfo.getName()); - applicationsLoaded.remove(appInfo.getName()); + String appName = appInfo.getName(); + readiness.remove(appName); + liveness.remove(appName); + health.remove(appName); + applicationClassLoaders.remove(appName); + applicationsLoaded.remove(appName); } } @@ -146,45 +152,15 @@ public boolean isSecurityEnabled() { } /** - * Register a Readiness to the Set of HealthChecks to execute when + * Register a HealthCheck to the Set of HealthChecks based on appName to execute when * performHealthChecks is called. * * @param appName The name of the application being deployed * @param healthCheck The HealthCheck to register */ - public void registerReadiness(String appName, HealthCheck healthCheck) { - registerHealthCheck(appName, healthCheck, readiness); - } - - /** - * Register a Liveness to the Set of HealthChecks to execute when - * performHealthChecks is called. - * - * @param appName The name of the application being deployed - * @param healthCheck The HealthCheck to register - */ - public void registerLiveness(String appName, HealthCheck healthCheck) { - registerHealthCheck(appName, healthCheck, liveness); - } - - /** - * Register a Health to the Set of HealthChecks to execute when - * performHealthChecks is called. - * - * @param appName The name of the application being deployed - * @param healthCheck The HealthCheck to register - */ - public void registerHealth(String appName, HealthCheck healthCheck) { - registerHealthCheck(appName, healthCheck, health); - } - - /** - * Register a HealthCheck to the Set of HealthChecks based on appName. - * - * @param appName The name of the application being deployed - * @param healthCheck The HealthCheck to register - */ - private void registerHealthCheck(String appName, HealthCheck healthCheck, Map> healthChecks) { + public void registerHealthCheck(String appName, HealthCheck healthCheck, HealthCheckType type) { + Map> healthChecks = getHealthChecks(type); + // If we don't already have the app registered, we need to create a new Set for it if (!healthChecks.containsKey(appName)) { // Sync so that we don't get clashes @@ -220,35 +196,50 @@ public void registerClassLoader(String appName, ClassLoader classloader) { } } } + + private Map> getHealthChecks(HealthCheckType type) { + final Map> healthChecks; + if (type == READINESS) { + healthChecks = readiness; + } else if (type == LIVENESS) { + healthChecks = liveness; + } else { + healthChecks = health; + } + return healthChecks; + } + + private Map> getCollectiveHealthChecks(HealthCheckType type){ + final Map> healthChecks; + if (type == READINESS) { + healthChecks = readiness; + } else if (type == LIVENESS) { + healthChecks = liveness; + } else { + healthChecks = new HashMap<>(health); + BiConsumer> mergeHealthCheckMap + = (key, value) -> healthChecks.merge(key, value, (oldValue, newValue) -> { + oldValue.addAll(newValue); + return oldValue; + }); + readiness.forEach(mergeHealthCheckMap); + liveness.forEach(mergeHealthCheckMap); + } + return healthChecks; + } /** * Execute the call method of every registered HealthCheck and generate the response. * * @param response The response to return + * @param type the type of health check * @throws IOException If there's an issue writing the response */ public void performHealthChecks(HttpServletResponse response, HealthCheckType type) throws IOException { Set healthCheckResponses = new HashSet<>(); - final Map> healthChecks; - if (type == HealthCheckType.READINESS) { - healthChecks = readiness; - } else if (type == HealthCheckType.LIVENESS) { - healthChecks = liveness; - } else { - healthChecks = new HashMap<>(health); - readiness.forEach((k, v) -> healthChecks.merge(k, v, (oldValue, newValue) -> { - oldValue.addAll(newValue); - return oldValue; - })); - liveness.forEach((k, v) -> healthChecks.merge(k, v, (oldValue, newValue) -> { - oldValue.addAll(newValue); - return oldValue; - })); - } - // Iterate over every HealthCheck stored in the Map - for (Map.Entry> healthChecksEntry : healthChecks.entrySet()) { + for (Entry> healthChecksEntry : getCollectiveHealthChecks(type).entrySet()) { for (HealthCheck healthCheck : healthChecksEntry.getValue()) { // Execute the call method of the HealthCheck and add its outcome to the set of responses try { @@ -273,7 +264,12 @@ public void performHealthChecks(HttpServletResponse response, HealthCheckType ty // No applications (yet), server is not ready. if (applicationsLoaded.isEmpty()) { // Application is not yet deployed - healthCheckResponses.add(HealthCheckResponse.builder().name("No Application deployed").down().build()); + healthCheckResponses.add( + HealthCheckResponse.builder() + .name("No Application deployed") + .down() + .build() + ); } // If we haven't encountered an exception, construct the JSON response @@ -294,11 +290,10 @@ private void constructResponse(HttpServletResponse httpResponse, // Add the name and state healthCheckObject.add("name", healthCheckResponse.getName()); - if(backwardCompatibilityEnabled && type == HealthCheckType.HEALTH) { - healthCheckObject.add("state", healthCheckResponse.getState().toString()); - } else { - healthCheckObject.add("status", healthCheckResponse.getState().toString()); - } + healthCheckObject.add( + backwardCompEnabled && type == HEALTH ? "state" : "status", + healthCheckResponse.getState().toString() + ); // Add data if present JsonObjectBuilder healthCheckData = Json.createObjectBuilder(); @@ -322,13 +317,11 @@ private void constructResponse(HttpServletResponse httpResponse, // Create the final aggregate object JsonObjectBuilder responseObject = Json.createObjectBuilder(); - // Set the aggregate outcome - String status = httpResponse.getStatus() == 200 ? "UP" : "DOWN"; - if (backwardCompatibilityEnabled && type == HealthCheckType.HEALTH) { - responseObject.add("outcome", status); - } else { - responseObject.add("status", status); - } + // Set the aggregate status + responseObject.add( + backwardCompEnabled && type == HEALTH ? "outcome" : "status", + httpResponse.getStatus() == 200 ? "UP" : "DOWN" + ); // Add all of the checks responseObject.add("checks", checksArray); diff --git a/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/HealthCheckType.java b/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/HealthCheckType.java index 0ae98653c03..f9ffea41327 100644 --- a/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/HealthCheckType.java +++ b/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/HealthCheckType.java @@ -39,10 +39,50 @@ */ package fish.payara.microprofile.healthcheck; +import java.lang.annotation.Annotation; +import java.util.Set; +import javax.enterprise.util.AnnotationLiteral; +import org.eclipse.microprofile.health.Health; +import org.eclipse.microprofile.health.Liveness; +import org.eclipse.microprofile.health.Readiness; + public enum HealthCheckType { - READINESS, - LIVENESS, - HEALTH; + READINESS("/ready", new AnnotationLiteral() { + }), + LIVENESS("/live", new AnnotationLiteral() { + }), + HEALTH(null, new AnnotationLiteral() { + }); + + String path; + AnnotationLiteral literal; + + private HealthCheckType(String path, AnnotationLiteral literal) { + this.path = path; + this.literal = literal; + } + + public AnnotationLiteral getLiteral() { + return literal; + } + + public static HealthCheckType fromPath(String path) { + for (HealthCheckType value : values()) { + if (value.path != null && value.path.equals(path)) { + return value; + } + } + return HEALTH; + } + + public static HealthCheckType fromQualifiers(Set qualifiers) { + for (HealthCheckType value : values()) { + if (qualifiers != null && qualifiers.contains(value.literal)) { + return value; + } + } + throw new IllegalStateException("HealthCheckType not found for : " + qualifiers); + } } diff --git a/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/servlet/HealthCheckServlet.java b/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/servlet/HealthCheckServlet.java index 05f74f3197d..b42741938ca 100644 --- a/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/servlet/HealthCheckServlet.java +++ b/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/servlet/HealthCheckServlet.java @@ -55,9 +55,6 @@ */ public class HealthCheckServlet extends HttpServlet { - private static final String READINESS_ENDPOINT = "/ready"; - private static final String LIVENESS_ENDPOINT = "/live"; - /** * Processes requests for both HTTP GET and POST methods. * @@ -80,13 +77,7 @@ protected void processRequest(HttpServletRequest request, HttpServletResponse re return; } - if (READINESS_ENDPOINT.equals(request.getPathInfo())) { - healthCheckService.performHealthChecks(response, HealthCheckType.READINESS); - } else if (LIVENESS_ENDPOINT.equals(request.getPathInfo())) { - healthCheckService.performHealthChecks(response, HealthCheckType.LIVENESS); - } else { - healthCheckService.performHealthChecks(response, HealthCheckType.HEALTH); - } + healthCheckService.performHealthChecks(response, HealthCheckType.fromPath(request.getPathInfo())); } diff --git a/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/servlet/HealthCheckServletContainerInitializer.java b/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/servlet/HealthCheckServletContainerInitializer.java index c14b080f897..07e2fef4326 100644 --- a/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/servlet/HealthCheckServletContainerInitializer.java +++ b/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/servlet/HealthCheckServletContainerInitializer.java @@ -40,6 +40,10 @@ package fish.payara.microprofile.healthcheck.servlet; import fish.payara.microprofile.healthcheck.HealthCheckService; +import fish.payara.microprofile.healthcheck.HealthCheckType; +import static fish.payara.microprofile.healthcheck.HealthCheckType.HEALTH; +import static fish.payara.microprofile.healthcheck.HealthCheckType.LIVENESS; +import static fish.payara.microprofile.healthcheck.HealthCheckType.READINESS; import fish.payara.microprofile.healthcheck.config.MetricsHealthCheckConfiguration; import static java.util.Arrays.asList; import java.util.HashSet; @@ -48,11 +52,9 @@ import static java.util.logging.Level.FINE; import static java.util.logging.Level.INFO; import java.util.logging.Logger; -import javax.enterprise.inject.Any; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.CDI; -import javax.enterprise.util.AnnotationLiteral; import javax.servlet.HttpConstraintElement; import javax.servlet.ServletContainerInitializer; import javax.servlet.ServletContext; @@ -60,10 +62,7 @@ import javax.servlet.ServletRegistration; import javax.servlet.ServletSecurityElement; import static javax.servlet.annotation.ServletSecurity.TransportGuarantee.CONFIDENTIAL; -import org.eclipse.microprofile.health.Health; import org.eclipse.microprofile.health.HealthCheck; -import org.eclipse.microprofile.health.Liveness; -import org.eclipse.microprofile.health.Readiness; import org.glassfish.api.invocation.InvocationManager; import static org.glassfish.common.util.StringHelper.isEmpty; import org.glassfish.hk2.api.ServiceLocator; @@ -79,13 +78,6 @@ public class HealthCheckServletContainerInitializer implements ServletContainerI private static final Logger LOGGER = Logger.getLogger(HealthCheckServletContainerInitializer.class.getName()); - private static final AnnotationLiteral READINESS = new AnnotationLiteral() { - }; - private static final AnnotationLiteral LIVENESS = new AnnotationLiteral() { - }; - private static final AnnotationLiteral HEALTH = new AnnotationLiteral() { - }; - @Override public void onStartup(Set> c, ServletContext ctx) throws ServletException { // Check if this context is the root one ("/") @@ -141,9 +133,9 @@ public void onStartup(Set> c, ServletContext ctx) throws ServletExcepti // register it to the HealthCheckService along with the application name Set> beans = new HashSet<>(); - beans.addAll(beanManager.getBeans(HealthCheck.class, READINESS)); - beans.addAll(beanManager.getBeans(HealthCheck.class, LIVENESS)); - beans.addAll(beanManager.getBeans(HealthCheck.class, HEALTH)); + beans.addAll(beanManager.getBeans(HealthCheck.class, READINESS.getLiteral())); + beans.addAll(beanManager.getBeans(HealthCheck.class, LIVENESS.getLiteral())); + beans.addAll(beanManager.getBeans(HealthCheck.class, HEALTH.getLiteral())); for (Bean bean : beans) { HealthCheck healthCheck = (HealthCheck) beanManager.getReference( @@ -152,15 +144,11 @@ public void onStartup(Set> c, ServletContext ctx) throws ServletExcepti beanManager.createCreationalContext(bean) ); - if (bean.getQualifiers().contains(READINESS)) { - healthCheckService.registerReadiness(appName, healthCheck); - } - if (bean.getQualifiers().contains(LIVENESS)) { - healthCheckService.registerLiveness(appName, healthCheck); - } - if (bean.getQualifiers().contains(HEALTH)) { - healthCheckService.registerHealth(appName, healthCheck); - } + healthCheckService.registerHealthCheck( + appName, + healthCheck, + HealthCheckType.fromQualifiers(bean.getQualifiers()) + ); healthCheckService.registerClassLoader( appName, healthCheck.getClass().getClassLoader() From 2e697d01062b68c27e897812c93cd258a245e949 Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Thu, 10 Oct 2019 09:06:09 +0530 Subject: [PATCH 3/3] PAYARA-3829 MicroProfile Healthcheck 2.1 implementation - property renamed to MP_HEALTH_BACKWARD_COMPATIBILITY_ENABLED --- .../payara/microprofile/healthcheck/HealthCheckService.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/HealthCheckService.java b/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/HealthCheckService.java index 7a6bc881ab6..1ec402fba7e 100644 --- a/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/HealthCheckService.java +++ b/appserver/payara-appserver-modules/microprofile/healthcheck/src/main/java/fish/payara/microprofile/healthcheck/HealthCheckService.java @@ -108,6 +108,8 @@ public class HealthCheckService implements EventListener, ConfigListener { private final Map applicationClassLoaders = new ConcurrentHashMap<>(); private final List applicationsLoaded = new CopyOnWriteArrayList<>(); + private static final String BACKWARD_COMP_ENABLED_PROPERTY = "MP_HEALTH_BACKWARD_COMPATIBILITY_ENABLED"; + @PostConstruct public void postConstruct() { if (events == null) { @@ -115,7 +117,7 @@ public void postConstruct() { } events.register(this); this.backwardCompEnabled = ConfigProvider.getConfig() - .getOptionalValue("mp.health.enable-backward-compatibility", Boolean.class) + .getOptionalValue(BACKWARD_COMP_ENABLED_PROPERTY, Boolean.class) .orElse(false); } @@ -209,7 +211,7 @@ private Map> getHealthChecks(HealthCheckType type) { return healthChecks; } - private Map> getCollectiveHealthChecks(HealthCheckType type){ + private Map> getCollectiveHealthChecks(HealthCheckType type) { final Map> healthChecks; if (type == READINESS) { healthChecks = readiness;