You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by bd...@apache.org on 2015/01/05 17:26:22 UTC

svn commit: r1649574 - in /sling/trunk/bundles/extensions/healthcheck: core/src/main/java/org/apache/sling/hc/api/execution/ core/src/main/java/org/apache/sling/hc/core/impl/ core/src/main/java/org/apache/sling/hc/core/impl/executor/ core/src/main/java...

Author: bdelacretaz
Date: Mon Jan  5 16:26:21 2015
New Revision: 1649574

URL: http://svn.apache.org/r1649574
Log:
SLING-3501 - allow for combining Health Checks selection tags with OR in addition to the default AND. Contributed by Georg Henzler, thanks!

Added:
    sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/api/execution/HealthCheckExecutionOptions.java
Modified:
    sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/api/execution/HealthCheckExecutor.java
    sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/api/execution/package-info.java
    sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/CompositeResult.java
    sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/executor/AsyncHealthCheckExecutor.java
    sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/executor/HealthCheckExecutorImpl.java
    sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/executor/HealthCheckFuture.java
    sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/util/FormattingResultLog.java
    sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/util/HealthCheckFilter.java
    sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/util/package-info.java
    sling/trunk/bundles/extensions/healthcheck/core/src/test/java/org/apache/sling/hc/jmx/impl/HealthCheckMBeanTest.java
    sling/trunk/bundles/extensions/healthcheck/webconsole/src/main/java/org/apache/sling/hc/webconsole/impl/HealthCheckWebconsolePlugin.java

Added: sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/api/execution/HealthCheckExecutionOptions.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/api/execution/HealthCheckExecutionOptions.java?rev=1649574&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/api/execution/HealthCheckExecutionOptions.java (added)
+++ sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/api/execution/HealthCheckExecutionOptions.java Mon Jan  5 16:26:21 2015
@@ -0,0 +1,83 @@
+/*
+ * 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.api.execution;
+
+
+/** Options for behavior of health check execution. */
+public class HealthCheckExecutionOptions {
+
+    private boolean forceInstantExecution = false;
+    private boolean combineTagsWithOr = false;
+    private int overrideGlobalTimeout = 0;
+
+    @Override
+    public String toString() {
+        return "[HealthCheckExecutionOptions forceInstantExecution=" + forceInstantExecution + ", combineTagsWithOr=" + combineTagsWithOr
+                + ", overrideGlobalTimeout=" + overrideGlobalTimeout + "]";
+    }
+
+    /**
+     * If activated, this will ensure that asynchronous checks will be executed immediately.
+     * 
+     * @param forceInstantExecution boolean flag
+     */
+    public void setForceInstantExecution(boolean forceInstantExecution) {
+        this.forceInstantExecution = forceInstantExecution;
+    }
+
+    /**
+     * If activated, the given tags will be combined with a logical "or" instead of "and".
+     * 
+     * @param combineTagsWithOr boolean flag
+     */
+    public void setCombineTagsWithOr(boolean combineTagsWithOr) {
+        this.combineTagsWithOr = combineTagsWithOr;
+    }
+
+    /**
+     * Allows to override the global timeout for this particular execution of the health check. 
+     * 
+     * @param overrideGlobalTimeout timeout in ms to be used for this execution of the execution
+     */
+    public void setOverrideGlobalTimeout(int overrideGlobalTimeout) {
+        this.overrideGlobalTimeout = overrideGlobalTimeout;
+    }
+
+    /**
+     * @return true if instant execution is turned on
+     */
+    public boolean isForceInstantExecution() {
+        return forceInstantExecution;
+    }
+
+    /**
+     * @return true if combining tags with or is turned on
+     */
+    public boolean isCombineTagsWithOr() {
+        return combineTagsWithOr;
+    }
+
+    /**
+     * @return the timeout to be used for this execution (overriding the global timeout)
+     */
+    public int getOverrideGlobalTimeout() {
+        return overrideGlobalTimeout;
+    }
+
+
+}
\ No newline at end of file

