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 2014/06/05 09:04:54 UTC

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

Author: bdelacretaz
Date: Thu Jun  5 07:04:54 2014
New Revision: 1600564

URL: http://svn.apache.org/r1600564
Log:
SLING-3500 - use HealthCheckExecutor in CompositeHealthCheck - contributed by Georg Henzler, thanks!

Added:
    sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/CompositeResult.java
    sling/trunk/bundles/extensions/healthcheck/core/src/test/java/org/apache/sling/hc/core/impl/CompositeHealthCheckTest.java
Modified:
    sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/api/Result.java
    sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/CompositeHealthCheck.java
    sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/executor/ExecutionResult.java
    sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/executor/HealthCheckExecutorImpl.java

Modified: sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/api/Result.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/api/Result.java?rev=1600564&r1=1600563&r2=1600564&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/api/Result.java (original)
+++ sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/api/Result.java Thu Jun  5 07:04:54 2014
@@ -22,7 +22,7 @@ import java.util.Iterator;
 /** The result of executing a {@link HealthCheck} */
 public class Result implements Iterable <ResultLog.Entry> {
 
-    private final ResultLog resultLog;
+    protected final ResultLog resultLog;
 
     public enum Status {
         DEBUG,              // used by ResultLog for debug messages, not an actual output status

Modified: sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/CompositeHealthCheck.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/CompositeHealthCheck.java?rev=1600564&r1=1600563&r2=1600564&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/CompositeHealthCheck.java (original)
+++ sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/CompositeHealthCheck.java Thu Jun  5 07:04:54 2014
@@ -18,7 +18,9 @@
 package org.apache.sling.hc.core.impl;
 
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -27,15 +29,22 @@ import org.apache.felix.scr.annotations.
 import org.apache.felix.scr.annotations.Properties;
 import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.PropertyUnbounded;
+import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.Service;
 import org.apache.sling.commons.osgi.PropertiesUtil;
 import org.apache.sling.hc.api.HealthCheck;
 import org.apache.sling.hc.api.Result;
 import org.apache.sling.hc.api.Result.Status;
-import org.apache.sling.hc.api.ResultLog;
+import org.apache.sling.hc.api.execution.HealthCheckExecutionResult;
+import org.apache.sling.hc.api.execution.HealthCheckExecutor;
 import org.apache.sling.hc.util.FormattingResultLog;
 import org.apache.sling.hc.util.HealthCheckFilter;
+import org.apache.sling.hc.util.HealthCheckMetadata;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.component.ComponentConstants;
 import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -66,73 +75,140 @@ import org.slf4j.LoggerFactory;
 public class CompositeHealthCheck implements HealthCheck {
 
     private final Logger log = LoggerFactory.getLogger(getClass());
-    private BundleContext bundleContext;
 
     @Property(unbounded=PropertyUnbounded.ARRAY,
               label="Filter Tags",
               description="Tags used to select which health checks the composite health check executes.")
-    private static final String PROP_FILTER_TAGS = "filter.tags";
+    static final String PROP_FILTER_TAGS = "filter.tags";
     private String [] filterTags;
 
-    private final ThreadLocal<Boolean> recursionLock = new ThreadLocal<Boolean>();
 
+    @Reference
+    private HealthCheckExecutor healthCheckExecutor;
+
+    private BundleContext bundleContext;
+    private ServiceReference referenceToThis;
+    private HealthCheckFilter healthCheckFilter;
+    
     @Activate
     protected void activate(final ComponentContext ctx) {
         bundleContext = ctx.getBundleContext();
+        healthCheckFilter = new HealthCheckFilter(bundleContext);
+        referenceToThis = getReferenceByPid(PropertiesUtil.toString(ctx.getProperties().get(Constants.SERVICE_PID), "-1"));
+
         filterTags = PropertiesUtil.toStringArray(ctx.getProperties().get(PROP_FILTER_TAGS), new String[] {});
         log.debug("Activated, will select HealthCheck having tags {}", Arrays.asList(filterTags));
     }
 
     @Deactivate
     protected void deactivate() {
-        this.bundleContext = null;
+        bundleContext = null;
+        healthCheckFilter = null;
+        referenceToThis = null;
     }
 
     @Override
     public Result execute() {
-        if ( recursionLock.get() != null ) {
-            // recursion
-            return new Result(Status.CRITICAL,
-                  "Recursive invocation of composite health checks with filter tags : " + Arrays.asList(filterTags));
-        }
-        final FormattingResultLog resultLog = new FormattingResultLog();
-        final HealthCheckFilter filter = new HealthCheckFilter(bundleContext);
-        this.recursionLock.set(Boolean.TRUE);
-        try {
-            final List<HealthCheck> checks = filter.getTaggedHealthChecks(filterTags);
-            if (checks.size() == 0) {
-                resultLog.warn("HealthCheckFilter returns no HealthCheck for tags {}", Arrays.asList(filterTags));
-                return new Result(resultLog);
-            }
 
-            int executed = 0;
-            resultLog.debug("Executing {} HealthCheck selected by the {} tags", checks.size(), Arrays.asList(filterTags));
-            int failures = 0;
-            for (final HealthCheck hc : checks) {
-                if(hc == this) {
-                    resultLog.info("Cowardly forfeiting execution of this HealthCheck in an infinite loop, ignoring it");
-                    continue;
-                }
-                resultLog.debug("Executing {}", hc);
-                executed++;
-                final Result sub = hc.execute();
-                if(!sub.isOk()) {
-                    failures++;
-                }
-                for(final ResultLog.Entry e : sub) {
-                    resultLog.add(e);
+        Result result = null;
+        if ((result = checkForRecursion(referenceToThis, new HashSet<String>())) != null) {
+            // return recursion error
+            return result;
+        }
+
+        FormattingResultLog resultLog = new FormattingResultLog();
+        List<HealthCheckExecutionResult> executionResults = healthCheckExecutor.execute(filterTags);
+        resultLog.debug("Executing {} HealthChecks selected by tags {}", executionResults.size(), Arrays.asList(filterTags));
+        result = new CompositeResult(resultLog, executionResults);
+
+        return result;
+    }
+
+
+    Result checkForRecursion(ServiceReference hcReference, Set<String> alreadyBannedTags) {
+
+        HealthCheckMetadata thisCheckMetadata = new HealthCheckMetadata(hcReference);
+
+        Set<String> bannedTagsForThisCompositeCheck = new HashSet<String>();
+        bannedTagsForThisCompositeCheck.addAll(alreadyBannedTags);
+        bannedTagsForThisCompositeCheck.addAll(thisCheckMetadata.getTags());
+
+        String[] tagsForIncludedChecksArr = PropertiesUtil.toStringArray(hcReference.getProperty(PROP_FILTER_TAGS), new String[0]);
+        Set<String> tagsForIncludedChecks = new HashSet<String>(Arrays.asList(tagsForIncludedChecksArr));
+        
+        
+        log.debug("HC {} has banned tags {}", thisCheckMetadata.getName(), bannedTagsForThisCompositeCheck);
+        log.debug("tagsForIncludedChecks {}", tagsForIncludedChecks);
+
+        // is this HC ok?
+        Set<String> intersection = new HashSet<String>();
+        intersection.addAll(bannedTagsForThisCompositeCheck);
+        intersection.retainAll(tagsForIncludedChecks);
+        
+        if (!intersection.isEmpty()) {
+            return new Result(Status.HEALTH_CHECK_ERROR,
+                    "INVALID CONFIGURATION: Cycle detected in composite health check hierarchy. Health check '" + thisCheckMetadata.getName()
+                            + "' (" + hcReference.getProperty(Constants.SERVICE_PID) + ") must not have tag(s) " + intersection
+                            + " as a composite check in the hierarchy is itself already tagged alike (tags assigned to composite checks: "
+                            + bannedTagsForThisCompositeCheck + ")");
+        }
+        
+        // check each sub composite check
+        ServiceReference[] hcRefsOfCompositeCheck = healthCheckFilter.getTaggedHealthCheckServiceReferences(tagsForIncludedChecksArr);
+        for (ServiceReference hcRefOfCompositeCheck : hcRefsOfCompositeCheck) {
+            if (CompositeHealthCheck.class.getName().equals(hcRefOfCompositeCheck.getProperty(ComponentConstants.COMPONENT_NAME))) {
+                log.debug("Checking sub composite HC {}, {}", hcRefOfCompositeCheck, hcRefOfCompositeCheck.getProperty(ComponentConstants.COMPONENT_NAME));
+                Result result = checkForRecursion(hcRefOfCompositeCheck, bannedTagsForThisCompositeCheck);
+                if (result != null) {
+                    // found recursion
+                    return result;
                 }
             }
 
-            if (failures == 0) {
-                resultLog.debug("{} HealthCheck executed, all ok", executed);
-            } else {
-                resultLog.warn("{} HealthCheck executed, {} failures", executed, failures);
-            }
-        } finally {
-            filter.dispose();
-            this.recursionLock.remove();
         }
-        return new Result(resultLog);
+
+        // no recursion detected
+        return null;
+
+    }
+
+    private ServiceReference getReferenceByPid(String servicePid) {
+
+        if (servicePid == null) {
+            return null;
+        }
+
+        String filterString = "(" + Constants.SERVICE_PID + "=" + servicePid + ")";
+        ServiceReference[] refs = null;
+        try {
+            refs = bundleContext.getServiceReferences(HealthCheck.class.getName(), filterString);
+        } catch (InvalidSyntaxException e) {
+            log.error("Invalid filter " + filterString, e);
+        }
+        if (refs == null || refs.length == 0) {
+            return null;
+        } else if (refs.length == 1) {
+            return refs[0];
+        } else {
+            throw new IllegalStateException("OSGi Framework returned more than one service reference for unique service pid =" + servicePid);
+        }
+
     }
+
+    void setHealthCheckFilter(HealthCheckFilter healthCheckFilter) {
+        this.healthCheckFilter = healthCheckFilter;
+    }
+
+    void setFilterTags(String[] filterTags) {
+        this.filterTags = filterTags;
+    }
+
+    void setHealthCheckExecutor(HealthCheckExecutor healthCheckExecutor) {
+        this.healthCheckExecutor = healthCheckExecutor;
+    }
+
+    void setReferenceToThis(ServiceReference referenceToThis) {
+        this.referenceToThis = referenceToThis;
+    }
+
 }

Added: 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=1600564&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/CompositeResult.java (added)
+++ sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/CompositeResult.java Thu Jun  5 07:04:54 2014
@@ -0,0 +1,29 @@
+package org.apache.sling.hc.core.impl;
+
+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 {
+
+    public CompositeResult(ResultLog log, List<HealthCheckExecutionResult> executionResults) {
+        super(log);
+
+        for (HealthCheckExecutionResult executionResult : executionResults) {
+            HealthCheckMetadata healthCheckMetadata = executionResult.getHealthCheckMetadata();
+            Result healthCheckResult = executionResult.getHealthCheckResult();
+            for (Entry entry : healthCheckResult) {
+                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)" : "")));
+        }
+    }
+
+}

Modified: sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/executor/ExecutionResult.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/executor/ExecutionResult.java?rev=1600564&r1=1600563&r2=1600564&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/executor/ExecutionResult.java (original)
+++ sling/trunk/bundles/extensions/healthcheck/core/src/main/java/org/apache/sling/hc/core/impl/executor/ExecutionResult.java Thu Jun  5 07:04:54 2014
@@ -43,7 +43,7 @@ public class ExecutionResult implements 
     /**
      * Full constructor
      */
-    ExecutionResult(final HealthCheckMetadata metadata,
+    public ExecutionResult(final HealthCheckMetadata metadata,
             final Result simpleResult,
             final long elapsedTimeInMs,
             final boolean timedout) {
@@ -57,7 +57,7 @@ public class ExecutionResult implements 
     /**
      * Shortcut constructor for a result
      */
-    ExecutionResult(final HealthCheckMetadata metadata,
+    public ExecutionResult(final HealthCheckMetadata metadata,
             final Result simpleResult,
             final long elapsedTimeInMs) {
         this(metadata, simpleResult, elapsedTimeInMs, false);
@@ -66,7 +66,7 @@ public class ExecutionResult implements 
     /**
      * Shortcut constructor to create error/timed out result.
      */
-    ExecutionResult(final HealthCheckMetadata metadata,
+    public ExecutionResult(final HealthCheckMetadata metadata,
             final Result.Status status,
             final String errorMessage,
             final long elapsedTime, boolean timedOut) {

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=1600564&r1=1600563&r2=1600564&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 Thu Jun  5 07:04:54 2014
@@ -408,7 +408,7 @@ public class HealthCheckExecutorImpl imp
         return result;
     }
 
-    static String msHumanReadable(final long millis) {
+    public static String msHumanReadable(final long millis) {
 
         double number = millis;
         final String[] units = new String[] { "ms", "sec", "min", "h", "days" };

Added: sling/trunk/bundles/extensions/healthcheck/core/src/test/java/org/apache/sling/hc/core/impl/CompositeHealthCheckTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/healthcheck/core/src/test/java/org/apache/sling/hc/core/impl/CompositeHealthCheckTest.java?rev=1600564&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/healthcheck/core/src/test/java/org/apache/sling/hc/core/impl/CompositeHealthCheckTest.java (added)
+++ sling/trunk/bundles/extensions/healthcheck/core/src/test/java/org/apache/sling/hc/core/impl/CompositeHealthCheckTest.java Thu Jun  5 07:04:54 2014
@@ -0,0 +1,226 @@
+/*
+ * 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.core.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.sling.hc.api.HealthCheck;
+import org.apache.sling.hc.api.Result;
+import org.apache.sling.hc.api.execution.HealthCheckExecutionResult;
+import org.apache.sling.hc.api.execution.HealthCheckExecutor;
+import org.apache.sling.hc.core.impl.executor.ExecutionResult;
+import org.apache.sling.hc.util.HealthCheckFilter;
+import org.apache.sling.hc.util.HealthCheckMetadata;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.component.ComponentConstants;
+
+public class CompositeHealthCheckTest {
+
+    @Spy
+    private CompositeHealthCheck compositeHealthCheck = new CompositeHealthCheck();
+
+    @Mock
+    private HealthCheckExecutor healthCheckExecutor;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        compositeHealthCheck.setHealthCheckExecutor(healthCheckExecutor);
+        compositeHealthCheck.setFilterTags(new String[] {});
+
+    }
+
+    @Test
+    public void testExecution() {
+
+        doReturn((Result) null).when(compositeHealthCheck).checkForRecursion(Matchers.<ServiceReference> any(), Matchers.<Set<String>> any());
+        String[] testTags = new String[] { "tag1" };
+        compositeHealthCheck.setFilterTags(testTags);
+
+        
+        List<HealthCheckExecutionResult> executionResults = new LinkedList<HealthCheckExecutionResult>();
+        executionResults.add(createExecutionResult("Check 1", testTags, new Result(Result.Status.INFO, "Good")));
+        executionResults.add(createExecutionResult("Check 2", testTags, new Result(Result.Status.CRITICAL, "Bad")));
+
+        when(healthCheckExecutor.execute(testTags)).thenReturn(executionResults);
+        
+        Result result = compositeHealthCheck.execute();
+
+        verify(healthCheckExecutor, times(1)).execute(testTags);
+        
+        assertEquals(Result.Status.CRITICAL, result.getStatus());
+
+    }
+
+    private HealthCheckExecutionResult createExecutionResult(String name, String[] testTags, Result result) {
+        HealthCheckExecutionResult healthCheckExecutionResult = new ExecutionResult(new HealthCheckMetadata(new DummyHcServiceReference(name, testTags,
+                new String[0])), result, 0L);
+        return healthCheckExecutionResult;
+    }
+
+    @Test
+    public void testRecursionCheckSimle() {
+
+        // composite check referencing itself
+        final String[] filterTags = new String[] { "check1" };
+        final DummyHcServiceReference hcRef = new DummyHcServiceReference("Check 1", new String[] { "check1" }, filterTags);
+
+        // test check is hcRef
+        compositeHealthCheck.setReferenceToThis(hcRef);
+        compositeHealthCheck.setFilterTags(filterTags);
+
+        compositeHealthCheck.setHealthCheckFilter(new HealthCheckFilter(null) { // not using @Spy because varargs matcher does not work with spies
+
+            @Override
+            public ServiceReference[] getTaggedHealthCheckServiceReferences(String... tags) {
+                ServiceReference[] result = new ServiceReference[] {};
+                if (tags.length > 0) {
+                    if (tags[0].equals(filterTags[0])) {
+                        result = new ServiceReference[] { hcRef };
+                    }
+                }
+                return result;
+            }
+
+        });
+
+        Result result = compositeHealthCheck.execute();
+
+        verify(healthCheckExecutor, never()).execute(Matchers.any(String[].class));
+        assertEquals(Result.Status.HEALTH_CHECK_ERROR, result.getStatus());
+    }
+
+    @Test
+    public void testRecursionCheckCyclic() {
+
+        // three checks, cyclic
+        final String[] filterTags = new String[] { "check2" };
+        final DummyHcServiceReference hcRef1 = new DummyHcServiceReference("Check 1", new String[] { "check1" }, filterTags);
+        final DummyHcServiceReference hcRef2 = new DummyHcServiceReference("Check 2", new String[] { "check2" }, new String[] { "check3" });
+        final DummyHcServiceReference hcRef3 = new DummyHcServiceReference("Check 3", new String[] { "check3" }, new String[] { "check1" });
+
+        // test check is hcRef1
+        compositeHealthCheck.setReferenceToThis(hcRef1);
+        compositeHealthCheck.setFilterTags(filterTags);
+
+        compositeHealthCheck.setHealthCheckFilter(new HealthCheckFilter(null) {
+
+            @Override
+            public ServiceReference[] getTaggedHealthCheckServiceReferences(String... tags) { // not using @Spy because varargs matcher does not work with spies
+                ServiceReference[] result = new ServiceReference[] {};
+                if (tags.length > 0) {
+                    if (tags[0].equals(filterTags[0])) {
+                        result = new ServiceReference[] { hcRef2 };
+                    } else if (tags[0].equals("check3")) {
+                        result = new ServiceReference[] { hcRef3 };
+                    } else if (tags[0].equals("check1")) {
+                        result = new ServiceReference[] { hcRef1 };
+                    }
+                }
+
+                return result;
+            }
+
+        });
+
+        Result result = compositeHealthCheck.execute();
+
+        verify(healthCheckExecutor, never()).execute(Matchers.any(String[].class));
+        assertEquals(Result.Status.HEALTH_CHECK_ERROR, result.getStatus());
+    }
+
+
+    private static class DummyHcServiceReference implements ServiceReference {
+
+        private long id;
+        private String name;
+        private String[] tags;
+        private String[] filterTags;
+
+        public DummyHcServiceReference(String name, String[] tags, String[] filterTags) {
+            super();
+            this.id = (long) (Math.random() * Long.MAX_VALUE);
+            this.name = name;
+            this.tags = tags;
+            this.filterTags = filterTags;
+        }
+
+        @Override
+        public Object getProperty(String key) {
+
+            if (Constants.SERVICE_ID.equals(key)) {
+                return id;
+            } else if (HealthCheck.NAME.equals(key)) {
+                return name;
+            } else if (HealthCheck.MBEAN_NAME.equals(key)) {
+                return name;
+            } else if (HealthCheck.TAGS.equals(key)) {
+                return tags;
+            } else if (CompositeHealthCheck.PROP_FILTER_TAGS.equals(key)) {
+                return filterTags;
+            } else if (ComponentConstants.COMPONENT_NAME.equals(key)) {
+                return filterTags != null ? CompositeHealthCheck.class.getName() : "some.other.HealthCheck";
+            } else {
+                return null;
+            }
+        }
+
+        @Override
+        public String[] getPropertyKeys() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Bundle getBundle() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Bundle[] getUsingBundles() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean isAssignableTo(Bundle bundle, String className) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int compareTo(Object reference) {
+            throw new UnsupportedOperationException();
+        }
+
+    }
+}