This is an automated email from the ASF dual-hosted git repository.

cziegeler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/felix-dev.git


The following commit(s) were added to refs/heads/master by this push:
     new 194d23e  FELIX-6399 : Reduce resource consumption during component 
checks
194d23e is described below

commit 194d23e253cb5c34a793f1e46363e7785e9addc7
Author: Carsten Ziegeler <[email protected]>
AuthorDate: Sun Aug 8 11:43:33 2021 +0200

    FELIX-6399 : Reduce resource consumption during component checks
---
 .../felix/systemready/impl/ComponentsCheck.java    | 82 ++++++++++++++++------
 .../felix/systemready/impl/ServicesCheck.java      | 12 ++--
 .../systemready/impl/SystemReadyMonitorImpl.java   | 55 +++++++++------
 .../systemready/osgi/ComponentsCheckTest.java      | 20 +++++-
 .../systemready/osgi/SystemReadyMonitorTest.java   |  2 +-
 .../felix/systemready/osgi/util/BaseTest.java      | 26 +++----
 6 files changed, 136 insertions(+), 61 deletions(-)

diff --git 
a/systemready/src/main/java/org/apache/felix/systemready/impl/ComponentsCheck.java
 
b/systemready/src/main/java/org/apache/felix/systemready/impl/ComponentsCheck.java
index 513813c..44fd724 100644
--- 
a/systemready/src/main/java/org/apache/felix/systemready/impl/ComponentsCheck.java
+++ 
b/systemready/src/main/java/org/apache/felix/systemready/impl/ComponentsCheck.java
@@ -19,7 +19,9 @@
 package org.apache.felix.systemready.impl;
 
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
 
 import org.apache.felix.rootcause.DSComp;
@@ -34,9 +36,12 @@ import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.ConfigurationPolicy;
 import org.osgi.service.component.annotations.Reference;
 import org.osgi.service.component.runtime.ServiceComponentRuntime;