Modified: sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/api/execution/HealthCheckExecutor.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/api/execution/HealthCheckExecutor.java?rev=1649574&r1=1649573&r2=1649574&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/api/execution/HealthCheckExecutor.java (original)
+++ sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/api/execution/HealthCheckExecutor.java Mon Jan  5 16:26:21 2015
@@ -38,4 +38,16 @@ public interface HealthCheckExecutor {
      * @return List of results. The list might be empty.
      */
     List<HealthCheckExecutionResult> execute(String... tags);
+
+    /**
+     * Executes all health checks with the supplied list of tags.
+     * If no tags are supplied, all health checks are executed.
+     * 
+     * @param options options for controlling execution behavior
+     * @param tags tags to be executed
+     *
+     * @return List of results. The list might be empty.
+     */
+    List<HealthCheckExecutionResult> execute(HealthCheckExecutionOptions options, String... tags);
+
 }
\ No newline at end of file

Modified: sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/api/execution/package-info.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/api/execution/package-info.java?rev=1649574&r1=1649573&r2=1649574&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/api/execution/package-info.java (original)
+++ sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/api/execution/package-info.java Mon Jan  5 16:26:21 2015
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-@Version("1.0.0")
+@Version("1.1.0")
 package org.apache.sling.hc.api.execution;
 
 import aQute.bnd.annotation.Version;

Modified: sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/CompositeResult.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/CompositeResult.java?rev=1649574&r1=1649573&r2=1649574&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/CompositeResult.java (original)
+++ sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/CompositeResult.java Mon Jan  5 16:26:21 2015
@@ -1,12 +1,13 @@
 package org.apache.sling.hc.core.impl;
 
+import static org.apache.sling.hc.util.FormattingResultLog.msHumanReadable;
+
 import java.util.List;
 
 import org.apache.sling.hc.api.Result;
 import org.apache.sling.hc.api.ResultLog;
 import org.apache.sling.hc.api.ResultLog.Entry;
 import org.apache.sling.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.sling.hc.core.impl.executor.HealthCheckExecutorImpl;
 import org.apache.sling.hc.util.HealthCheckMetadata;
 
 public class CompositeResult extends Result {
@@ -21,8 +22,7 @@ public class CompositeResult extends Res
                 resultLog.add(new ResultLog.Entry(entry.getStatus(), healthCheckMetadata.getName() + ": " + entry.getMessage(), entry.getException()));
             }
             resultLog.add(new ResultLog.Entry(Result.Status.DEBUG, healthCheckMetadata.getName() + " finished after "
-                    + HealthCheckExecutorImpl.msHumanReadable(executionResult.getElapsedTimeInMs())
-                    + (executionResult.hasTimedOut() ? " (timed out)" : "")));
+                    + msHumanReadable(executionResult.getElapsedTimeInMs()) + (executionResult.hasTimedOut() ? " (timed out)" : "")));
         }
     }
 

Modified: sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/executor/AsyncHealthCheckExecutor.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/executor/AsyncHealthCheckExecutor.java?rev=1649574&r1=1649573&r2=1649574&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/executor/AsyncHealthCheckExecutor.java (original)
+++ sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/executor/AsyncHealthCheckExecutor.java Mon Jan  5 16:26:21 2015
@@ -193,11 +193,17 @@ public class AsyncHealthCheckExecutor im
 
     }
 
+    void updateWith(HealthCheckExecutionResult result) {
+        if (isAsync(result.getHealthCheckMetadata())) {
+            asyncResultsByDescriptor.put(result.getHealthCheckMetadata(), (ExecutionResult) result);
+            LOG.debug("Updated result for async hc {} with {}", result.getHealthCheckMetadata(), result);
+        }
+    }
+    
     private boolean isAsync(HealthCheckMetadata healthCheckMetadata) {
         return StringUtils.isNotBlank(healthCheckMetadata.getAsyncCronExpression());
     }
 
-    
     private class HealthCheckAsyncJob implements Runnable {
 
         private final HealthCheckMetadata healthCheckDescriptor;
@@ -220,21 +226,12 @@ public class AsyncHealthCheckExecutor im
 
                 @Override
                 public void finished(HealthCheckExecutionResult result) {
-                    // no action needed here
-                    
+                    updateWith(result);
                 }});
 
             // run future in same thread (as we are already async via scheduler)
             healthCheckFuture.run();
 
