blob: a858a29b78846c54c109cf846f51dc7aaa7341c9 [file] [log] [blame]
/*
* 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 SF 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.felix.hc.webconsole.impl;
import static org.apache.commons.lang3.StringEscapeUtils.escapeHtml4;
import static org.apache.felix.hc.api.FormattingResultLog.msHumanReadable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Collection;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.felix.hc.api.Result;
import org.apache.felix.hc.api.ResultLog;
import org.apache.felix.hc.api.execution.HealthCheckExecutionOptions;
import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
import org.apache.felix.hc.api.execution.HealthCheckExecutor;
import org.apache.felix.hc.api.execution.HealthCheckSelector;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/** Webconsole plugin to execute health check services */
@Component(service=Servlet.class, property={
org.osgi.framework.Constants.SERVICE_DESCRIPTION + "=Apache Felix Health Check Web Console Plugin",
"felix.webconsole.label=" + HealthCheckWebconsolePlugin.LABEL,
"felix.webconsole.title="+ HealthCheckWebconsolePlugin.TITLE,
"felix.webconsole.category="+ HealthCheckWebconsolePlugin.CATEGORY,
"felix.webconsole.css=/healthcheck/res/ui/healthcheck.css"
})
@SuppressWarnings("serial")
public class HealthCheckWebconsolePlugin extends HttpServlet {
public static final String TITLE = "Health Check";
public static final String LABEL = "healthcheck";
public static final String CATEGORY = "Main";
public static final String PARAM_TAGS = "tags";
public static final String PARAM_DEBUG = "debug";
public static final String PARAM_QUIET = "quiet";
public static final String PARAM_FORCE_INSTANT_EXECUTION = "forceInstantExecution";
public static final String PARAM_COMBINE_TAGS_WITH_OR = "combineTagsWithOr";
public static final String PARAM_OVERRIDE_GLOBAL_TIMEOUT = "overrideGlobalTimeout";
@Reference
private HealthCheckExecutor healthCheckExecutor;
/** Serve static resource if applicable, and return true in that case */
private boolean getStaticResource(final HttpServletRequest req, final HttpServletResponse resp)
throws ServletException, IOException {
final String pathInfo = req.getPathInfo();
if (pathInfo!= null && pathInfo.contains("res/ui")) {
final String prefix = "/" + LABEL;
final InputStream is = getClass().getResourceAsStream(pathInfo.substring(prefix.length()));
if (is == null) {
resp.sendError(HttpServletResponse.SC_NOT_FOUND, pathInfo);
} else {
final OutputStream os = resp.getOutputStream();
try {
final byte [] buffer = new byte[16384];
int n=0;
while( (n = is.read(buffer, 0, buffer.length)) > 0) {
os.write(buffer, 0, n);
}
} finally {
try {
is.close();
} catch ( final IOException ignore ) {
// ignore
}
}
}
return true;
}
return false;
}
@Override
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp)
throws ServletException, IOException {
if (getStaticResource(req, resp)) {
return;
}
final String tags = getParam(req, PARAM_TAGS, null);
final boolean debug = Boolean.valueOf(getParam(req, PARAM_DEBUG, "false"));
final boolean quiet = Boolean.valueOf(getParam(req, PARAM_QUIET, "false"));
final boolean combineTagsWithOr = Boolean.valueOf(getParam(req, PARAM_COMBINE_TAGS_WITH_OR, "false"));
final boolean forceInstantExecution = Boolean.valueOf(getParam(req, PARAM_FORCE_INSTANT_EXECUTION, "false"));
final String overrideGlobalTimeoutStr = getParam(req, PARAM_OVERRIDE_GLOBAL_TIMEOUT, "");
final PrintWriter pw = resp.getWriter();
doForm(pw, tags, debug, quiet, combineTagsWithOr, forceInstantExecution, overrideGlobalTimeoutStr);
// Execute health checks only if tags are specified (even if empty)
if (tags != null) {
HealthCheckExecutionOptions options = new HealthCheckExecutionOptions();
options.setCombineTagsWithOr(combineTagsWithOr);
options.setForceInstantExecution(forceInstantExecution);
try {
options.setOverrideGlobalTimeout(Integer.valueOf(overrideGlobalTimeoutStr));
} catch (NumberFormatException nfe) {
// override not set in UI
}
HealthCheckSelector selector = StringUtils.isNotBlank(tags) ? HealthCheckSelector.tags(tags.split(",")) : HealthCheckSelector.empty();
Collection<HealthCheckExecutionResult> results = healthCheckExecutor.execute(selector, options);
pw.println("<table class='content healthcheck' cellpadding='0' cellspacing='0' width='100%'>");
int total = 0;
int failed = 0;
for (final HealthCheckExecutionResult exR : results) {
final Result r = exR.getHealthCheckResult();
total++;
if (!r.isOk()) {
failed++;
}
if (!quiet || !r.isOk()) {
renderResult(pw, exR, debug);
}
}
final WebConsoleHelper c = new WebConsoleHelper(resp.getWriter());
c.titleHtml("Summary", total + " HealthCheck executed, " + failed + " failures");
pw.println("</table>");
pw.println("<a href='configMgr/org.apache.felix.hc.core.impl.executor.HealthCheckExecutorImpl'>Configure executor</a><br/><br/>");
}
}
void renderResult(final PrintWriter pw, final HealthCheckExecutionResult exResult, final boolean debug) throws IOException {
final Result result = exResult.getHealthCheckResult();
final WebConsoleHelper c = new WebConsoleHelper(pw);
final StringBuilder status = new StringBuilder();
status.append("Tags: ").append(exResult.getHealthCheckMetadata().getTags());
status.append(" Finished: ").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(exResult.getFinishedAt()) + " after "
+ msHumanReadable(exResult.getElapsedTimeInMs()));
c.titleHtml(exResult.getHealthCheckMetadata().getTitle(), null);
c.tr();
c.tdContent();
c.writer().print(escapeHtml4(status.toString()));
c.writer().print("<br/>Result: <span class='resultOk");
c.writer().print(result.isOk());
c.writer().print("'>");
c.writer().print(result.getStatus().toString());
c.writer().print("</span>");
c.closeTd();
c.closeTr();
c.tr();
c.tdContent();
for(final ResultLog.Entry e : result) {
if (!debug && e.isDebug()) {
continue;
}
c.writer().print("<div class='log");
c.writer().print(e.isDebug() ? "DEBUG" : e.getStatus().toString());
c.writer().print("'>");
c.writer().print(e.getStatus().toString());
c.writer().print(' ');
c.writer().print(escapeHtml4(e.getMessage()));
if (e.getException() != null) {
c.writer().print(" ");
c.writer().print(escapeHtml4(e.getException().toString()));
}
c.writer().println("</div>");
}
c.closeTd();
}
private void doForm(final PrintWriter pw,
final String tags,
final boolean debug,
final boolean quiet,
final boolean combineTagsWithOr,
final boolean forceInstantExecution,
final String overrideGlobalTimeoutStr)
throws IOException {
final WebConsoleHelper c = new WebConsoleHelper(pw);
pw.print("<form method='get'>");
pw.println("<table class='content' cellpadding='0' cellspacing='0' width='100%'>");
c.titleHtml(TITLE, "Enter tags to selected health checks to be executed. Leave empty to execute default checks or use '*' to execute all checks."
+ " Prefix a tag with a minus sign (-) to omit checks having that tag (can be also used in combination with '*', e.g. '*,-excludedtag').");
c.tr();
c.tdLabel("Health Check tags (comma-separated)");
c.tdContent();
c.writer().print("<input type='text' name='" + PARAM_TAGS + "' value='");
if ( tags != null ) {
c.writer().print(escapeHtml4(tags));
}
c.writer().println("' class='input' size='80'>");
c.closeTd();
c.closeTr();
c.tr();
c.tdLabel("Combine tags with logical 'OR' instead of the default 'AND'");
c.tdContent();
c.writer().print("<input type='checkbox' name='" + PARAM_COMBINE_TAGS_WITH_OR + "' class='input' value='true'");
if (combineTagsWithOr) {
c.writer().print(" checked=true");
}
c.writer().println(">");
c.closeTd();
c.closeTr();
c.tr();
c.tdLabel("Show DEBUG logs");
c.tdContent();
c.writer().print("<input type='checkbox' name='" + PARAM_DEBUG + "' class='input' value='true'");
if ( debug ) {
c.writer().print(" checked=true");
}
c.writer().println(">");
c.closeTd();
c.closeTr();
c.tr();
c.tdLabel("Show failed checks only");
c.tdContent();
c.writer().print("<input type='checkbox' name='" + PARAM_QUIET + "' class='input' value='true'");
if ( quiet ) {
c.writer().print(" checked=true");
}
c.writer().println(">");
c.closeTd();
c.closeTr();
c.tr();
c.tdLabel("Force instant execution (no cache, async checks are executed)");
c.tdContent();
c.writer().print("<input type='checkbox' name='" + PARAM_FORCE_INSTANT_EXECUTION + "' class='input' value='true'");
if (forceInstantExecution) {
c.writer().print(" checked=true");
}
c.writer().println(">");
c.closeTd();
c.closeTr();
c.tr();
c.tdLabel("Override global timeout");
c.tdContent();
c.writer().print("<input type='text' name='" + PARAM_OVERRIDE_GLOBAL_TIMEOUT + "' value='");
if (overrideGlobalTimeoutStr != null) {
c.writer().print(escapeHtml4(overrideGlobalTimeoutStr));
}
c.writer().println("' class='input' size='80'>");
c.closeTd();
c.closeTr();
c.tr();
c.tdContent();
c.writer().println("<input type='submit' value='Execute selected health checks'/>");
c.closeTd();
c.closeTr();
c.writer().println("</table></form>");
}
private String getParam(final HttpServletRequest req, final String name, final String defaultValue) {
String result = req.getParameter(name);
if(result == null) {
result = defaultValue;
}
return result;
}
}