You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ge...@apache.org on 2009/12/06 20:10:11 UTC

svn commit: r887748 - in /felix/trunk/karaf/shell/dev: ./ src/main/java/org/apache/felix/karaf/shell/dev/ src/main/java/org/apache/felix/karaf/shell/dev/util/ src/main/resources/OSGI-INF/blueprint/ src/test/java/org/apache/felix/karaf/shell/dev/util/

Author: gertv
Date: Sun Dec  6 19:10:11 2009
New Revision: 887748

URL: http://svn.apache.org/viewvc?rev=887748&view=rev
Log:
FELIX-1914: Add a command to enable/disable dynamic imports to troubleshoot classloading issues

Added:
    felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/AbstractBundleCommand.java
    felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/DynamicImport.java
Modified:
    felix/trunk/karaf/shell/dev/pom.xml
    felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/ShowBundleTree.java
    felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/util/Node.java
    felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/util/Tree.java
    felix/trunk/karaf/shell/dev/src/main/resources/OSGI-INF/blueprint/shell-dev.xml
    felix/trunk/karaf/shell/dev/src/test/java/org/apache/felix/karaf/shell/dev/util/TreeTest.java

Modified: felix/trunk/karaf/shell/dev/pom.xml
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/shell/dev/pom.xml?rev=887748&r1=887747&r2=887748&view=diff
==============================================================================
--- felix/trunk/karaf/shell/dev/pom.xml (original)
+++ felix/trunk/karaf/shell/dev/pom.xml Sun Dec  6 19:10:11 2009
@@ -40,6 +40,11 @@
     </dependency>
 
     <dependency>
+        <groupId>org.ops4j.pax.url</groupId>
+        <artifactId>pax-url-wrap</artifactId>
+    </dependency>
+
+    <dependency>
         <groupId>org.apache.felix</groupId>
         <artifactId>org.osgi.core</artifactId>
         <scope>provided</scope>

Added: felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/AbstractBundleCommand.java
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/AbstractBundleCommand.java?rev=887748&view=auto
==============================================================================
--- felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/AbstractBundleCommand.java (added)
+++ felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/AbstractBundleCommand.java Sun Dec  6 19:10:11 2009
@@ -0,0 +1,93 @@
+/*
+ * 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.karaf.shell.dev;
+
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.felix.karaf.shell.console.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+/**
+ * Base class for a dev: command that takes a bundle id as an argument
+ *
+ * It also provides convient access to the PackageAdmin service
+ */
+public abstract class AbstractBundleCommand extends OsgiCommandSupport {
+
+    @Argument(index = 0, name = "id", description = "The bundle ID", required = true)
+    Long id;
+
+    private PackageAdmin admin;
+
+    @Override
+    protected Object doExecute() throws Exception {
+        // Get package admin service.
+        ServiceReference ref = getBundleContext().getServiceReference(PackageAdmin.class.getName());
+        if (ref == null) {
+            System.out.println("PackageAdmin service is unavailable.");
+            return null;
+        }
+
+        // using the getService call ensures that the reference will be released at the end
+        admin = getService(PackageAdmin.class, ref);
+
+        Bundle bundle = getBundleContext().getBundle(id);
+        if (bundle == null) {
+            System.err.println("Bundle ID" + id + " is invalid");
+            return null;
+        }
+
+        doExecute(bundle);
+        
+        return null;
+    }
+
+    protected abstract void doExecute(Bundle bundle) throws Exception;
+
+    /*
+     * Get the list of bundles from which the given bundle imports packages
+     */
+    protected Map<String, Bundle> getWiredBundles(Bundle bundle) {
+        // the set of bundles from which the bundle imports packages
+        Map<String, Bundle> exporters = new HashMap<String, Bundle>();
+
+        for (ExportedPackage pkg : getPackageAdmin().getExportedPackages((Bundle) null)) {
+            Bundle[] bundles = pkg.getImportingBundles();
+            if (bundles != null) {
+                for (Bundle importingBundle : bundles) {
+                    if (bundle.equals(importingBundle)
+                            && !(pkg.getExportingBundle().getBundleId() == 0)
+                            && !(pkg.getExportingBundle().equals(bundle))) {
+                        exporters.put(pkg.getName(), pkg.getExportingBundle());
+                    }
+                }
+            }
+        }
+        return exporters;
+    }
+
+    protected PackageAdmin getPackageAdmin() {
+        return admin;    
+    }
+}

