FELIX-6250 Improved documentation, allow to send events for changes or
for all executions
diff --git a/healthcheck/README.md b/healthcheck/README.md
index 5567d5f..4147de1 100644
--- a/healthcheck/README.md
+++ b/healthcheck/README.md
@@ -247,7 +247,7 @@
 
 ### Webconsole plugin
 
-If the `org.apache.felix.hc.webconsole` bundle is active, a webconsole plugin 
+If the `org.apache.felix.hc.webconsole` bundle is installed, a webconsole plugin 
 at `/system/console/healthcheck` allows for executing health checks, optionally selected
 based on their tags (positive and negative selection, see the `HealthCheckFilter` mention above).
 
@@ -255,7 +255,7 @@
 
 ### Gogo Console
 
-The Gogo command `hc:exec` that can be used as follows:
+The Gogo command `hc:exec` can be used as follows:
 
     hc:exec [-v] [-a] tag1,tag2
       -v verbose/debug
@@ -276,7 +276,7 @@
 `registerHealthyMarkerService` | boolean | true | For the case a given tag/name is healthy, will register a service `org.apache.felix.hc.api.condition.Healthy` with property tag=<tagname> (or name=<hc.name>) that other services can depend on. For the special case of the tag `systemready`, the marker service `org.apache.felix.hc.api.condition.SystemReady` is registered
 `registerUnhealthyMarkerService` | boolean | false | For the case a given tag/name is **un**healthy, will register a service `org.apache.felix.hc.api.condition.Unhealthy` with property tag=<tagname> (or name=<hc.name>) that other services can depend on
 `treatWarnAsHealthy` | boolean | true | `WARN` usually means [the system is usable](#semantic-meaning-of-health-check-results), hence WARN is treated as healthy by default. When set to false `WARN` is treated as `Unhealthy`
-`sendEvents` | boolean | false | Whether to send events for health check status changes. See [below](#osgi-events-for-health-check-status-changes) for details.
+`sendEvents` | enum `NONE`, `STATUS_CHANGES` or `ALL` | `STATUS_CHANGES` | Whether to send events for health check status changes. See [below](#osgi-events-for-health-check-status-changes) for details.
 
 ### Marker Service to depend on a health status in SCR Components
 
@@ -305,15 +305,38 @@
 
 NOTE: This does not support the [RFC 242 Condition Service](https://github.com/osgi/design/blob/master/rfcs/rfc0242/rfc-0242-Condition-Service.pdf) yet - however once final the marker services will also be able to implement the `Condition` interface.
 
-### OSGi events for Health Check status changes
+### OSGi events for Health Check status changes and updates
 
-For tags/names that a `HealthCheckMonitor` is configured for and `sendEvents` is set to true, events are sent whenever a result status for any of the given tags/names has changed. The events are sent for the topic `org/apache/felix/healthchange/*`. The following events are sent:
+OSGi events with topic `org/apache/felix/health/*` are sent for tags/names that a `HealthCheckMonitor` is configured for and if `sendEvents` is set to `STATUS_CHANGES` or `ALL`:
 
-Topic Prefix | Example | Description  
------- | ------- | -----
-`org/apache/felix/healthchange/tag/`  | `org/apache/felix/healthchange/tag/mytag` | Sent whenever the aggregate status for any of the tags as configured in `HealthCheckMonitor` changed
-`org/apache/felix/healthchange/name/`  | `org/apache/felix/healthchange/tag/My_Tag_Name ` (spaces are replaced with underscores to ensure valid topic names) | Sent whenever the status for any of the names as configured in `HealthCheckMonitor` changed
-`org/apache/felix/healthchange/component/`  | `org/apache/felix/healthchange/component/com/mycomp/myproj/MyHealthCheck` (`.` are replaced with slashes to produce valid topic names) | Sent for health checks that are based on SCR components (in addition to the name event)
+* `STATUS_CHANGES` notifies only of status changes with suffix `/STATUS_CHANGED`
+* `ALL` sends events whenever the monitor runs, depending on status will either send the event with suffix `/UPDATED` or `/STATUS_CHANGED`
+
+All events sent generally carry the properties `executionResult`, `status` and `previousStatus`.
+
+| Example | Description  
+------- | -----
+`org/apache/felix/health/tag/mytag/STATUS_CHANGED` | Status for tag `mytag` has changed compared to last execution
+`org/apache/felix/health/tag/My_HC_Name/UPDATED ` (spaces in names are replaced with underscores to ensure valid topic names) | Status for name `My HC Name` has not changed but HC was executed and execution result is available in event property `executionResult`.
+`org/apache/felix/health/component/com/myprj/MyHealthCheck/UPDATED` (`.` are replaced with slashes to produce valid topic names) | HC based on SCR component `com.myprj.MyHealthCheck` was executed without having the status changed. The SCR component event is sent in addition to the name event
+
+Event listener example: 
+
+```
+@Component(property = { EventConstants.EVENT_TOPIC + "=org/apache/felix/health/*"})
+public class HealthEventHandler implements EventHandler {
+    private static final Logger LOG = LoggerFactory.getLogger(HealthEventHandler.class);
+
+    public void handleEvent(Event event) {
+        LOG.info("Received event: "+event.getTopic());
+        LOG.info("    previousStatus:  "+event.getProperty("previousStatus"));
+        LOG.info("    status:  "+event.getProperty("status"));
+        HealthCheckExecutionResult executionResult = (HealthCheckExecutionResult) event.getProperty("executionResult");
+        LOG.info("    executionResult:  "+executionResult);
+        LOG.info("    result:  "+executionResult.getHealthCheckResult());
+    }
+}
+```
 
 ## Servlet Filters
 
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitor.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitor.java
index f2f991a..c6bf00f 100644
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitor.java
+++ b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitor.java
@@ -76,7 +76,10 @@
 
     public static final String TAG_SYSTEMREADY = "systemready";
 
-    public static final String EVENT_TOPIC_PREFIX = "org/apache/felix/healthchange";
+    public static final String EVENT_TOPIC_PREFIX = "org/apache/felix/health";
+    public static final String EVENT_TOPIC_SUFFIX_STATUS_CHANGED = "STATUS_CHANGED";
+    public static final String EVENT_TOPIC_SUFFIX_STATUS_UPDATED = "UPDATED";
+
     public static final String EVENT_PROP_EXECUTION_RESULT = "executionResult";
     public static final String EVENT_PROP_STATUS = "status";
     public static final String EVENT_PROP_PREVIOUS_STATUS = "previousStatus";
@@ -88,6 +91,10 @@
     static final SystemReady MARKER_SERVICE_SYSTEMREADY = new SystemReady() {
     };
 
+    public enum SendEventsConfig {
+        NONE, STATUS_CHANGES, ALL
+    }
+    
     @ObjectClassDefinition(name = "Health Check Monitor", description = "Regularly executes health checks according to given interval/cron expression")
     public @interface Config {
 
@@ -112,8 +119,8 @@
         @AttributeDefinition(name = "Treat WARN as Healthy", description = "Whether to treat status WARN as healthy (it normally should because WARN indicates a working system that only possibly might become unavailable if no action is taken")
         boolean treatWarnAsHealthy() default true;
 
-        @AttributeDefinition(name = "Send Events", description = "Whether to send OSGi events for the case a status has changed")
-        boolean sendEvents() default true;
+        @AttributeDefinition(name = "Send Events", description = "Send OSGi events for the case a status has changed or for all executions or for none.")
+        SendEventsConfig sendEvents() default SendEventsConfig.STATUS_CHANGES;
 
         @AttributeDefinition
         String webconsole_configurationFactory_nameHint() default "Health Monitor for '{tags}'/'{names}', {intervalInSec}sec/{cronExpression}, Marker Service Healthy:{registerHealthyMarkerService} Unhealthy:{registerUnhealthyMarkerService}, Send Events {sendEvents}";
@@ -145,7 +152,7 @@
 
     private boolean treatWarnAsHealthy;
 
-    private boolean sendEvents;
+    private SendEventsConfig sendEventsConfig;
 
     private BundleContext bundleContext;
     
@@ -168,7 +175,7 @@
         this.registerUnhealthyMarkerService = config.registerUnhealthyMarkerService();
 
         this.treatWarnAsHealthy = config.treatWarnAsHealthy();
-        this.sendEvents = config.sendEvents();
+        this.sendEventsConfig = config.sendEvents();
 
         this.intervalInSec = config.intervalInSec();
         this.cronExpression = config.cronExpression();
@@ -272,7 +279,7 @@
             LOG.trace("  {}: isHealthy={} statusChanged={}", tagOrName, isHealthy, statusChanged);
 
             registerMarkerServices();
-            sendEvent(executionResult, previousStatus);
+            sendEvents(executionResult, previousStatus);
         }
 
         private void registerMarkerServices() {
@@ -339,26 +346,28 @@
             }
         }
 
-        private void sendEvent(HealthCheckExecutionResult executionResult, Result.Status previousStatus) {
-            if (sendEvents && statusChanged) {
+        private void sendEvents(HealthCheckExecutionResult executionResult, Result.Status previousStatus) {
+            if ((sendEventsConfig == SendEventsConfig.STATUS_CHANGES && statusChanged) || sendEventsConfig == SendEventsConfig.ALL) {
+                
+                String eventSuffix = statusChanged ? EVENT_TOPIC_SUFFIX_STATUS_CHANGED : EVENT_TOPIC_SUFFIX_STATUS_UPDATED;
+                String logMsg = "Posted event for topic '{}': " + (statusChanged ? "Status change from {} to {}" : "Result updated (status {})");
+
                 Map<String, Object> properties = new HashMap<>();
                 properties.put(EVENT_PROP_STATUS, status);
                 if (previousStatus != null) {
                     properties.put(EVENT_PROP_PREVIOUS_STATUS, previousStatus);
                 }
                 properties.put(EVENT_PROP_EXECUTION_RESULT, executionResult);
-                String topic = EVENT_TOPIC_PREFIX + "/" + propertyName + "/" + tagOrName.replaceAll("\\s+", "_");
+                String topic = String.join("/", EVENT_TOPIC_PREFIX, propertyName, tagOrName.replaceAll("\\s+", "_"), eventSuffix);
                 eventAdmin.postEvent(new Event(topic, properties));
-                LOG.debug("HealthCheckMonitor: Posted event for topic '{}': Status change from {} to {}", topic,
-                        previousStatus, status);
+                LOG.debug(logMsg, topic, previousStatus, status);
                 if (!(executionResult instanceof CombinedExecutionResult)) {
                     String componentName = (String) executionResult.getHealthCheckMetadata().getServiceReference()
                             .getProperty(ComponentConstants.COMPONENT_NAME);
                     if (StringUtils.isNotBlank(componentName)) {
-                        String topicClass = EVENT_TOPIC_PREFIX + "/component/" + componentName.replace(".", "/");
+                        String topicClass = String.join("/", EVENT_TOPIC_PREFIX, "component", componentName.replace(".", "/"), eventSuffix);
                         eventAdmin.postEvent(new Event(topicClass, properties));
-                        LOG.debug("HealthCheckMonitor: Posted event for topic '{}': Status change from {} to {}",
-                                topicClass, previousStatus, status);
+                        LOG.debug(logMsg, topicClass, previousStatus, status);
                     }
                 }
             }
diff --git a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitorTest.java b/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitorTest.java
index 7610015..3426cf6 100644
--- a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitorTest.java
+++ b/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitorTest.java
@@ -96,7 +96,7 @@
 
     @Captor
     private ArgumentCaptor<Event> postedEventsCaptor2;
-    
+        
     @Mock
     private ServiceRegistration<? extends Healthy> healthyRegistration;
     
@@ -192,9 +192,9 @@
     }
     
     @Test
-    public void testRunSendEvents() throws InvalidSyntaxException {
+    public void testRunSendEventsStatusChanges() throws InvalidSyntaxException {
 
-        when(config.sendEvents()).thenReturn(true);
+        when(config.sendEvents()).thenReturn(HealthCheckMonitor.SendEventsConfig.STATUS_CHANGES);
         when(healthCheckServiceRef.getProperty(ComponentConstants.COMPONENT_NAME)).thenReturn("org.apache.felix.TestHealthCheck");
 
         healthCheckMonitor.activate(bundleContext, config, componentContext);
@@ -208,9 +208,9 @@
         verify(eventAdmin, times(2)).postEvent(postedEventsCaptor1.capture());
         List<Event> postedEvents = postedEventsCaptor1.getAllValues();
         assertEquals(2, postedEvents.size());
-        assertEquals("org/apache/felix/healthchange/tag/test-tag", postedEvents.get(0).getTopic());
+        assertEquals("org/apache/felix/health/tag/test-tag/STATUS_CHANGED", postedEvents.get(0).getTopic());
         assertEquals(Result.Status.OK, postedEvents.get(0).getProperty(HealthCheckMonitor.EVENT_PROP_STATUS));
-        assertEquals("org/apache/felix/healthchange/component/org/apache/felix/TestHealthCheck", postedEvents.get(1).getTopic());
+        assertEquals("org/apache/felix/health/component/org/apache/felix/TestHealthCheck/STATUS_CHANGED", postedEvents.get(1).getTopic());
 
         reset(eventAdmin);
         // without status change
@@ -225,18 +225,54 @@
         verify(eventAdmin, times(2)).postEvent(postedEventsCaptor2.capture());
         postedEvents = postedEventsCaptor2.getAllValues();
         assertEquals(2, postedEvents.size());
-        assertEquals("org/apache/felix/healthchange/tag/test-tag", postedEvents.get(0).getTopic());
+        assertEquals("org/apache/felix/health/tag/test-tag/STATUS_CHANGED", postedEvents.get(0).getTopic());
         assertEquals(Result.Status.CRITICAL, postedEvents.get(0).getProperty(HealthCheckMonitor.EVENT_PROP_STATUS));
         assertEquals(Result.Status.OK, postedEvents.get(0).getProperty(HealthCheckMonitor.EVENT_PROP_PREVIOUS_STATUS));
-        assertEquals("org/apache/felix/healthchange/component/org/apache/felix/TestHealthCheck", postedEvents.get(1).getTopic());
+        assertEquals("org/apache/felix/health/component/org/apache/felix/TestHealthCheck/STATUS_CHANGED", postedEvents.get(1).getTopic());
         
         reset(eventAdmin);
         // without status change
         healthCheckMonitor.run();
         // no event
         verifyNoInteractions(eventAdmin);
+
     }
 
+    @Test
+    public void testRunSendEventsAll() throws InvalidSyntaxException {
+
+        when(config.sendEvents()).thenReturn(HealthCheckMonitor.SendEventsConfig.ALL);
+        when(healthCheckServiceRef.getProperty(ComponentConstants.COMPONENT_NAME)).thenReturn("org.apache.felix.TestHealthCheck");
+
+        healthCheckMonitor.activate(bundleContext, config, componentContext);
+
+        setHcResult(Result.Status.OK);
+
+        healthCheckMonitor.run();
+        
+        verify(healthCheckExecutor).execute(HealthCheckSelector.tags(TEST_TAG));
+        
+        verify(eventAdmin, times(2)).postEvent(postedEventsCaptor1.capture());
+        List<Event> postedEvents = postedEventsCaptor1.getAllValues();
+        assertEquals(2, postedEvents.size());
+        assertEquals("org/apache/felix/health/tag/test-tag/STATUS_CHANGED", postedEvents.get(0).getTopic());
+        assertEquals(Result.Status.OK, postedEvents.get(0).getProperty(HealthCheckMonitor.EVENT_PROP_STATUS));
+        assertEquals("org/apache/felix/health/component/org/apache/felix/TestHealthCheck/STATUS_CHANGED", postedEvents.get(1).getTopic());
+
+        reset(eventAdmin);
+        // without status change
+        healthCheckMonitor.run();
+
+        verify(eventAdmin, times(2)).postEvent(postedEventsCaptor2.capture());
+        postedEvents = postedEventsCaptor2.getAllValues();
+        assertEquals(2, postedEvents.size());
+        assertEquals("org/apache/felix/health/tag/test-tag/UPDATED", postedEvents.get(0).getTopic());
+        assertEquals(Result.Status.OK, postedEvents.get(0).getProperty(HealthCheckMonitor.EVENT_PROP_STATUS));
+        assertEquals(Result.Status.OK, postedEvents.get(0).getProperty(HealthCheckMonitor.EVENT_PROP_PREVIOUS_STATUS));
+        assertEquals("org/apache/felix/health/component/org/apache/felix/TestHealthCheck/UPDATED", postedEvents.get(1).getTopic());
+    }
+    
+    
     private void setHcResult(Result.Status status) {
         when(healthCheckExecutor.execute(HealthCheckSelector.tags(TEST_TAG)))
             .thenReturn(Arrays.asList(new ExecutionResult(healthCheckMetadata, new Result(status, status.toString()), 1)));