FELIX-4352: Added support of circular dependency in the wtf command (work in progress)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1554191 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/shell/src/main/java/org/apache/felix/dm/shell/DMCommand.java b/dependencymanager/shell/src/main/java/org/apache/felix/dm/shell/DMCommand.java
index 36d3aab..02437e5 100644
--- a/dependencymanager/shell/src/main/java/org/apache/felix/dm/shell/DMCommand.java
+++ b/dependencymanager/shell/src/main/java/org/apache/felix/dm/shell/DMCommand.java
@@ -210,11 +210,11 @@
             long numberOfDependencies = 0;
             long lastBundleId = -1;
             while (iterator.hasNext()) {
-                DependencyManager manager = (DependencyManager) iterator.next();
+                DependencyManager manager = iterator.next();
                 List<Component> complist = manager.getComponents();
                 Iterator<Component> componentIterator = complist.iterator();
                 while (componentIterator.hasNext()) {
-                    Component component = (Component) componentIterator.next();
+                    Component component = componentIterator.next();
                     ComponentDeclaration sc = (ComponentDeclaration) component;
                     String name = sc.getName();
                     // check if this component is enabled or disabled.
@@ -314,7 +314,7 @@
         }
         
         for (int i = 0; i < ids.size(); i ++) {
-            String id = (String) ids.get(i);
+            String id = ids.get(i);
             try {
                 Long longId = Long.valueOf(id);
                 if (longId == bundle.getBundleId()) {
@@ -512,7 +512,7 @@
     private Set<ComponentId> getTheRootCouses(List<ComponentDeclaration> downComponents) {
         Set<ComponentId> downComponentsRoot = new TreeSet<ComponentId>();
         for (ComponentDeclaration c : downComponents) {
-            ComponentId root = getRoot(downComponents, c);
+            ComponentId root = getRoot(downComponents, c, new ArrayList<ComponentId>());
             if (root != null) {
                 downComponentsRoot.add(root);
             }
@@ -622,7 +622,7 @@
         return false;
     }
     
-    private ComponentId getRoot(List<ComponentDeclaration> downComponents, ComponentDeclaration c) {
+    private ComponentId getRoot(List<ComponentDeclaration> downComponents, ComponentDeclaration c, List<ComponentId> backTrace) {
         ComponentDependencyDeclaration[] componentDependencies = c.getComponentDependencies();
         int downDeps = 0;
         for (ComponentDependencyDeclaration cdd : componentDependencies) {
@@ -636,7 +636,19 @@
                     }
                     return new ComponentId(cdd.getName(), cdd.getType(), contextName);
                 }
-                return getRoot(downComponents, component);
+                // Detect circular dependency
+                ComponentId componentId = new ComponentId(cdd.getName(), cdd.getType(), null);
+                if (backTrace.contains(componentId)) {
+                    // We already got this one so its a circular dependency
+                    System.out.print("Circular dependency found:\n *");
+                    for (ComponentId cid : backTrace) {
+                        System.out.print(" -> " + cid.getName() + " ");
+                    }
+                    System.out.println(" -> " + componentId.getName());
+                    return new ComponentId(c.getName(), SERVICE, c.getBundleContext().getBundle().getSymbolicName());
+                }
+                backTrace.add(componentId);
+                return getRoot(downComponents, component, backTrace);
             }
         }
         if (downDeps > 0) {
diff --git a/dependencymanager/shell/src/test/java/org/apache/felix/dm/shell/DMCommandTest.java b/dependencymanager/shell/src/test/java/org/apache/felix/dm/shell/DMCommandTest.java
index 7293b58..c154a4c 100644
--- a/dependencymanager/shell/src/test/java/org/apache/felix/dm/shell/DMCommandTest.java
+++ b/dependencymanager/shell/src/test/java/org/apache/felix/dm/shell/DMCommandTest.java
@@ -28,6 +28,8 @@
 import java.io.PrintStream;
 import java.util.Properties;
 
+import javax.crypto.Cipher;
+
 import org.apache.felix.dm.Component;
 import org.apache.felix.dm.DependencyManager;
 import org.junit.After;
@@ -53,6 +55,8 @@
     @Before
     public void setUp() {
         m_bundleContext = mock(BundleContext.class);
+        Bundle bundle = mock(Bundle.class);
+        when(m_bundleContext.getBundle()).thenReturn(bundle);
         System.setOut(new PrintStream(outContent));
         System.setErr(new PrintStream(errContent));
         dm = new DependencyManager(m_bundleContext);
@@ -109,6 +113,35 @@
         dm.remove(component);
     }
     
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testComponentThatHaveCycliclyDependencyOnAOtheComponentShouldRegisterAsFailure() {
+        setupEmptyBundles();
+        DependencyManager dm = new DependencyManager(m_bundleContext);
+        DependencyManager.getDependencyManagers().add(dm);
+        
+        Component component1 = dm.createComponent()
+            .setImplementation(Cipher.class)
+            .setInterface(Cipher.class.getName(), null)
+            .add(dm.createServiceDependency().setService(Math.class).setRequired(true));
+        dm.add(component1);
+        
+        Component component2 = dm.createComponent()
+            .setImplementation(Math.class)
+            .setInterface(Math.class.getName(), null)
+            .add(dm.createServiceDependency().setService(Cipher.class).setRequired(true));
+        dm.add(component2);
+        
+        dme.wtf();
+        String output = outContent.toString();
+        assertTrue(output.contains("Circular dependency found:"));
+        assertTrue(output.contains("-> java.lang.Math  -> javax.crypto.Cipher  -> java.lang.Math"));
+        
+        // remove the mess
+        dm.remove(component1);
+        dm.remove(component2);
+    }
+    
     @Test
     public void testCanFindRootFailure() {
         setupEmptyBundles();