-            try {
-                ExecutionResult result = healthCheckFuture.get();
-                LOG.debug("Aync execution of {} returned {}", healthCheckDescriptor, result);
-                asyncResultsByDescriptor.put(healthCheckDescriptor, result);
-            } catch (Exception e) {
-                LOG.warn("Could not upated async execution result for " + healthCheckDescriptor + ". Exception: " + e, e);
-            }
-
         }
 
         @Override

Modified: sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/executor/HealthCheckExecutorImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/executor/HealthCheckExecutorImpl.java?rev=1649574&r1=1649573&r2=1649574&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/executor/HealthCheckExecutorImpl.java (original)
+++ sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/executor/HealthCheckExecutorImpl.java Mon Jan  5 16:26:21 2015
@@ -17,7 +17,8 @@
  */
 package org.apache.sling.hc.core.impl.executor;
 
-import java.text.NumberFormat;
+import static org.apache.sling.hc.util.FormattingResultLog.msHumanReadable;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -28,7 +29,6 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
@@ -46,6 +46,7 @@ import org.apache.sling.commons.threads.
 import org.apache.sling.commons.threads.ThreadPoolManager;
 import org.apache.sling.hc.api.HealthCheck;
 import org.apache.sling.hc.api.Result;
+import org.apache.sling.hc.api.execution.HealthCheckExecutionOptions;
 import org.apache.sling.hc.api.execution.HealthCheckExecutionResult;
 import org.apache.sling.hc.api.execution.HealthCheckExecutor;
 import org.apache.sling.hc.util.HealthCheckFilter;
@@ -171,13 +172,21 @@ public class HealthCheckExecutorImpl imp
      */
     @Override
     public List<HealthCheckExecutionResult> execute(final String... tags) {
-        logger.debug("Starting executing checks for {}", tags == null ? "*" : tags);
+        return execute(/*default options*/new HealthCheckExecutionOptions(), tags);
+    }
+
+    /**
+     * @see org.apache.sling.hc.api.execution.HealthCheckExecutor#execute(HealthCheckExecutionOptions, String...)
+     */
+    @Override
+    public List<HealthCheckExecutionResult> execute(HealthCheckExecutionOptions options, final String... tags) {
+        logger.debug("Starting executing checks for tags {} and execution options {}", tags == null ? "*" : tags, options);
 
         final HealthCheckFilter filter = new HealthCheckFilter(this.bundleContext);
         try {
-            final ServiceReference[] healthCheckReferences = filter.getTaggedHealthCheckServiceReferences(tags);
+            final ServiceReference[] healthCheckReferences = filter.getTaggedHealthCheckServiceReferences(options.isCombineTagsWithOr(), tags);
 
-            return this.execute(healthCheckReferences);
+            return this.execute(healthCheckReferences, options);
         } finally {
             filter.dispose();
         }
@@ -192,10 +201,14 @@ public class HealthCheckExecutorImpl imp
         return createResultsForDescriptor(metadata);
     }
 
+    private List<HealthCheckExecutionResult> execute(final ServiceReference[] healthCheckReferences) {
+        return execute(healthCheckReferences, new HealthCheckExecutionOptions());
+    }
+
     /**
      * Execute a set of health checks
      */
