You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by cz...@apache.org on 2021/08/14 13:46:59 UTC
[felix-dev] branch master updated: FELIX-6445 : Recursive reference
detection throws excepteion
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 286d7da FELIX-6445 : Recursive reference detection throws excepteion
286d7da is described below
commit 286d7dae45e132369fd19e5bb1459dabadd80c13
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Sat Aug 14 15:45:57 2021 +0200
FELIX-6445 : Recursive reference detection throws excepteion
---
rootcause/bnd.bnd | 0
rootcause/pom.xml | 30 +++--
.../java/org/apache/felix/rootcause/DSComp.java | 6 +
.../java/org/apache/felix/rootcause/DSRef.java | 7 ++
.../org/apache/felix/rootcause/DSRootCause.java | 122 ++++++++++++++++-----
.../apache/felix/rootcause/DSRootCauseTest.java | 29 +++--
.../org/apache/felix/rootcause/util/BaseTest.java | 10 +-
7 files changed, 149 insertions(+), 55 deletions(-)
diff --git a/rootcause/bnd.bnd b/rootcause/bnd.bnd
deleted file mode 100644
index e69de29..0000000
diff --git a/rootcause/pom.xml b/rootcause/pom.xml
index cfb7636..96de07e 100644
--- a/rootcause/pom.xml
+++ b/rootcause/pom.xml
@@ -15,7 +15,7 @@
<parent>
<groupId>org.apache.felix</groupId>
<artifactId>felix-parent</artifactId>
- <version>5</version>
+ <version>7</version>
<relativePath />
</parent>
@@ -40,7 +40,7 @@
<plugin>
<groupId>biz.aQute.bnd</groupId>
<artifactId>bnd-maven-plugin</artifactId>
- <version>4.0.0</version>
+ <version>5.3.0</version>
<executions>
<execution>
<goals>
@@ -75,20 +75,32 @@
<dependencies>
<dependency>
<groupId>org.osgi</groupId>
- <artifactId>osgi.cmpn</artifactId>
- <version>6.0.0</version>
+ <artifactId>osgi.core</artifactId>
+ <version>7.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
- <artifactId>org.osgi.core</artifactId>
- <version>6.0.0</version>
+ <artifactId>org.osgi.service.component</artifactId>
+ <version>1.4.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
- <artifactId>osgi.annotation</artifactId>
- <version>7.0.0</version>
+ <artifactId>org.osgi.annotation.versioning</artifactId>
+ <version>1.1.1</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.annotation.bundle</artifactId>
+ <version>1.0.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.service.component.annotations</artifactId>
+ <version>1.4.0</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -150,7 +162,7 @@
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.framework</artifactId>
- <version>5.6.10</version>
+ <version>6.0.5</version>
<scope>test</scope>
</dependency>
diff --git a/rootcause/src/main/java/org/apache/felix/rootcause/DSComp.java b/rootcause/src/main/java/org/apache/felix/rootcause/DSComp.java
index d232fa0..c3253ff 100644
--- a/rootcause/src/main/java/org/apache/felix/rootcause/DSComp.java
+++ b/rootcause/src/main/java/org/apache/felix/rootcause/DSComp.java
@@ -25,8 +25,14 @@ import org.osgi.dto.DTO;
import org.osgi.service.component.runtime.dto.ComponentConfigurationDTO;
import org.osgi.service.component.runtime.dto.ComponentDescriptionDTO;
+/**
+ * DTO for a component
+ */
public class DSComp extends DTO {
+ /** The component description */
public ComponentDescriptionDTO desc;
+ /** The component configuration */
public ComponentConfigurationDTO config;
+ /** List of unsatisfied references. */
public List<DSRef> unsatisfied = new ArrayList<>();
}
diff --git a/rootcause/src/main/java/org/apache/felix/rootcause/DSRef.java b/rootcause/src/main/java/org/apache/felix/rootcause/DSRef.java
index 0989e70..b443d7a 100644
--- a/rootcause/src/main/java/org/apache/felix/rootcause/DSRef.java
+++ b/rootcause/src/main/java/org/apache/felix/rootcause/DSRef.java
@@ -23,9 +23,16 @@ import java.util.List;
import org.osgi.dto.DTO;
+/**
+ * DTO for a reference
+ */
public class DSRef extends DTO {
+ /** Reference name */
public String name;
+ /** Referenced interface */
public String iface;
+ /** Optional filter */
public String filter;
+ /** List of candidates - might be empty. */
public List<DSComp> candidates = new ArrayList<>();
}
diff --git a/rootcause/src/main/java/org/apache/felix/rootcause/DSRootCause.java b/rootcause/src/main/java/org/apache/felix/rootcause/DSRootCause.java
index 5fac3f1..cee6be9 100644
--- a/rootcause/src/main/java/org/apache/felix/rootcause/DSRootCause.java
+++ b/rootcause/src/main/java/org/apache/felix/rootcause/DSRootCause.java
@@ -18,10 +18,13 @@
*/
package org.apache.felix.rootcause;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import java.util.stream.Collectors;
import org.osgi.service.component.runtime.ServiceComponentRuntime;
@@ -30,67 +33,132 @@ import org.osgi.service.component.runtime.dto.ComponentDescriptionDTO;
import org.osgi.service.component.runtime.dto.ReferenceDTO;
import org.osgi.service.component.runtime.dto.UnsatisfiedReferenceDTO;
+/**
+ * Utility class to get the root cause for declarative services.
+ */
public class DSRootCause {
private static final int MAX_RECURSION = 10;
-
- private ServiceComponentRuntime scr;
-
- public DSRootCause(ServiceComponentRuntime scr) {
+
+ private final ServiceComponentRuntime scr;
+
+ /**
+ * Create new instance
+ * @param scr Service component runtime
+ */
+ public DSRootCause(final ServiceComponentRuntime scr) {
this.scr = scr;
}
-
- public Optional<DSComp> getRootCause(String iface) {
- return scr.getComponentDescriptionDTOs().stream()
+
+ /**
+ * Get the root cause based on an interface name
+ * @param iface The interface name
+ * @return Optional root cause
+ */
+ public Optional<DSComp> getRootCause(final String iface) {
+ return this.getRootCause(iface, null);
+ }
+
+ /**
+ * Get the root cause based on an interface name
+ * @param iface The interface name
+ * @param allDTOs A collection with all dtos as a cache to lookup, optional
+ * @return Optional root cause
+ * @since 0.2.0
+ */
+ public Optional<DSComp> getRootCause(final String iface, final Collection<ComponentDescriptionDTO> allDTOs) {
+ final Collection<ComponentDescriptionDTO> cache = allDTOs == null ? scr.getComponentDescriptionDTOs() : allDTOs;
+ return cache.stream()
.filter(desc -> offersInterface(desc, iface))
- .map(this::getRootCause)
+ .map(d -> getRootCause(d, cache))
.findFirst();
}
-
- public DSComp getRootCause(ComponentDescriptionDTO desc) {
- return getRootCause(desc, 0);
+
+ /**
+ * Get the root cause for a component description
+ * @param desc The description
+ * @return The root cause
+ */
+ public DSComp getRootCause(final ComponentDescriptionDTO desc) {
+ return getRootCause(desc, null);
+ }
+
+ /**
+ * Get the root cause for a component description
+ * @param desc The description
+ * @param allDTOs A collection with all dtos as a cache to lookup, optional
+ * @return The root cause
+ * @since 0.2.0
+ */
+ public DSComp getRootCause(final ComponentDescriptionDTO desc, final Collection<ComponentDescriptionDTO> allDTOs) {
+ final Set<String> visitedNames = new HashSet<>();
+ return getRootCause(desc, visitedNames, 0, allDTOs == null ? new ArrayList<>() : allDTOs);
}
- private DSComp getRootCause(ComponentDescriptionDTO desc, int level) {
- if (level > MAX_RECURSION) {
- throw new IllegalStateException("Aborting after because of cyclic references");
+ private DSComp getRootCause(final ComponentDescriptionDTO desc,
+ final Set<String> visitedNames,
+ final int level,
+ final Collection<ComponentDescriptionDTO> cache) {
+ if (level > MAX_RECURSION || visitedNames.contains(desc.name)) {
+ return null;
}
- DSComp dsComp = new DSComp();
+ final boolean added = visitedNames.add(desc.name);
+ final DSComp dsComp = new DSComp();
dsComp.desc = desc;
- Collection<ComponentConfigurationDTO> instances = scr.getComponentConfigurationDTOs(desc);
+
+ final Collection<ComponentConfigurationDTO> instances = scr.getComponentConfigurationDTOs(desc);
if (instances.isEmpty()) {
return dsComp;
}
- for (ComponentConfigurationDTO instance : instances) {
- for (UnsatisfiedReferenceDTO ref : instance.unsatisfiedReferences) {
- ReferenceDTO refdef = getReference(desc, ref.name);
- DSRef unresolvedRef = createRef(ref, refdef);
- unresolvedRef.candidates = getCandidates(ref, refdef, level + 1);
+ for (final ComponentConfigurationDTO instance : instances) {
+ for (final UnsatisfiedReferenceDTO ref : instance.unsatisfiedReferences) {
+ final ReferenceDTO refdef = getReference(desc, ref.name);
+
+ final DSRef unresolvedRef = createRef(ref, refdef);
+ unresolvedRef.candidates = getCandidates(ref, refdef, visitedNames, level + 1, cache);
dsComp.unsatisfied.add(unresolvedRef);
}
}
+ if ( added ) {
+ visitedNames.remove(desc.name);
+ }
return dsComp;
}
private DSRef createRef(UnsatisfiedReferenceDTO unsatifiedRef, ReferenceDTO refdef) {
- DSRef ref = new DSRef();
+ final DSRef ref = new DSRef();
ref.name = unsatifiedRef.name;
ref.filter = unsatifiedRef.target;
ref.iface = refdef.interfaceName;
return ref;
}
- private List<DSComp> getCandidates(UnsatisfiedReferenceDTO ref, ReferenceDTO refdef, int level) {
- return scr.getComponentDescriptionDTOs().stream()
- .filter(desc -> offersInterface(desc, refdef.interfaceName))
- .map(desc -> getRootCause(desc, level)).collect(Collectors.toList());
+ private List<DSComp> getCandidates(final UnsatisfiedReferenceDTO ref,
+ final ReferenceDTO refdef,
+ final Set<String> visitedNames,
+ final int level,
+ final Collection<ComponentDescriptionDTO> cache) {
+ if ( cache.isEmpty() ) {
+ cache.addAll(scr.getComponentDescriptionDTOs());
+ }
+ final List<DSComp> result = new ArrayList<>();
+
+ final List<ComponentDescriptionDTO> candidates = cache.stream()
+ .filter(desc -> offersInterface(desc, refdef.interfaceName)).collect(Collectors.toList());
+ for(final ComponentDescriptionDTO c : candidates) {
+ final DSComp r = getRootCause(c, visitedNames, level, cache);
+ if ( r != null ) {
+ result.add(r);
+ }
+ }
+ return result;
}
private boolean offersInterface(ComponentDescriptionDTO desc, String interfaceName) {
return Arrays.asList(desc.serviceInterfaces).contains(interfaceName);
}
- private ReferenceDTO getReference(ComponentDescriptionDTO desc, String name) {
+ private ReferenceDTO getReference(final ComponentDescriptionDTO desc, final String name) {
return Arrays.asList(desc.references).stream().filter(ref -> ref.name.equals(name)).findFirst().get();
}
diff --git a/rootcause/src/test/java/org/apache/felix/rootcause/DSRootCauseTest.java b/rootcause/src/test/java/org/apache/felix/rootcause/DSRootCauseTest.java
index 4f2b24a..61804e2 100644
--- a/rootcause/src/test/java/org/apache/felix/rootcause/DSRootCauseTest.java
+++ b/rootcause/src/test/java/org/apache/felix/rootcause/DSRootCauseTest.java
@@ -22,14 +22,11 @@ import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
import static org.ops4j.pax.tinybundles.core.TinyBundles.bundle;
import javax.inject.Inject;
-import org.apache.felix.rootcause.DSComp;
-import org.apache.felix.rootcause.DSRef;
-import org.apache.felix.rootcause.DSRootCause;
-import org.apache.felix.rootcause.RootCausePrinter;
import org.apache.felix.rootcause.examples.CompWithCyclicRef;
import org.apache.felix.rootcause.examples.CompWithMissingConfig;
import org.apache.felix.rootcause.examples.CompWithMissingRef;
@@ -54,9 +51,9 @@ public class DSRootCauseTest extends BaseTest {
@Inject
ServiceComponentRuntime scr;
-
+
DSRootCause dsRootCause;
-
+
@Configuration
public Option[] configuration() {
return new Option[] {
@@ -70,13 +67,13 @@ public class DSRootCauseTest extends BaseTest {
)
};
}
-
+
@Before
public void before() {
dsRootCause = new DSRootCause(scr);
}
-
+
@Test
public void testMissingConfig() throws InterruptedException {
ComponentDescriptionDTO desc = getComponentDesc(CompWithMissingConfig.class);
@@ -85,7 +82,7 @@ public class DSRootCauseTest extends BaseTest {
assertEquals("CompWithMissingConfig", rootCause.desc.name);
assertNull(rootCause.config);
}
-
+
@Test
public void testMissingRef() throws InterruptedException {
ComponentDescriptionDTO desc = getComponentDesc(CompWithMissingRef.class);
@@ -98,7 +95,7 @@ public class DSRootCauseTest extends BaseTest {
assertEquals("CompWithMissingConfig", candidate.desc.name);
assertNull(candidate.config);
}
-
+
@Test
public void testMissingRef2() throws InterruptedException {
ComponentDescriptionDTO desc = getComponentDesc(CompWithMissingRef2.class);
@@ -111,19 +108,21 @@ public class DSRootCauseTest extends BaseTest {
assertEquals("CompWithMissingRef", candidate.desc.name);
assertNull(candidate.config);
}
-
+
@Test
public void testNoService() throws InterruptedException {
ComponentDescriptionDTO desc = getComponentDesc(CompWithoutService.class);
DSComp rootCause = dsRootCause.getRootCause(desc);
- assertThat(rootCause.desc.serviceInterfaces.length, equalTo(0));
+ assertThat(rootCause.desc.serviceInterfaces.length, equalTo(0));
new RootCausePrinter().print(rootCause);
}
-
- @Test(expected = IllegalStateException.class)
+
+ @Test
public void testCyclic() throws InterruptedException {
ComponentDescriptionDTO desc = getComponentDesc(CompWithCyclicRef.class);
- dsRootCause.getRootCause(desc);
+ DSComp rootCause = dsRootCause.getRootCause(desc);
+ assertEquals(1, rootCause.unsatisfied.size());
+ assertTrue(rootCause.unsatisfied.get(0).candidates.isEmpty());
}
}
diff --git a/rootcause/src/test/java/org/apache/felix/rootcause/util/BaseTest.java b/rootcause/src/test/java/org/apache/felix/rootcause/util/BaseTest.java
index 17bd4f8..a3dfe8e 100644
--- a/rootcause/src/test/java/org/apache/felix/rootcause/util/BaseTest.java
+++ b/rootcause/src/test/java/org/apache/felix/rootcause/util/BaseTest.java
@@ -51,19 +51,21 @@ 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.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"),
bundle("reference:file:target/classes/")
);
}
-
+
public ComponentDescriptionDTO getComponentDesc(String compName) {
return getComponentDesc(desc -> desc.name.equals(compName), compName);
}