Bundles, Bnd, PAX, Maven and More
OSGi à la Carte - Part 2
Finally, no section on OSGi development would be complete without mentioning the component models, which greatly simplify using the OSGi dynamism and in particular OSGi services (or μServices as Peter Kriens has dubbed them). These two models absolve the developer from the responsibility of using the complicated OSGi API for services and in part can also hide some of the dynamism where it is not desired. Two component models are standardized as part of the OSGi compendium specification and are in wide use.
The older component model, Declarative Services, allows the definition of service components. These components are plain Java classes that are instantiated as well as activated and deactivated by the Declarative Services extender. Components can have dependencies on other services, which are injected by the extender. At runtime components are defined in XML. However, the Bnd tool also allows a simpler property syntax as well as annotations. Built on top of that the BndTools have a set of nice editors and project templates for working with Declarative Services. The code sample below shows an example of an annotated component and the equivalent (generated) XML. The sample component uses zero or more StockProvider services, which can dynamically be added or removed (Listing 2).
The newer component model, Blueprint, which was introduced with OSGi Compendium Specification revision 4.2, offers a full dependency injection framework, syntactically based on the popular Spring framework, together with first class support for interacting with the OSGi service registry. Apart from the syntax of the XML component descriptors, perhaps the most notable difference between Declarative Services and Blueprint lies in how both expose OSGi service dynamics.
Declarative services expose dynamics directly to the client code. A service that is injected via one method (addProvider in the example), is removed via another method (remove-Provider in the example). A component is deactivated when a mandatory service dependency goes away and re-activated when a replacement becomes available. Blueprint, on the other hand, attempts to hide some of these dynamics. For example if a required service dependency becomes unavailable, a Blueprint bean is not stopped or notified. In fact it will continue to operate until the component tries to use the required service in some way. At that point the Blueprint container will pause to wait for a replacement service to become available and then continues the suspended method call on the service. Through this service damping OSGi bundles built on Blueprint can remain oblivious to short outages (due to for example live updates) of their service dependencies while still enjoying the benefits of potentially continuous updates. Of course Blueprint also has support for letting components listen and react directly to service lifecycle events. The Blueprint snippet in Listing 3 shows the Blueprint descriptor equivalent to the Declarative services descriptor shown before.
Testing with Pax Exam
Sooner or later in the life of an OSGi the question arises of how to test integration between different bundles, especially projects that use OSGi services (for example through one of the component models discussed above). The components can be quite thoroughly unit tested but some level of in-container testing is necessary, for example to ensure that components are defined correctly.
Several options exist for running in-container tests, each offering a different way to accomplish the basic goal: launching a bundle with a number of JUnit tests in an OSGi framework with specific runtime bundles. Available choices include Bnd itself, the BndTools integration test configuration based on it and PaxExam. Of these, the authors have found PaxExam with its annotation based configuration and stream-lined configuration DSL particularly elegant. A basic PaxExam test (using the 2.0.0 branch) can look like Listing 4.
The code snippet shows a simple integration test, which in this case test drives a Blueprint exposed service. Note that even though there is only one @Test annotated method the given test class actually executes two tests: one on Equinox and one on Felix. As shown in the code, Pax Exam works particularly well in conjunction with a Maven build infrastructure. However, it can also be used with plain file URLs.
The question of deployment arises relatively early for OSGi projects. With OSGi it is no longer enough to run the Java command with an appropriately long classpath and point to some main class. Instead the task of deploying an OSGi application is complicated by the need first to bootstrap an OSGi framework and then to install and activate the application bundles plus all their dependencies.
At the simplest one can start each of the three common open source frameworks (Apache Felix, Eclipse Equinox and Knopflerfish) and then use the default console of the framework – all of which are different! – or the OSGi framework API to install further bundles. This approach soon becomes highly inconvenient and does not help the accessibility of OSGi frameworks. Especially, for complex applications that use one or more of the compendium or enterprise specifications, this way clearly is not an option.
Fortunately there are various options both for easily bootstrapping custom slim OSGi runtimes as well as full enterprise strength pre-built runtimes. In almost all cases the various runtime bits are completely runtime-independent – as one would expect from the components of a technology focussed primarily on modularity – and can be reused in any other of the runtimes.
As an alternative to the basic native launch facilities of the individual OSGi frameworks, the Pax Runner project provides a unified way to launch any of three open source frameworks (always with the default console installed) and to provision application and support bundles into that framework. This makes starting a simple OSGi application a breeze.
Bundles can be provisioned from a variety of sources: files on disk, URLs as well as simple provisioning text files that list bundle resources. In particular there is a lot of flexibility in the URL scheme (through the embedded use of the Pax URL projects); bundles can be sourced directly from a Maven repository by its Maven coordinates. Finally, profiles provide a number of very useful pre-built bundle selections for common capabilities such as a Declarative Services runtime. The command below shows one particular invocation.
pax-run --platform=equinox --profiles=felix.ds <bundles ...>
This command bootstraps an Equinox framework, then installs all the bundles required for the Apache Felix Declarative Services implementation and finally installs all the listed files as bundles into that runtime.
One step up from Pax Runner comes Karaf, which provides a much more full-fledged proto-runtime while still being very light-weight and fast to start. By default Karaf comes with excellent built-in support for the Blueprint component model, remote access, JAAS based security and provisioning – not to mention an excellent and extensible shell (based on the Felix Gogo shell), all of which make working with Karaf a real pleasure.
Similar to the profiles that Pax Runner offers are Karaf features, which capture commonly needed functionality. For example, to deploy a Web Application Bundle (WAB) one can simply install the ‘war’ feature. There is even a feature to deploy a full-fledged Spring dmServer runtime (see below). With a plentitude of predefined EE features, a custom enterprisey runtime that contains just the needed features can be bootstrapped in a breeze. Karaf features can also be used from Pax Runner through the custom ‘scan-features’ URL scheme. Note that in some cases prerequisites that are available by default in Karaf need to be added manually in a blank Pax Runner framework.
One particularly nice option that deserves separate mention is the possibility of provisioning a web console, based on the Apache Felix Web Console, which of course can also be installed separately in other runtimes.
Pax Runner profiles and Karaf features rely on pre-defined collections of bundles. This has the benefit that the concrete selection of bundles that make up a feature can be tested reliably. However, this provisioning mechanism is not able to exploit the existence of capabilities in the runtime. For example, the war feature needs the servlet API and will install the Geronimo servlet API bundle regardless of whether the servlet classes are already available (at the right package version) in the runtime. The key challenge for features and profiles are that they are oblivious to the declared bundle dependencies (Import-Package, Require-Bundle etc) and hence the provisioning system cannot avoid duplication.
An interesting alternative that does not suffer from this problem is the OSGi Bundle repository technology (OBR), available from Apache Felix OBR. An OSGi bundle repository stores not just bundle binaries but also their capabilities and requirements as extracted from the bundle manifest and possibly other sources (such as component descriptors etc). The provisioner can use these and information about the current runtime to provision exactly what is needed. So provisioning bundle A, which depends on capabilities X and Y where Y is already available in the runtime, will install A and a bundle that provides X (plus any additional transitive dependencies of that bundle). This is a powerful provisioning model that eliminates the need to exactly specify bundle combinations once and for all and shifts the brunt of the work to the provisioning system.
For playing around with OBR, Karaf offers an OBR provisioner in the OBR feature. Generating bundle repositories can either be done via the Bindex tool, which supports just package and bundle dependencies declared in the bundle manifest, or the Apache Aries repository generator, which also generates capabilities and requirements for Blueprint defined services and references – this allows service based provisioning. A collection of public OBR repositories can be found at Apache Felix Sigil.
Pre-built enterprise stacks
The runtime options presented so far were all concerned with simplifying the task of managing custom runtimes with just the features needed for the desired OSGi application and no excess baggage. However, in some cases the benefit of a smaller runtime and faster startup time will not outweigh the extra effort required to manage what is essentially a light-weight app server runtime rather than taking a pre-defined all inclusive package.
There are three open-source options: Apache Geronimo 3.0, Eclipse Virgo and Glassfish. Each of these supports different types of applications. Underlying those full fledged options are a number of Enterprise specifications, amongst others Blueprint, JNDI, Web and JPA. These are available as individual components from Eclipse Gemini and also Apache Aries – with the exception of the Web Application Bundle support, which is available from Pax Web – for separate consumption (for example via Karaf, which has pre-defined features for the Aries components). Even though the application models are syntactically different, the core idea is the same between Geronimo and Virgo.
An OSGi application, defined via an application manifest in Geronimo, or a plan in Virgo, defines the collection of bundles (and even configuration artifacts in Virgo’s plans) that logically define the application. As an example, the descriptor for the Apache Aries Blog sample application is shown above.
Soon after the first steps of OSGi development are taken, many people wonder how to connect things up. For example how can I make two OSGi applications running in two different frameworks talk to each other? How easily can OSGi applications be distributed across multiple deployment systems?
How can I connect an OSGi application to a legacy JEE application? Apache CXF and Tuscany provide some answers. Apache CXF is the reference implementation of the Distributed OSGi specification. With very simple metadata additions it allows two OSGi frameworks to be connected on the OSGi service levels. This method of distribution fits very nicely with the OSGi service model, which is dynamic from the start. As with most OSGi specification implementations there is an Eclipse equivalent for distributed OSGi in the ECF project.
Apache Tuscany can be used to connect OSGi services to non-OSGi artifacts such as traditional Java, Spring or Web components. For this Tuscany supports SCA (Service component architecture) components to be implemented by OSGi services. In this way such services can be consumed by other SCA components, which are not necessarily implemented in OSGi. Similarly OSGi services defined as SCA components can declare references to, and thus consume, other SCA components as services, which again need no longer be implemented in OSGi.
This brief tour has hopefully inspired the reader with the sense of how much the OSGi ecosystem has to offer. In the authors’ experience the amount of reuse between the various different tools and frameworks is staggering. This only goes to show the maturity of the components and benefits of OSGi as a platform for modularity and reuse.
Article taken from Java Tech Journal.
- Developing Bundles
- Component Models