-    private List<HealthCheckExecutionResult> execute(final ServiceReference[] healthCheckReferences) {
+    private List<HealthCheckExecutionResult> execute(final ServiceReference[] healthCheckReferences, HealthCheckExecutionOptions options) {
         final StopWatch stopWatch = new StopWatch();
         stopWatch.start();
 
@@ -203,7 +216,7 @@ public class HealthCheckExecutorImpl imp
         final List<HealthCheckMetadata> healthCheckDescriptors = getHealthCheckMetadata(healthCheckReferences);
 
         
-        createResultsForDescriptors(healthCheckDescriptors, results);
+        createResultsForDescriptors(healthCheckDescriptors, results, options);
 
         stopWatch.stop();
         if ( logger.isDebugEnabled() ) {
@@ -223,21 +236,25 @@ public class HealthCheckExecutorImpl imp
     }
 
     private void createResultsForDescriptors(final List<HealthCheckMetadata> healthCheckDescriptors,
-            final Collection<HealthCheckExecutionResult> results) {
+            final Collection<HealthCheckExecutionResult> results, HealthCheckExecutionOptions options) {
         // -- All methods below check if they can transform a healthCheckDescriptor into a result
         // -- if yes the descriptor is removed from the list and the result added
 
         // get async results
-        asyncHealthCheckExecutor.collectAsyncResults(healthCheckDescriptors, results);
+        if (!options.isForceInstantExecution()) {
+            asyncHealthCheckExecutor.collectAsyncResults(healthCheckDescriptors, results);
+        }
         
         // reuse cached results where possible
-        healthCheckResultCache.useValidCacheResults(healthCheckDescriptors, results, resultCacheTtlInMs);
+        if (!options.isForceInstantExecution()) {
+            healthCheckResultCache.useValidCacheResults(healthCheckDescriptors, results, resultCacheTtlInMs);
+        }
 
         // everything else is executed in parallel via futures
         List<HealthCheckFuture> futures = createOrReuseFutures(healthCheckDescriptors);
 
         // wait for futures at most until timeout (but will return earlier if all futures are finished)
-        waitForFuturesRespectingTimeout(futures);
+        waitForFuturesRespectingTimeout(futures, options);
         collectResultsFromFutures(futures, results);
     }
 
@@ -257,7 +274,7 @@ public class HealthCheckExecutorImpl imp
             }
 
             // wait for futures at most until timeout (but will return earlier if all futures are finished)
-            waitForFuturesRespectingTimeout(Collections.singletonList(future));
+            waitForFuturesRespectingTimeout(Collections.singletonList(future), null);
             result = collectResultFromFuture(future);
         }
 
@@ -317,6 +334,7 @@ public class HealthCheckExecutorImpl imp
                 @Override
                 public void finished(final HealthCheckExecutionResult result) {
                     healthCheckResultCache.updateWith(result);
+                    asyncHealthCheckExecutor.updateWith(result);
                     synchronized ( stillRunningFutures ) {
                         stillRunningFutures.remove(metadata);
                     }
@@ -333,10 +351,16 @@ public class HealthCheckExecutorImpl imp
     /**
      * Wait for the futures until the timeout is reached
      */
-    private void waitForFuturesRespectingTimeout(final List<HealthCheckFuture> futuresForResultOfThisCall) {
+    private void waitForFuturesRespectingTimeout(final List<HealthCheckFuture> futuresForResultOfThisCall, HealthCheckExecutionOptions options) {
         final StopWatch callExcutionTimeStopWatch = new StopWatch();
         callExcutionTimeStopWatch.start();
         boolean allFuturesDone;
+
+        long effectiveTimeout = this.timeoutInMs;
+        if (options != null && options.getOverrideGlobalTimeout() > 0) {
+            effectiveTimeout = options.getOverrideGlobalTimeout();
+        }
+
         do {
             try {
                 Thread.sleep(50);
@@ -348,7 +372,7 @@ public class HealthCheckExecutorImpl imp
             for (final HealthCheckFuture healthCheckFuture : futuresForResultOfThisCall) {
                 allFuturesDone &= healthCheckFuture.isDone();
             }
-        } while (!allFuturesDone && callExcutionTimeStopWatch.getTime() < this.timeoutInMs);
+        } while (!allFuturesDone && callExcutionTimeStopWatch.getTime() < effectiveTimeout);
     }
 
     /**
@@ -415,27 +439,6 @@ public class HealthCheckExecutorImpl imp
         return result;
     }
 
-    public static String msHumanReadable(final long millis) {
-
-        double number = millis;
-        final String[] units = new String[] { "ms", "sec", "min", "h", "days" };
-        final double[] divisors = new double[] { 1000, 60, 60, 24 };
-
-        int magnitude = 0;
-        do {
-            double currentDivisor = divisors[Math.min(magnitude, divisors.length - 1)];
-            if (number < currentDivisor) {
-                break;
-            }
-            number /= currentDivisor;
-            magnitude++;
-        } while (magnitude < units.length - 1);
-        NumberFormat format = NumberFormat.getNumberInstance(Locale.UK);
-        format.setMinimumFractionDigits(0);
-        format.setMaximumFractionDigits(1);
-        String result = format.format(number) + units[magnitude];
-        return result;
-    }
 
     public void setTimeoutInMs(final long timeoutInMs) {
         this.timeoutInMs = timeoutInMs;

Modified: sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/executor/HealthCheckFuture.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/executor/HealthCheckFuture.java?rev=1649574&r1=1649573&r2=1649574&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/executor/HealthCheckFuture.java (original)
+++ sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/executor/HealthCheckFuture.java Mon Jan  5 16:26:21 2015
@@ -17,6 +17,8 @@
  */
 package org.apache.sling.hc.core.impl.executor;
 
+import static org.apache.sling.hc.util.FormattingResultLog.msHumanReadable;
+
 import java.util.Date;
 import java.util.concurrent.Callable;
 import java.util.concurrent.FutureTask;
@@ -79,7 +81,7 @@ class HealthCheckFuture extends FutureTa
                         // wrap the result in an execution result
                         executionResult = new ExecutionResult(metadata, resultFromHealthCheck, elapsedTime);
                     }
-                    LOG.debug("Time consumed for {}: {}", metadata, HealthCheckExecutorImpl.msHumanReadable(elapsedTime));
+                    LOG.debug("Time consumed for {}: {}", metadata, msHumanReadable(elapsedTime));
                 }
 
                 callback.finished(executionResult);

