Search code examples
osgiosgi-bundle

How can I obtain all of the OSGi bundles that my bundle depends on via Import-Package at runtime?


I know I could (in theory) obtain the Import-Package header for my bundle, parse it, and check which bundles export that package, but that seems error prone (and it seems like it might even return the wrong results). Is there no better way to find out my bundle's dependencies than obtaining my current bundle (via FrameworkUtil.getBundle(ClassFromMyBundle.class)), examining the Import-Package header (by calling Bundle.getHeaders().get("Import-Package")), and comparing the imported packages with packages exported by other bundles (again by calling Bundle.getHeaders().get("Export-Package"))?


Solution

  • Do not try to parse and compare headers. Instead, you can use the BundleWiring of your bundle to obtain the required wires of your bundle (based specifically on required packages). Then you can obtain the bundles that provide those wires to get a list of all the bundles that your bundle depends on via Import-Package.

    BundleWiring.getRequiredWires(BundleRevision.PACKAGE_NAMESPACE) will return all the required BundleWires that provide packages to a bundle. Then you can call BundleWire.getProvider() on each BundleWire to obtain the bundle that provides one or more of those packages. However, getRequiredWires(BundleRevision.PACKAGE_NAMESPACE) doesn't discriminate between packages imported via the Import-Package header and the DynamicImport-Package header. Since you only want to find Import-Package dependencies, you'll need to check whether the BundleWire is a dynamic dependency. You can do this by checking the directives of the BundleRequirement:

    BundleRequirement requirement = bundleWire.getRequirement();
    
    if (requirement != null) {
    
        Map<String, String> directives = requirement.getDirectives();
        String resolution = directives.get("resolution");
    
        if ("dynamic".equalsIgnoreCase(resolution)) {
            // The dependency was obtained via DynamicImport-Package.
        }
    }
    

    Here's a method that will do all of the above for you:

    public static Set<Bundle> getBundlePackageImportDependencies(Bundle bundle) {
    
        BundleWiring bundleWiring = bundle.adapt(BundleWiring.class);
    
        if (bundleWiring == null) {
            return Collections.emptySet();
        }
    
        List<BundleWire> bundleWires =
            bundleWiring.getRequiredWires(BundleRevision.PACKAGE_NAMESPACE);
    
        if (bundleWires == null) {
            return Collections.emptySet();
        }
    
        Set<Bundle> bundleDependencies = new HashSet<Bundle>();
    
        for (BundleWire bundleWire : bundleWires) {
    
            BundleRevision provider = bundleWire.getProvider();
    
            if (provider == null) {
                continue;
            }
    
            Bundle providerBundle = provider.getBundle();
            BundleRequirement requirement = bundleWire.getRequirement();
    
            if (requirement != null) {
    
                Map<String, String> directives = requirement.getDirectives();
                String resolution = directives.get("resolution");
    
                if ("dynamic".equalsIgnoreCase(resolution)) {
                    continue;
                }
            }
    
            bundleDependencies.add(providerBundle);
        }
    
        return Collections.unmodifiableSet(bundleDependencies);
    }
    

    This method can be used to obtain the dependencies of the current bundle like so:

    getBundlePackageImportDependencies(
        FrameworkUtil.getBundle(ClassFromYourBundle.class))