aboutsummaryrefslogtreecommitdiffstats
path: root/container-di
diff options
context:
space:
mode:
authorgjoranv <gv@verizonmedia.com>2019-07-22 11:36:06 +0200
committergjoranv <gv@verizonmedia.com>2019-07-22 13:14:34 +0200
commit220dd38e6c4e0c7e2c651ef77759920f844aaa12 (patch)
tree75ddcc60a3cac42b0d0553e118d15aa603cf4f54 /container-di
parent49f480288a0f84034ac204bd52d92177ce19a9d1 (diff)
Output cycle in the dependency graph.
Diffstat (limited to 'container-di')
-rw-r--r--container-di/src/main/java/com/yahoo/container/di/componentgraph/core/ComponentGraph.java24
-rw-r--r--container-di/src/test/java/com/yahoo/container/di/componentgraph/core/ComponentGraphTest.java1
2 files changed, 23 insertions, 2 deletions
diff --git a/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/ComponentGraph.java b/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/ComponentGraph.java
index 4a0598d3436..cdd886b4672 100644
--- a/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/ComponentGraph.java
+++ b/container-di/src/main/java/com/yahoo/container/di/componentgraph/core/ComponentGraph.java
@@ -13,6 +13,8 @@ import com.yahoo.component.ComponentId;
import com.yahoo.component.provider.ComponentRegistry;
import com.yahoo.config.ConfigInstance;
import com.yahoo.container.di.componentgraph.Provider;
+import com.yahoo.container.di.componentgraph.cycle.CycleFinder;
+import com.yahoo.container.di.componentgraph.cycle.Graph;
import com.yahoo.log.LogLevel;
import com.yahoo.vespa.config.ConfigKey;
import net.jcip.annotations.NotThreadSafe;
@@ -372,13 +374,19 @@ public class ComponentGraph {
/**
* The returned list is the nodes from the graph bottom-up.
*
+ * For each iteration, the algorithm finds the components that are not "wanted by" any other component,
+ * and prepends those components into the resulting 'sorted' list. Hence, the first element in the returned
+ * list is the component that is directly or indirectly wanted by "most" other components.
+ *
* @return A list where a earlier than b in the list implies that there is no path from a to b
*/
private static List<Node> topologicalSort(Collection<Node> nodes) {
Map<ComponentId, Integer> numIncoming = new HashMap<>();
nodes.forEach(
- node -> node.usedComponents().forEach(injectedNode -> numIncoming.merge(injectedNode.componentId(), 1, (a, b) -> a + b)));
+ node -> node.usedComponents().forEach(
+ injectedNode -> numIncoming.merge(injectedNode.componentId(), 1, (a, b) -> a + b)));
+
LinkedList<Node> sorted = new LinkedList<>();
List<Node> unsorted = new ArrayList<>(nodes);
@@ -394,7 +402,7 @@ public class ComponentGraph {
});
if (ready.isEmpty()) {
- throw new IllegalStateException("There is a cycle in the component injection graph.");
+ throw new IllegalStateException("There is a cycle in the component injection graph: " + findCycle(notReady));
}
ready.forEach(node -> node.usedComponents()
@@ -404,4 +412,16 @@ public class ComponentGraph {
}
return sorted;
}
+
+ private static List<String> findCycle(List<Node> nodes) {
+ var cyclicGraph = new Graph<String>();
+ for (var node : nodes) {
+ for (var adjacent : node.usedComponents()) {
+ cyclicGraph.edge(node.componentId().stringValue(),
+ adjacent.componentId().stringValue());
+ }
+ }
+ return new CycleFinder<>(cyclicGraph).findCycle();
+ }
+
}
diff --git a/container-di/src/test/java/com/yahoo/container/di/componentgraph/core/ComponentGraphTest.java b/container-di/src/test/java/com/yahoo/container/di/componentgraph/core/ComponentGraphTest.java
index a5934bcf098..8d323233ef5 100644
--- a/container-di/src/test/java/com/yahoo/container/di/componentgraph/core/ComponentGraphTest.java
+++ b/container-di/src/test/java/com/yahoo/container/di/componentgraph/core/ComponentGraphTest.java
@@ -323,6 +323,7 @@ public class ComponentGraphTest {
fail("Cycle exception expected.");
} catch (Throwable e) {
assertThat(e.getMessage(), containsString("cycle"));
+ assertThat(e.getMessage(), containsString("ComponentCausingCycle"));
}
}