The OSGi Ecosystem

All deployed artifacts or bundles are modules that coexist in an OSGi container. Much like how Java has access modifiers, these modules can export selected packages to be consumed by other modules. Packages not explicitly exported will not be available to other modules. Conversely, modules can import certain packages from other modules. When a bundle is deployed, it goes to the “installed” state. If the container can resolve its dependencies, it transitions to a “resolved” state. Next, it will transition through “starting”, “active” and “stopping” states and finally to an “uninstalled” state when the bundle has been removed from the OSGi runtime. During the dependency resolution, the following rules are followed.

  • For every Import-Package package declaration, there must be a corresponding Export-Package with the same package
  • Import-Package dictates exactly what version (or attribute) it needs, and a corresponding Export-Package with the same attribute must exist
  • Bundles installed first are used to satisfy a dependency when multiple packages with the same version are found
  • Bundles that have already been resolved have a higher precedence than those not resolved

OSGi build checklist

  1. Packages inside a module should be unique and specific. This is often an overlooked aspect. Any packages exported should be unique to that OSGi module within the entire OSGi runtime. For example, you should not have two modules in the same OSGi container attempting to export the same package. If that happens, we can’t guarantee which package will be used both other modules.
  2. Dependencies should have the right version.
  3. If there is a plan to export certain packages, that should be specified appropriately.
  4. If there is a plan to use packages from other modules, the import packages section needs careful consideration. Be specific and deliberate about the version and the exact package name considered (and excluded) since OSGi runtime can potentially have more than one option to resolve dependency from multiple bundles with different package structure and versions.
  5. Compile dependencies are dependencies that will be included with the artifact that is being built. That means those dependencies will be bundled within the JAR file deployed into the OSGi container. This is the “old school” approach where every deployed artifact has its dependencies within the bundle itself and is not leveraging the benefits of the OSGi container. This list is generally small because it’s better to leverage a “shared” module in the container.
  6. CompileOnly dependencies are dependencies that will be provided by the OSGi runtime before the state transition to the resolved state. This list is usually larger than the list of compile dependencies. Consideration should be taken to explicitly name packages (and their versions) in ‘Import-Package’ section that matches the exported package from the bundle that provides the dependency.
  7. Export-Package section dictates the designated packages to export to the OSGi runtime that can be imported by other modules. Generally, only interfaces are exported, and implementation is not.
  8. The ‘Import-Package’ section lists package that this module needs from the OSGi runtime. This generally matches a compileOnly dependency for the build. For example, if package javax.servlet is imported, we only need classes from that specific package. If package javax.xml.ws.* is imported, all packages immediately under javax.xml.ws is imported. The “.*” does not imply recursive import of all sub-packages. If packages are not explicitly imported, sometimes OSGi runtime might provide the package needed from another bundle – but this behavior cannot be depended upon – so we recommend explicitly declaring the import for all dependencies needed. You should also verify that the bundle providing the dependency has the exact package (and version) exported.
  9. When a package is imported, we can specify a fixed version or a range of versions. If a fixed version is declared, it implied that it is the minimum range. Semantic versioning is the recommended practice. If we do not specify a version, the manifest will infer the version based on the version in the compile or compileOnly section. If the version specified is a major version eg. 1, it includes all minor versions eg. 1.* versions.
  10. If a package is available from more than one bundle, we can exclude in the Import-Package section eg. !org.apache.log – say if I have this in my compile dependency and want to exclude the possibility that this can be included from one of my compileOnly dependencies. This is sometimes another overlooked aspect.
  11. You can build the manifest in build.gradle dynamically and if you do so, please define ‘Bundle-Classpath’ to include the artifacts in ‘OSGI-INF/lib/’ folder in the classpath and similarly for ‘-includeresource’ to include all these artifacts.
  12. To find out the right bundles, you can use Apache Felix Gogo shell to debug your modules. By default you can telnet to the Felix Gogo shell using this command:

$ telnet localhost 11311

Here’s a list of useful Gogo shell commands:

lb = list bundles command

