Package dependencies and exports

November 14, 2023

Before going any further, it is very important to understand what OSGi package dependencies are, how they work and how to use them properly.

The OSGi framework will only let you access a Java package located in another OSGi bundle if:

  • It is exported by an OSGi bundle
  • It is imported by your own bundle

OSGi imports and exports must be declared in the META-INF/MANIFEST.MF file, with the Import-Package and Export-Package headers. If an import or export is missing, or if versions of packages don’t match, no access to the package will be allowed by the bundle’s class loader. Management of the package imports and exports is the main learning curve involved in learning to use OSGi. Fortunately, Jahia offers tooling such as the Jahia Maven Plugin that helps generate dependencies for common module projects. There are also other OSGi plugins available on the Internet but usually the Felix Maven Bundle Plugin and the Jahia Maven plugin should be sufficient for most projects.

If your module is based on jahia-modules, the plugins are preconfigured to detect the required imports. You can manually add or modify packages by setting the  jahia.modules.importPackage property in your pom.xml file. It is also possible to completely override the default configuration by setting the import-package property. If you need to expose a package to other modules, you must declare it explicitly by setting the export-package property :

    <properties>
        <jahia.modules.importPackage>org.jahia.defaults.config.spring,org.jahia.modules.external</jahia.modules.importPackage>
        <export-package>org.jahia.modules.external.users</export-package>
    </properties>

Import and exports can also specify versions and options. More documentation can be found here and here.

Module dependency with Jahia-depends

When developing modules with dependencies outside of core functionality, developers need to specify jahia-depends configuration in the pom.xml to explicitly declare all its required module dependencies. This allows the module to perform checks within the OSGI lifecycle and ensure dependencies are met prior to resolving and starting the module within the OSGI runtime. This configuration is implemented through the use and declaration of OSGI Requirements and Capabilities constraint model within the generated module manifest file.

This can be declared in one of the two ways within the module pom.xml as following, where dependency-module is the artifact ID that this module depends on:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.felix</groupId>
            <artifactId>maven-bundle-plugin</artifactId>
            <configuration>
                <instructions>
                    <Jahia-Depends>dependency-module</Jahia-Depends>
                </instructions>
            </configuration>
        </plugin>
    </plugins>
</build>

or through the properties element defintion:

<properties>
  <jahia-depends>dependency-module</jahia-depends>
</properties>
Either declaration is equivalent in functionality but should only use one way or the other. Declaring dependencies using both formats at the same time would lead to one overriding the other.

What to put in jahia-depends

It is important that you read this section and follow the described procedure.

For every module you need to check the following:

  • Check all imported packages and make sure that there is a jahia-depends counterpart for every package. To do so execute the following command in karaf where bundleId is the id of your bundle:  bundle:requirements --namespace=osgi.wiring.package <bundleid>

  • Check if your module imports definitions from modules not listed in jahia-depends. To do so execute the following command in karaf where bundleId is the id of your bundle: bundle:requirements --namespace=com.jahia.services.content <bundleid>

  • Inspect javascript in your module (if applicable) and make sure that you have graphql-dxm-provider in jahia-depends if your module is using graphql queries.

  • Check JSPs and makes sure that if there are any taglibs or views in use (pay attention to tags like template:include etc.) which come from other modules these modules are in jahia-depends. 

  • Make sure that dependencies on core modules are explicit. In other words if your modules uses definitions or relies on code provided by default, assets, jquery, ckeditor they need to be in jahia-depends. Conversely, if your module doesn't use anything from a module this module should not be in jahia-depends (it is often the case that default is found in jahia-depends without any real dependency).

Version Restriction

With the 8.0.3 release, Jahia added support for specifying additional versions' requirement of the dependency specified. This is helpful in cases, for example, where the current module depends on certain features that is only available in certain versions of the dependency. The version restriction clause of module dependency is specified with =  followed by either a lower bound version restriction e.g. dependency-module=<version> or by a specific version range restriction e.g. dependency-module=[<version-lower-bound>, <version-upper-bound>] . The version range clause follows OSGI version syntax and supports use of inclusive range ( [  or ] ) and exclusive range ( (  or ) ) for lower-bound or higher-bound restrictions, or both. The version number parameter itself supports and matches major, minor and micro level granularity that conforms to OSGI version number syntax. The Jahia-depends configuration also supports mixed usage of dependencies with version restriction clause as optional.

As an example, the following Jahia-depends configuration can be added in the pom.xml file:

<Jahia-Depends>dep-module1=4.1,dep-module2,dep-module3=[0, 3)</Jahia-Depends> 

This means that the module requires:

  • dep-module1 that is greater than or equal to 4.1.x version
  • dep-module2 (any version available)
  • dep-module3 that is less than but not equal to any 3.x.x version

The module itself also needs to require at least 8.0.3 as parent version to use the version restriction clause:

<parent>
    <artifactId>jahia-modules</artifactId>
    <groupId>org.jahia.modules</groupId>
    <version>8.0.3.0</version>
</parent>

Optional dependencies

Since Jahia 8.1.4, a dependency can also be declared as optional, where some features of the module will not be operational due to the absence of a dependency, but some parts of the modules will still be fully functional. This means module can still be started and active even if optional dependencies for that module are not resolved or missing.

Optional dependecies can be declared as:

<jahia-depends>dep-module1=optional</jahia-depends>

or after a version restriction, separated with a semicolon:

<jahia-depends>dep-module1=[2,5];optional</jahia-depends>

Module Administration

When a module has a dependency requirement and is deployed, OSGI framework will try to resolve those dependencies and start the module as necessary. If a dependency is resolved successfully, then the module itself should be started within the framework. This can be verified within the Jahia Module Administration (v2.2.1) page (Settings > Modules and Extensions > Modules). If one or more dependencies are not resolved (either dependency does not exist at all or version restriction is not satisfied) then the module will get an error message within the Module Administration page like the following:

jahia-depends1.png

In this example, the jahia-starter-template module has a declared dependency page-builder-components=[2,4] where it requires page-builder-components module to be installed with version between 2.x.x and 4.x.x inclusive. In this example, page-builder-components module installed has a 1.0.0 version and is out of range of the specified version restriction. Notice that the jahia-starter-template module also has an Inactive status and cannot be started until the conflict is resolved.

For unresolved or stopped optional dependencies, modules will have the following message:

unresolved optional dependency message