[VOL-2950][VOL-3110][AETHER-457] Fix a bug in component readiness check

The existing code checks for onos-core-net only due to the bug

In addition, this patch introduces a new REST API to check health of given app.
Active in app status only indicates that the app has been activated.
This new API will look deeper and make sure all OSGi features, bundles and components of given app are all ready.

Change-Id: If91326ba9cffdbe25821eeaaa092ec9d2ab952ea
diff --git a/core/net/src/main/java/org/onosproject/cluster/impl/ComponentsMonitor.java b/core/net/src/main/java/org/onosproject/cluster/impl/ComponentsMonitorManager.java
similarity index 81%
rename from core/net/src/main/java/org/onosproject/cluster/impl/ComponentsMonitor.java
rename to core/net/src/main/java/org/onosproject/cluster/impl/ComponentsMonitorManager.java
index 86636f7..551c646 100644
--- a/core/net/src/main/java/org/onosproject/cluster/impl/ComponentsMonitor.java
+++ b/core/net/src/main/java/org/onosproject/cluster/impl/ComponentsMonitorManager.java
@@ -16,6 +16,7 @@
 
 package org.onosproject.cluster.impl;
 
+import com.google.common.collect.Lists;
 import org.osgi.service.component.runtime.ServiceComponentRuntime;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
@@ -25,6 +26,7 @@
 import org.apache.karaf.features.Feature;
 import org.apache.karaf.features.FeaturesService;
 import org.onosproject.cluster.ClusterAdminService;
+import org.onosproject.cluster.ComponentsMonitorService;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.service.component.ComponentContext;
@@ -33,6 +35,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.List;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
@@ -45,8 +48,8 @@
  * are properly activated and keeps the cluster node service appropriately
  * updated.
  */
-@Component(immediate = true)
-public class ComponentsMonitor {
+@Component(immediate = true, service = { ComponentsMonitorService.class })
+public class ComponentsMonitorManager implements ComponentsMonitorService {
 
     private Logger log = LoggerFactory.getLogger(getClass());
 
@@ -133,7 +136,7 @@
     private boolean isFullyStarted() {
         try {
             for (Feature feature : featuresService.listInstalledFeatures()) {
-                if (!isFullyStarted(feature)) {
+                if (needToCheck(feature) && !isFullyStarted(feature)) {
                     return false;
                 }
             }
@@ -149,20 +152,30 @@
                !feature.getId().contains("thirdparty");
     }
 
-    private boolean isFullyStarted(Feature feature) {
-        if (needToCheck(feature)) {
+    @Override
+    public boolean isFullyStarted(List<String> featureStrings) {
+        List<Feature> features = Lists.newArrayList();
+        for (String featureString : featureStrings) {
             try {
-                return feature.getBundles().stream()
-                    .map(info -> bundleContext.getBundle())
-                    .allMatch(this::isFullyStarted);
-            } catch (NullPointerException npe) {
-                // FIXME: Remove this catch block when Felix fixes the bug
-                // Due to a bug in the Felix implementation, this can throw an NPE.
-                // Catch the error and do something sensible with it.
+                features.add(featuresService.getFeature(featureString));
+            } catch (Exception e) {
+                log.debug("Feature {} not found", featureString);
                 return false;
             }
-        } else {
-            return true;
+        }
+        return features.stream().allMatch(this::isFullyStarted);
+    }
+
+    private boolean isFullyStarted(Feature feature) {
+        try {
+            return feature.getBundles().stream()
+                .map(info -> bundleContext.getBundle(info.getLocation()))
+                .allMatch(bundle -> bundle != null && isFullyStarted(bundle));
+        } catch (NullPointerException npe) {
+            // FIXME: Remove this catch block when Felix fixes the bug
+            // Due to a bug in the Felix implementation, this can throw an NPE.
+            // Catch the error and do something sensible with it.
+            return false;
         }
     }