SLING-3127 - prepare Health Check tools release
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1526476 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..7c5f192
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,71 @@
+<?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/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>sling</artifactId>
+ <version>17</version>
+ <relativePath/>
+ </parent>
+
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.hc.webconsole</artifactId>
+ <packaging>bundle</packaging>
+ <version>0.0.2-SNAPSHOT</version>
+
+ <name>Sling Health Check Webconsole Plugin</name>
+ <inceptionYear>2013</inceptionYear>
+
+ <description>
+ Webconsole plugin for Sling Health Check Services
+ </description>
+
+ <properties>
+ <sling.java.version>6</sling.java.version>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-scr-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.hc.core</artifactId>
+ <version>0.0.2-SNAPSHOT</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.api</artifactId>
+ <version>2.1.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/src/main/java/org/apache/sling/hc/webconsole/impl/HealthCheckWebconsolePlugin.java b/src/main/java/org/apache/sling/hc/webconsole/impl/HealthCheckWebconsolePlugin.java
new file mode 100644
index 0000000..0f0de37
--- /dev/null
+++ b/src/main/java/org/apache/sling/hc/webconsole/impl/HealthCheckWebconsolePlugin.java
@@ -0,0 +1,237 @@
+/*
+ * 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.sling.hc.webconsole.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+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.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.request.ResponseUtil;
+import org.apache.sling.hc.api.HealthCheck;
+import org.apache.sling.hc.api.Result;
+import org.apache.sling.hc.api.ResultLog;
+import org.apache.sling.hc.util.HealthCheckFilter;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.component.ComponentContext;
+
+/** Webconsole plugin to execute health check services */
+@Component(immediate=true)
+@Service(Servlet.class)
+@SuppressWarnings("serial")
+@Properties({
+ @Property(name=org.osgi.framework.Constants.SERVICE_DESCRIPTION, value="Sling Health Check Web Console Plugin"),
+ @Property(name=org.osgi.framework.Constants.SERVICE_VENDOR, value="The Apache Software Foundation"),
+ @Property(name="felix.webconsole.label", value=HealthCheckWebconsolePlugin.LABEL),
+ @Property(name="felix.webconsole.title", value=HealthCheckWebconsolePlugin.TITLE),
+ @Property(name="felix.webconsole.category", value=HealthCheckWebconsolePlugin.CATEGORY),
+ @Property(name="felix.webconsole.css", value="/healthcheck/res/ui/healthcheck.css")
+})
+public class HealthCheckWebconsolePlugin extends HttpServlet {
+
+ public static final String TITLE = "Sling Health Check";
+ public static final String LABEL = "healthcheck";
+ public static final String CATEGORY = "Sling";
+ public static final String PARAM_TAGS = "tags";
+ public static final String PARAM_DEBUG = "debug";
+ public static final String PARAM_QUIET = "quiet";
+
+ private BundleContext bundleContext;
+
+ @Activate
+ protected void activate(ComponentContext ctx) {
+ bundleContext = ctx.getBundleContext();
+ }
+
+ /** Serve static resource if applicable, and return true in that case */
+ private boolean getStaticResource(HttpServletRequest req, 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);
+ }
+ final byte [] buffer = new byte[16384];
+ int n=0;
+ while( (n = is.read(buffer, 0, buffer.length)) > 0) {
+ resp.getOutputStream().write(buffer, 0, n);
+ }
+ resp.getOutputStream().flush();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ if(getStaticResource(req, resp)) {
+ return;
+ }
+
+ final String tags = getParam(req, PARAM_TAGS, "");
+ final boolean debug = Boolean.valueOf(getParam(req, PARAM_DEBUG, "false"));
+ final boolean quiet = Boolean.valueOf(getParam(req, PARAM_QUIET, "false"));
+
+ doForm(req, resp, tags, debug, quiet);
+
+ // Execute health checks only if tags are specified (even if empty)
+ if(req.getParameter(PARAM_TAGS) != null) {
+ final ServiceReference[] references = new HealthCheckFilter(bundleContext).getTaggedHealthCheckServiceReferences(tags.split(","));
+
+ final PrintWriter pw = resp.getWriter();
+ pw.println("<table class='content healthcheck' cellpadding='0' cellspacing='0' width='100%'>");
+ int total = 0;
+ int failed = 0;
+ for(final ServiceReference ref : references) {
+ final HealthCheck hc = (HealthCheck) this.bundleContext.getService(ref);
+ if ( hc != null ) {
+ try {
+ final Result r = hc.execute();
+ total++;
+ if (!r.isOk()) {
+ failed++;
+ }
+ if (!quiet || !r.isOk()) {
+ renderResult(resp, ref, hc, r, debug);
+ }
+ } finally {
+ this.bundleContext.ungetService(ref);
+ }
+ }
+ }
+ final WebConsoleHelper c = new WebConsoleHelper(resp.getWriter());
+ c.titleHtml("Summary", total + " HealthCheck executed, " + failed + " failures");
+ pw.println("</table>");
+ }
+ }
+
+ private void renderResult(HttpServletResponse resp, final ServiceReference ref, HealthCheck hc, Result result, boolean debug) throws IOException {
+ final WebConsoleHelper c = new WebConsoleHelper(resp.getWriter());
+
+ final StringBuilder status = new StringBuilder();
+ final Object tags = ref.getProperty(HealthCheck.TAGS);
+ final String tagString;
+ if ( tags == null ) {
+ tagString = "";
+ } else if ( tags instanceof String[] ) {
+ tagString = Arrays.toString((String[])tags);
+ } else {
+ tagString = tags.toString();
+ }
+ status.append("Tags: ").append(tagString);
+ c.titleHtml(getName(ref, hc), null);
+
+ c.tr();
+ c.tdContent();
+ c.writer().print(ResponseUtil.escapeXml(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(ResultLog.Entry e : result) {
+ if(!debug && e.getStatus().equals(Result.Status.DEBUG)) {
+ continue;
+ }
+ final StringBuilder sb = new StringBuilder();
+ sb.append("<div class='log").append(e.getStatus()).append("'>");
+ sb.append(e.getStatus())
+ .append(" ")
+ .append(ResponseUtil.escapeXml(e.getMessage()))
+ .append("</div>");
+ c.writer().println(sb.toString());
+ }
+ c.closeTd();
+ }
+
+ private String getName(final ServiceReference ref, HealthCheck hc) {
+ Object result = ref.getProperty(HealthCheck.NAME);
+ if (result == null) {
+ result = hc;
+ }
+ return result.toString();
+ }
+
+ private void doForm(HttpServletRequest req, HttpServletResponse resp, String tags, boolean debug, boolean quiet)
+ throws IOException {
+ final PrintWriter pw = resp.getWriter();
+ 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, "To execute health check services, enter "
+ + " an optional list of tags, to select specific health checks, or no tags for all checks."
+ + " Prefix a tag with a minus sign (-) to omit checks having that tag.");
+
+ c.tr();
+ c.tdLabel("Health Check tags (comma-separated)");
+ c.tdContent();
+ pw.println("<input type='text' name='" + PARAM_TAGS + "' value='" + tags + "' class='input' size='80'>");
+ c.closeTd();
+ c.closeTr();
+
+ c.tr();
+ c.tdLabel("Show DEBUG logs");
+ c.tdContent();
+ pw.println("<input type='checkbox' name='" + PARAM_DEBUG + "' class='input' value='true'"
+ + (debug ? " checked=true " : "") + ">");
+ c.closeTd();
+ c.closeTr();
+
+ c.tr();
+ c.tdLabel("Show failed checks only");
+ c.tdContent();
+ pw.println("<input type='checkbox' name='" + PARAM_QUIET + "' class='input' value='true'"
+ + (quiet ? " checked=true " : "") + ">");
+ c.closeTd();
+ c.closeTr();
+
+ c.tr();
+ c.tdContent();
+ pw.println("<input type='submit' value='Execute selected health checks'/>");
+ c.closeTd();
+ c.closeTr();
+
+ pw.println("</table></form>");
+ }
+
+ private String getParam(HttpServletRequest req, String name, String defaultValue) {
+ String result = req.getParameter(name);
+ if(result == null) {
+ result = defaultValue;
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/org/apache/sling/hc/webconsole/impl/WebConsoleHelper.java b/src/main/java/org/apache/sling/hc/webconsole/impl/WebConsoleHelper.java
new file mode 100644
index 0000000..beb1b34
--- /dev/null
+++ b/src/main/java/org/apache/sling/hc/webconsole/impl/WebConsoleHelper.java
@@ -0,0 +1,68 @@
+/*
+ * 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.sling.hc.webconsole.impl;
+
+import java.io.PrintWriter;
+
+import org.apache.sling.api.request.ResponseUtil;
+
+/** Webconsole plugin to execute health check rules */
+class WebConsoleHelper {
+
+ final PrintWriter pw;
+
+ WebConsoleHelper(PrintWriter w) {
+ pw = w;
+ }
+
+ PrintWriter writer() {
+ return pw;
+ }
+
+ void tdContent() {
+ pw.print("<td class='content' colspan='2'>");
+ }
+
+ void closeTd() {
+ pw.print("</td>");
+ }
+
+ void closeTr() {
+ pw.println("</tr>");
+ }
+
+ void tdLabel(final String label) {
+ pw.println("<td class='content'>" + ResponseUtil.escapeXml(label) + "</td>");
+ }
+
+ void tr() {
+ pw.println("<tr class='content'>");
+ }
+
+ void titleHtml(String title, String description) {
+ tr();
+ pw.println("<th colspan='3' class='content container'>" + ResponseUtil.escapeXml(title) + "</th>");
+ closeTr();
+
+ if (description != null) {
+ tr();
+ pw.println("<td colspan='3' class='content'>" +ResponseUtil.escapeXml(description) + "</th>");
+ closeTr();
+ }
+ }
+}
diff --git a/src/main/resources/res/ui/healthcheck.css b/src/main/resources/res/ui/healthcheck.css
new file mode 100644
index 0000000..75b79cf
--- /dev/null
+++ b/src/main/resources/res/ui/healthcheck.css
@@ -0,0 +1,46 @@
+/*
+ * 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 ASF 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.
+ */
+
+.healthcheck .logDEBUG {
+ color:grey;
+}
+
+.healthcheck .logINFO {
+ color:blue;
+}
+
+.healthcheck .logWARN,
+.healthcheck .logCRITICAL,
+.healthcheck .logHEALTH_CHECK_ERROR
+{
+ color:red;
+}
+
+.healthcheck .logERROR {
+ color:red;
+ font-weight:bold;
+}
+
+.healthcheck .resultOktrue {
+ color:green;
+ font-weight:bold;
+}
+
+.healthcheck .resultOkfalse {
+ color:red;
+ font-weight:bold;
+}