Added: felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/DynamicImport.java
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/DynamicImport.java?rev=887748&view=auto
==============================================================================
--- felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/DynamicImport.java (added)
+++ felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/DynamicImport.java Sun Dec  6 19:10:11 2009
@@ -0,0 +1,125 @@
+/*
+ * 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.karaf.shell.dev;
+
+import java.io.IOException;
+import static java.lang.String.format;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.karaf.shell.console.OsgiCommandSupport;
+import org.ops4j.pax.url.wrap.Handler;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+/**
+ * Command for enabling/disabling debug logging on a bundle and calculating the difference in
+ * wired imports.
+ */
+@Command(scope = "dev", name = "dynamic-import",
+         description = "Enable/disable dynamic-import for a given bundle")
+public class DynamicImport extends AbstractBundleCommand {
+
+    private static final Log LOG = LogFactory.getLog(DynamicImport.class);
+
+    /**
+     * The header key where we store the active wires when we enable DynamicImport=*
+     */
+    protected static final String ORIGINAL_WIRES = "Original-Wires";
+
+    @Override
+    protected void doExecute(Bundle bundle) throws Exception {
+        if (bundle.getHeaders().get(ORIGINAL_WIRES) == null) {
+            enableDynamicImports(bundle);
+        } else {
+            disableDynamicImports(bundle);
+        }
+    }
+
+    /*
+     * Enable DynamicImport=* on the bundle
+     */
+    private void enableDynamicImports(Bundle bundle) throws IOException, BundleException {
+        System.out.printf("Enabling dynamic imports on bundle %s%n", bundle);
+
+        String location =
+                String.format("wrap:%s$" +
+                        "Bundle-UpdateLocation=%s&" +
+                        "DynamicImport-Package=*&" +
+                        "%s=%s&" +
+                        "overwrite=merge",
+                        bundle.getLocation(),
+                        bundle.getLocation(),
+                        ORIGINAL_WIRES,
+                        explode(getWiredBundles(bundle).keySet()));
+        LOG.debug(format("Updating %s with URL %s", bundle, location));
+
+        URL url = new URL(location);
+        bundle.update(url.openStream());
+        getPackageAdmin().refreshPackages(new Bundle[] {bundle});
+    }
+
+    /*
+     * Disable DynamicImport=* on the bundle
+     *
+     * At this time, we will also calculate the difference in package wiring for the bundle compared to
+     * when we enabled the DynamicImport
+     */
+    private void disableDynamicImports(Bundle bundle) throws BundleException {
+        System.out.printf("Disabling dynamic imports on bundle %s%n", bundle);
+
+        Set<String> current = getWiredBundles(bundle).keySet();
+        for (String original : bundle.getHeaders().get(ORIGINAL_WIRES).toString().split(",")) {
+            current.remove(original);
+        }
+
+        if (current.isEmpty()) {
+            System.out.println("(no additional packages have been wired since dynamic import was enabled)");
+        } else {
+            System.out.printf("%nAdditional packages wired since dynamic import was enabled:%n");
+            for (String pkg : current) {
+                System.out.printf("- %s%n", pkg);
+            }
+        }
+
+        bundle.update();
+    }
+
+    /*
+     * Explode a set of string values in to a ,-delimited string
+     */
+    private String explode(Set<String> set) {
+        StringBuffer result = new StringBuffer();
+        Iterator<String> it = set.iterator();
+        while (it.hasNext()) {
+            result.append(it.next());
+            if (it.hasNext()) {
+                result.append(",");
+            }
+        }
+        return result.toString();
+    }
+}

Modified: felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/ShowBundleTree.java
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/ShowBundleTree.java?rev=887748&r1=887747&r2=887748&view=diff
==============================================================================
--- felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/ShowBundleTree.java (original)
+++ felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/ShowBundleTree.java Sun Dec  6 19:10:11 2009
@@ -17,6 +17,7 @@
 package org.apache.felix.karaf.shell.dev;
 
 import static java.lang.String.format;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
@@ -41,46 +42,22 @@
  * Command for showing the full tree of bundles that have been used to resolve
  * a given bundle.
  */
