Blogs
We can use the "bnd" command to see what the bundle imports are.
Introduction
So many times I have answered the question, "How do I find out what import packages my bundle needs?" with the tired and unsatisfactory response that uses the following process:
- Build your module.
- Deploy your module.
- Use Gogo to see why your bundle doesn't start, often from an Unresolved Requirement on an Import Package.
- Either include the lib in your jar or use the Import-Package bnd.bnd property to exclude the package.
- Go to step 1, repeat until no further Unresolved Requirements are found.
Yeah, so this is really a pain, but it was the only way I knew of how to see what the imports are that your module needs.
Until Today.
Introducing Bnd
The Bnd tool (available https://bnd.bndtools.org/) is the heart of building an OSGi bundle. Whether you're using Blade, Gradle, Maven, etc it doesn't matter; under the covers you are likely invoking Bnd to build the module.
Most of us don't know about Bnd only because we're lazy developers. Well okay, maybe not lazy, but we're only going to learn new tools if we have to. But if we can do our jobs without knowing every detail of the process, we're generally fine with it.
It is Bnd which is responsible for applying the directives in your bnd.bnd file and generating the bundle full of the OSGi details necessary for your bundle to deploy and run.
As it turns out, the Bnd tool knows a heck of a lot more about our bundles than we do.
To find this out, though, we need the Bnd tool installed.
Follow the instructions from https://bnd.bndtools.org/chapters/120-install.html on 3.1 to install the command line client.
Bnd Printing
The command line tool actually gives you a full basket of tools to play with, you can find the whole list here: https://bnd.bndtools.org/chapters/860-commands.html
Honestly I have not really played with many of them yet. I surely need to because there are definitely useful nuggets of gold in them there hills.
For example, the one nugget I've found so far is the print command.
I'm talking specifically about using bnd print
--impexp to list imports and exports as described https://bnd.bndtools.org/chapters/390-wrapping.html in
section 20.3.
Turns out this command will list the imports and exports Bnd has identified for your module.
I turned this on one of my own modules to see what I would get:
bnd print --impexp build/libs/com.example.hero.rules.engine.simple-1.1.0.jar
[IMPEXP]
Import-Package
com.example.data.model {version=[1.0,2)}
com.example.hero.rules.engine {version=[1.1,2)}
com.example.hero.rules.model {version=[1.0,2)}
com.example.hero.rules.service {version=[1.0,2)}
com.liferay.portal.kernel.log {version=[7.0,8)}
com.liferay.portal.kernel.util {version=[7.3,8)}
com.liferay.portal.kernel.uuid {version=[6.2,7)}
javax.xml.datatype
javax.xml.namespace
javax.xml.parsers
org.w3c.dom
org.w3c.dom.bootstrap
org.w3c.dom.ls
org.xml.sax
Cool, huh? I can see that my module wants to import stuff that I have defined off in the API module, but I can also see that I'm leveraging portal-kernel as well as XML processing.
POI Portlet
One of the frequent requests is what is necessary to use POI in a portlet. Let's find out together, shall we?
So I created a simple module using Blade and use the following build.gradle file:
dependencies {
compileOnly group: "com.liferay.portal", name: "com.liferay.portal.kernel", version: "2.0.0"
compileOnly group: "com.liferay.portal", name: "com.liferay.util.taglib", version: "2.0.0"
compileOnly group: "javax.portlet", name: "portlet-api", version: "2.0"
compileOnly group: "javax.servlet", name: "javax.servlet-api", version: "3.0.1"
compileOnly group: "jstl", name: "jstl", version: "1.2"
compileOnly group: "org.osgi", name: "osgi.cmpn", version: "6.0.0"
compileInclude group: 'org.apache.poi', name: 'poi-ooxml', version: '3.17'
}
The only thing I added here was the compileInclude directive. As we all know, this will automagically include the declared dependency and some of the transitive dependencies in the bundle. But what many of us have seen, if you deploy this guy you would still get Unresolved Reference messages.
Well, using Bnd we can now see why that is:
bnd print --impexp build/libs/com.example.poi-1.0.0.jar
[IMPEXP]
Import-Package
com.liferay.portal.kernel.portlet.bridges.mvc {version=[1.0,2)}
com.microsoft.schemas.office.powerpoint
com.microsoft.schemas.office.word
com.sun.javadoc
com.sun.tools.javadoc
javax.crypto
javax.crypto.spec
javax.imageio
javax.imageio.metadata
javax.imageio.stream
javax.portlet {version=[2.0,3)}
javax.security.auth.x500
javax.servlet {version=[3.0,4)}
javax.servlet.http {version=[3.0,4)}
javax.swing
javax.xml.bind
javax.xml.bind.annotation
javax.xml.bind.annotation.adapters
javax.xml.crypto
javax.xml.crypto.dom
javax.xml.crypto.dsig
javax.xml.crypto.dsig.dom
javax.xml.crypto.dsig.keyinfo
javax.xml.crypto.dsig.spec
javax.xml.parsers
javax.xml.transform
javax.xml.transform.dom
javax.xml.transform.stream
javax.xml.validation
javax.xml.xpath
junit.framework
org.apache.commons.logging
org.apache.crimson.jaxp
org.apache.jcp.xml.dsig.internal.dom
org.apache.poi.hsmf
org.apache.poi.hsmf.datatypes
org.apache.poi.hsmf.extractor
org.apache.poi.hwpf.extractor
org.apache.tools.ant
org.apache.tools.ant.taskdefs
org.apache.tools.ant.types
org.apache.xml.resolver
org.apache.xml.resolver.tools
org.apache.xml.security
org.apache.xml.security.c14n
org.apache.xml.security.signature
org.apache.xml.security.utils
org.bouncycastle.asn1
org.bouncycastle.asn1.cmp
org.bouncycastle.asn1.nist
org.bouncycastle.asn1.ocsp
org.bouncycastle.asn1.x500
org.bouncycastle.asn1.x509
org.bouncycastle.cert
org.bouncycastle.cert.jcajce
org.bouncycastle.cert.ocsp
org.bouncycastle.cms
org.bouncycastle.cms.bc
org.bouncycastle.operator
org.bouncycastle.operator.bc
org.bouncycastle.tsp
org.bouncycastle.util
org.etsi.uri.x01903.v14
org.junit
org.junit.internal
org.junit.runner
org.junit.runner.notification
org.openxmlformats.schemas.officeDocument.x2006.math
org.openxmlformats.schemas.schemaLibrary.x2006.main
org.w3c.dom
org.w3c.dom.events
org.w3c.dom.ls
org.xml.sax
org.xml.sax.ext
org.xml.sax.helpers
Export-Package
com.example.poi.constants {version=1.0.0}
Nuts!
Now we can see just what OSGi is going to want us to deal with. We can add the compileInclude directives for these artifacts if we want to include them, or we could mask them using the ! syntax for the bnd.bnd Import-Package directive, or even mark them as optional by listing them in the Import-Package directive with the resolution:=optional instruction, ala:
Import-Package:\ ...\ org.apache.tools.ant.*;resolution:=optional,\ ...
Conclusion
During my testing, I did find that this is not the perfect solution. Not even the Bnd tool will process transitive dependencies correctly.
For example, we can see from above that BouncyCastle is imported, but what we can't see are any transitive dependencies to BouncyCastle that might be lurking if we decide to include BouncyCastle in. We would have to re-run the bnd print --impexp command again, but that will still be better than the old tired answer I used to have to give.
Enjoy!

