package io.vertx.ext.healthchecks.impl;

import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.impl.ContextInternal;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.healthchecks.HealthChecks;
import io.vertx.ext.healthchecks.CheckResult;
import io.vertx.ext.healthchecks.Status;

import java.util.Objects;

Author:Clement Escoffier
/** * @author <a href="http://escoffier.me">Clement Escoffier</a> */
public class HealthChecksImpl implements HealthChecks { private final Vertx vertx; private CompositeProcedure root = new DefaultCompositeProcedure(); public HealthChecksImpl(Vertx vertx) { this.vertx = Objects.requireNonNull(vertx); } @Override public HealthChecks register(String name, Handler<Promise<Status>> procedure) { return register(name, 1000L, procedure); } @Override public HealthChecks register(String name, long timeout, Handler<Promise<Status>> procedure) { Objects.requireNonNull(name); if (timeout <= 0) { throw new IllegalArgumentException("The timeout must be strictly positive"); } if (name.isEmpty()) { throw new IllegalArgumentException("The name must not be empty"); } Objects.requireNonNull(procedure); String[] segments = name.split("/"); CompositeProcedure parent = traverseAndCreate(segments); String lastSegment = segments[segments.length - 1]; parent.add(lastSegment, new DefaultProcedure(vertx, lastSegment, timeout, procedure)); return this; } private CompositeProcedure traverseAndCreate(String[] segments) { int i; CompositeProcedure parent = root; for (i = 0; i < segments.length - 1; i++) { Procedure c = parent.get(segments[i]); if (c == null) { DefaultCompositeProcedure composite = new DefaultCompositeProcedure(); parent.add(segments[i], composite); parent = composite; } else if (c instanceof CompositeProcedure) { parent = (CompositeProcedure) c; } else { // Illegal. throw new IllegalArgumentException("Unable to find the procedure `" + segments[i] + "`, `" + segments[i] + "` is not a composite."); } } return parent; } @Override public HealthChecks unregister(String name) { Objects.requireNonNull(name); if (name.isEmpty()) { throw new IllegalArgumentException("The name must not be empty"); } String[] segments = name.split("/"); CompositeProcedure parent = findLastParent(segments); if (parent != null) { String lastSegment = segments[segments.length - 1]; parent.remove(lastSegment); } return this; } @Override public HealthChecks invoke(Handler<JsonObject> resultHandler) { checkStatus(ar -> resultHandler.handle(ar.result().toJson())); return this; } @Override public HealthChecks invoke(String name, Handler<AsyncResult<JsonObject>> resultHandler) { checkStatus(name, ar -> resultHandler.handle(ar.map(CheckResult::toJson))); return this; } @Override public Future<JsonObject> invoke(String name) { return checkStatus(name).map(CheckResult::toJson); } @Override public Future<CheckResult> checkStatus() { Promise<CheckResult> promise = ((ContextInternal)vertx.getOrCreateContext()).promise(); checkStatus(promise); return promise.future(); } @Override public void checkStatus(Handler<AsyncResult<CheckResult>> resultHandler) { Promise<CheckResult> promise = ((ContextInternal)vertx.getOrCreateContext()).promise(resultHandler); compute(root, promise); } @Override public Future<CheckResult> checkStatus(String name) { Promise<CheckResult> promise = ((ContextInternal)vertx.getOrCreateContext()).promise(); checkStatus(name, promise); return promise.future(); } @Override public void checkStatus(String name, Handler<AsyncResult<CheckResult>> resultHandler) { Promise<CheckResult> promise = ((ContextInternal)vertx.getOrCreateContext()).promise(resultHandler); if (name == null || name.isEmpty() || name.equals("/")) { checkStatus(promise); } else { String[] segments = name.split("/"); Procedure check = root; for (String segment : segments) { if (segment.trim().isEmpty()) { continue; } if (check instanceof CompositeProcedure) { check = ((CompositeProcedure) check).get(segment); if (check == null) { // Not found promise.fail("Not found"); return; } // Else continue... } else { // Not a composite promise.fail("'" + segment + "' is not a composite"); return; } } if (check == null) { // ???? promise.handle(null); return; } compute(check, promise); } } private CompositeProcedure findLastParent(String[] segments) { int i; CompositeProcedure parent = root; for (i = 0; i < segments.length - 1; i++) { Procedure c = parent.get(segments[i]); if (c instanceof CompositeProcedure) { parent = (CompositeProcedure) c; } else { return null; } } return parent; } private void compute(Procedure procedure, Promise<CheckResult> resultHandler) { procedure.check(resultHandler::complete); } }