Modified: sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/util/FormattingResultLog.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/util/FormattingResultLog.java?rev=1649574&r1=1649573&r2=1649574&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/util/FormattingResultLog.java (original)
+++ sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/util/FormattingResultLog.java Mon Jan  5 16:26:21 2015
@@ -16,6 +16,10 @@
  * specific language governing permissions and limitations under the License.
  */
 package org.apache.sling.hc.util;
+
+import java.text.NumberFormat;
+import java.util.Locale;
+
 import org.apache.sling.hc.api.Result;
 import org.apache.sling.hc.api.ResultLog;
 import org.slf4j.helpers.MessageFormatter;
@@ -46,4 +50,31 @@ public class FormattingResultLog extends
     public void healthCheckError(String format, Object ... args) {
         add(createEntry(Result.Status.HEALTH_CHECK_ERROR, format, args));
     }
+
+    /** Utility method to return any magnitude of milliseconds in a human readable format using the appropriate time unit (ms, sec, min) depending on the
+     * magnitude of the input.
+     * 
+     * @param millis milliseconds
+     * @return a string with a number and a unit */
+    public static String msHumanReadable(final long millis) {
+
+        double number = millis;
+        final String[] units = new String[] { "ms", "sec", "min", "h", "days" };
+        final double[] divisors = new double[] { 1000, 60, 60, 24 };
+
+        int magnitude = 0;
+        do {
+            double currentDivisor = divisors[Math.min(magnitude, divisors.length - 1)];
+            if (number < currentDivisor) {
+                break;
+            }
+            number /= currentDivisor;
+            magnitude++;
+        } while (magnitude < units.length - 1);
+        NumberFormat format = NumberFormat.getNumberInstance(Locale.UK);
+        format.setMinimumFractionDigits(0);
+        format.setMaximumFractionDigits(1);
+        String result = format.format(number) + units[magnitude];
+        return result;
+    }
 }
\ No newline at end of file

