Skip to content

Commit

Permalink
FISH-660 Merge pull request payara#4986 from MattGill98/FISH-660
Browse files Browse the repository at this point in the history
FISH-660 Upgrade MicroProfile Health Implementation to 3.0-RC5
  • Loading branch information
MattGill98 authored and Pandrex247 committed Jun 23, 2021
1 parent 7fdc2c3 commit 410ffa8
Show file tree
Hide file tree
Showing 16 changed files with 114 additions and 145 deletions.
Expand Up @@ -59,8 +59,8 @@
import fish.payara.appserver.micro.services.PayaraInstanceImpl;
import fish.payara.micro.ClusterCommandResult;
import fish.payara.micro.data.InstanceDescriptor;
import fish.payara.microprofile.healthcheck.config.MicroprofileHealthCheckConfiguration;
import fish.payara.nucleus.healthcheck.configuration.MicroProfileHealthCheckerConfiguration;
import fish.payara.microprofile.healthcheck.config.MetricsHealthCheckConfiguration;
import fish.payara.monitoring.collect.MonitoringData;
import fish.payara.monitoring.collect.MonitoringDataCollector;
import fish.payara.monitoring.collect.MonitoringDataSource;
Expand Down Expand Up @@ -229,10 +229,10 @@ private Map<String, Future<Integer>> pingAllInstances(long timeoutMillis) {
String instanceName = server.getName();
tasks.put(instanceName, payaraExecutorService.submit(() -> {
// get the remote server's MP HealthCheck config
MetricsHealthCheckConfiguration metricsConfig = server.getConfig()
.getExtensionByType(MetricsHealthCheckConfiguration.class);
if (metricsConfig != null && Boolean.valueOf(metricsConfig.getEnabled())) {
return pingHealthEndpoint(buildURI(server, metricsConfig.getEndpoint()));
MicroprofileHealthCheckConfiguration healthCheckConfig = server.getConfig()
.getExtensionByType(MicroprofileHealthCheckConfiguration.class);
if (healthCheckConfig != null && Boolean.valueOf(healthCheckConfig.getEnabled())) {
return pingHealthEndpoint(buildURI(server, healthCheckConfig.getEndpoint()));
}
return -1;
}));
Expand Down
Expand Up @@ -39,18 +39,16 @@
*/
package fish.payara.microprofile.healthcheck;

import fish.payara.microprofile.healthcheck.config.MetricsHealthCheckConfiguration;
import fish.payara.microprofile.healthcheck.checks.PayaraHealthCheck;
import fish.payara.nucleus.healthcheck.configuration.Checker;
import fish.payara.nucleus.healthcheck.events.PayaraHealthCheckServiceEvents;
import fish.payara.monitoring.collect.MonitoringData;
import fish.payara.monitoring.collect.MonitoringDataCollector;
import fish.payara.monitoring.collect.MonitoringDataSource;
import fish.payara.monitoring.collect.MonitoringWatchCollector;
import fish.payara.monitoring.collect.MonitoringWatchSource;
import static fish.payara.microprofile.healthcheck.HealthCheckType.LIVENESS;
import static fish.payara.microprofile.healthcheck.HealthCheckType.READINESS;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonMap;
import static java.util.logging.Level.WARNING;
import static java.util.stream.Collectors.joining;

import java.beans.PropertyChangeEvent;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
Expand All @@ -70,12 +68,14 @@
import javax.json.Json;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObjectBuilder;
import javax.json.JsonWriter;
import javax.json.JsonWriterFactory;
import javax.json.stream.JsonGenerator;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.HealthCheckResponse.State;
import org.eclipse.microprofile.health.HealthCheckResponse.Status;
import org.glassfish.api.StartupRunLevel;
import org.glassfish.api.event.EventListener;
import org.glassfish.api.event.Events;
Expand All @@ -88,17 +88,15 @@
import org.jvnet.hk2.config.UnprocessedChangeEvent;
import org.jvnet.hk2.config.UnprocessedChangeEvents;

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.io.StringWriter;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonMap;
import static java.util.logging.Level.WARNING;
import static java.util.stream.Collectors.joining;
import javax.json.JsonWriter;
import javax.json.JsonWriterFactory;
import javax.json.stream.JsonGenerator;
import fish.payara.microprofile.healthcheck.checks.PayaraHealthCheck;
import fish.payara.microprofile.healthcheck.config.MicroprofileHealthCheckConfiguration;
import fish.payara.monitoring.collect.MonitoringData;
import fish.payara.monitoring.collect.MonitoringDataCollector;
import fish.payara.monitoring.collect.MonitoringDataSource;
import fish.payara.monitoring.collect.MonitoringWatchCollector;
import fish.payara.monitoring.collect.MonitoringWatchSource;
import fish.payara.nucleus.healthcheck.configuration.Checker;
import fish.payara.nucleus.healthcheck.events.PayaraHealthCheckServiceEvents;

/**
* Service that handles the registration, execution, and response of MicroProfile HealthChecks.
Expand All @@ -116,16 +114,10 @@ public class HealthCheckService implements EventListener, ConfigListener, Monito
private ApplicationRegistry applicationRegistry;

@Inject
private MetricsHealthCheckConfiguration configuration;

@Inject
private ConfigProviderResolver microConfigResolver;

private boolean backwardCompEnabled;
private MicroprofileHealthCheckConfiguration configuration;

private static final Logger LOG = Logger.getLogger(HealthCheckService.class.getName());

private final Map<String, Set<HealthCheck>> health = new ConcurrentHashMap<>();
private final Map<String, Set<HealthCheck>> readiness = new ConcurrentHashMap<>();
private final Map<String, Set<HealthCheck>> liveness = new ConcurrentHashMap<>();

Expand All @@ -134,45 +126,37 @@ public class HealthCheckService implements EventListener, ConfigListener, Monito

private final AtomicReference<Map<String, Set<String>>> checksCollected = new AtomicReference<>();

private static final String BACKWARD_COMP_ENABLED_PROPERTY = "MP_HEALTH_BACKWARD_COMPATIBILITY_ENABLED";

@PostConstruct
public void postConstruct() {
if (events == null) {
events = Globals.getDefaultBaseServiceLocator().getService(Events.class);
}
events.register(this);
this.backwardCompEnabled = microConfigResolver.getConfig()
.getOptionalValue(BACKWARD_COMP_ENABLED_PROPERTY, Boolean.class)
.orElse(false);
}

@Override
@MonitoringData(ns = "health", intervalSeconds = 12)
public void collect(MonitoringDataCollector collector) {
Map<String, Set<String>> collected = new HashMap<>();
Map<String, List<HealthCheckResponse>> healthResponsesByAppName = collectChecks(collector, health, collected);
Map<String, List<HealthCheckResponse>> readinessResponsesByAppName = collectChecks(collector, readiness, collected);
Map<String, List<HealthCheckResponse>> livenessResponsesByAppName = collectChecks(collector, liveness, collected);
checksCollected.set(collected);
if (!collected.isEmpty()) {
List<HealthCheckResponse> overall = new ArrayList<>();
overall.addAll(collectJointType(collector, "Health", healthResponsesByAppName));
overall.addAll(collectJointType(collector, "Readiness", readinessResponsesByAppName));
overall.addAll(collectJointType(collector, "Liveness", livenessResponsesByAppName));
collectUpDown(collector, computeJointState("Overall", overall));
}
for (String appName : collected.keySet()) {
List<HealthCheckResponse> overallByApp = new ArrayList<>();
overallByApp.addAll(healthResponsesByAppName.getOrDefault(appName, emptyList()));
overallByApp.addAll(readinessResponsesByAppName.getOrDefault(appName, emptyList()));
overallByApp.addAll(livenessResponsesByAppName.getOrDefault(appName, emptyList()));
collectUpDown(collector.group(appName), computeJointState("Overall", overallByApp));
}
}

private static void collectUpDown(MonitoringDataCollector collector, HealthCheckResponse response) {
collector.collect(response.getName(), response.getState() == State.UP ? 1 : 0);
collector.collect(response.getName(), response.getStatus() == Status.UP ? 1 : 0);
}

private static List<HealthCheckResponse> collectJointType(MonitoringDataCollector collector, String type,
Expand All @@ -196,16 +180,14 @@ public void collect(MonitoringWatchCollector collector) {
for (String metric : e.getValue()) {
addWatch(collector, appName, metric);
}
addWatch(collector, appName, "Health");
addWatch(collector, appName, "Readiness");
addWatch(collector, appName, "Liveness");
addWatch(collector, appName, "Overall");
addWatch(collector, appName, "Health");
}
if (!collected.isEmpty()) {
addWatch(collector, null, "Health");
addWatch(collector, null, "Readiness");
addWatch(collector, null, "Liveness");
addWatch(collector, null, "Overall");
addWatch(collector, null, "Health");
}
}
}
Expand All @@ -221,7 +203,7 @@ private static void addWatch(MonitoringWatchCollector collector, String appName,

private Map<String, List<HealthCheckResponse>> collectChecks(MonitoringDataCollector collector,
Map<String, Set<HealthCheck>> checks, Map<String, Set<String>> collected) {
Map<String, List<HealthCheckResponse>> stateByApp = new HashMap<>();
Map<String, List<HealthCheckResponse>> statusByApp = new HashMap<>();
for (Entry<String, Set<HealthCheck>> entry : checks.entrySet()) {
String appName = entry.getKey();
MonitoringDataCollector appCollector = collector.group(appName);
Expand All @@ -231,29 +213,29 @@ private Map<String, List<HealthCheckResponse>> collectChecks(MonitoringDataColle
Set<String> appCollected = collected.get(appName);
// prevent adding same check more then once, unfortunately we have to run it to find that out
if (appCollected == null || !appCollected.contains(metric)) {
stateByApp.computeIfAbsent(appName, key -> new ArrayList<>()).add(response);
statusByApp.computeIfAbsent(appName, key -> new ArrayList<>()).add(response);
collectUpDown(appCollector, response);
if (response.getState() == State.DOWN && response.getData().isPresent()) {
if (response.getStatus() == Status.DOWN && response.getData().isPresent()) {
appCollector.annotate(metric, 0L, createAnnotation(response.getData().get()));
}
collected.computeIfAbsent(appName, key -> new HashSet<>()).add(metric);
}
}
}
return stateByApp;
return statusByApp;
}

private static HealthCheckResponse computeJointState(String name, Collection<HealthCheckResponse> responses) {
long ups = responses.stream().filter(response -> response.getState() == State.UP).count();
long ups = responses.stream().filter(response -> response.getStatus() == Status.UP).count();
if (ups == responses.size()) {
return HealthCheckResponse.up(name);
}
String upNames = responses.stream()
.filter(r -> r.getState() == State.UP)
.filter(r -> r.getStatus() == Status.UP)
.map(r -> r.getName())
.collect(joining(","));
String downNames = responses.stream()
.filter(r -> r.getState() == State.DOWN)
.filter(r -> r.getStatus() == Status.DOWN)
.map(r -> r.getName())
.collect(joining(","));
return HealthCheckResponse.named(name).down()
Expand All @@ -275,7 +257,7 @@ private static String[] createAnnotation(Map<String, Object> data) {
}

@Override
public void event(Event event) {
public void event(Event<?> event) {
// Remove healthchecks when the app is undeployed.
Deployment.APPLICATION_UNLOADED.onMatch(event, appInfo -> unregisterHealthCheck(appInfo.getName()));

Expand Down Expand Up @@ -341,7 +323,6 @@ public void registerHealthCheck(String healthCheckName, HealthCheck healthCheck,
public void unregisterHealthCheck(String appName) {
readiness.remove(appName);
liveness.remove(appName);
health.remove(appName);
applicationClassLoaders.remove(appName);
applicationsLoaded.remove(appName);
}
Expand All @@ -367,15 +348,19 @@ public void registerClassLoader(String appName, ClassLoader classloader) {
}

private Map<String, Set<HealthCheck>> getHealthChecks(HealthCheckType type) {
final Map<String, Set<HealthCheck>> healthChecks;
if (type == READINESS) {
healthChecks = readiness;
} else if (type == LIVENESS) {
healthChecks = liveness;
} else {
healthChecks = health;
if (type == null) {
type = HealthCheckType.UNKNOWN;
}
switch (type) {
case LIVENESS:
return liveness;
case READINESS:
return readiness;
case UNKNOWN:
default:
LOG.warning("Unrecognised HealthCheckType: " + type);
return new HashMap<>();
}
return healthChecks;
}

private Map<String, Set<HealthCheck>> getCollectiveHealthChecks(HealthCheckType type) {
Expand All @@ -385,7 +370,7 @@ private Map<String, Set<HealthCheck>> getCollectiveHealthChecks(HealthCheckType
} else if (type == LIVENESS) {
healthChecks = liveness;
} else {
healthChecks = new HashMap<>(health);
healthChecks = new HashMap<>();
BiConsumer<? super String, ? super Set<HealthCheck>> mergeHealthCheckMap
= (key, value) -> healthChecks.merge(key, value, (oldValue, newValue) -> {
oldValue.addAll(newValue);
Expand Down Expand Up @@ -463,12 +448,9 @@ private void constructResponse(HttpServletResponse httpResponse,
for (HealthCheckResponse healthCheckResponse : healthCheckResponses) {
JsonObjectBuilder healthCheckObject = Json.createObjectBuilder();

// Add the name and state
// Add the name and status
healthCheckObject.add("name", healthCheckResponse.getName());
healthCheckObject.add(
backwardCompEnabled && type == HEALTH ? "state" : "status",
healthCheckResponse.getState().toString()
);
healthCheckObject.add("status", healthCheckResponse.getStatus().toString());

// Add data if present
JsonObjectBuilder healthCheckData = Json.createObjectBuilder();
Expand All @@ -484,7 +466,7 @@ private void constructResponse(HttpServletResponse httpResponse,

// Check if we need to set the response as 503. Check against status 200 so we don't repeatedly set it
if (httpResponse.getStatus() == 200
&& healthCheckResponse.getState().equals(HealthCheckResponse.State.DOWN)) {
&& healthCheckResponse.getStatus().equals(HealthCheckResponse.Status.DOWN)) {
httpResponse.setStatus(503);
}
}
Expand All @@ -493,10 +475,7 @@ private void constructResponse(HttpServletResponse httpResponse,
JsonObjectBuilder responseObject = Json.createObjectBuilder();

// Set the aggregate status
responseObject.add(
backwardCompEnabled && type == HEALTH ? "outcome" : "status",
httpResponse.getStatus() == 200 ? "UP" : "DOWN"
);
responseObject.add("status", httpResponse.getStatus() == 200 ? "UP" : "DOWN");

// Add all of the checks
responseObject.add("checks", checksArray);
Expand Down
@@ -1,7 +1,7 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) [2019] Payara Foundation and/or its affiliates. All rights reserved.
* Copyright (c) [2019-2020] 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
Expand Down Expand Up @@ -41,39 +41,38 @@

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("/ready", new AnnotationLiteral<Readiness>() {
}),
LIVENESS("/live", new AnnotationLiteral<Liveness>() {
}),
HEALTH(null, new AnnotationLiteral<Health>() {
});
READINESS("/ready", new Readiness.Literal()),
LIVENESS("/live", new Liveness.Literal()),
UNKNOWN(null, null);

String path;
AnnotationLiteral literal;
private String path;
private AnnotationLiteral<?> literal;

private HealthCheckType(String path, AnnotationLiteral literal) {
private HealthCheckType(String path, AnnotationLiteral<?> literal) {
this.path = path;
this.literal = literal;
}

public AnnotationLiteral getLiteral() {
public AnnotationLiteral<?> getLiteral() {
return literal;
}

public static HealthCheckType fromPath(String path) {
for (HealthCheckType value : values()) {
if (value.path != null && value.path.equals(path)) {
// If the path is equal (with or without the slash)
if (value.path != null && (value.path.equals(path) || value.path.substring(1).equals(path))) {
return value;
}
}
return HEALTH;
return UNKNOWN;
}

public static HealthCheckType fromQualifiers(Set<Annotation> qualifiers) {
Expand Down
Expand Up @@ -55,7 +55,6 @@ public class HealthSniffer extends MicroProfileSniffer {
public Class<? extends Annotation>[] getAnnotationTypes() {
return new Class[] {
// Search for Health annotations
org.eclipse.microprofile.health.Health.class,
org.eclipse.microprofile.health.Readiness.class,
org.eclipse.microprofile.health.Liveness.class,

Expand Down

0 comments on commit 410ffa8

Please sign in to comment.