FELIX-4352: committed the jan15.path attached in the felix issue (many thanks Jago !).


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1558409 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 95b008f..1b16279 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
@@ -25,6 +25,8 @@
 import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map.Entry;
+import java.util.Properties;
 import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.TreeSet;
@@ -512,10 +514,8 @@
     private Set<ComponentId> getTheRootCouses(List<ComponentDeclaration> downComponents) {
         Set<ComponentId> downComponentsRoot = new TreeSet<ComponentId>();
         for (ComponentDeclaration c : downComponents) {
-            ComponentId root = getRoot(downComponents, c, new ArrayList<ComponentId>());
-            if (root != null) {
-                downComponentsRoot.add(root);
-            }
+            List<ComponentId> root = getRoot(downComponents, c, new ArrayList<ComponentId>());
+            downComponentsRoot.addAll(root);
         }
         return downComponentsRoot;
     }
@@ -622,22 +622,25 @@
         return false;
     }
     
-    private ComponentId getRoot(List<ComponentDeclaration> downComponents, ComponentDeclaration c, List<ComponentId> backTrace) {
+    private List<ComponentId> getRoot(List<ComponentDeclaration> downComponents, ComponentDeclaration c, List<ComponentId> backTrace) {
         ComponentDependencyDeclaration[] componentDependencies = c.getComponentDependencies();
         int downDeps = 0;
+        List<ComponentId> result = new ArrayList<ComponentId>();
         for (ComponentDependencyDeclaration cdd : componentDependencies) {
             if (cdd.getState() == ComponentDependencyDeclaration.STATE_UNAVAILABLE_REQUIRED) {
                 downDeps++;
                 // Detect missing configuration dependency
                 if (CONFIGURATION.equals(cdd.getType())) {
                     String bsn = c.getBundleContext().getBundle().getSymbolicName();
-                    return new ComponentId(cdd.getName(), cdd.getType(), bsn);
+                    result.add(new ComponentId(cdd.getName(), cdd.getType(), bsn));
+                    continue;
                 }
 
                 // Detect if the missing dependency is a root cause failure
                 ComponentDeclaration component = getComponentDeclaration(cdd.getName(), downComponents);
                 if (component == null) {
-                    return new ComponentId(cdd.getName(), cdd.getType(), null);
+                    result.add(new ComponentId(cdd.getName(), cdd.getType(), null));
+                    continue;
                 }
                 // Detect circular dependency
                 ComponentId componentId = new ComponentId(cdd.getName(), cdd.getType(), null);
@@ -648,30 +651,69 @@
                         System.out.print(" -> " + cid.getName() + " ");
                     }
                     System.out.println(" -> " + componentId.getName());
-                    return new ComponentId(c.getName(), SERVICE, c.getBundleContext().getBundle().getSymbolicName());
+                    result.add(new ComponentId(c.getName(), SERVICE, c.getBundleContext().getBundle().getSymbolicName()));
+                    continue;
                 }
                 backTrace.add(componentId);
                 return getRoot(downComponents, component, backTrace);
             }
         }
-        if (downDeps > 0) {
-            return new ComponentId(c.getName(), SERVICE, c.getBundleContext().getBundle().getSymbolicName());
+        if (downDeps > 0 && result.isEmpty()) {
+            result.add(new ComponentId(c.getName(), SERVICE, c.getBundleContext().getBundle().getSymbolicName()));
+        }
+        return result;
+    }
+    
+    private ComponentDeclaration getComponentDeclaration(final String fullName, List<ComponentDeclaration> list) {
+        String simpleName = getSimpleName(fullName);
+        Properties props = parseProperties(fullName);
+        for (ComponentDeclaration c : list) {
+            String serviceNames = c.getName();
+            int cuttOff = serviceNames.indexOf("(");
+            if (cuttOff != -1) {
+                serviceNames = serviceNames.substring(0, cuttOff).trim();
+            }
+            for (String serviceName : serviceNames.split(",")) {
+                if (simpleName.equals(serviceName.trim()) && doPropertiesMatch(props, parseProperties(c.getName()))) {
+                    return c;
+                }
+            }
         }
         return null;
     }
     
-    private ComponentDeclaration getComponentDeclaration(String name, List<ComponentDeclaration> list) {
-        for (ComponentDeclaration c : list) {
-            String serviceName = c.getName();
-            int cuttOff = serviceName.indexOf("(");
-            if (cuttOff != -1) {
-                serviceName = serviceName.substring(0, cuttOff);
-            }
-            if (name.equals(serviceName)) {
-                return c;
+    private boolean doPropertiesMatch(Properties need, Properties provide) {
+        for (Entry<Object, Object> entry : need.entrySet()) {
+            Object prop = provide.get(entry.getKey());
+            if (prop == null || !prop.equals(entry.getValue())) {
+                return false;
             }
         }
-        return null;
+        return true;
+    }
+
+    private String getSimpleName(String name) {
+        int cuttOff = name.indexOf("(");
+        if (cuttOff != -1) {
+            return name.substring(0, cuttOff).trim();
+        }
+        return name.trim();
+    }
+    
+    private Properties parseProperties(String name) {
+        Properties result = new Properties();
+        int cuttOff = name.indexOf("(");
+        if (cuttOff != -1) {
+            String propsText = name.substring(cuttOff + 1, name.indexOf(")"));
+            String[] split = propsText.split(",");
+            for (String prop : split) {
+                String[] kv = prop.split("=");
+                if (kv.length == 2) {
+                    result.put(kv[0], kv[1]);
+                }
+            }
+        }
+        return result;
     }
     
     public static class DependencyManagerSorter implements Comparator<DependencyManager> {
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 c154a4c..3da016d 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
@@ -169,6 +169,62 @@
     }
     
     @Test
+    public void testCanFindRootFailureWithSecondair() {
+        setupEmptyBundles();
+        
+        Component component1 = dm.createComponent()
+            .setImplementation(Object.class)
+            .setInterface(Object.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(Float.class).setRequired(true));
+        dm.add(component2);
+        
+        Component component3 = dm.createComponent()
+            .setImplementation(Object.class)
+            .setInterface(new String[] {Object.class.getName(), Float.class.getName()}, null)
+            .add(dm.createServiceDependency().setService(String.class).setRequired(true));
+        dm.add(component3);
+        
+        dme.wtf();
+        String output = outContent.toString();
+        assertTrue(output.contains("3 missing"));
+        assertTrue(output.contains("java.lang.String"));
+        assertFalse(output.contains("java.lang.Float"));
+        
+        // remove the mess
+        dm.remove(component1);
+        dm.remove(component2);
+        dm.remove(component3);
+    }
+    
+    @Test
+    public void testCanFindRootFailureWithTwoFailures() {
+        setupEmptyBundles();
+        
+        Component component1 = dm.createComponent()
+            .setImplementation(Object.class)
+            .setInterface(Object.class.getName(), null)
+            .add(dm.createServiceDependency().setService(Math.class).setRequired(true))
+            .add(dm.createServiceDependency().setService(Long.class).setRequired(true));
+        dm.add(component1);
+        
+        
+        dme.wtf();
+        String output = outContent.toString();
+        assertTrue(output.contains("1 missing"));
+        assertTrue(output.contains("java.lang.Math"));
+        assertTrue(output.contains("java.lang.Long"));
+        
+        // remove the mess
+        dm.remove(component1);
+    }
+    
+    @Test
     public void testInstalledBundleListing() {
         Bundle bundle1 = mock(Bundle.class);
         when(bundle1.getState()).thenReturn(Bundle.INSTALLED);