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 2014/03/29 23:10:12 UTC
svn commit: r1583068 - in
/felix/sandbox/pderop/dependencymanager-prototype/dm.shell: ./ .settings/
bin/ bin_test/ generated/ src/ src/dm/ src/dm/shell/ test/ test/test/
Author: pderop
Date: Sat Mar 29 22:10:11 2014
New Revision: 1583068
URL: http://svn.apache.org/r1583068
Log:
added proto for the shell
Added:
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/.classpath (with props)
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/.gitignore
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/.project (with props)
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/.settings/
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/.settings/org.eclipse.jdt.core.prefs
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/bin/
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/bin_test/
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/bnd.bnd
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/build.xml (with props)
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/generated/
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/src/
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/src/.gitignore
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/src/dm/
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/src/dm/shell/
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/src/dm/shell/Activator.java
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/src/dm/shell/ComponentId.java
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/src/dm/shell/DMCommand.java
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/test/
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/test/.gitignore
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/test/test/
felix/sandbox/pderop/dependencymanager-prototype/dm.shell/test/test/DMCommandTest.java
Added: felix/sandbox/pderop/dependencymanager-prototype/dm.shell/.classpath
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.shell/.classpath?rev=1583068&view=auto
==============================================================================
Binary file - no diff available.
Propchange: felix/sandbox/pderop/dependencymanager-prototype/dm.shell/.classpath
------------------------------------------------------------------------------
svn:mime-type = application/xml
Added: felix/sandbox/pderop/dependencymanager-prototype/dm.shell/.gitignore
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.shell/.gitignore?rev=1583068&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.shell/.gitignore (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.shell/.gitignore Sat Mar 29 22:10:11 2014
@@ -0,0 +1,3 @@
+/generated/
+/bin/
+/bin_test/
Added: felix/sandbox/pderop/dependencymanager-prototype/dm.shell/.project
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.shell/.project?rev=1583068&view=auto
==============================================================================
Binary file - no diff available.
Propchange: felix/sandbox/pderop/dependencymanager-prototype/dm.shell/.project
------------------------------------------------------------------------------
svn:mime-type = application/xml
Added: felix/sandbox/pderop/dependencymanager-prototype/dm.shell/.settings/org.eclipse.jdt.core.prefs
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.shell/.settings/org.eclipse.jdt.core.prefs?rev=1583068&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.shell/.settings/org.eclipse.jdt.core.prefs (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.shell/.settings/org.eclipse.jdt.core.prefs Sat Mar 29 22:10:11 2014
@@ -0,0 +1,11 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.7
Added: felix/sandbox/pderop/dependencymanager-prototype/dm.shell/bnd.bnd
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.shell/bnd.bnd?rev=1583068&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.shell/bnd.bnd (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.shell/bnd.bnd Sat Mar 29 22:10:11 2014
@@ -0,0 +1,9 @@
+-buildpath: \
+ osgi.core;version=4.2,\
+ dm;version=latest,\
+ org.apache.felix.gogo.runtime;version=0.10,\
+ org.mockito.mockito-all,\
+ osgi.cmpn
+Private-Package: \
+ dm.shell
+Bundle-Activator:dm.shell.Activator
\ No newline at end of file
Added: felix/sandbox/pderop/dependencymanager-prototype/dm.shell/build.xml
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.shell/build.xml?rev=1583068&view=auto
==============================================================================
Binary file - no diff available.
Propchange: felix/sandbox/pderop/dependencymanager-prototype/dm.shell/build.xml
------------------------------------------------------------------------------
svn:mime-type = application/xml
Added: felix/sandbox/pderop/dependencymanager-prototype/dm.shell/src/.gitignore
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.shell/src/.gitignore?rev=1583068&view=auto
==============================================================================
(empty)
Added: felix/sandbox/pderop/dependencymanager-prototype/dm.shell/src/dm/shell/Activator.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.shell/src/dm/shell/Activator.java?rev=1583068&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.shell/src/dm/shell/Activator.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.shell/src/dm/shell/Activator.java Sat Mar 29 22:10:11 2014
@@ -0,0 +1,43 @@
+/*
+ * 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 dm.shell;
+
+import java.util.Hashtable;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Bundle activator for the dependency manager shell command.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Activator implements BundleActivator {
+ public void start(BundleContext context) throws Exception {
+ // Provide DependencyManager shell commands for the Gogo Shell.
+ Hashtable props = new Hashtable();
+ props.put(org.apache.felix.service.command.CommandProcessor.COMMAND_SCOPE, "dependencymanager");
+ props.put(org.apache.felix.service.command.CommandProcessor.COMMAND_FUNCTION,
+ new String[] { "dm" });
+ context.registerService(DMCommand.class.getName(), new DMCommand(context), props);
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ }
+}
Added: felix/sandbox/pderop/dependencymanager-prototype/dm.shell/src/dm/shell/ComponentId.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.shell/src/dm/shell/ComponentId.java?rev=1583068&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.shell/src/dm/shell/ComponentId.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.shell/src/dm/shell/ComponentId.java Sat Mar 29 22:10:11 2014
@@ -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 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);
+ }
+
+}
Added: felix/sandbox/pderop/dependencymanager-prototype/dm.shell/src/dm/shell/DMCommand.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.shell/src/dm/shell/DMCommand.java?rev=1583068&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.shell/src/dm/shell/DMCommand.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.shell/src/dm/shell/DMCommand.java Sat Mar 29 22:10:11 2014
@@ -0,0 +1,727 @@
+/*
+ * 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 dm.shell;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+import org.apache.felix.service.command.CommandSession;
+import org.apache.felix.service.command.Descriptor;
+import org.apache.felix.service.command.Parameter;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+
+import dm.Component;
+import dm.ComponentDeclaration;
+import dm.ComponentDependencyDeclaration;
+import dm.DependencyManager;
+
+/**
+ * Shell command for showing all services and dependencies that are managed
+ * by the dependency manager.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@Descriptor("Commands used to dump all existing Dependency Manager components")
+public class DMCommand {
+ /**
+ * Bundle context used to create OSGI filters.
+ */
+ private final BundleContext m_context;
+
+ /**
+ * Sorter used to sort components.
+ */
+ 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
+ */
+ private final static String ENV_COMPACT = "dependencymanager.compact";
+
+ /**
+ * Name of a specific gogo shell variable, which may be used to configure an OSGI filter, normally
+ * passed to the "dm services" option. It is used to display only some service providing components
+ * matching the given filter. The filter can contain an "objectClass" option.
+ * Example:
+ * g! dependencymanager.services="(protocol=http)"
+ * g! dependencymanager.services="(&(objectClass=foo.Bar)(protocol=http))"
+ */
+ private final static String ENV_SERVICES = "dependencymanager.services";
+
+ /**
+ * Name of a specific gogo shell variable, which may be used to configure a filter on the
+ * component implementation class name.
+ * The value of this shell variable may contain multiple regex (space separated), and each regex can
+ * be negated using "!".
+ * Example: g! dependencymanager.components="foo.bar.* ga.bu.zo.*"
+ */
+ private final static String ENV_COMPONENTS = "dependencymanager.components";
+
+ /**
+ * Constructor.
+ */
+ public DMCommand(BundleContext context) {
+ m_context = context;
+ }
+
+ /**
+ * Dependency Manager "dm" command. We use gogo annotations, in order to automate documentation,
+ * and also to automatically manage optional flags/options and parameters ordering.
+ *
+ * @param session the gogo command session, used to get some variables declared in the shell
+ * This parameter is automatically passed by the gogo runtime.
+ * @param nodeps false means that dependencies are not displayed
+ * @param compact true means informations are displayed in a compact format. This parameter can also be
+ * set using the "dependencymanager.compact" gogo shell variable.
+ * @param notavail only unregistered components / unavailable dependencies are displayed
+ * @param stats true means some statistics are displayed
+ * @param services an osgi filter used to filter on some given osgi service properties. This parameter can also be
+ * set using the "dependencymanager.services" gogo shell variable.
+ * @param components a regular expression to match either component implementation class names. This parameter can also be
+ * set using the "dependencymanager.components" gogo shell variable.
+ * @param componentIds only components matching one of the specified components ids are displayed
+ * @param bundleIds a list of bundle ids or symbolic names, used to filter on some given bundles
+ */
+ @Descriptor("List dependency manager components")
+ public void dm(
+ CommandSession session,
+
+ @Descriptor("Hides component dependencies")
+ @Parameter(names = {"nodeps", "nd"}, presentValue = "true", absentValue = "false")
+ boolean nodeps,
+
+ @Descriptor("Displays components using a compact form")
+ @Parameter(names = {"compact", "cp"}, presentValue = "true", absentValue = "")
+ String compact,
+
+ @Descriptor("Only displays unavailable components")
+ @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,
+
+ @Descriptor("OSGi filter used to filter some service properties")
+ @Parameter(names = {"services", "s"}, absentValue = "")
+ String services,
+
+ @Descriptor("Regex(s) used to filter on component implementation class names (comma separated, can be negated using \"!\" prefix)")
+ @Parameter(names = {"components", "c"}, absentValue = "")
+ String components,
+
+ @Descriptor("Component identifiers to display (list of longs, comma separated)")
+ @Parameter(names = {"componentIds", "cid", "ci"}, absentValue = "")
+ String componentIds,
+
+ @Descriptor("List of bundle ids or bundle symbolic names to display (comma separated)")
+ @Parameter(names = {"bundleIds", "bid", "bi"}, absentValue = "")
+ String bundleIds) {
+
+ try {
+ boolean comp = Boolean.parseBoolean(getParam(session, ENV_COMPACT, compact));
+ services = getParam(session, ENV_SERVICES, services);
+ String[] componentsRegex = getParams(session, ENV_COMPONENTS, components);
+
+ ArrayList<String> bids = new ArrayList<String>(); // list of bundle ids or bundle symbolic names
+ ArrayList<Long> cids = new ArrayList<Long>(); // list of component ids
+
+ // Parse and check componentIds option
+ StringTokenizer tok = new StringTokenizer(componentIds, ", ");
+ while (tok.hasMoreTokens()) {
+ try {
+ cids.add(Long.parseLong(tok.nextToken()));
+ } catch (NumberFormatException e) {
+ System.out.println("Invalid value for componentIds option");
+ return;
+ }
+ }
+
+ // Parse services fitler
+ Filter servicesFilter = null;
+ try {
+ if (services != null) {
+ servicesFilter = m_context.createFilter(services);
+ }
+ } catch (InvalidSyntaxException e) {
+ System.out.println("Invalid services OSGi filter: " + services);
+ e.printStackTrace(System.err);
+ return;
+ }
+
+ // Parse and check bundleIds option
+ tok = new StringTokenizer(bundleIds, ", ");
+ while (tok.hasMoreTokens()) {
+ bids.add(tok.nextToken());
+ }
+
+ if (wtf) {
+ wtf();
+ return;
+ }
+
+ // lookup all dependency manager service components
+ List<DependencyManager> managers = DependencyManager.getDependencyManagers();
+ Collections.sort(managers, SORTER);
+ Iterator<DependencyManager> iterator = managers.iterator();
+ 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;
+ }
+ 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;
+ }
+ }
+
+ numberOfComponents++;
+ if (lastBundleId != bundleId) {
+ lastBundleId = bundleId;
+ if (comp) {
+ System.out.println("[" + bundleId + "] " + compactName(bundle.getSymbolicName()));
+ } else {
+ System.out.println("[" + bundleId + "] " + bundle.getSymbolicName());
+ }
+ }
+ 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();
+ }
+ }
+ }
+ }
+
+ if (stats) {
+ System.out.println("Statistics:");
+ System.out.println(" - Dependency managers: " + managers.size());
+ System.out.println(" - Components: " + numberOfComponents);
+ if (!nodeps) {
+ System.out.println(" - Dependencies: " + numberOfDependencies);
+ }
+ }
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+
+ private boolean isUnavailable(ComponentDependencyDeclaration dep) {
+ switch (dep.getState()) {
+ case ComponentDependencyDeclaration.STATE_UNAVAILABLE_OPTIONAL:
+ case ComponentDependencyDeclaration.STATE_UNAVAILABLE_REQUIRED:
+ return true;
+ }
+ return false;
+ }
+
+ private boolean matchBundle(Bundle bundle, List<String> ids) {
+ if (ids.size() == 0) {
+ return true;
+ }
+
+ for (int i = 0; i < ids.size(); i ++) {
+ String id = ids.get(i);
+ try {
+ Long longId = Long.valueOf(id);
+ if (longId == bundle.getBundleId()) {
+ return true;
+ }
+ } catch (NumberFormatException e) {
+ // must match symbolic name
+ if (id.equals(bundle.getSymbolicName())) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the value of a command arg parameter, or from the gogo shell if the parameter is not passed to
+ * the command.
+ */
+ private String getParam(CommandSession session, String param, String value) {
+ if (value != null && value.length() > 0) {
+ return value;
+ }
+ Object shellParamValue = session.get(param);
+ return shellParamValue != null ? shellParamValue.toString() : null;
+ }
+
+ /**
+ * Returns the value of a command arg parameter, or from the gogo shell if the parameter is not passed to
+ * the command. The parameter value is meant to be a list of values separated by a blank or a comma.
+ * The values are split and returned as an array.
+ */
+ private String[] getParams(CommandSession session, String name, String value) {
+ String values = null;
+ if (value == null || value.length() == 0) {
+ value = (String) session.get(name);
+ if (value != null) {
+ values = value;
+ }
+ } else {
+ values = value;
+ }
+ if (values == null) {
+ return new String[0];
+ }
+ return values.trim().split(", ");
+ }
+
+ /**
+ * Checks if a component can be displayed. We make a logical OR between the three following conditions:
+ *
+ * - the component service properties are matching a given service filter ("services" option)
+ * - the component implementation class name is matching some regex ("components" option)
+ * - the component declaration name is matching some regex ("names" option)
+ *
+ * If some component ids are provided, then the component must also match one of them.
+ */
+ private boolean mayDisplay(Component component, Filter servicesFilter, String[] components, List<Long> componentIds) {
+ // Check component id
+ if (componentIds.size() > 0) {
+ long componentId = ((ComponentDeclaration) component).getId();
+ if (componentIds.indexOf(componentId) == -1) {
+ return false;
+ }
+ }
+
+ if (servicesFilter == null && components.length == 0) {
+ return true;
+ }
+
+ // Check component service properties
+ boolean servicesMatches = servicesMatches(component, servicesFilter);
+
+ // Check components regexs, which may match component implementation class name
+ boolean componentsMatches = componentMatches(((ComponentDeclaration) component).getClassName(), components);
+
+ // Logical OR between service properties match and component service/impl match.
+ return servicesMatches || componentsMatches;
+ }
+
+ /**
+ * Checks if a given filter is matching some service properties possibly provided by a component
+ */
+ private boolean servicesMatches(Component component, Filter servicesFilter) {
+ boolean match = false;
+ if (servicesFilter != null) {
+ String[] services = ((ComponentDeclaration) component).getServices();
+ if (services != null) {
+ Dictionary<String, Object> properties = component.getServiceProperties();
+ if (properties == null) {
+ properties = new Hashtable<String, Object>();
+ }
+ if (properties.get(Constants.OBJECTCLASS) == null) {
+ properties.put(Constants.OBJECTCLASS, services);
+ }
+ match = servicesFilter.match(properties);
+ }
+ }
+ return match;
+ }
+
+ /**
+ * Checks if the component implementation class name (or some possible provided services) are matching
+ * some regular expressions.
+ */
+ private boolean componentMatches(String description, String[] names) {
+ for (int i = 0; i < names.length; i ++) {
+ String name = names[i];
+ boolean not = false;
+ if (name.startsWith("!")) {
+ name = name.substring(1);
+ not = true;
+ }
+ boolean match = false;
+
+ if (description.matches(name)) {
+ match = true;
+ }
+
+ if (not) {
+ match = !match;
+ }
+
+ if (match) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Compact names that look like state strings. State strings consist of
+ * one or more words. Each word will be shortened to the first letter,
+ * all letters concatenated and uppercased.
+ */
+ private String compactState(String input) {
+ StringBuffer output = new StringBuffer();
+ StringTokenizer st = new StringTokenizer(input);
+ while (st.hasMoreTokens()) {
+ output.append(st.nextToken().toUpperCase().charAt(0));
+ }
+ return output.toString();
+ }
+
+ /**
+ * Compacts names that look like fully qualified class names. All packages
+ * will be shortened to the first letter, except for the last one. So
+ * something like "org.apache.felix.MyClass" will become "o.a.f.MyClass".
+ */
+ private String compactName(String input) {
+ StringBuffer output = new StringBuffer();
+ int lastIndex = 0;
+ for (int i = 0; i < input.length(); i++) {
+ char c = input.charAt(i);
+ switch (c) {
+ case '.' :
+ output.append(input.charAt(lastIndex));
+ output.append('.');
+ lastIndex = i + 1;
+ break;
+ case ' ' :
+ case ',' :
+ if (lastIndex < i) {
+ output.append(input.substring(lastIndex, i));
+ }
+ output.append(c);
+ lastIndex = i + 1;
+ break;
+ }
+ }
+ if (lastIndex < input.length()) {
+ output.append(input.substring(lastIndex));
+ }
+ return output.toString();
+ }
+
+ public 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) {
+ List<ComponentId> root = getRoot(downComponents, c, new ArrayList<ComponentId>());
+ downComponentsRoot.addAll(root);
+ }
+ return downComponentsRoot;
+ }
+
+ private List<ComponentDeclaration> getDependenciesThatAreDown() {
+ List<DependencyManager> dependencyManagers = DependencyManager.getDependencyManagers();
+ List<ComponentDeclaration> downComponents = new ArrayList<ComponentDeclaration>();
+ for (DependencyManager dm : dependencyManagers) {
+ List<Component> components = dm.getComponents();
+ // first create a list of all down components
+ for (Component c : components) {
+ ComponentDeclaration cd = c.getComponentDeclaration();
+ if (cd.getState() == ComponentDeclaration.STATE_UNREGISTERED) {
+ downComponents.add(cd);
+ }
+ }
+ }
+ 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 List<ComponentId> getRoot(List<ComponentDeclaration> downComponents, ComponentDeclaration c, List<ComponentId> backTrace) {
+ ComponentDependencyDeclaration[] componentDependencies = c.getComponentDependencies();
+ int downDeps = 0;
+ List<ComponentId> result = new ArrayList<ComponentId>();
+ for (ComponentDependencyDeclaration cdd : componentDependencies) {
+ if (cdd.getState() == ComponentDependencyDeclaration.STATE_UNAVAILABLE_REQUIRED) {
+ downDeps++;
+ // Detect missing configuration dependency
+ if (CONFIGURATION.equals(cdd.getType())) {
+ String bsn = c.getBundleContext().getBundle().getSymbolicName();
+ 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 its a circular dependency
+ System.out.print("Circular dependency found:\n *");
+ for (ComponentId cid : backTrace) {
+ System.out.print(" -> " + cid.getName() + " ");
+ }
+ System.out.println(" -> " + componentId.getName());
+ result.add(new ComponentId(c.getName(), SERVICE, c.getBundleContext().getBundle().getSymbolicName()));
+ continue;
+ }
+ backTrace.add(componentId);
+ return getRoot(downComponents, component, backTrace);
+ }
+ }
+ if (downDeps > 0 && result.isEmpty()) {
+ result.add(new ComponentId(c.getName(), SERVICE, c.getBundleContext().getBundle().getSymbolicName()));
+ }
+ return result;
+ }
+
+ private ComponentDeclaration getComponentDeclaration(final String fullName, List<ComponentDeclaration> list) {
+ String simpleName = getSimpleName(fullName);
+ Properties props = parseProperties(fullName);
+ for (ComponentDeclaration c : list) {
+ String serviceNames = c.getName();
+ int cuttOff = serviceNames.indexOf("(");
+ if (cuttOff != -1) {
+ serviceNames = serviceNames.substring(0, cuttOff).trim();
+ }
+ for (String serviceName : serviceNames.split(",")) {
+ if (simpleName.equals(serviceName.trim()) && doPropertiesMatch(props, parseProperties(c.getName()))) {
+ return c;
+ }
+ }
+ }
+ return null;
+ }
+
+ private 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;
+ }
+ }
+}
Added: felix/sandbox/pderop/dependencymanager-prototype/dm.shell/test/.gitignore
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.shell/test/.gitignore?rev=1583068&view=auto
==============================================================================
(empty)
Added: felix/sandbox/pderop/dependencymanager-prototype/dm.shell/test/test/DMCommandTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.shell/test/test/DMCommandTest.java?rev=1583068&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.shell/test/test/DMCommandTest.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.shell/test/test/DMCommandTest.java Sat Mar 29 22:10:11 2014
@@ -0,0 +1,308 @@
+/*
+ * 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 test;
+
+import static java.lang.System.out;
+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.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.util.Properties;
+
+import javax.crypto.Cipher;
+
+import dm.Component;
+import dm.DependencyManager;
+import dm.shell.DMCommand;
+
+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 {
+ /** System output just used to debug **/
+ private final static PrintStream OUT =
+ new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.out), 128));
+
+ /** 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);
+ Bundle bundle = mock(Bundle.class);
+ when(m_bundleContext.getBundle()).thenReturn(bundle);
+ System.setOut(new PrintStream(outContent));
+ System.setErr(new PrintStream(errContent));
+ dm = new DependencyManager(m_bundleContext);
+ 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() {
+ OUT.println("testWithoutAnyDependcyManagersShouldNotCrash");
+ setupEmptyBundles();
+
+ dme.wtf();
+ assertEquals("No missing dependencies found.\n", outContent.toString());
+ }
+
+ @Test
+ public void testASingleComponentShouldNotRegisterAsFailure() {
+ OUT.println("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() {
+ OUT.println("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);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testComponentThatHaveCycliclyDependencyOnAOtheComponentShouldRegisterAsFailure() {
+ OUT.println("testComponentThatHaveCycliclyDependencyOnAOtheComponentShouldRegisterAsFailure");
+ setupEmptyBundles();
+ DependencyManager dm = new DependencyManager(m_bundleContext);
+ DependencyManager.getDependencyManagers().add(dm);
+
+ Component component1 = dm.createComponent()
+ .setImplementation(Cipher.class)
+ .setInterface(Cipher.class.getName(), null)
+ .add(dm.createServiceDependency().setService(Math.class).setRequired(true));
+ dm.add(component1);
+
+ Component component2 = dm.createComponent()
+ .setImplementation(Math.class)
+ .setInterface(Math.class.getName(), null)
+ .add(dm.createServiceDependency().setService(Cipher.class).setRequired(true));
+ dm.add(component2);
+
+ dme.wtf();
+ String output = outContent.toString();
+ assertTrue(output.contains("Circular dependency found:"));
+ assertTrue(output.contains("-> java.lang.Math -> javax.crypto.Cipher -> java.lang.Math"));
+
+ // remove the mess
+ dm.remove(component1);
+ dm.remove(component2);
+ }
+
+ @Test
+ public void testCanFindRootFailure() {
+ OUT.println("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 testCanFindRootFailureWithSecondair() {
+ OUT.println("testCanFindRootFailureWithSecondair");
+ setupEmptyBundles();
+
+ Component component1 = dm.createComponent()
+ .setImplementation(Object.class)
+ .setInterface(Object.class.getName(), null)
+ .add(dm.createServiceDependency().setService(Math.class).setRequired(true));
+ dm.add(component1);
+
+ Component component2 = dm.createComponent()
+ .setImplementation(Math.class)
+ .setInterface(Math.class.getName(), null)
+ .add(dm.createServiceDependency().setService(Float.class).setRequired(true));
+ dm.add(component2);
+
+ Component component3 = dm.createComponent()
+ .setImplementation(Object.class)
+ .setInterface(new String[] {Object.class.getName(), Float.class.getName()}, null)
+ .add(dm.createServiceDependency().setService(String.class).setRequired(true));
+ dm.add(component3);
+
+ dme.wtf();
+ String output = outContent.toString();
+ assertTrue(output.contains("3 missing"));
+ assertTrue(output.contains("java.lang.String"));
+ assertFalse(output.contains("java.lang.Float"));
+
+ // remove the mess
+ dm.remove(component1);
+ dm.remove(component2);
+ dm.remove(component3);
+ }
+
+ @Test
+ public void testCanFindRootFailureWithTwoFailures() {
+ OUT.println("testCanFindRootFailureWithTwoFailures");
+ setupEmptyBundles();
+
+ Component component1 = dm.createComponent()
+ .setImplementation(Object.class)
+ .setInterface(Object.class.getName(), null)
+ .add(dm.createServiceDependency().setService(Math.class).setRequired(true))
+ .add(dm.createServiceDependency().setService(Long.class).setRequired(true));
+ dm.add(component1);
+
+
+ dme.wtf();
+ String output = outContent.toString();
+ assertTrue(output.contains("1 missing"));
+ assertTrue(output.contains("java.lang.Math"));
+ assertTrue(output.contains("java.lang.Long"));
+
+ // remove the mess
+ dm.remove(component1);
+ }
+
+ @Test
+ public void testInstalledBundleListing() {
+ OUT.println("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() {
+ OUT.println("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() {
+ OUT.println("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[] {});
+ }
+
+}