g! lb
START LEVEL 20
   ID|State      |Level|Name
	0|Active     |	0|OSGi System Bundle (3.10.200.v20150831-0856)
	1|Resolved   |	1|Liferay Portal OSGi Web Jasper Plugins (1.0.4)
	2|Active     |	6|Liferay Util Taglib (2.10.6)
   45|Active     |   10|Liferay Collaboration (7.0.39)
   46|Active     |   10|Liferay Announcements API (1.0.1)
   47|Active     |   10|Liferay Announcements Web (2.0.7)
   48|Active     |   10|Liferay Blogs API (3.1.0)
   49|Active     |   10|Liferay Blogs Editor Configuration (1.0.10)
   50|Active     |   10|Liferay Blogs Item Selector API (1.1.0)
   51|Active     |   10|Liferay Blogs Item Selector Web (2.0.0)
   52|Active     |   10|Liferay Blogs Layout Prototype (2.0.12)
   53|Active     |   10|Liferay Blogs Recent Bloggers API (1.0.1)
   54|Active     |   10|Liferay Blogs Recent Bloggers Web (2.0.0)
   55|Active     |   10|Liferay Blogs Service (1.1.11)
   56|Active     |   10|Liferay Blogs Web (2.0.2)

lb | grep keyword = list bundles that match the keyword

g! lb | grep Blogs
   48|Active     |   10|Liferay Blogs API (3.1.0)
   49|Active     |   10|Liferay Blogs Editor Configuration (1.0.10)
   50|Active     |   10|Liferay Blogs Item Selector API (1.1.0)
   51|Active     |   10|Liferay Blogs Item Selector Web (2.0.0)
   52|Active     |   10|Liferay Blogs Layout Prototype (2.0.12)
   53|Active     |   10|Liferay Blogs Recent Bloggers API (1.0.1)
   54|Active     |   10|Liferay Blogs Recent Bloggers Web (2.0.0)
   55|Active     |   10|Liferay Blogs Service (1.1.11)
   56|Active     |   10|Liferay Blogs Web (2.0.2)

b = list bundle details

g! b 48
com.liferay.blogs.api_3.1.0 [48]
  Id=48, Status=ACTIVE  	Data Root=C:\***\osgi\state\org.eclipse.osgi\48\data
  "Registered Services"
    {com.liferay.portal.kernel.settings.definition.ConfigurationBeanDeclaration}={component.name=com.liferay.blogs.confi
guration.definition.BlogsGroupServiceConfigurationBeanDeclaration, component.id=43, service.id=105, service.bundleid=48,
 service.scope=bundle}
    {com.liferay.portal.kernel.settings.definition.ConfigurationPidMapping}={component.name=com.liferay.blogs.configurat
ion.definition.BlogsGroupServiceConfigurationPidMapping, component.id=44, service.id=106, service.bundleid=48, service.s
cope=bundle}
  Services in use:
	{org.osgi.service.log.LogService, org.eclipse.equinox.log.ExtendedLogService}={service.id=2, service.bundleid=0, ser
vice.scope=bundle}
  Exported packages
	com.liferay.blogs.configuration; version="1.0.3"[exported]
	com.liferay.blogs.configuration.definition; version="1.0.1"[exported]
	com.liferay.blogs.constants; version="1.0.0"[exported]
  Imported packages
	aQute.bnd.annotation.metatype; version="1.45.0" <org.eclipse.osgi_3.10.200.v20150831-0856 [0]>
	com.liferay.portal.configuration.metatype.annotations; version="1.0.1" <com.liferay.portal.configuration.metatype_2.
0.11 [619]>
	com.liferay.portal.kernel.settings; version="1.2.0" <org.eclipse.osgi_3.10.200.v20150831-0856 [0]>
	com.liferay.portal.kernel.settings.definition; version="1.0.0" <org.eclipse.osgi_3.10.200.v20150831-0856 [0]>
	com.liferay.portal.kernel.util; version="7.41.0" <org.eclipse.osgi_3.10.200.v20150831-0856 [0]>
  No fragment bundles
  No required bundles


This shows the services in use, Exported packages and Imported packages with their version and the bundle name and id from which it is imported from eg. com.liferay.portal.configuration.metatype.annotations version 1.0.1 is imported from bundle 619 with name com.liferay.portal.configuration.metatype

Summary

When you are debugging through a deployment issue in OSGi runtime, please review the checklist above and examine each aspect of the build file.

Additional Resources
If you have questions on how you can best leverage XTIVIA’s experience and/or need help with your Liferay DXP implementation, please engage with us via comments on this blog post, or reach out to us.