Tuesday, July 07, 2015

Additional considerations on the new Features service

I want to give a bit more detailed explanations on some new stuff provided by the Karaf 4.x FeaturesService.

Transitive closure

The karaf-maven-plugin provides a new goal named verify.  It's an enhanced version of the previous validate goal.  This goal helps ensuring that all features are transitively closed with respect to their requirements.  This means that all the requirements for a given feature can be fulfilled without relying on already installed bundles or features.  This new goal actually uses the OSGi resolver to ensure that, so that it ensures your features can be deployed everywhere, without additional requirements.

Viable deployments

One of the new features I mentioned in my previous post is that the new Features service ensures that the features requirements are fully available.  When installing new features, this is usually not much of a problem, as we usually only add new bundles.  However, when uninstalling a feature, things can be a bit more complicated when those features are not transitively closed.  Some features do not express all their dependencies so that the user is required to install a few features in order to make them work.  Problems can appear when a feature which is required, but not explicitly, is uninstalled by the user.  The previous behaviour when uninstalling a feature was to uninstall all the bundles that were previously installed by that feature and were not directly required by another feature.  What this means is that if a feature did not have a dependency on that bundle, the removal of that bundle would cause the feature not to work anymore.
The new Features service does not have this bad behaviour anymore.  If the user uninstalls a feature, a new resolution will take place, making sure that all requirements are solved for the remaining features.
A simple example involves camel, but the same is true for cxf or activemq features.  The camel-core feature depends on the shell for the commands.  The shell has changed in Karaf 4, but a compatibility layer is provided to allow the installation of previous commands.  However, the camel-core feature does not have an expressed dependency on the shell-compat feature, which means that in order to install camel-core, you also need to install the shell-compat feature.  Once that's done, you won't be able to uninstall the shell-compat feature unless you also uninstall camel-core.
Your deployments are always valid and safe !

Full uninstallation of a feature

In order to minimise the disruption when uninstalling features, the FeaturesService in Karaf 2.x and 3.x did not uninstall feature dependencies when uninstalling a given feature (see the above point).  Now that the service uses a set of requirements as the input for the resolution, uninstalling a feature will automatically uninstall all the bundles that are not needed anymore.
I'll talk about the input requirements in more details in a next post.

Optional feature dependencies

In Karaf 2.x or 3.x, a feature can have feature dependencies.  The behaviour is that the Features Service will install those dependencies when the feature is installed.  The goal is usually to solve some requirements, for example the webconsole feature depends on the http feature.
Karaf 4.0 adds new possibilities: those dependencies can be flagged as being optional.  This means that the Features Service will install them if they are actually needed to solve some requirements, but if this feature is not really needed, it won't be installed.
This is particularly useful when we define features that provide a given specification along with an implementation.  For example, the http feature provides the OSGi HttpService, but pax-web provides multiple containers that can be used such as jetty or tomcat.  In Karaf 2.x and 3.x, having a dependency on the http feature always lead to the jetty container being installed.  With Karaf 4.x, the real behaviour is that the HttpService will be installed, using jetty by default, but not necessarily.
This is modelled using the following:

<feature name="pax-http">
    <feature dependency="true">pax-http-jetty</feature>
    <requirement>
        pax.http.provider
    </requirement>
</feature>

<feature name="pax-http-jetty">
    ... jetty bundles ...
    <capability>
       pax.http.provider;provider:=jetty
    </capability>
</feature>

<feature name="pax-http-tomcat">
    ... tomcat bundles ...
    <capability>
       pax.http.provider;provider:=tomcat
    </capability>
</feature>
The benefit is that features can safely depend on pax-http.  This will always provide the HttpService.  If no specific http provider is installed, it will install the pax-http-jetty feature, but if the user installs pax-http-tomcat explicitly, the jetty provider will not be installed.

No comments: