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)