Service sharing between modules

November 11, 2022

OSGi bundles may declare or implement OSGi services that will then be registered in a global framework service registry. Through the registry, other bundles may access the services to interact with them. This simply but powerful mechanism makes it possible to decouple bundles while still allowing strong interactions between them. We will illustrate how to do this by using the example of how our external provider bundles are setup.

Spring osgi:service and osgi:reference tags

First we have a Digital Experience Manager module that provides an interface and implements it. We will use the Spring OSGi XML tags to register the service with OSGi’s service registry. This can be simply done by using the XML as in this example:

<bean id="ProviderInitializerService" class="org.jahia.modules.external.id.ExternalProviderInitializerServiceImpl">
    <property name="hibernateSessionFactory" ref="moduleSessionFactory"/>
    <property name="cacheProvider" ref="ehCacheProvider"/>
    <property name="extensionProvider" ref="DefaulJCRStoreProvider"/>
    <property name="overridableItemsForLocks">
        <list>
            <value>jmix:lockable.j:locktoken</value>
            <value>jmix:lockable.j:lockTypes</value>
            <value>mix:lockable.jcr:lockIsDeep</value>
            <value>mix:lockable.jcr:lockOwner</value>
        </list>
    </property>
</bean>
<osgi:service id="ExternalProviderInitializerService" ref="ProviderInitializerService" interface="org.jahia.modules.external.ExternalProviderInitializerService"/>

Note that for more complex service registrations, it might be a good idea to have two separate modules, one for the interfaces, and another for the implementation. This way you can deploy each separately, for example if the interfaces are stable but the implementation is still ongoing. Once the service has been registered, in our second module, we can use a Spring OSGi service reference XML tag to access the registered service in the OSGi service registry:

<osgi:reference id="ExternalProviderInitializerService"
                interface="org.jahia.modules.external.ExternalProviderInitializerService"/>
    <bean class="org.jahia.modules.external.MountPointListener">
    <property name="externalProviderInitializerService" ref="ExternalProviderInitializerService"/>
</bean>

But the work is not yet complete. An important step must still be completed: package export and import.

OSGi:list usage

You can define a list of services implementing a specified interface. Note that if the current bundle provides both the interface and an implementation, the implementation reference must be set as optional.

<osgi:list id="ModuleGlobalObjectList" interface="org.jahia.services.content.rules.ModuleGlobalObject"
 availability="optional">
 <osgi:listener ref="org.jahia.modules.default.ModuleRegistry" bind-method="osgiBind"unbind-method="osgiUnbind"/>
</osgi:list>

Export-Package instruction

To make the service interface available to other bundles, we must export the package in which it is located. This is achieved by adding the following line to the Felix Maven Bundle Plugin configuration:

<Export-Package>org.jahia.modules.external</Export-Package>

Import-Package instruction

Now the last piece of the puzzle is the Import-Package instruction. In most cases this doesn’t need to be manually configured as the Felix Maven Bundle Plugin will scan the module’s code for any dependencies and pick up the service reference. Also, the Jahia Maven Plugin will also pick up the reference inside the Spring XML file.

Other service declaration and referencing mechanisms

In OSGi there are a lot of different ways of registering and referencing services. In the above example, we have illustrated the most common one for Digital Experience Manager modules, but we will quickly list other alternatives:

  • OSGi BluePrint: this is most recent service framework in OSGi. It is based on the Spring OSGi implementation but is now part of the standard. This is directly available in Digital Experience Manager and is useable out of the box.
  • OSGi Declarative Services: this service framework is also part of the OSGi specification, but it is not part of the Digital Experience Manager out of the box configuration so you will need to deploy an implementation bundle if you want to use it. Declarative services are one of the most mature services frameworks in the OSGi and relatively simple, so it might be an interesting alternative to some.
  • OSGi ServiceTracker: it is possible to manually register services using code, but OSGi provides a ServiceTracker class that makes it easier to track services as they appear and disappear at runtime due to OSGi highly dynamic nature. As this much lower level it is available in all OSGi frameworks and usable out of the box in Digital Experience Manager. It is however much more difficult to setup so it not recommended unless you have a good reason to use it (such as building your own extender for example).
  • Manual service registration: this is always possible but really not recommended, unless you really know what you are doing. As services may appear or disappear any time, your implementation must handle this properly, and this can be quite complicated to handle.