Modified: sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/util/HealthCheckFilter.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/util/HealthCheckFilter.java?rev=1649574&r1=1649573&r2=1649574&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/util/HealthCheckFilter.java (original)
+++ sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/util/HealthCheckFilter.java Mon Jan  5 16:26:21 2015
@@ -84,27 +84,52 @@ public class HealthCheckFilter {
     }
 
     /**
-     * Get all service references for health check services with one of the supplied tags.
+     * Get all service references for health check services with one of the supplied tags. Uses logical "and" to combine tags.
      * @return An array of service references - might be an empty error if none matches
      */
     public ServiceReference[] getTaggedHealthCheckServiceReferences(final String... tags) {
+        return getTaggedHealthCheckServiceReferences(false, tags);
+    }
+
+    /**
+     * Get all service references for health check services with one of the supplied tags.
+     * 
+     * @param combineWithOr If true will return all health checks that have at least one of the tags set. 
+     *        If false will return only health checks that have all given tags assigned.
+     * @param tags the tags to look for
+     * @return An array of service references - might be an empty error if none matches
+     */
+    public ServiceReference[] getTaggedHealthCheckServiceReferences(boolean combineWithOr, final String... tags) {
         // Build service filter
         final StringBuilder filterBuilder = new StringBuilder();
         filterBuilder.append("(&(objectClass=").append(HealthCheck.class.getName()).append(")");
         final int prefixLen = OMIT_PREFIX.length();
+        final StringBuilder filterBuilderForOrOperator = new StringBuilder(); // or filters
         for(String tag : tags) {
             tag = tag.trim();
             if(tag.length() == 0) {
                 continue;
             }
             if(tag.startsWith(OMIT_PREFIX)) {
+                // ommit tags always have to be added as and-clause
                 filterBuilder.append("(!(").append(HealthCheck.TAGS).append("=").append(tag.substring(prefixLen)).append("))");
             } else {
-                filterBuilder.append("(").append(HealthCheck.TAGS).append("=").append(tag).append(")");
+                // add regular tags in the list either to outer and-clause or inner or-clause 
+                if (combineWithOr) {
+                    filterBuilderForOrOperator.append("(").append(HealthCheck.TAGS).append("=").append(tag).append(")");
+                } else {
+                    filterBuilder.append("(").append(HealthCheck.TAGS).append("=").append(tag).append(")");
+                }
             }
         }
+        // add "or" clause if we have accumulated any 
+        if (filterBuilderForOrOperator.length() > 0) {
+            filterBuilder.append("(|").append(filterBuilderForOrOperator).append(")");
+        }
         filterBuilder.append(")");
 
+        log.debug("OSGi service filter in getTaggedHealthCheckServiceReferences(): {}", filterBuilder);
+
         try {
             final String filterString = filterBuilder.length() == 0 ? null : filterBuilder.toString();
             bundleContext.createFilter(filterString); // check syntax early

Modified: sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/util/package-info.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/util/package-info.java?rev=1649574&r1=1649573&r2=1649574&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/util/package-info.java (original)
+++ sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/util/package-info.java Mon Jan  5 16:26:21 2015
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-@Version("1.1.0")
+@Version("1.2.0")
 package org.apache.sling.hc.util;
 
 import aQute.bnd.annotation.Version;

Modified: sling/trunk/bundles/extensions/healthcheck/core/src/test/java/org/apache/sling/hc/jmx/impl/HealthCheckMBeanTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/healthcheck/core/src/test/java/org/apache/sling/hc/jmx/impl/HealthCheckMBeanTest.java?rev=1649574&r1=1649573&r2=1649574&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/healthcheck/core/src/test/java/org/apache/sling/hc/jmx/impl/HealthCheckMBeanTest.java (original)
+++ sling/trunk/bundles/extensions/healthcheck/core/src/test/java/org/apache/sling/hc/jmx/impl/HealthCheckMBeanTest.java Mon Jan  5 16:26:21 2015
@@ -30,6 +30,7 @@ import javax.management.ObjectName;
 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.api.execution.HealthCheckExecutionOptions;
 import org.apache.sling.hc.api.execution.HealthCheckExecutionResult;
 import org.apache.sling.hc.core.impl.executor.ExtendedHealthCheckExecutor;
 import org.apache.sling.hc.util.HealthCheckMetadata;
@@ -145,6 +146,11 @@ public class HealthCheckMBeanTest {
                     }
                 };
             }
+
+            @Override
+            public List<HealthCheckExecutionResult> execute(HealthCheckExecutionOptions options, String... tags) {
+                return null;
+            }
         });
         final ObjectName name = new ObjectName(OBJECT_NAME);
         jmxServer.registerMBean(mbean, name);