+import org.osgi.service.component.runtime.dto.ComponentDescriptionDTO;
 import org.osgi.service.metatype.annotations.AttributeDefinition;
 import org.osgi.service.metatype.annotations.Designate;
 import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 @Component(
         name = ComponentsCheck.PID,
@@ -56,27 +61,28 @@ public class ComponentsCheck implements SystemReadyCheck {
 
         @AttributeDefinition(name = "Components list", description = "The 
components that need to come up before this check reports GREEN")
         String[] components_list();
-        
-        @AttributeDefinition(name = "Check type") 
+
+        @AttributeDefinition(name = "Check type")
         StateType type() default StateType.ALIVE;
 
     }
+    private final Logger log = LoggerFactory.getLogger(getClass());
 
     private List<String> componentsList;
-    
+
     private DSRootCause analyzer;
 
     private StateType type;
-    
-    @Reference
-    ServiceComponentRuntime scr;
 
+    volatile ServiceComponentRuntime scr;
+
+    private final AtomicReference<CheckStatus> cache = new AtomicReference<>();
 
     @Activate
     public void activate(final BundleContext ctx, final Config config) throws 
InterruptedException {
         this.analyzer = new DSRootCause(scr);
         this.type = config.type();
-        componentsList = Arrays.asList(config.components_list());
+        this.componentsList = Arrays.asList(config.components_list());
     }
 
     @Override
@@ -84,30 +90,66 @@ public class ComponentsCheck implements SystemReadyCheck {
         return "Components Check " + componentsList;
     }
 
+    private List<DSComp> getComponents(final 
Collection<ComponentDescriptionDTO> descriptions) {
+        try {
+            return descriptions.stream()
+                .filter(desc -> componentsList.contains(desc.name))
+                .map(analyzer::getRootCause)
+                .collect(Collectors.toList());
+        } catch (Throwable e) {
+            log.error("Exception while getting ds component dtos {}", 
e.getMessage(), e);
+            throw e;
+        }
+    }
+
     @Override
     public CheckStatus getStatus() {
-        StringBuilder details = new StringBuilder();
-        List<DSComp> watchedComps = scr.getComponentDescriptionDTOs().stream()
-            .filter(desc -> componentsList.contains(desc.name))
-            .map(analyzer::getRootCause)
-            .collect(Collectors.toList());
-        if (watchedComps.size() < componentsList.size()) {
-            throw new IllegalStateException("Not all named components could be 
found");
-        };
-        watchedComps.stream().forEach(dsComp -> addDetails(dsComp, details));
-        final CheckStatus.State state = 
CheckStatus.State.worstOf(watchedComps.stream().map(this::status));
-        return new CheckStatus(getName(), type, state, details.toString());
+        CheckStatus result = this.cache.get();
+        if ( result == null ) {
+            // get component description DTOs only once
+            final Collection<ComponentDescriptionDTO> descriptions = 
scr.getComponentDescriptionDTOs();
+
+            final List<DSComp> watchedComps = getComponents(descriptions);
+            if (watchedComps.size() < componentsList.size()) {
+                result = new CheckStatus(getName(), type, 
CheckStatus.State.RED, "Not all named components could be found");
+            } else {
+                try {
+                    final StringBuilder details = new StringBuilder();
+                    watchedComps.stream().forEach(dsComp -> addDetails(dsComp, 
details));
+                    final CheckStatus.State state = 
CheckStatus.State.worstOf(watchedComps.stream().map(this::status));
+                    result = new CheckStatus(getName(), type, state, 
details.toString());
+                } catch (Throwable e) {
+                    log.error("Exception while checking ds component dtos {}", 
e.getMessage(), e);
+                    throw e;
+                }
+            }
+            this.cache.set(result);
+        }
+        return result;
     }
-    
+
     private CheckStatus.State status(DSComp component) {
         boolean missingConfig = component.config == null && 
"require".equals(component.desc.configurationPolicy);
         boolean unsatisfied = !component.unsatisfied.isEmpty();
         return (missingConfig || unsatisfied) ? CheckStatus.State.YELLOW : 
CheckStatus.State.GREEN;
     }
 
-    private void addDetails(DSComp component, StringBuilder details) {
+    private void addDetails(final DSComp component, final StringBuilder 
details) {
         RootCausePrinter printer = new RootCausePrinter(st -> 
details.append(st + "\n"));
         printer.print(component);
     }
 
+    @Reference(updated = "updatedServiceComponentRuntime")
+    private void setServiceComponentRuntime(final ServiceComponentRuntime c) {
+        this.scr = c;
+    }
+
+    private void unsetServiceComponentRuntime(final ServiceComponentRuntime c) 
{
+        this.scr = null;
+    }
+
+    private void updatedServiceComponentRuntime(final ServiceComponentRuntime 
c) {
+        // change in DS - clear cache
+        this.cache.set(null);
+    }
 }
diff --git 
a/systemready/src/main/java/org/apache/felix/systemready/impl/ServicesCheck.java
 
b/systemready/src/main/java/org/apache/felix/systemready/impl/ServicesCheck.java
index 0884ef3..dfd7685 100644
--- 
a/systemready/src/main/java/org/apache/felix/systemready/impl/ServicesCheck.java
+++ 
b/systemready/src/main/java/org/apache/felix/systemready/impl/ServicesCheck.java
@@ -63,14 +63,14 @@ public class ServicesCheck implements SystemReadyCheck {
         @AttributeDefinition(name = "Services list", description = "The 
services that need to be registered for the check to pass")
         String[] services_list();
 
-        @AttributeDefinition(name = "Check type") 
+        @AttributeDefinition(name = "Check type")
         StateType type() default StateType.ALIVE;
     }
 
     private List<String> servicesList;
 
     private Map<String, Tracker> trackers;
-    
+
     private DSRootCause analyzer;
 
     private StateType type;
@@ -102,14 +102,14 @@ public class ServicesCheck implements SystemReadyCheck {
 
     @Override
     public CheckStatus getStatus() {
-        boolean allPresent = 
trackers.values().stream().allMatch(Tracker::present);
+        final List<String> missing = getMissing();
+        boolean allPresent = missing.isEmpty();
         // TODO: RED on timeouts
         final CheckStatus.State state = State.fromBoolean(allPresent);
-        return new CheckStatus(getName(), type, state, getDetails()); // TODO: 
out of sync? do we care?
+        return new CheckStatus(getName(), type, state, getDetails(missing)); 
// TODO: out of sync? do we care?
     }
 
-    private String getDetails() {
-        List<String> missing = getMissing();
+    private String getDetails(List<String> missing) {
         StringBuilder missingSt = new StringBuilder();
         RootCausePrinter printer = new RootCausePrinter(st -> 
missingSt.append(st + "\n"));
         for (String iface : missing) {
diff --git 
a/systemready/src/main/java/org/apache/felix/systemready/impl/SystemReadyMonitorImpl.java
 
b/systemready/src/main/java/org/apache/felix/systemready/impl/SystemReadyMonitorImpl.java
index 3471df9..63084a4 100644
--- 
a/systemready/src/main/java/org/apache/felix/systemready/impl/SystemReadyMonitorImpl.java
+++ 
b/systemready/src/main/java/org/apache/felix/systemready/impl/SystemReadyMonitorImpl.java
@@ -43,7 +43,6 @@ import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Deactivate;
 import org.osgi.service.component.annotations.Reference;
 import org.osgi.service.component.annotations.ReferencePolicy;
-import org.osgi.service.component.annotations.ReferencePolicyOption;
 import org.osgi.service.metatype.annotations.AttributeDefinition;
 import org.osgi.service.metatype.annotations.Designate;
 import org.osgi.service.metatype.annotations.ObjectClassDefinition;
@@ -70,16 +69,16 @@ public class SystemReadyMonitorImpl implements 
SystemReadyMonitor {
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
-    @Reference(policyOption = ReferencePolicyOption.GREEDY, policy = 
ReferencePolicy.DYNAMIC)
+    @Reference(policy = ReferencePolicy.DYNAMIC)
     private volatile List<SystemReadyCheck> checks;
 
     private BundleContext context;
 
-    private ServiceRegistration<SystemReady> sreg;
+    private final AtomicReference<ServiceRegistration<SystemReady>> sreg = new 
AtomicReference<>();
 
-    private ScheduledExecutorService executor;
-    
-    private AtomicReference<Collection<CheckStatus>> curStates;
+    private final AtomicReference<ScheduledExecutorService> executor = new 
AtomicReference<>();
+
+    private final AtomicReference<Collection<CheckStatus>> curStates;
 
     public SystemReadyMonitorImpl() {
        CheckStatus checkStatus = new CheckStatus("dummy", StateType.READY, 
State.YELLOW, "");
@@ -89,14 +88,20 @@ public class SystemReadyMonitorImpl implements 
SystemReadyMonitor {
     @Activate
     public void activate(BundleContext context, final Config config) {
         this.context = context;
-        this.executor = Executors.newSingleThreadScheduledExecutor();
-        this.executor.scheduleAtFixedRate(this::check, 0, 
config.poll_interval(), TimeUnit.MILLISECONDS);
+        this.executor.set(Executors.newSingleThreadScheduledExecutor());
+        this.executor.get().scheduleAtFixedRate(this::check, 0, 
config.poll_interval(), TimeUnit.MILLISECONDS);
         log.info("Activated. Running checks every {} ms.", 
config.poll_interval());
     }
 
     @Deactivate
     public void deactivate() {
-        executor.shutdown();
+        final ScheduledExecutorService s = this.executor.getAndSet(null);
+        s.shutdownNow();
+        final ServiceRegistration<SystemReady> reg = this.sreg.getAndSet(null);
+        if ( reg != null ) {
+            reg.unregister();
+        }
+        log.info("Deactivated.");
     }
 
     @Override
@@ -130,25 +135,35 @@ public class SystemReadyMonitorImpl implements 
SystemReadyMonitor {
 
     private List<CheckStatus> evaluateAllChecks(List<SystemReadyCheck> 
currentChecks) {
         return currentChecks.stream()
-                .map(SystemReadyMonitorImpl::getStatus)
+                .map(s -> getStatus(s))
                 .sorted(Comparator.comparing(CheckStatus::getCheckName))
                 .collect(Collectors.toList());
     }
-    
+
     private void manageMarkerService(CheckStatus.State currState) {
-        if (currState == CheckStatus.State.GREEN) {
-            SystemReady readyService = new SystemReady() {
-            };
-            sreg = context.registerService(SystemReady.class, readyService, 
null);
-        } else if (sreg != null) {
-            sreg.unregister();
+        if ( this.executor.get() != null ) {
+            if (currState == CheckStatus.State.GREEN) {
+                SystemReady readyService = new SystemReady() {
+                };
+                sreg.compareAndSet(null, 
context.registerService(SystemReady.class, readyService, null));
+            } else {
+                final ServiceRegistration<SystemReady> reg = 
this.sreg.getAndSet(null);
+                if ( reg != null ) {
+                    reg.unregister();
+                }
+            }
         }
     }
 
-    private static final CheckStatus getStatus(SystemReadyCheck c) {
+    private final CheckStatus getStatus(final SystemReadyCheck c) {
         try {
-            return c.getStatus();
-        } catch (Throwable e) {
+            final CheckStatus status = c.getStatus();
+            if ( status.getState() != State.GREEN ) {
+                log.info("Executing systemready checked {} returned {}", 
c.getName(), status);
+            }
+            return status;
+        } catch (final Throwable e) {
+            log.error("Exception while executing systemready check {} : {}", 
c.getClass().getName(), e.getMessage(), e);
             return new CheckStatus(c.getName(), StateType.READY, 
CheckStatus.State.RED, e.getMessage());
         }
     }
diff --git 
a/systemready/src/test/java/org/apache/felix/systemready/osgi/ComponentsCheckTest.java
 
b/systemready/src/test/java/org/apache/felix/systemready/osgi/ComponentsCheckTest.java
index 12c78e4..123243a 100644
--- 
a/systemready/src/test/java/org/apache/felix/systemready/osgi/ComponentsCheckTest.java
+++ 
b/systemready/src/test/java/org/apache/felix/systemready/osgi/ComponentsCheckTest.java
@@ -23,6 +23,7 @@ import static org.junit.Assert.assertThat;
 import static org.ops4j.pax.tinybundles.core.TinyBundles.bundle;
 
 import java.io.IOException;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import javax.inject.Inject;
 
@@ -40,7 +41,11 @@ import org.ops4j.pax.exam.Configuration;
 import org.ops4j.pax.exam.Option;
 import org.ops4j.pax.exam.junit.PaxExam;
 import org.ops4j.pax.exam.util.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
 import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.component.runtime.ServiceComponentRuntime;
 
 @RunWith(PaxExam.class)
 public class ComponentsCheckTest extends BaseTest {
@@ -48,7 +53,7 @@ public class ComponentsCheckTest extends BaseTest {
     @Inject
     @Filter("(component.name=" + ComponentsCheck.PID + ")")
     SystemReadyCheck check;
-    
+
     @Inject
     ConfigurationAdmin configAdmin;
 
@@ -65,12 +70,23 @@ public class ComponentsCheckTest extends BaseTest {
     }
 
     @Test
-    public void test() throws IOException {
+    public void test() throws IOException, InvalidSyntaxException, 
InterruptedException {
+        AtomicBoolean changed = new AtomicBoolean(false);
+        context.addServiceListener(new ServiceListener() {
+
+            @Override
+            public void serviceChanged(ServiceEvent event) {
+                changed.set(true);
+            }
+        }, "(objectClass=" + ServiceComponentRuntime.class.getName() + ")");
         CheckStatus status = check.getStatus();
         assertThat(status.getState(),  Matchers.is(CheckStatus.State.YELLOW));
         assertThat(status.getDetails(), containsString("unsatisfied 
references"));
         //configAdmin.getConfiguration("CompWithoutService").update();
         context.registerService(Runnable.class, () -> {}, null);
+        while ( changed.get() == false ) {
+            Thread.sleep(10);
+        }
         CheckStatus status2 = check.getStatus();
         System.out.println(status2);
         assertThat(status2.getState(),  Matchers.is(CheckStatus.State.GREEN));
diff --git 
a/systemready/src/test/java/org/apache/felix/systemready/osgi/SystemReadyMonitorTest.java
 
b/systemready/src/test/java/org/apache/felix/systemready/osgi/SystemReadyMonitorTest.java
index 17077e8..0bd6c41 100644
--- 
a/systemready/src/test/java/org/apache/felix/systemready/osgi/SystemReadyMonitorTest.java
+++ 
b/systemready/src/test/java/org/apache/felix/systemready/osgi/SystemReadyMonitorTest.java
@@ -104,7 +104,7 @@ public class SystemReadyMonitorTest extends BaseTest {
         wait.until(this::getState, is(State.GREEN));
 
     }
-    
+
     private State getState() {
        return monitor.getStatus(StateType.READY).getState();
     }
diff --git 
a/systemready/src/test/java/org/apache/felix/systemready/osgi/util/BaseTest.java
 
b/systemready/src/test/java/org/apache/felix/systemready/osgi/util/BaseTest.java
index c8e4ff2..1ed4d97 100644
--- 
a/systemready/src/test/java/org/apache/felix/systemready/osgi/util/BaseTest.java
+++ 
b/systemready/src/test/java/org/apache/felix/systemready/osgi/util/BaseTest.java
@@ -50,7 +50,7 @@ import org.slf4j.LoggerFactory;
 
 public class BaseTest {
     private Logger log = LoggerFactory.getLogger(this.getClass());
-    
+
     @Inject
     public BundleContext context;
 
@@ -63,7 +63,7 @@ public class BaseTest {
             System.setProperty("org.ops4j.pax.url.mvn.localRepository", 
localRepo);
         }
         return CoreOptions.composite(
-                       
+
                 systemProperty("pax.exam.invoker").value("junit"),
                 systemProperty("pax.exam.osgi.unresolved.fail").value("true"),
                 systemProperty("logback.configurationFile")
@@ -71,45 +71,47 @@ public class BaseTest {
                 
mavenBundle().groupId("org.slf4j").artifactId("slf4j-api").version("1.7.6"),
                 
mavenBundle().groupId("ch.qos.logback").artifactId("logback-core").version("1.0.13"),
                 
mavenBundle().groupId("ch.qos.logback").artifactId("logback-classic").version("1.0.13"),
-                
+
                 
bundle("link:classpath:META-INF/links/org.ops4j.pax.tipi.junit.link"),
                 
bundle("link:classpath:META-INF/links/org.ops4j.pax.exam.invoker.junit.link"),
                 
mavenBundle().groupId("org.apache.servicemix.bundles").artifactId("org.apache.servicemix.bundles.hamcrest").version("1.3_1"),
                 
mavenBundle().groupId("org.awaitility").artifactId("awaitility").version("3.1.0"),
 
-                
mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.scr").version("2.0.14"),
-                
mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.configadmin").version("1.8.16"),
-                
mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.rootcause").version("0.1.0-SNAPSHOT"),
+                
mavenBundle().groupId("org.osgi").artifactId("org.osgi.util.function").version("1.1.0"),
+                
mavenBundle().groupId("org.osgi").artifactId("org.osgi.util.promise").version("1.1.1"),
+                
mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.scr").version("2.1.26"),
+                
mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.configadmin").version("1.9.22"),
+                
mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.rootcause").version("0.1.0"),
                 bundle("reference:file:target/classes/")
 
         );
     }
-    
+
     protected static OptionalCompositeOption localRepo() {
         String localRepo = System.getProperty("maven.repo.local", "");
         return when(localRepo.length() > 0)
                
.useOptions(systemProperty("org.ops4j.pax.url.mvn.localRepository").value(localRepo));
     }
-    
+
     public Option servicesCheckConfig(StateType type, String... services) {
         return 
ConfigurationAdminOptions.factoryConfiguration(ServicesCheck.PID)
                 .put("services.list", services)
                 .put("type", type.name())
                 .asOption();
     }
-    
+
     public Option componentsCheckConfig(String... components) {
         return newConfiguration(ComponentsCheck.PID)
                 .put("components.list", components)
                 .asOption();
     }
-    
+
     public Option monitorConfig() {
         return newConfiguration(SystemReadyMonitor.PID)
                 .put("poll.interval", 100)
                 .asOption();
     }
-    
+
     public Option httpService() {
         return CoreOptions.composite(
                 mavenBundle("org.apache.felix", 
"org.apache.felix.http.servlet-api", "1.1.2"),
@@ -122,7 +124,7 @@ public class BaseTest {
                 .put("osgi.http.whiteboard.servlet.pattern", path)
                 .asOption();
     }
-    
+
     public Option aliveServletConfig(String path) {
         return newConfiguration(SystemAliveServlet.PID)
                 .put("osgi.http.whiteboard.servlet.pattern", path)

Reply via email to