Committed the following contributions provided by Raluca Grigoras (many thanks):

[FELIX-4873] - Enhance DM API to get missing and circular dependencies
[FELIX-4889] - Refactor dm shell command to use the org.apache.dm.diagnostics api



git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1680380 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/DiagnosticsTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/DiagnosticsTest.java
new file mode 100644
index 0000000..5a459d5
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/DiagnosticsTest.java
@@ -0,0 +1,464 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.itest.api;
+
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentDeclaration;
+import org.apache.felix.dm.ConfigurationDependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ServiceDependency;
+import org.apache.felix.dm.diagnostics.CircularDependency;
+import org.apache.felix.dm.diagnostics.DependencyGraph;
+import org.apache.felix.dm.diagnostics.DependencyGraph.ComponentState;
+import org.apache.felix.dm.diagnostics.DependencyGraph.DependencyState;
+import org.apache.felix.dm.diagnostics.MissingDependency;
+import org.apache.felix.dm.itest.util.TestBase;
+
+public class DiagnosticsTest extends TestBase {
+	
+	// there is one TestComponent created by org.apache.felix.dm.itest.bundle
+	// this component is always registered, so we only need to take it into 
+	// account when we build the graph with ALL components
+	private static final int InitialComponentCount = 1;
+	
+	private boolean checkComponentCount(int expected, int count) {
+		return count == expected + InitialComponentCount;
+	}
+	
+	public void testWithoutComponents() throws Exception {
+		
+		DependencyGraph graph = DependencyGraph.getGraph(
+				ComponentState.ALL, 
+				DependencyState.ALL);
+	
+		assertTrue(checkComponentCount(0, graph.getAllComponents().size()));
+		assertTrue(graph.getAllDependencies().isEmpty());
+	}
+	
+	public void testSingleComponent() throws Exception {
+		
+		DependencyManager dm = getDM();
+		
+		Component component = dm.createComponent()
+								.setImplementation(Object.class);
+							
+		dm.add(component);
+		
+		DependencyGraph graph = DependencyGraph.getGraph(
+				ComponentState.ALL, DependencyState.ALL);
+		
+		assertTrue(checkComponentCount(1, graph.getAllComponents().size()));
+		assertTrue(graph.getAllDependencies().isEmpty());
+		
+		graph = DependencyGraph.getGraph(
+				ComponentState.UNREGISTERED, DependencyState.ALL_UNAVAILABLE);
+		
+		assertTrue(graph.getAllComponents().isEmpty());
+		assertTrue(graph.getAllDependencies().isEmpty());
+	}
+
+	public void testServiceDependencyMissing() throws Exception {
+		
+		DependencyManager dm = getDM();
+		
+		ServiceDependency serviceDependency1 = dm.createServiceDependency()
+				.setService(S1.class)
+				.setRequired(true);
+		ServiceDependency serviceDependency2 = dm.createServiceDependency()
+				.setService(S2.class)
+				.setRequired(true);
+		
+		Component component1 = dm.createComponent()
+				.setImplementation(C0.class)
+				.add(serviceDependency1);
+		Component component2 = dm.createComponent()
+				.setImplementation(S1Impl1.class)
+				.setInterface(S1.class.getName(), null)
+				.add(serviceDependency2);
+		
+		dm.add(component1);
+		dm.add(component2);
+		
+		DependencyGraph graph = DependencyGraph.getGraph(
+				ComponentState.UNREGISTERED, DependencyState.REQUIRED_UNAVAILABLE);
+		
+		assertEquals(2, graph.getAllComponents().size());
+		assertEquals(2, graph.getAllDependencies().size());
+		
+		List<MissingDependency> missingDependencies = graph.getMissingDependencies("service");
+		assertEquals(1, missingDependencies.size());
+		assertEquals(S2.class.getName(), missingDependencies.get(0).getName());
+		
+		assertTrue(graph.getMissingDependencies("configuration").isEmpty());
+		assertTrue(graph.getMissingDependencies("bundle").isEmpty());
+		assertTrue(graph.getMissingDependencies("resource").isEmpty());
+		
+	}
+	
+	public void testConfigurationDependencyMissing() throws Exception {
+		
+		DependencyManager dm = getDM();
+		
+		ConfigurationDependency configurationDependency1 = dm.createConfigurationDependency()
+				.setPid("missing.configuration.pid");
+		
+		Component component1 = dm.createComponent()
+				.setImplementation(Object.class)
+				.add(configurationDependency1);
+		m_dm.add(component1);
+		
+		DependencyGraph graph = DependencyGraph.getGraph(ComponentState.UNREGISTERED, DependencyState.REQUIRED_UNAVAILABLE);
+		
+		assertEquals(1, graph.getAllComponents().size());
+		assertEquals(1, graph.getAllDependencies().size());
+		
+		List<MissingDependency> missingServiceDependencies = graph.getMissingDependencies("service");
+		assertTrue(missingServiceDependencies.isEmpty());
+		
+		List<MissingDependency> missingConfigDependencies = graph.getMissingDependencies("configuration");
+		assertEquals(1, missingConfigDependencies.size());
+		
+		MissingDependency missingConfigDependency = missingConfigDependencies.get(0);
+		assertEquals("missing.configuration.pid", missingConfigDependency.getName());
+	}
+
+	public void testProvidersWithoutProperties() throws Exception {
+		DependencyManager dm = getDM();
+		
+		ServiceDependency serviceDependency1 = dm.createServiceDependency()
+				.setService(S1.class)
+				.setRequired(true);
+		
+		Component component1 = dm.createComponent()
+				.setImplementation(C0.class)
+				.add(serviceDependency1);
+		
+		Component component2 = dm.createComponent()
+				.setImplementation(S1Impl1.class)
+				.setInterface(S1.class.getName(), null);
+		Component component3 = dm.createComponent()
+				.setImplementation(S1Impl2.class)
+				.setInterface(S1.class.getName(), null);
+		
+		dm.add(component1);
+		dm.add(component2);
+		dm.add(component3);
+		
+		DependencyGraph graph = DependencyGraph.getGraph(ComponentState.ALL, DependencyState.ALL);
+		
+		List<ComponentDeclaration> providers = graph.getProviders(serviceDependency1);
+		assertEquals(2, providers.size());
+		assertTrue(providers.contains(component2));
+		assertTrue(providers.contains(component3));
+	}
+	
+	public void testProvidersWithProperties() throws Exception {
+		
+		DependencyManager dm = getDM();
+		
+		ServiceDependency serviceDependency1 = dm.createServiceDependency()
+				.setService(S1.class, "(key=value)")
+				.setRequired(true);
+		
+		Component component1 = dm.createComponent()
+				.setImplementation(C0.class)
+				.add(serviceDependency1);
+
+		Properties component2Properties = new Properties();
+		component2Properties.put("key", "value");
+		Properties component4Properties = new Properties();
+		component4Properties.put("key", "otherValue");
+
+		Component component2 = dm.createComponent()
+				.setImplementation(S1Impl1.class)
+				.setInterface(S1.class.getName(), component2Properties);
+		Component component3 = dm.createComponent()
+				.setImplementation(S1Impl2.class)
+				.setInterface(S1.class.getName(), null);
+		Component component4 = dm.createComponent()
+				.setImplementation(S1Impl3.class)
+				.setInterface(S1.class.getName(), component4Properties);
+
+		m_dm.add(component1);
+		m_dm.add(component2);
+		m_dm.add(component3);
+		m_dm.add(component4);
+		
+		DependencyGraph graph = DependencyGraph.getGraph(ComponentState.ALL, DependencyState.ALL);
+		
+		List<ComponentDeclaration> providers = graph.getProviders(serviceDependency1);
+		assertEquals(1, providers.size());
+		assertTrue(providers.contains(component2));
+		assertFalse(providers.contains(component3));
+		assertFalse(providers.contains(component4));
+		
+	}
+
+	public void testCircularDependencies() throws Exception {
+		
+		DependencyManager dm = getDM();
+		
+		Component component0 = dm.createComponent()
+				.setImplementation(C0.class)
+				.add(dm.createServiceDependency()
+						.setService(S1.class)
+						.setRequired(true));
+
+		Component component1 = dm.createComponent()
+				.setImplementation(S1Impl1.class)
+				.setInterface(S1.class.getName(), null)
+				.add(dm.createServiceDependency()
+						.setService(S2.class)
+						.setRequired(true));
+		Component component2 = dm.createComponent()
+				.setImplementation(S2Impl1.class)
+				.setInterface(S2.class.getName(), null)
+				.add(dm.createServiceDependency()
+						.setService(S1.class)
+						.setRequired(true));
+		
+		m_dm.add(component0);
+		m_dm.add(component1);
+		m_dm.add(component2);
+		
+		DependencyGraph graph = DependencyGraph.getGraph(ComponentState.UNREGISTERED,DependencyState.REQUIRED_UNAVAILABLE);
+		List<CircularDependency> circularDependencies = graph.getCircularDependencies();
+		
+		assertEquals(1, circularDependencies.size());
+		
+		List<ComponentDeclaration> circularDependencyComponents = circularDependencies.get(0).getComponents(); 
+		assertTrue(circularDependencyComponents.contains(component1));
+		assertTrue(circularDependencyComponents.contains(component2));
+		assertFalse(circularDependencyComponents.contains(component0));
+		
+	}
+	
+	public void testWithTwoProvidersOneUnavailable() {
+		DependencyManager dm = getDM();
+		
+		Component component0 = dm.createComponent()
+				.setImplementation(C0.class)
+				.add(dm.createServiceDependency()
+						.setService(S1.class)
+						.setRequired(true));
+		Component component1 = dm.createComponent()
+				.setImplementation(S1Impl1.class)
+				.setInterface(S1.class.getName(), null);
+		Component component2 = dm.createComponent()
+				.setImplementation(S1Impl2.class)
+				.setInterface(S1.class.getName(), null)
+				.add(dm.createServiceDependency()
+						.setService(S2.class)
+						.setRequired(true));
+		
+		dm.add(component0);
+		dm.add(component1);
+		dm.add(component2);
+		
+		DependencyGraph graph = DependencyGraph.getGraph(ComponentState.UNREGISTERED, DependencyState.REQUIRED_UNAVAILABLE);
+		
+		assertEquals(1, graph.getAllComponents().size());
+		List<MissingDependency> missingDependencies = graph.getMissingDependencies("service");
+		assertEquals(1, missingDependencies.size());
+	
+		MissingDependency missingDependency = missingDependencies.get(0);
+		assertTrue(missingDependency.getName().equals(S2.class.getName()));
+		
+	}
+	
+	public void testGraphsWithSeveralComponentDependencyCombinations() throws Exception {
+		
+		DependencyManager dm = getDM();
+		
+		Component component0 = dm.createComponent()
+				.setImplementation(C0.class)
+				.add(dm.createServiceDependency()
+						.setService(S1.class)
+						.setRequired(true))
+				.add(dm.createServiceDependency()
+						.setService(S3.class)
+						.setRequired(true))
+				.add(dm.createServiceDependency()
+						.setService(S4.class)
+						.setRequired(true));
+		Component component1 = dm.createComponent()
+				.setImplementation(C1.class)
+				.add(dm.createServiceDependency()
+						.setService(S5.class)
+						.setRequired(true))
+				.add(dm.createServiceDependency()
+						.setService(S6.class)
+						.setRequired(true))
+				.add(dm.createServiceDependency()
+						.setService(S7.class)
+						.setRequired(true));
+		Component s1Impl1 = dm.createComponent()
+				.setImplementation(S1Impl1.class)
+				.setInterface(S1.class.getName(), null)
+				.add(dm.createServiceDependency()
+						.setService(S2.class)
+						.setRequired(true));
+		Component s1Impl2 = dm.createComponent()
+				.setImplementation(S1Impl2.class)
+				.setInterface(S1.class.getName(), null);
+
+		Component s3Impl1 = dm.createComponent()
+				.setImplementation(S3Impl1.class)
+				.setInterface(S3.class.getName(), null)
+				.add(dm.createConfigurationDependency()
+						.setPid("missing.config.pid"));
+		Component s3s4Impl = dm.createComponent()
+				.setImplementation(S3S4Impl.class)
+				.setInterface( new String[] {S3.class.getName(), S4.class.getName()}, null);
+		Component s4Impl1 = dm.createComponent()
+				.setImplementation(S4Impl1.class)
+				.setInterface(S4.class.getName(), null);
+		Component s5Impl1 = dm.createComponent()
+				.setImplementation(S5Impl1.class)
+				.setInterface(S5.class.getName(), null);
+		Component s6s7Impl = dm.createComponent()
+				.setImplementation(S6S7Impl.class)
+				.setInterface( new String[] {S6.class.getName(), S7.class.getName()}, null)
+				.add(dm.createServiceDependency()
+						.setService(S8.class)
+						.setRequired(true));
+		Component s8Impl1 = dm.createComponent()
+				.setImplementation(S8Impl1.class)
+				.setInterface(S8.class.getName(), null)
+				.add(dm.createServiceDependency()
+						.setService(S6.class)
+						.setRequired(true));
+		dm.add(component0);
+		dm.add(component1);
+		dm.add(s1Impl1); dm.add(s1Impl2);
+		dm.add(s3Impl1); dm.add(s3s4Impl);
+		dm.add(s4Impl1); dm.add(s5Impl1);
+		dm.add(s6s7Impl); dm.add(s8Impl1);
+		
+		
+		// graph containing all components and dependencies
+		DependencyGraph graph = DependencyGraph.getGraph(ComponentState.ALL, DependencyState.ALL);
+		
+		List<ComponentDeclaration> allComponents = graph.getAllComponents();
+		assertTrue(checkComponentCount(10, allComponents.size()));
+		
+		List<MissingDependency> missingDependencies = graph.getMissingDependencies("service");
+		assertEquals(1, missingDependencies.size());
+		
+		missingDependencies = graph.getMissingDependencies("configuration");
+		assertEquals(1, missingDependencies.size());
+		
+		List<CircularDependency> circularDependencies = graph.getCircularDependencies();
+		assertEquals(1, circularDependencies.size());
+		CircularDependency circularDependency = circularDependencies.get(0);
+
+		assertEquals(3, circularDependency.getComponents().size());
+		assertTrue(circularDependency.getComponents().contains(s6s7Impl));
+		assertTrue(circularDependency.getComponents().contains(s8Impl1));
+
+		// graph containing unregistered components and unavailable required dependencies
+		graph = null;
+		graph = DependencyGraph.getGraph(ComponentState.UNREGISTERED, DependencyState.REQUIRED_UNAVAILABLE);
+	
+		List<ComponentDeclaration> unregComponents = graph.getAllComponents();
+		assertEquals(5, unregComponents.size());
+		assertTrue(unregComponents.contains(s1Impl1));
+		assertTrue(unregComponents.contains(s3Impl1));
+		assertTrue(unregComponents.contains(component1));
+		assertTrue(unregComponents.contains(s6s7Impl));
+		assertTrue(unregComponents.contains(s8Impl1));
+		assertFalse(unregComponents.contains(component0));
+		
+		circularDependencies = graph.getCircularDependencies();
+		assertEquals(1, circularDependencies.size());
+		circularDependency = circularDependencies.get(0);
+
+		assertEquals(3, circularDependency.getComponents().size());
+		assertTrue(circularDependency.getComponents().contains(s6s7Impl));
+		assertTrue(circularDependency.getComponents().contains(s8Impl1));
+		
+		missingDependencies = graph.getMissingDependencies("service");
+		assertEquals(1, missingDependencies.size());	
+		
+		missingDependencies = graph.getMissingDependencies("configuration");
+		assertEquals(1, missingDependencies.size());
+
+		// call getCircularDependencies again on the same graph
+		circularDependencies = graph.getCircularDependencies();
+		assertEquals(1, circularDependencies.size());
+		circularDependency = circularDependencies.get(0);
+
+		assertEquals(3, circularDependency.getComponents().size());
+		assertTrue(circularDependency.getComponents().contains(s6s7Impl));
+		assertTrue(circularDependency.getComponents().contains(s8Impl1));
+		
+		List<MissingDependency> allMissingDependencies = graph.getMissingDependencies(null);
+		assertEquals(2, allMissingDependencies.size());
+
+	}
+	
+	static interface S1 {}
+	static interface S2 {}
+	static interface S3 {}
+	static interface S4 {}
+	static interface S5 {}
+	static interface S6 {}
+	static interface S7 {}
+	static interface S8 {}
+	static class C0 {
+		public C0() {}
+	}
+	static class C1 {
+		public C1() {}
+	}
+	static class S1Impl1 implements S1 {
+		public S1Impl1(){}
+	}
+	static class S1Impl2 implements S1 {
+		public S1Impl2() {}
+	}
+	static class S1Impl3 implements S1 {
+		public S1Impl3() {}
+	}
+	static class S2Impl1 implements S2 {
+		public S2Impl1() {}
+	}
+	static class S3Impl1 implements S3 {
+		public S3Impl1() {}
+	}
+	static class S3S4Impl implements S3, S4 {
+		public S3S4Impl() {}
+	}
+	static class S4Impl1 implements S4 {
+		public S4Impl1() {}
+	}
+	static class S5Impl1 implements S5 {
+		public S5Impl1() {}
+	}
+	static class S6S7Impl implements S6, S7 {
+		public S6S7Impl() {}
+	}
+	static class S8Impl1 implements S8 {
+		public S8Impl1() {}
+	}
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.shell/bnd.bnd b/dependencymanager/org.apache.felix.dependencymanager.shell/bnd.bnd
index 08e99e2..1a649f0 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.shell/bnd.bnd
+++ b/dependencymanager/org.apache.felix.dependencymanager.shell/bnd.bnd
@@ -24,7 +24,7 @@
 Private-Package: \
 	org.apache.felix.dm.shell
 Bundle-Activator:org.apache.felix.dm.shell.Activator
-Bundle-Version: 4.0.1
+Bundle-Version: 4.0.2
 Include-Resource: META-INF/=resources/LICENSE,\
 	META-INF/=resources/NOTICE,\
 	META-INF/=resources/DEPENDENCIES,\
diff --git a/dependencymanager/org.apache.felix.dependencymanager.shell/resources/changelog.txt b/dependencymanager/org.apache.felix.dependencymanager.shell/resources/changelog.txt
index 2e15e6d..51be153 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.shell/resources/changelog.txt
+++ b/dependencymanager/org.apache.felix.dependencymanager.shell/resources/changelog.txt
@@ -1,3 +1,9 @@
+Release org.apache.felix.dependencymanager.r3:
+---------------------------------------------
+
+** Improvement
+    * [FELIX-4889] - Refactor dm shell command to use the org.apache.dm.diagnostics api
+
 Release org.apache.felix.dependencymanager.r1:
 ---------------------------------------------
 
diff --git a/dependencymanager/org.apache.felix.dependencymanager.shell/src/org/apache/felix/dm/shell/DMCommand.java b/dependencymanager/org.apache.felix.dependencymanager.shell/src/org/apache/felix/dm/shell/DMCommand.java
index e4171cb..440566c 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.shell/src/org/apache/felix/dm/shell/DMCommand.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.shell/src/org/apache/felix/dm/shell/DMCommand.java
@@ -23,19 +23,19 @@
 import java.util.Comparator;
 import java.util.Dictionary;
 import java.util.Hashtable;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Properties;
-import java.util.Set;
 import java.util.StringTokenizer;
-import java.util.TreeSet;
 
 import org.apache.felix.dm.Component;
 import org.apache.felix.dm.ComponentDeclaration;
 import org.apache.felix.dm.ComponentDependencyDeclaration;
 import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.diagnostics.CircularDependency;
+import org.apache.felix.dm.diagnostics.DependencyGraph;
+import org.apache.felix.dm.diagnostics.DependencyGraph.ComponentState;
+import org.apache.felix.dm.diagnostics.DependencyGraph.DependencyState;
+import org.apache.felix.dm.diagnostics.MissingDependency;
 import org.apache.felix.service.command.CommandSession;
 import org.apache.felix.service.command.Descriptor;
 import org.apache.felix.service.command.Parameter;
@@ -57,11 +57,11 @@
      * Bundle context used to create OSGi filters.
      */
     private final BundleContext m_context;
-    
+        
     /**
-     * Sorter used to sort components.
+     * Comparator used to compare component declarations based on their bundle ids
      */
-    private static final DependencyManagerSorter SORTER = new DependencyManagerSorter();
+    private static final ComponentDeclarationComparator COMPONENT_DECLARATION_COMPARATOR = new ComponentDeclarationComparator();
 
     /**
      * Constant used by the wtf command, when listing missing services.
@@ -72,6 +72,16 @@
      * Constant used by the wtf command, when listing missing configurations.
      */
     private static final String CONFIGURATION = "configuration";
+    
+    /**
+     * Constant used by the wtf command, when listing missing resource dependencies
+     */
+    private static final String RESOURCE = "resource";
+    
+    /**
+     * Constant used by the wtf command, when listing missing bundle dependencies
+     */
+    private static final String BUNDLE = "bundle";
 
     /**
      * Name of a specific gogo shell variable, which may be used to configure "compact" mode.
@@ -212,100 +222,96 @@
             wtf();
             return;
         }
-
-        // lookup all dependency manager service components
-        List<DependencyManager> managers = DependencyManager.getDependencyManagers();
-        Collections.sort(managers, SORTER);
-        Iterator<DependencyManager> iterator = managers.iterator();
+        
+        DependencyGraph graph = null;
+        if(notavail) {
+        	graph = DependencyGraph.getGraph(ComponentState.UNREGISTERED, DependencyState.ALL_UNAVAILABLE);
+        } else {
+        	graph = DependencyGraph.getGraph(ComponentState.ALL, DependencyState.ALL);
+        }
+        
+        List<ComponentDeclaration> allComponents = graph.getAllComponents();
+        Collections.sort(allComponents, COMPONENT_DECLARATION_COMPARATOR);
         long numberOfComponents = 0;
         long numberOfDependencies = 0;
         long lastBundleId = -1;
-        while (iterator.hasNext()) {
-            DependencyManager manager = iterator.next();
-            List<Component> complist = manager.getComponents();
-            Iterator<Component> componentIterator = complist.iterator();
-            while (componentIterator.hasNext()) {
-                Component component = componentIterator.next();
-                ComponentDeclaration sc = component.getComponentDeclaration();
-                String name = sc.getName();
-                // check if this component is enabled or disabled.
-                if (!mayDisplay(component, servicesFilter, componentsRegex, cids)) {
-                    continue;
+        
+        for(ComponentDeclaration cd : allComponents) {
+        	Bundle bundle = cd.getBundleContext().getBundle();
+        	if(!matchBundle(bundle, bids)) {
+        		continue;
+        	}
+        	
+        	Component component = (Component)cd;
+        	String name = cd.getName();
+        	if (!mayDisplay(component, servicesFilter, componentsRegex, cids)) {
+                continue;
+            }
+        	
+        	numberOfComponents++;
+    		long bundleId = bundle.getBundleId();
+    		if(lastBundleId != bundleId) {
+    			lastBundleId = bundleId;
+    			if (comp) {
+                    System.out.println("[" + bundleId + "] " + compactName(bundle.getSymbolicName()));
+                } else {
+                    System.out.println("[" + bundleId + "] " + bundle.getSymbolicName());
                 }
-                int state = sc.getState();
-                Bundle bundle = sc.getBundleContext().getBundle();
-                if (matchBundle(bundle, bids)) {
-                    long bundleId = bundle.getBundleId();
-                    if (notavail) {
-                        if (sc.getState() != ComponentDeclaration.STATE_UNREGISTERED) {
-                            continue;
-                        }
+    		}
+    		if (comp) {
+                System.out.print(" [" + cd.getId() + "] " + compactName(name) + " "
+                        + compactState(ComponentDeclaration.STATE_NAMES[cd.getState()]));
+            } else {
+                System.out.println(" [" + cd.getId() + "] " + name + " "
+                        + ComponentDeclaration.STATE_NAMES[cd.getState()]);
+            }
+    		
+    		if(!nodeps) {
+    			List<ComponentDependencyDeclaration> dependencies = graph.getDependecies(cd);
+    			if(!dependencies.isEmpty()) {
+    				numberOfDependencies += dependencies.size();
+    				if (comp) {
+                        System.out.print('(');
                     }
+    				for(int j = 0; j < dependencies.size(); j ++) {
+        				ComponentDependencyDeclaration dep = dependencies.get(j);
+        				
+        				String depName = dep.getName();
+                        String depType = dep.getType();
+                        int depState = dep.getState();
 
-                    numberOfComponents++;
-                    if (lastBundleId != bundleId) {
-                        lastBundleId = bundleId;
                         if (comp) {
-                            System.out.println("[" + bundleId + "] " + compactName(bundle.getSymbolicName()));
+                            if (j > 0) {
+                                System.out.print(' ');
+                            }
+                            System.out.print(compactName(depName) + " " + compactState(depType) + " "
+                                    + compactState(ComponentDependencyDeclaration.STATE_NAMES[depState]));
                         } else {
-                            System.out.println("[" + bundleId + "] " + bundle.getSymbolicName());
+                            System.out.println("    " + depName + " " + depType + " "
+                                    + ComponentDependencyDeclaration.STATE_NAMES[depState]);
                         }
-                    }
-                    if (comp) {
-                        System.out.print(" [" + sc.getId() + "] " + compactName(name) + " "
-                                + compactState(ComponentDeclaration.STATE_NAMES[state]));
-                    } else {
-                        System.out.println(" [" + sc.getId() + "] " + name + " "
-                                + ComponentDeclaration.STATE_NAMES[state]);
-                    }
-                    if (!nodeps) {
-                        ComponentDependencyDeclaration[] dependencies = sc.getComponentDependencies();
-                        if (dependencies != null && dependencies.length > 0) {
-                            numberOfDependencies += dependencies.length;
-                            if (comp) {
-                                System.out.print('(');
-                            }
-                            for (int j = 0; j < dependencies.length; j++) {
-                                ComponentDependencyDeclaration dep = dependencies[j];
-                                if (notavail && !isUnavailable(dep)) {
-                                    continue;
-                                }
-                                String depName = dep.getName();
-                                String depType = dep.getType();
-                                int depState = dep.getState();
 
-                                if (comp) {
-                                    if (j > 0) {
-                                        System.out.print(' ');
-                                    }
-                                    System.out.print(compactName(depName) + " " + compactState(depType) + " "
-                                            + compactState(ComponentDependencyDeclaration.STATE_NAMES[depState]));
-                                } else {
-                                    System.out.println("    " + depName + " " + depType + " "
-                                            + ComponentDependencyDeclaration.STATE_NAMES[depState]);
-                                }
-                            }
-                            if (comp) {
-                                System.out.print(')');
-                            }
-                        }
-                    }
+        			}
                     if (comp) {
-                        System.out.println();
+                        System.out.print(')');
                     }
-                }
+    			}
+    		}
+    		if (comp) {
+                System.out.println();
             }
         }
-
-        if (stats) {
-            System.out.println("Statistics:");
-            System.out.println(" - Dependency managers: " + managers.size());
-            System.out.println(" - Components: " + numberOfComponents);
+        
+        if(stats) {
+        	System.out.println("Statistics:");
+        	System.out.println(" - Dependency managers: " + DependencyManager.getDependencyManagers().size());
+        	System.out.println(" - Components: " + numberOfComponents);
             if (!nodeps) {
                 System.out.println(" - Dependencies: " + numberOfDependencies);
             }
         }
-    }
+
+        }
 
     /**
      * Displays components callbacks (init/start/stop/destroy) elapsed time.
@@ -356,16 +362,6 @@
         }
     }
 
-    private boolean isUnavailable(ComponentDependencyDeclaration dep) {
-        switch (dep.getState()) {
-            case ComponentDependencyDeclaration.STATE_UNAVAILABLE_OPTIONAL:
-            case ComponentDependencyDeclaration.STATE_UNAVAILABLE_REQUIRED:
-                return true;
-            default:
-                return false;
-        }
-    }
-
     private boolean matchBundle(Bundle bundle, List<String> ids) {
         if (ids.size() == 0) {
             return true;
@@ -553,46 +549,68 @@
     }
 
     public void wtf() {
-        List<ComponentDeclaration> downComponents = getComponentsThatAreUnregistered();
-        if (downComponents.isEmpty()) {
-            System.out.println("No missing dependencies found.");
-        }
-        else {
-            String message = downComponents.size() + " missing dependencies found.";
+    	
+    	DependencyGraph graph = DependencyGraph.getGraph(ComponentState.UNREGISTERED, DependencyState.REQUIRED_UNAVAILABLE);
+    	List<ComponentDeclaration> unregisteredComponents = graph.getAllComponents();
+    	
+    	if(unregisteredComponents.isEmpty()) {
+    		System.out.println("No unregistered components found");
+    	} else {
+    		String message = unregisteredComponents.size() + " unregistered components found";
 			System.out.println(message);
             System.out.println("----------------------------------------------------".substring(0, message.length()));
-        }
-        listResolvedBundles();
-        listInstalledBundles();
-        Set<ComponentId> downComponentsRoot = getTheRootCouses(downComponents);
-        listAllMissingConfigurations(downComponentsRoot);
-        listAllMissingServices(downComponents, downComponentsRoot);
+    	}
+    	
+    	listResolvedBundles();
+    	listInstalledBundles();
+    	
+    	List<CircularDependency> circularDependencies = graph.getCircularDependencies();
+    	if(!circularDependencies.isEmpty()) {
+    		System.out.println("Circular dependencies:");
+    		printCircularDependencies(circularDependencies);
+    	}
+    	
+    	List<MissingDependency> missingConfigDependencies = graph.getMissingDependencies(CONFIGURATION);
+    	if(!missingConfigDependencies.isEmpty()) {
+    		System.out.println("The following configuration(s) are missing: ");
+    		printMissingDependencies(missingConfigDependencies);
+    	}
+    	
+    	List<MissingDependency> missingServiceDependencies = graph.getMissingDependencies(SERVICE);
+    	if(!missingServiceDependencies.isEmpty()) {
+    		System.out.println("The following service(s) are missing: ");
+    		printMissingDependencies(missingServiceDependencies);
+    	}
+
+    	
+    	List<MissingDependency> missingResourceDependencies = graph.getMissingDependencies(RESOURCE);
+    	if(!missingResourceDependencies.isEmpty()) {
+    		System.out.println("The following resource(s) are missing: ");
+    		printMissingDependencies(missingResourceDependencies);
+    	}
+    	
+    	List<MissingDependency> missingBundleDependencies = graph.getMissingDependencies(BUNDLE);
+    	if(!missingBundleDependencies.isEmpty()) {
+    		System.out.println("The following bundle(s) are missing: ");
+    		printMissingDependencies(missingBundleDependencies);
+    	}
     }
 
-    private Set<ComponentId> getTheRootCouses(List<ComponentDeclaration> downComponents) {
-        Set<ComponentId> downComponentsRoot = new TreeSet<ComponentId>();
-        for (ComponentDeclaration c : downComponents) {
-            List<ComponentId> root = getRoot(downComponents, c, new ArrayList<ComponentId>());
-            downComponentsRoot.addAll(root);
-        }
-        return downComponentsRoot;
-    }
+	private void printCircularDependencies(List<CircularDependency> circularDependencies) {
+		for(CircularDependency c : circularDependencies) {
+			System.out.print(" *");
+			for(ComponentDeclaration cd : c.getComponents()) {
+				System.out.print(" -> " + cd.getName());
+			}
+			System.out.println();
+		}
+	}
 
-    private List<ComponentDeclaration> getComponentsThatAreUnregistered() {
-        List<DependencyManager> dependencyManagers = DependencyManager.getDependencyManagers();
-        List<ComponentDeclaration> unregisteredComponents = new ArrayList<ComponentDeclaration>();
-        for (DependencyManager dm : dependencyManagers) {
-            List<Component> components = dm.getComponents();
-            // create a list of all components that are unregistered
-            for (Component c : components) {
-                ComponentDeclaration cd = c.getComponentDeclaration();
-                if (cd.getState() == ComponentDeclaration.STATE_UNREGISTERED) {
-                    unregisteredComponents.add(cd);
-                }
-            }
-        }
-        return unregisteredComponents;
-    }
+	private void printMissingDependencies(List<MissingDependency> missingConfigDependencies) {
+		for(MissingDependency m : missingConfigDependencies) {
+			System.out.println(" * " + m.getName() + " for bundle " + m.getBundleName());
+		}
+	}
 
     private void listResolvedBundles() {
         boolean areResolved = false;
@@ -635,150 +653,19 @@
         Dictionary<String, String> headers = b.getHeaders();
         return headers.get("Fragment-Host") != null;
     }
-
-    private void listAllMissingConfigurations(Set<ComponentId> unregisteredComponentsRoot) {
-        if (hasMissingType(unregisteredComponentsRoot, CONFIGURATION)) {
-            System.out.println("The following configuration(s) are missing: ");
-            for (ComponentId s : unregisteredComponentsRoot) {
-                if (CONFIGURATION.equals(s.getType())) {
-                    System.out.println(" * " + s.getName() + " for bundle " + s.getBundleName());
-                }
-            }
-        }
-    }
-
-    private void listAllMissingServices(List<ComponentDeclaration> downComponents, Set<ComponentId> unregisteredComponentsRoot) {
-        if (hasMissingType(unregisteredComponentsRoot, SERVICE)) {
-            System.out.println("The following service(s) are missing: ");
-            for (ComponentId s : unregisteredComponentsRoot) {
-                if (SERVICE.equals(s.getType())) {
-                    System.out.print(" * " + s.getName());
-                    ComponentDeclaration component = getComponentDeclaration(s.getName(), downComponents);
-                    if (component == null) {
-                        System.out.println(" is not found in the service registry");
-                    } else {
-                        ComponentDependencyDeclaration[] componentDependencies = component.getComponentDependencies();
-                        System.out.println(" and needs:");
-                        for (ComponentDependencyDeclaration cdd : componentDependencies) {
-                            if (cdd.getState() == ComponentDependencyDeclaration.STATE_UNAVAILABLE_REQUIRED) {
-                                System.out.println(cdd.getName());
-                            }
-                        }
-                        System.out.println(" to work");
-                    }
-                }
-            }
-        }
-    }
-
-    private boolean hasMissingType(Set<ComponentId> downComponentsRoot, String type) {
-        for (ComponentId s : downComponentsRoot) {
-            if (type.equals(s.getType())) {
-                return true;
-            }
-        }
-        return false;
-    }
     
-    private List<ComponentId> getRoot(List<ComponentDeclaration> downComponents, ComponentDeclaration c, List<ComponentId> backTrace) {
-        ComponentDependencyDeclaration[] componentDependencies = c.getComponentDependencies();
-        int unregisteredDeps = 0;
-        List<ComponentId> result = new ArrayList<ComponentId>();
-        for (ComponentDependencyDeclaration cdd : componentDependencies) {
-            if (cdd.getState() == ComponentDependencyDeclaration.STATE_UNAVAILABLE_REQUIRED) {
-                unregisteredDeps++;
-                // Detect missing configuration dependency
-                if (CONFIGURATION.equals(cdd.getType())) {
-                    String bsn = c.getBundleContext().getBundle().getSymbolicName();
-                    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) {
-                    result.add(new ComponentId(cdd.getName(), cdd.getType(), null));
-                    continue;
-                }
-                // Detect circular dependency
-                ComponentId componentId = new ComponentId(cdd.getName(), cdd.getType(), null);
-                if (backTrace.contains(componentId)) {
-                    // We already got this one so it's a circular dependency
-                    System.out.print("Circular dependency found:\n *");
-                    for (ComponentId cid : backTrace) {
-                        System.out.print(" -> " + cid.getName() + " ");
-                    }
-                    System.out.println(" -> " + componentId.getName());
-                    result.add(new ComponentId(c.getName(), SERVICE, c.getBundleContext().getBundle().getSymbolicName()));
-                    continue;
-                }
-                backTrace.add(componentId);
-                return getRoot(downComponents, component, backTrace);
-            }
-        }
-        if (unregisteredDeps > 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 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 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> {
-        public int compare(DependencyManager dm1, DependencyManager dm2) {
-            long id1 = dm1.getBundleContext().getBundle().getBundleId();
-            long id2 = dm2.getBundleContext().getBundle().getBundleId();
-            return id1 > id2 ? 1 : -1;
-        }
+    public static class ComponentDeclarationComparator implements Comparator<ComponentDeclaration> {
+		@Override
+		public int compare(ComponentDeclaration cd1, ComponentDeclaration cd2) {
+			long id1 = cd1.getBundleContext().getBundle().getBundleId();
+			long id2 = cd2.getBundleContext().getBundle().getBundleId();
+			if(id1 == id2) {
+				// sort by component id
+				long cid1 = cd1.getId();
+				long cid2 = cd2.getId();
+				return cid1 > cid2 ? 1 : -1;
+			} 
+			return id1 > id2 ? 1 : -1;
+		}
     }
 }
diff --git a/dependencymanager/org.apache.felix.dependencymanager.shell/test/test/DMCommandTest.java b/dependencymanager/org.apache.felix.dependencymanager.shell/test/test/DMCommandTest.java
index 18d0f0e..24ea66d 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.shell/test/test/DMCommandTest.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.shell/test/test/DMCommandTest.java
@@ -70,14 +70,12 @@
         System.setErr(new PrintStream(errContent));
         dm = new DependencyManager(m_bundleContext);
         dme = new DMCommand(m_bundleContext);
-        DependencyManager.getDependencyManagers().add(dm);
     }
 
     @After
     public void cleanUp() {
         System.setOut(null);
         System.setErr(null);
-        DependencyManager.getDependencyManagers().remove(dm);
     }
 
     @Test
@@ -86,7 +84,7 @@
         setupEmptyBundles();
         
         dme.wtf();
-        assertEquals("No missing dependencies found.\n", outContent.toString());
+        assertEquals("No unregistered components found\n", outContent.toString());
     }
 
     @Test
@@ -99,7 +97,7 @@
             .setInterface(Object.class.getName(), null)
             );
         dme.wtf();
-        assertEquals("No missing dependencies found.\n", outContent.toString());
+        assertEquals("No unregistered components found\n", outContent.toString());
     }
     
     @Test
@@ -117,7 +115,7 @@
         
         dme.wtf();
         String output = outContent.toString();
-        assertTrue(output.contains("1 missing"));
+        assertTrue(output.contains("1 unregistered"));
         assertTrue(output.contains("java.lang.Math"));
         
         // remove the mess
@@ -145,8 +143,8 @@
         
         dme.wtf();
         String output = outContent.toString();
-        assertTrue(output.contains("Circular dependency found:"));
-        assertTrue(output.contains("-> java.lang.Math  -> javax.crypto.Cipher  -> java.lang.Math"));
+        assertTrue(output.contains("-> java.lang.Math -> javax.crypto.Cipher -> java.lang.Math") ||
+        		output.contains("-> javax.crypto.Cipher -> java.lang.Math -> javax.crypto.Cipher"));
         
         // remove the mess
         dm.remove(component1);
@@ -172,7 +170,7 @@
         
         dme.wtf();
         String output = outContent.toString();
-        assertTrue(output.contains("2 missing"));
+        assertTrue(output.contains("2 unregistered"));
         assertTrue(output.contains("java.lang.String"));
         
         // remove the mess
@@ -205,7 +203,7 @@
         
         dme.wtf();
         String output = outContent.toString();
-        assertTrue(output.contains("3 missing"));
+        assertTrue(output.contains("3 unregistered"));
         assertTrue(output.contains("java.lang.String"));
         assertFalse(output.contains("java.lang.Float"));
         
@@ -230,7 +228,7 @@
         
         dme.wtf();
         String output = outContent.toString();
-        assertTrue(output.contains("1 missing"));
+        assertTrue(output.contains("1 unregistered"));
         assertTrue(output.contains("java.lang.Math"));
         assertTrue(output.contains("java.lang.Long"));
         
diff --git a/dependencymanager/org.apache.felix.dependencymanager/bnd.bnd b/dependencymanager/org.apache.felix.dependencymanager/bnd.bnd
index b42cd3c..c461405 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/bnd.bnd
+++ b/dependencymanager/org.apache.felix.dependencymanager/bnd.bnd
@@ -24,10 +24,11 @@
 	org.apache.felix.dm.impl.index,\
 	org.apache.felix.dm.impl.index.multiproperty,\
 	org.apache.felix.dm.impl.metatype
-Export-Package: \
+Export-Package:  \
 	org.apache.felix.dm,\
 	org.apache.felix.dm.tracker,\
-	org.apache.felix.dm.context
+	org.apache.felix.dm.context,\
+	org.apache.felix.dm.diagnostics
 Include-Resource: META-INF/=resources/LICENSE,\
 	META-INF/=resources/NOTICE,\
 	META-INF/=resources/DEPENDENCIES,\
diff --git a/dependencymanager/org.apache.felix.dependencymanager/resources/changelog.txt b/dependencymanager/org.apache.felix.dependencymanager/resources/changelog.txt
index b96214a..98e76f2 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/resources/changelog.txt
+++ b/dependencymanager/org.apache.felix.dependencymanager/resources/changelog.txt
@@ -10,7 +10,8 @@
     * [FELIX-4878] - Support more signatures for Dependency callbacks
     * [FELIX-4879] - ConfigurationDependency should always "need instance".
     * [FELIX-4880] - Missing callback instance support for some adapters
-
+    * [FELIX-4873] - Enhance DM API to get missing and circular dependencies
+    
 ** Wish
     * [FELIX-4875] - Update DM integration test with latest ConfigAdmin
 
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/diagnostics/CircularDependency.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/diagnostics/CircularDependency.java
new file mode 100644
index 0000000..8d4de08
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/diagnostics/CircularDependency.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.diagnostics;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.felix.dm.ComponentDeclaration;
+
+public class CircularDependency {
+	
+	private List<ComponentDeclaration> m_components = new ArrayList<>();
+	
+	void addComponent(ComponentDeclaration component) {
+		m_components.add(component);
+	}
+	
+	public List<ComponentDeclaration> getComponents() {
+		return Collections.unmodifiableList(m_components);
+	}
+	
+	@Override
+	public String toString() {
+		String result = "";
+		for(ComponentDeclaration c : m_components) {
+			result += " -> " + c.getName();
+		}
+		return result;
+	}
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/diagnostics/ComponentNode.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/diagnostics/ComponentNode.java
new file mode 100644
index 0000000..8d8fcdb
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/diagnostics/ComponentNode.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.diagnostics;
+
+import org.apache.felix.dm.ComponentDeclaration;
+
+class ComponentNode extends DependencyGraphNode {
+	
+	private ComponentDeclaration m_componentDeclaration;
+	
+	public ComponentNode(ComponentDeclaration componentDeclaration) {
+		m_componentDeclaration = componentDeclaration;
+	}
+	
+	public ComponentDeclaration getComponentDeclaration() {
+		return m_componentDeclaration;
+	}
+	
+	@Override
+	public String toString() {
+		return m_componentDeclaration.getName();
+	}
+	
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/diagnostics/DependencyGraph.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/diagnostics/DependencyGraph.java
new file mode 100644
index 0000000..5822242
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/diagnostics/DependencyGraph.java
@@ -0,0 +1,392 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.diagnostics;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Stack;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentDeclaration;
+import org.apache.felix.dm.ComponentDependencyDeclaration;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+
+/**
+ * The dependency graph is a view of all components managed by the dependency manager 
+ * and of their dependencies. Using this API you can get the dependencies of a given component,
+ * the components providing a given service, the circular dependencies that might exist. 
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ *
+ */
+public class DependencyGraph {
+
+	/**
+	 * Use this to specify which components the dependency graph should contain 
+	 */
+	public enum ComponentState {
+		ALL, 
+		UNREGISTERED
+	};
+
+	/**
+	 * Use this to specify which dependencies the graph should contain
+	 */
+	public enum DependencyState {
+		ALL,
+		ALL_UNAVAILABLE,
+		REQUIRED_UNAVAILABLE
+	};
+	
+	private static final String SERVICE = "service";
+
+	private Map<ComponentDeclaration, DependencyGraphNode> m_componentToNode = new HashMap<>();
+	private Map<ComponentDependencyDeclaration, DependencyGraphNode> m_dependencyToNode = new HashMap<>();
+	private List<List<DependencyGraphNode>> m_circularDependencies = new ArrayList<>();
+	private Map<DependencyGraphNode, DependencyGraphNode> m_parent = new HashMap<>();
+	
+	private ComponentState m_componentState = ComponentState.ALL;
+	private DependencyState m_dependencyState = DependencyState.ALL; 
+	
+	private DependencyGraph(ComponentState componentState, DependencyState dependencyState) {
+		
+		m_componentState = componentState;
+		m_dependencyState = dependencyState;
+		
+		buildComponentNodes();
+		buildDependecyNodesAndEdges();
+		
+	}
+	
+	private void buildComponentNodes() {
+		List<DependencyManager> dependencyManagers = DependencyManager.getDependencyManagers();
+		for(DependencyManager dm : dependencyManagers) {
+			List<Component> components = dm.getComponents();
+			for(Component c : components) {
+				ComponentDeclaration cd = c.getComponentDeclaration();
+				if(componentMustBeAddedToGraph(cd)) {
+					m_componentToNode.put(cd, new ComponentNode(cd));
+				}
+			}
+		}
+	}
+	
+	private boolean componentMustBeAddedToGraph(ComponentDeclaration cd) {
+		if(m_componentState == ComponentState.ALL) {
+			return true;
+		} else if(m_componentState == ComponentState.UNREGISTERED) {
+			return cd.getState() == ComponentDeclaration.STATE_UNREGISTERED;
+		}
+		return false;
+	}
+
+	private void buildDependecyNodesAndEdges() {
+		
+		for(DependencyGraphNode node : m_componentToNode.values()) {
+			ComponentNode componentNode = (ComponentNode)node;
+			ComponentDependencyDeclaration[] dependencyDeclarations = componentNode.getComponentDeclaration().getComponentDependencies();
+			
+			for(ComponentDependencyDeclaration cdd : dependencyDeclarations) {
+				if(dependencyMustBeAddedToGraph(cdd)) {
+					DependencyNode dependencyNode = new DependencyNode(cdd);
+					m_dependencyToNode.put(cdd, dependencyNode);
+					
+					// add edges from the component node to newly created dependency node
+					componentNode.addSuccessor(dependencyNode);
+					
+					// add edges from the newly created dependency node to the components 
+					// providing those dependencies (only applicable to service dependencies)
+					List<ComponentNode> providerComponents = getProviderComponents(dependencyNode);
+					for(ComponentNode p : providerComponents) {
+						dependencyNode.addSuccessor(p);
+					}
+				}
+			}
+		}
+	}
+
+	private List<ComponentNode> getProviderComponents(DependencyNode dependencyNode) {
+		List<ComponentNode> result = new ArrayList<>();
+
+		ComponentDependencyDeclaration cdd = dependencyNode.getDependencyDeclaration();
+		if(!SERVICE.equals(cdd.getType())) {
+			return result;
+		}
+		
+		for(DependencyGraphNode n : m_componentToNode.values()) {
+			ComponentNode componentNode = (ComponentNode)n;
+			if(componentProvidesDependency(componentNode, dependencyNode)) {
+				result.add(componentNode);
+			}
+		}
+		
+		return result;
+	}
+
+	private boolean componentProvidesDependency(ComponentNode componentNode, DependencyNode dependencyNode) {
+		ComponentDeclaration cd = componentNode.getComponentDeclaration();
+		
+		String dependencyName = dependencyNode.getDependencyDeclaration().getName();
+		String simpleName = getSimpleName(dependencyName);
+		Properties properties = parseProperties(dependencyName);
+
+        String componentName = cd.getName();
+        int cuttOff = componentName.indexOf("(");
+        if (cuttOff != -1) {
+            componentName = componentName.substring(0, cuttOff).trim();
+        }
+        for (String serviceName : componentName.split(",")) {
+            if (simpleName.equals(serviceName.trim()) && doPropertiesMatch(properties, parseProperties(cd.getName()))) {
+                return true;
+            }
+        }
+		return false;
+	}
+
+	private boolean dependencyMustBeAddedToGraph(ComponentDependencyDeclaration cdd) {
+		if(m_dependencyState == DependencyState.ALL) {
+			return true;
+		} else if(m_dependencyState == DependencyState.ALL_UNAVAILABLE) {
+			return 
+					(cdd.getState() == ComponentDependencyDeclaration.STATE_UNAVAILABLE_REQUIRED) ||
+					(cdd.getState() == ComponentDependencyDeclaration.STATE_UNAVAILABLE_OPTIONAL);
+		} else if(m_dependencyState == DependencyState.REQUIRED_UNAVAILABLE) {
+			return cdd.getState() == ComponentDependencyDeclaration.STATE_UNAVAILABLE_REQUIRED;
+			
+		}
+		return false;
+	}	
+
+	/**
+	 * Build the dependency graph. It will contain all the components managed by the dependency manager, provided
+	 * that those components are in the given state, and all dependencies of their dependencies, provided that the
+	 * dependencies are in the given state.
+	 * 
+	 * <p>This implementation currently only builds a graph of unregistered components and 
+	 * required unavailable dependencies.
+	 * 
+	 * @param componentState Include only the components in this state
+	 * @param dependencyState Include only the dependencies in this state
+	 * @return
+	 */
+	public static DependencyGraph getGraph(ComponentState componentState, DependencyState dependencyState) {
+		return new DependencyGraph(componentState, dependencyState);
+	}
+	
+	/**
+	 * Returns the list of components in the graph
+	 * @return the list of components in the graph
+	 */
+	public List<ComponentDeclaration> getAllComponents() {
+		return new ArrayList<ComponentDeclaration>(m_componentToNode.keySet());
+	}
+	
+	/**
+	 * Returns a list all dependencies in the graph
+	 * @return the list of all dependencies in the graph 
+	 */
+	public List<ComponentDependencyDeclaration> getAllDependencies() {
+		return new ArrayList<ComponentDependencyDeclaration>(m_dependencyToNode.keySet());
+
+	}
+	
+	/**
+	 * For a given component declaration, it returns a list of its dependencies in the state
+	 * specified when the graph was built.
+	 * @param componentDeclaration
+	 * @return the list of dependencies or null if the component declaration is not in the graph
+	 */
+	public List<ComponentDependencyDeclaration> getDependecies(ComponentDeclaration componentDeclaration) {
+		List<ComponentDependencyDeclaration> result = new ArrayList<>();
+		
+		DependencyGraphNode node = m_componentToNode.get(componentDeclaration);
+		if(node == null) {
+			return null;
+		}
+
+		for(DependencyGraphNode s : node.getSuccessors()) {
+			result.add( ((DependencyNode)s).getDependencyDeclaration() );
+		}
+		
+		return result;
+	}
+	
+	/**
+	 * Returns the list of components that provide the given dependency. This only returns the components
+	 * managed by the dependency manager that are in the state specified when the graph was built. The 
+	 * dependency can only be a service dependency.
+	 * 
+	 * @param dependency 
+	 * @return the list of components providing this dependency or null if the dependency declaration is 
+	 * 		   not in the graph
+	 */
+	public List<ComponentDeclaration> getProviders(ComponentDependencyDeclaration dependency) {
+		List<ComponentDeclaration> result = new ArrayList<>();
+		
+		DependencyGraphNode node = m_dependencyToNode.get(dependency);
+		if(node == null) {
+			return null;
+		}
+		
+		for(DependencyGraphNode s : node.getSuccessors()) {
+			result.add(((ComponentNode)s).getComponentDeclaration());
+		}
+		
+		return result;
+	}
+	
+	/**
+	 * Returns the list of circular dependencies in the graph
+	 * @return the list of circular dependencies
+	 */
+	public List<CircularDependency> getCircularDependencies() {
+    	List<CircularDependency> result = new ArrayList<CircularDependency>();
+		
+		for(DependencyGraphNode n : m_componentToNode.values()) {
+			if(n.isUndiscovered()) {
+				depthFirstSearch(n);
+			}
+		}		
+		
+		for(List<DependencyGraphNode> cycle : m_circularDependencies) {
+			CircularDependency circularDependency = new CircularDependency();
+			for(DependencyGraphNode n : cycle) {
+				if(n instanceof ComponentNode) {
+					circularDependency.addComponent(((ComponentNode) n).getComponentDeclaration());
+				}
+			}
+			
+			result.add(circularDependency);
+		}
+		
+		return result;
+	}
+	
+	private void depthFirstSearch(DependencyGraphNode n) {
+
+		n.setState(DependencyGraphNode.DependencyGraphNodeState.DISCOVERED);
+		for(DependencyGraphNode s : n.getSuccessors()) {
+			if(s.isUndiscovered()) {
+				m_parent.put(s, n);
+				depthFirstSearch(s);
+			} else if(s.isDiscovered()) {
+				addCycle(n, s);
+			}
+		}
+		n.setState(DependencyGraphNode.DependencyGraphNodeState.PROCESSED);
+	}
+	
+    private void addCycle(DependencyGraphNode n, DependencyGraphNode s) {
+		List<DependencyGraphNode> cycle = new ArrayList<>();
+		Stack<DependencyGraphNode> stack = new Stack<>();
+		
+		stack.push(s);
+		for(DependencyGraphNode p = n; p != s; p = m_parent.get(p)) {
+			stack.push(p);
+		}
+		stack.push(s);
+		
+		while(!stack.isEmpty()) {
+			cycle.add(stack.pop());
+		}
+		m_circularDependencies.add(cycle);
+	}
+
+	/**
+     * Returns all the missing dependencies of a given type.
+     * @param type The type of the dependencies to be returned. This can be either one of the types 
+     * 			   known by the DependencyManager (service, bundle, configuration, resource), 
+     * 			   a user defined type or null, in which case all missing dependencies must be returned.
+     * 			   
+     * @return The missing dependencies of the given type or all the missing dependencies.
+     */
+	public List<MissingDependency> getMissingDependencies(String type) {
+		
+		List<MissingDependency> result = new ArrayList<>();
+
+		// get all dependency nodes that have no out-going edges
+		List<DependencyNode> missingDependencies = new ArrayList<>();
+		for(DependencyGraphNode node : m_dependencyToNode.values()) {
+			DependencyNode dependencyNode = (DependencyNode)node;
+			if(!dependencyNode.isUnavailable()) {
+				continue;
+			}
+			
+			if( (type != null) && (!dependencyNode.getDependencyDeclaration().getType().equals(type)) ) {
+				continue;
+			}
+			if (dependencyNode.getSuccessors().isEmpty()) {
+				missingDependencies.add(dependencyNode);
+			}
+		}
+		
+		for(DependencyNode node : missingDependencies) {
+			for(DependencyGraphNode p : node.getPredecessors()) {
+				ComponentNode componentNode = (ComponentNode)p;
+				Bundle bundle = componentNode.getComponentDeclaration().getBundleContext().getBundle();
+				MissingDependency missingDependency = new MissingDependency(
+						node.getDependencyDeclaration().getName(), 
+						node.getDependencyDeclaration().getType(), 
+						bundle.getSymbolicName());
+				result.add(missingDependency);
+			}
+		}
+		return result;
+	}
+    
+    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;
+    }
+    
+    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 true;
+    }
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/diagnostics/DependencyGraphNode.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/diagnostics/DependencyGraphNode.java
new file mode 100644
index 0000000..775cd28
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/diagnostics/DependencyGraphNode.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.diagnostics;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+class DependencyGraphNode {
+	
+	public enum DependencyGraphNodeState {
+		UNDISCOVERED,
+		DISCOVERED,
+		PROCESSED
+	};
+	
+	private List<DependencyGraphNode> m_successors = new ArrayList<>();
+	private List<DependencyGraphNode> m_predecessors = new ArrayList<>();
+	private DependencyGraphNodeState m_state = DependencyGraphNodeState.UNDISCOVERED;
+	
+	public void addSuccessor(DependencyGraphNode successor) {
+		m_successors.add(successor);	
+		successor.addPredecessor(this);
+	}
+	
+	private void addPredecessor(DependencyGraphNode predecessor) {
+		m_predecessors.add(predecessor);
+	}
+	
+	public List<DependencyGraphNode> getSuccessors() {
+		return Collections.unmodifiableList(m_successors);
+	}
+	
+	public List<DependencyGraphNode> getPredecessors() {
+		return Collections.unmodifiableList(m_predecessors);
+	}
+	
+	void setState(DependencyGraphNodeState state) {
+		m_state = state;
+	}
+	
+	boolean isDiscovered() {
+		return m_state == DependencyGraphNodeState.DISCOVERED;
+	}
+	
+	boolean isUndiscovered() {
+		return m_state == DependencyGraphNodeState.UNDISCOVERED;
+	}
+	
+	boolean isProcessed() {
+		return m_state == DependencyGraphNodeState.PROCESSED;
+	}
+	
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/diagnostics/DependencyNode.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/diagnostics/DependencyNode.java
new file mode 100644
index 0000000..5eab1d2
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/diagnostics/DependencyNode.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.diagnostics;
+
+import org.apache.felix.dm.ComponentDependencyDeclaration;
+
+class DependencyNode extends DependencyGraphNode {
+	
+	private ComponentDependencyDeclaration m_dependencyDeclaration;
+	
+	public DependencyNode(ComponentDependencyDeclaration dependencyDeclaration) {
+		m_dependencyDeclaration = dependencyDeclaration;
+	}
+	
+	public ComponentDependencyDeclaration getDependencyDeclaration() {
+		return m_dependencyDeclaration;
+	}
+	
+	public boolean isUnavailableRequired() {
+		return m_dependencyDeclaration.getState() == ComponentDependencyDeclaration.STATE_UNAVAILABLE_REQUIRED;
+	}
+	
+	public boolean isUnavailableOptional() {
+		return m_dependencyDeclaration.getState() == ComponentDependencyDeclaration.STATE_UNAVAILABLE_OPTIONAL;
+	}
+	
+	public boolean isUnavailable() {
+		return m_dependencyDeclaration.getState() == ComponentDependencyDeclaration.STATE_UNAVAILABLE_OPTIONAL
+				|| m_dependencyDeclaration.getState() == ComponentDependencyDeclaration.STATE_UNAVAILABLE_REQUIRED;
+	}
+	
+	@Override
+	public String toString() {
+		return m_dependencyDeclaration.getName();
+	}
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/diagnostics/MissingDependency.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/diagnostics/MissingDependency.java
new file mode 100644
index 0000000..3d872e4
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/diagnostics/MissingDependency.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.diagnostics;
+
+/**
+ * This represents a missing dependency. It can have any of the four types known to the Dependency Manager (service,
+ * configuration, bundle and resource) or it can be a type defined by the programmer.
+ * A missing dependency is defined by its name, its type and the bundle name of the bundle for which
+ * this dependency is unavailable.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ *
+ */
+public class MissingDependency {
+	
+	private final String name;
+	private final String type;
+	private final String bundleName;
+	
+	public MissingDependency(String name, String type, String bundleName) {
+		this.name = name;
+		this.type = type;
+		this.bundleName = bundleName;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public String getType() {
+		return type;
+	}
+
+	public String getBundleName() {
+		return bundleName;
+	}
+	
+	@Override
+	public String toString() {
+		return "Missing dependency: " 
+				+ "name = " + name + " "
+				+ "type = " + type + " "
+				+ "bundleName = " + bundleName;
+	}
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/diagnostics/packageinfo b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/diagnostics/packageinfo
new file mode 100644
index 0000000..e252556
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/diagnostics/packageinfo
@@ -0,0 +1 @@
+version 1.0.0
\ No newline at end of file