Modified: sling/trunk/bundles/extensions/healthcheck/webconsole/src/main/java/org/apache/sling/hc/webconsole/impl/HealthCheckWebconsolePlugin.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/healthcheck/webconsole/src/main/java/org/apache/sling/hc/webconsole/impl/HealthCheckWebconsolePlugin.java?rev=1649574&r1=1649573&r2=1649574&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/healthcheck/webconsole/src/main/java/org/apache/sling/hc/webconsole/impl/HealthCheckWebconsolePlugin.java (original)
+++ sling/trunk/bundles/extensions/healthcheck/webconsole/src/main/java/org/apache/sling/hc/webconsole/impl/HealthCheckWebconsolePlugin.java Mon Jan  5 16:26:21 2015
@@ -17,10 +17,13 @@
  */
 package org.apache.sling.hc.webconsole.impl;
 
+import static org.apache.sling.hc.util.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;
@@ -37,6 +40,7 @@ import org.apache.felix.scr.annotations.
 import org.apache.sling.api.request.ResponseUtil;
 import org.apache.sling.hc.api.Result;
 import org.apache.sling.hc.api.ResultLog;
+import org.apache.sling.hc.api.execution.HealthCheckExecutionOptions;
 import org.apache.sling.hc.api.execution.HealthCheckExecutionResult;
 import org.apache.sling.hc.api.execution.HealthCheckExecutor;
 
@@ -60,6 +64,10 @@ public class HealthCheckWebconsolePlugin
     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;
 
@@ -103,13 +111,25 @@ public class HealthCheckWebconsolePlugin
         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);
+        doForm(pw, tags, debug, quiet, combineTagsWithOr, forceInstantExecution, overrideGlobalTimeoutStr);
 
         // Execute health checks only if tags are specified (even if empty)
         if (tags != null) {
-            Collection<HealthCheckExecutionResult> results = healthCheckExecutor.execute(tags.split(","));
+            HealthCheckExecutionOptions options = new HealthCheckExecutionOptions();
+            options.setCombineTagsWithOr(combineTagsWithOr);
+            options.setForceInstantExecution(forceInstantExecution);
+            try {
+                options.setOverrideGlobalTimeout(Integer.valueOf(overrideGlobalTimeoutStr));
+            } catch (NumberFormatException nfe) {
+                // override not set in UI
+            }
+
+            Collection<HealthCheckExecutionResult> results = healthCheckExecutor.execute(options, tags.split(","));
 
             pw.println("<table class='content healthcheck' cellpadding='0' cellspacing='0' width='100%'>");
             int total = 0;
@@ -129,6 +149,8 @@ public class HealthCheckWebconsolePlugin
             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.sling.hc.core.impl.executor.HealthCheckExecutorImpl'>Configure executor</a><br/><br/>");
+
         }
     }
 
@@ -142,6 +164,9 @@ public class HealthCheckWebconsolePlugin
         final StringBuilder status = new StringBuilder();
 
         status.append("Tags: ").append(exResult.getHealthCheckMetadata().getTags());
+        status.append(" Finished: ").append(new SimpleDateFormat("yyyy-MM-dd mm:ss").format(exResult.getFinishedAt()) + " after "
+                + msHumanReadable(exResult.getElapsedTimeInMs()));
+
         c.titleHtml(exResult.getHealthCheckMetadata().getTitle(), null);
 
         c.tr();
@@ -167,6 +192,10 @@ public class HealthCheckWebconsolePlugin
             c.writer().print(e.getStatus().toString());
             c.writer().print(' ');
             c.writer().print(ResponseUtil.escapeXml(e.getMessage()));
+            if (e.getException() != null) {
+                c.writer().print(" ");
+                c.writer().print(ResponseUtil.escapeXml(e.getException().toString()));
+            }
             c.writer().println("</div>");
         }
         c.closeTd();
@@ -175,7 +204,10 @@ public class HealthCheckWebconsolePlugin
     private void doForm(final PrintWriter pw,
             final String tags,
             final boolean debug,
-            final boolean quiet)
+            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'>");
@@ -196,6 +228,17 @@ public class HealthCheckWebconsolePlugin
         c.closeTr();
 
         c.tr();
+        c.tdLabel("Combine tags with logical 'or'");
+        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'");
@@ -217,6 +260,28 @@ public class HealthCheckWebconsolePlugin
         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(ResponseUtil.escapeXml(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'/>");