You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pd...@apache.org on 2013/12/14 08:40:09 UTC
svn commit: r1550897 - in /felix/trunk/dependencymanager/shell: ./
src/main/java/org/apache/felix/dm/shell/ src/test/ src/test/java/
src/test/java/org/ src/test/java/org/apache/
src/test/java/org/apache/felix/ src/test/java/org/apache/felix/dm/ src/tes...
Author: pderop
Date: Sat Dec 14 07:40:09 2013
New Revision: 1550897
URL: http://svn.apache.org/r1550897
Log:
FELIX-4352: Added the wtf enhancement (thanks Jago !). I think that service properties should be taken into account by the getRoot method.
Added:
felix/trunk/dependencymanager/shell/src/main/java/org/apache/felix/dm/shell/ComponentId.java
felix/trunk/dependencymanager/shell/src/test/
felix/trunk/dependencymanager/shell/src/test/java/
felix/trunk/dependencymanager/shell/src/test/java/org/
felix/trunk/dependencymanager/shell/src/test/java/org/apache/
felix/trunk/dependencymanager/shell/src/test/java/org/apache/felix/
felix/trunk/dependencymanager/shell/src/test/java/org/apache/felix/dm/
felix/trunk/dependencymanager/shell/src/test/java/org/apache/felix/dm/shell/
felix/trunk/dependencymanager/shell/src/test/java/org/apache/felix/dm/shell/DMCommandTest.java
Modified:
felix/trunk/dependencymanager/shell/changelog.txt
felix/trunk/dependencymanager/shell/pom.xml
felix/trunk/dependencymanager/shell/src/main/java/org/apache/felix/dm/shell/DMCommand.java
Modified: felix/trunk/dependencymanager/shell/changelog.txt
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/shell/changelog.txt?rev=1550897&r1=1550896&r2=1550897&view=diff
==============================================================================
--- felix/trunk/dependencymanager/shell/changelog.txt (original)
+++ felix/trunk/dependencymanager/shell/changelog.txt Sat Dec 14 07:40:09 2013
@@ -2,7 +2,7 @@ Latest version
--------------
FELIX-4294 - Dependency Manager Shell improvements
-
+FELIX-4352 - Extend shell command to give better insight in where the problem is
Release 3.0.0
-------------
Modified: felix/trunk/dependencymanager/shell/pom.xml
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/shell/pom.xml?rev=1550897&r1=1550896&r2=1550897&view=diff
==============================================================================
--- felix/trunk/dependencymanager/shell/pom.xml (original)
+++ felix/trunk/dependencymanager/shell/pom.xml Sat Dec 14 07:40:09 2013
@@ -53,6 +53,12 @@
<artifactId>org.apache.felix.gogo.runtime</artifactId>
<version>0.10.0</version>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
<resources>
Added: felix/trunk/dependencymanager/shell/src/main/java/org/apache/felix/dm/shell/ComponentId.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/shell/src/main/java/org/apache/felix/dm/shell/ComponentId.java?rev=1550897&view=auto
==============================================================================
--- felix/trunk/dependencymanager/shell/src/main/java/org/apache/felix/dm/shell/ComponentId.java (added)
+++ felix/trunk/dependencymanager/shell/src/main/java/org/apache/felix/dm/shell/ComponentId.java Sat Dec 14 07:40:09 2013
@@ -0,0 +1,94 @@
+/*
+ * 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.shell;
+
+public class ComponentId implements Comparable<ComponentId> {
+ private final String name;
+ private final String type;
+ private final String bundleName;
+
+ public ComponentId(String name, String type, String bundleName) {
+ super();
+ 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 int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((bundleName == null) ? 0 : bundleName.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((type == null) ? 0 : type.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ComponentId other = (ComponentId) obj;
+ if (bundleName == null) {
+ if (other.bundleName != null)
+ return false;
+ }
+ else if (!bundleName.equals(other.bundleName))
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ }
+ else if (!name.equals(other.name))
+ return false;
+ if (type == null) {
+ if (other.type != null)
+ return false;
+ }
+ else if (!type.equals(other.type))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "ComponentId [name=" + name + ", type=" + type + ", bundleName=" + bundleName + "]";
+ }
+
+ public int compareTo(ComponentId o) {
+ return name.compareTo(o.name);
+ }
+
+}
Modified: felix/trunk/dependencymanager/shell/src/main/java/org/apache/felix/dm/shell/DMCommand.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/shell/src/main/java/org/apache/felix/dm/shell/DMCommand.java?rev=1550897&r1=1550896&r2=1550897&view=diff
==============================================================================
--- felix/trunk/dependencymanager/shell/src/main/java/org/apache/felix/dm/shell/DMCommand.java (original)
+++ felix/trunk/dependencymanager/shell/src/main/java/org/apache/felix/dm/shell/DMCommand.java Sat Dec 14 07:40:09 2013
@@ -25,7 +25,9 @@ import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
import java.util.StringTokenizer;
+import java.util.TreeSet;
import org.apache.felix.dm.Component;
import org.apache.felix.dm.ComponentDeclaration;
@@ -59,6 +61,16 @@ public class DMCommand {
private static final DependencyManagerSorter SORTER = new DependencyManagerSorter();
/**
+ * Constant used by the wtf command, when listing missing services.
+ */
+ private static final String SERVICE = "service";
+
+ /**
+ * Constant used by the wtf command, when listing missing configurations.
+ */
+ private static final String CONFIGURATION = "configuration";
+
+ /**
* Name of a specific gogo shell variable, which may be used to configure "compact" mode.
* Example: g! dependencymanager.compact=true
*/
@@ -124,6 +136,10 @@ public class DMCommand {
@Parameter(names = {"notavail", "na"}, presentValue = "true", absentValue = "false")
boolean notavail,
+ @Descriptor("Detects where are the root failures")
+ @Parameter(names = {"wtf"}, presentValue = "true", absentValue = "false")
+ boolean wtf,
+
@Descriptor("Displays components statistics")
@Parameter(names = {"stats", "st"}, presentValue = "true", absentValue = "false")
boolean stats,
@@ -180,6 +196,11 @@ public class DMCommand {
while (tok.hasMoreTokens()) {
bids.add(tok.nextToken());
}
+
+ if (wtf) {
+ wtf();
+ return;
+ }
// lookup all dependency manager service components
List<DependencyManager> managers = DependencyManager.getDependencyManagers();
@@ -472,6 +493,172 @@ public class DMCommand {
return output.toString();
}
+ protected void wtf() {
+ List<ComponentDeclaration> downComponents = getDependenciesThatAreDown();
+ if (downComponents.isEmpty()) {
+ System.out.println("No missing dependencies found.");
+ }
+ else {
+ System.out.println(downComponents.size() + " missing dependencies found.");
+ System.out.println("-------------------------------------");
+ }
+ listResolvedBundles();
+ listInstalledBundles();
+ Set<ComponentId> downComponentsRoot = getTheRootCouses(downComponents);
+ listAllMissingConfigurations(downComponentsRoot);
+ listAllMissingServices(downComponents, downComponentsRoot);
+ }
+
+ private Set<ComponentId> getTheRootCouses(List<ComponentDeclaration> downComponents) {
+ Set<ComponentId> downComponentsRoot = new TreeSet<ComponentId>();
+ for (ComponentDeclaration c : downComponents) {
+ ComponentId root = getRoot(downComponents, c);
+ if (root != null) {
+ downComponentsRoot.add(root);
+ }
+ }
+ return downComponentsRoot;
+ }
+
+ @SuppressWarnings("unchecked")
+ private List<ComponentDeclaration> getDependenciesThatAreDown() {
+ List<DependencyManager> dependencyManagers = DependencyManager.getDependencyManagers();
+ List<ComponentDeclaration> downComponents = new ArrayList<ComponentDeclaration>();
+ for (DependencyManager dm : dependencyManagers) {
+ List<ComponentDeclaration> components = dm.getComponents();
+ // first create a list of all down components
+ for (ComponentDeclaration c : components) {
+ if (c.getState() == ComponentDeclaration.STATE_UNREGISTERED) {
+ downComponents.add(c);
+ }
+ }
+ }
+ return downComponents;
+ }
+
+ private void listResolvedBundles() {
+ boolean areResolved = false;
+ for (Bundle b : m_context.getBundles()) {
+ if (b.getState() == Bundle.RESOLVED && !isFragment(b)) {
+ areResolved = true;
+ break;
+ }
+ }
+ if (areResolved) {
+ System.out.println("Please note that the following bundles are in the RESOLVED state:");
+ for (Bundle b : m_context.getBundles()) {
+ if (b.getState() == Bundle.RESOLVED && !isFragment(b)) {
+ System.out.println(" * [" + b.getBundleId() + "] " + b.getSymbolicName());
+ }
+ }
+ }
+ }
+
+ private void listInstalledBundles() {
+ boolean areResolved = false;
+ for (Bundle b : m_context.getBundles()) {
+ if (b.getState() == Bundle.INSTALLED) {
+ areResolved = true;
+ break;
+ }
+ }
+ if (areResolved) {
+ System.out.println("Please note that the following bundles are in the INSTALLED state:");
+ for (Bundle b : m_context.getBundles()) {
+ if (b.getState() == Bundle.INSTALLED) {
+ System.out.println(" * [" + b.getBundleId() + "] " + b.getSymbolicName());
+ }
+ }
+ }
+ }
+
+ private boolean isFragment(Bundle b) {
+ @SuppressWarnings("unchecked")
+ Dictionary<String, String> headers = b.getHeaders();
+ return headers.get("Fragment-Host") != null;
+ }
+
+ private void listAllMissingConfigurations(Set<ComponentId> downComponentsRoot) {
+ if (hasMissingType(downComponentsRoot, CONFIGURATION)) {
+ System.out.println("The following configuration(s) are missing: ");
+ for (ComponentId s : downComponentsRoot) {
+ if (CONFIGURATION.equals(s.getType())) {
+ System.out.println(" * " + s.getName() + " for bundle " + s.getBundleName());
+ }
+ }
+ }
+ }
+
+ private void listAllMissingServices(List<ComponentDeclaration> downComponents, Set<ComponentId> downComponentsRoot) {
+ if (hasMissingType(downComponentsRoot, SERVICE)) {
+ System.out.println("The following service(s) are missing: ");
+ for (ComponentId s : downComponentsRoot) {
+ 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 ComponentId getRoot(List<ComponentDeclaration> downComponents, ComponentDeclaration c) {
+ ComponentDependencyDeclaration[] componentDependencies = c.getComponentDependencies();
+ int downDeps = 0;
+ for (ComponentDependencyDeclaration cdd : componentDependencies) {
+ if (cdd.getState() == ComponentDependencyDeclaration.STATE_UNAVAILABLE_REQUIRED) {
+ downDeps++;
+ ComponentDeclaration component = getComponentDeclaration(cdd.getName(), downComponents);
+ if (component == null) {
+ String contextName = null;
+ if (CONFIGURATION.equals(cdd.getType())) {
+ contextName = c.getBundleContext().getBundle().getSymbolicName();
+ }
+ return new ComponentId(cdd.getName(), cdd.getType(), contextName);
+ }
+ return getRoot(downComponents, component);
+ }
+ }
+ if (downDeps > 0) {
+ return new ComponentId(c.getName(), SERVICE, c.getBundleContext().getBundle().getSymbolicName());
+ }
+ 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;
+ }
+ }
+ return null;
+ }
+
public static class DependencyManagerSorter implements Comparator<DependencyManager> {
public int compare(DependencyManager dm1, DependencyManager dm2) {
long id1 = dm1.getBundleContext().getBundle().getBundleId();
Added: felix/trunk/dependencymanager/shell/src/test/java/org/apache/felix/dm/shell/DMCommandTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/shell/src/test/java/org/apache/felix/dm/shell/DMCommandTest.java?rev=1550897&view=auto
==============================================================================
--- felix/trunk/dependencymanager/shell/src/test/java/org/apache/felix/dm/shell/DMCommandTest.java (added)
+++ felix/trunk/dependencymanager/shell/src/test/java/org/apache/felix/dm/shell/DMCommandTest.java Sat Dec 14 07:40:09 2013
@@ -0,0 +1,200 @@
+/*
+ * 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.shell;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.Properties;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+public class DMCommandTest {
+
+ /** Setup a ByteArrayOutputStream to capture the system out printlines */
+ private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
+ private final ByteArrayOutputStream errContent = new ByteArrayOutputStream();
+
+ private DependencyManager dm;
+ @Spy @InjectMocks private DMCommand dme;
+ @Mock private BundleContext m_bundleContext;
+
+ @SuppressWarnings("unchecked")
+ @Before
+ public void setUp() {
+ m_bundleContext = mock(BundleContext.class);
+ System.setOut(new PrintStream(outContent));
+ 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
+ public void testWithoutAnyDependcyManagersShouldNotCrash() {
+ setupEmptyBundles();
+
+ dme.wtf();
+ assertEquals("No missing dependencies found.\n", outContent.toString());
+ }
+
+ @Test
+ public void testASingleComponentShouldNotRegisterAsFailure() {
+ setupEmptyBundles();
+
+ dm.add(dm.createComponent()
+ .setImplementation(Object.class)
+ .setInterface(Object.class.getName(), null)
+ );
+ dme.wtf();
+ assertEquals("No missing dependencies found.\n", outContent.toString());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testComponentThatDependsOnAOtheComponentShouldRegisterAsFailure() {
+ setupEmptyBundles();
+ DependencyManager dm = new DependencyManager(m_bundleContext);
+ DependencyManager.getDependencyManagers().add(dm);
+
+ Component component = dm.createComponent()
+ .setImplementation(Object.class)
+ .setInterface(Object.class.getName(), null)
+ .add(dm.createServiceDependency().setService(Math.class).setRequired(true));
+ dm.add(component);
+
+ dme.wtf();
+ String output = outContent.toString();
+ assertTrue(output.contains("1 missing"));
+ assertTrue(output.contains("java.lang.Math"));
+
+ // remove the mess
+ dm.remove(component);
+ }
+
+ @Test
+ public void testCanFindRootFailure() {
+ 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(String.class).setRequired(true));
+ dm.add(component2);
+
+ dme.wtf();
+ String output = outContent.toString();
+ assertTrue(output.contains("2 missing"));
+ assertTrue(output.contains("java.lang.String"));
+
+ // remove the mess
+ dm.remove(component1);
+ dm.remove(component2);
+ }
+
+ @Test
+ public void testInstalledBundleListing() {
+ Bundle bundle1 = mock(Bundle.class);
+ when(bundle1.getState()).thenReturn(Bundle.INSTALLED);
+ when(bundle1.getSymbolicName()).thenReturn("BadBundle");
+
+ setupBundles(bundle1);
+
+ dme.wtf();
+ String output = outContent.toString();
+ assertTrue(output.contains("following bundles are in the INSTALLED"));
+ assertTrue(output.contains("[0] BadBundle"));
+ // Will print null if it gets bundle 2, that should not happen
+ assertFalse(output.contains("null"));
+ }
+
+ @Test
+ public void testResolvedBundleListing() {
+ Bundle bundle1 = mock(Bundle.class);
+ when(bundle1.getState()).thenReturn(Bundle.RESOLVED);
+ when(bundle1.getSymbolicName()).thenReturn("BadBundle");
+ Properties headers = new Properties();
+ when(bundle1.getHeaders()).thenReturn(headers);
+
+ setupBundles(bundle1);
+
+ dme.wtf();
+ String output = outContent.toString();
+ assertTrue(output.contains("following bundles are in the RESOLVED"));
+ assertTrue(output.contains("[0] BadBundle"));
+ assertFalse(output.contains("null"));
+ }
+
+ @Test
+ public void testResolvedBundleListingButNoFragements() {
+ Bundle bundle1 = mock(Bundle.class);
+ when(bundle1.getState()).thenReturn(Bundle.RESOLVED);
+ when(bundle1.getSymbolicName()).thenReturn("BadBundle");
+ Properties headers = new Properties();
+ headers.put("Fragment-Host", "some value");
+ when(bundle1.getHeaders()).thenReturn(headers);
+ setupBundles(bundle1);
+
+ dme.wtf();
+ String output = outContent.toString();
+ assertFalse(output.contains("following bundles are in the RESOLVED"));
+ // Will print null if it gets bundle 2, that should not happen
+ assertFalse(output.contains("null"));
+ }
+
+ private void setupBundles( Bundle bundle1) {
+ Bundle bundle2 = mock(Bundle.class);
+ when(bundle2.getState()).thenReturn(Bundle.ACTIVE);
+
+ when(m_bundleContext.getBundles()).thenReturn(new Bundle[] { bundle1, bundle2});
+ }
+
+ /** Sets up the bundle context without any bundles */
+ private void setupEmptyBundles() {
+ when(m_bundleContext.getBundles()).thenReturn(new Bundle[] {});
+ }
+
+}