-@Command(scope = "dev", name = "bundle-tree",
+@Command(scope = "dev", name = "show-tree",
          description = "Show the tree of bundles based on the wiring information")
-public class ShowBundleTree extends OsgiCommandSupport {
+public class ShowBundleTree extends AbstractBundleCommand {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(ShowBundleTree.class);
 
-    @Argument(index = 0, name = "id", description = "The ID of the bundle to check", required = true)
-    Long id;
-
-    // the package admin service reference
-    private PackageAdmin admin;
-
     // a cache of all exported packages
     private ExportedPackage[] allExportedPackages;
 
     @Override
-    protected Object doExecute() throws Exception {
-        // Get package admin service.
-        ServiceReference ref = getBundleContext().getServiceReference(PackageAdmin.class.getName());
-        if (ref == null) {
-            System.out.println("PackageAdmin service is unavailable.");
-            return null;
-        }
-
-        // using the getService call ensures that the reference will be released at the end
-        admin = getService(PackageAdmin.class, ref);
-
-
-        Bundle bundle = getBundleContext().getBundle(id);
-        if (bundle == null) {
-            System.err.println("Bundle ID" + id + " is invalid");
-            return null;
-        }
-
+    protected void doExecute(Bundle bundle) throws Exception {
         // let's do the real work here
         printHeader(bundle);
         Tree<Bundle> tree = createTree(bundle);
         printTree(tree);
         printDuplicatePackages(tree);
-        return null;
     }
 
     /*
@@ -98,19 +75,26 @@
      */
     private void printTree(Tree<Bundle> tree) {
         System.out.printf("%n");
-        tree.write(System.out);
+        tree.write(System.out, new Tree.Converter<Bundle>() {
+
+            public String toString(Node<Bundle> node) {
+                return String.format("%s [%s]",
+                                     node.getValue().getSymbolicName(),
+                                     node.getValue().getBundleId());
+            }
+        });
     }
 
     /*
      * Check for bundles in the tree exporting the same package
-     * (possible cause for 'Unresolved constraint...' on a uses-conflict
+     * as a possible cause for 'Unresolved constraint...' on a uses-conflict
      */
     private void printDuplicatePackages(Tree<Bundle> tree) {
         Set<Bundle> bundles = tree.flatten();
         Map<String, Set<Bundle>> exports = new HashMap<String, Set<Bundle>>();
 
         for (Bundle bundle : bundles) {
-            ExportedPackage[] packages = admin.getExportedPackages(bundle);
+            ExportedPackage[] packages = getPackageAdmin().getExportedPackages(bundle);
             if (packages != null) {
                 for (ExportedPackage p : packages) {
                     if (exports.get(p.getName()) == null) {
@@ -142,7 +126,7 @@
             createNode(tree, trail);
         } else {
             for (Import i : Import.parse(String.valueOf(bundle.getHeaders().get("Import-Package")))) {
-                for (ExportedPackage ep : admin.getExportedPackages(i.getPackage())) {
+                for (ExportedPackage ep : getPackageAdmin().getExportedPackages(i.getPackage())) {
                     if (ep.getVersion().compareTo(i.getVersion()) >= 0) {
                         if (!bundle.equals(ep.getExportingBundle())) {
                             Node child = tree.addChild(ep.getExportingBundle());
@@ -161,7 +145,7 @@
      */
     private void createNode(Node<Bundle> node, Set<Bundle> trail) {
         Bundle bundle = node.getValue();
-        Set<Bundle> exporters = getWiredBundles(bundle);
+        Collection<Bundle> exporters = getWiredBundles(bundle).values();
 
         for (Bundle exporter : exporters) {
             if (trail.contains(exporter)) {
@@ -175,36 +159,4 @@
             }
         }
     }
-
-    /*
-     * Get the list of bundles from which the given bundle imports packages
-     */
-    private Set<Bundle> getWiredBundles(Bundle bundle) {
-        // the set of bundles from which the bundle imports packages
-        Set<Bundle> exporters = new LinkedHashSet<Bundle>();
-
-        for (ExportedPackage pkg : getAllExportedPackages()) {
-            Bundle[] bundles = pkg.getImportingBundles();
-            if (bundles != null) {
-                for (Bundle importingBundle : bundles) {
-                    if (bundle.equals(importingBundle)
-                            && !(pkg.getExportingBundle().getBundleId() == 0)
-                            && !(pkg.getExportingBundle().equals(bundle))) {
-                        exporters.add(pkg.getExportingBundle());
-                    }
-                }
-            }
-        }
-        return exporters;
-    }
-
-    /*
-     * Get the full list of package exports from PackageAdmin
-     */
-    private ExportedPackage[] getAllExportedPackages() {
-        if (allExportedPackages == null) {
-            allExportedPackages = admin.getExportedPackages((Bundle) null);
-        }
-        return allExportedPackages;
-    }
 }

Modified: felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/util/Node.java
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/util/Node.java?rev=887748&r1=887747&r2=887748&view=diff
==============================================================================
--- felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/util/Node.java (original)
+++ felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/util/Node.java Sun Dec  6 19:10:11 2009
@@ -99,13 +99,13 @@
      * array is <code>true</code>, there should be a | to connect to the next
      * sibling.
      */
-    protected void write(PrintWriter writer, boolean... indents) {
+    protected void write(PrintWriter writer, Tree.Converter<T> converter, boolean... indents) {
         for (boolean indent : indents) {
             writer.printf("%-3s", indent ? "|" : "");
         }
-        writer.printf("+- %s%n", value);
+        writer.printf("+- %s%n", converter.toString(this));
         for (Node<T> child : getChildren()) {
-            child.write(writer, concat(indents, hasNextSibling()));
+            child.write(writer, converter, concat(indents, hasNextSibling()));
         }
     }
 

Modified: felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/util/Tree.java
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/util/Tree.java?rev=887748&r1=887747&r2=887748&view=diff
==============================================================================
--- felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/util/Tree.java (original)
+++ felix/trunk/karaf/shell/dev/src/main/java/org/apache/felix/karaf/shell/dev/util/Tree.java Sun Dec  6 19:10:11 2009
@@ -45,7 +45,8 @@
     }
 
     /**
-     * Write the tree to a PrintStream
+     * Write the tree to a PrintStream, using the default toString() method to output the node values
+     *
      * @param stream
      */
     public void write(PrintStream stream) {
@@ -53,14 +54,50 @@
     }
 
     /**
-     * Write the tree to a PrintWriter
+     * Write the tree to a PrintStream, using the provided converter to output the node values
+     *
+     * @param stream
+     * @param converter
+     */
+    public void write(PrintStream stream, Converter<T> converter) {
+        write(new PrintWriter(stream), converter);
+    }
+
+    /**
+     * Write the tree to a PrintWriter, using the default toString() method to output the node values
+     *
      * @param writer
      */
     public void write(PrintWriter writer) {
-        writer.printf("%s%n", getValue());
+        write(writer, new Converter() {
+            public String toString(Node node) {
+                return node.getValue().toString();
+            }
+        });
+    }
+
+    /**
+     * Write the tree to a PrintWriter, using the provided converter to output the node values
+     *
+     * @param writer
+     * @param converter
+     */
+    public void write(PrintWriter writer, Converter<T> converter) {
+        writer.printf("%s%n", converter.toString(this));
         for (Node<T> child : getChildren()) {
-            child.write(writer);
+            child.write(writer, converter);
         }
         writer.flush();
     }
+
+    /**
+     * Interface to convert node values to string
+     *
+     * @param <T> the object type for the node value
+     */
+    public static interface Converter<T> {
+
+        public String toString(Node<T> node);
+
+    }
 }

Modified: felix/trunk/karaf/shell/dev/src/main/resources/OSGI-INF/blueprint/shell-dev.xml
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/shell/dev/src/main/resources/OSGI-INF/blueprint/shell-dev.xml?rev=887748&r1=887747&r2=887748&view=diff
==============================================================================
--- felix/trunk/karaf/shell/dev/src/main/resources/OSGI-INF/blueprint/shell-dev.xml (original)
+++ felix/trunk/karaf/shell/dev/src/main/resources/OSGI-INF/blueprint/shell-dev.xml Sun Dec  6 19:10:11 2009
@@ -26,6 +26,9 @@
         <command name="dev/framework">
             <action class="org.apache.felix.karaf.shell.dev.FrameworkDebug" />
         </command>
+        <command name="dev/dynamic-import">
+            <action class="org.apache.felix.karaf.shell.dev.DynamicImport" />
+        </command>
     </command-bundle>
 
 </blueprint>

Modified: felix/trunk/karaf/shell/dev/src/test/java/org/apache/felix/karaf/shell/dev/util/TreeTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/karaf/shell/dev/src/test/java/org/apache/felix/karaf/shell/dev/util/TreeTest.java?rev=887748&r1=887747&r2=887748&view=diff
==============================================================================
--- felix/trunk/karaf/shell/dev/src/test/java/org/apache/felix/karaf/shell/dev/util/TreeTest.java (original)
+++ felix/trunk/karaf/shell/dev/src/test/java/org/apache/felix/karaf/shell/dev/util/TreeTest.java Sun Dec  6 19:10:11 2009
@@ -46,6 +46,24 @@
     }
 
     @Test
+    public void writeTreeWithOneChildAndNodeConverter() throws IOException {
+        Tree<String> tree = new Tree<String>("root");
+        tree.addChild("child");
+
+        StringWriter writer = new StringWriter();
+        tree.write(new PrintWriter(writer), new Tree.Converter<String>() {
+            public String toString(Node<String> node) {
+                return "my " + node.getValue();
+            }
+        });
+
+        BufferedReader reader = new BufferedReader(new StringReader(writer.getBuffer().toString()));
+
+        assertEquals("my root"     , reader.readLine());
+        assertEquals("+- my child" , reader.readLine());
+    }
+
+    @Test
     public void writeTreeWithChildAndGrandChild() throws IOException {
         Tree<String> tree = new Tree<String>("root");
         Node<String> node = tree.addChild("child");