Whats New in Core 4.3?
OSGi Director of Technology, Peter Kriens, and Senior Technical Staff Member at IBM, BJ Hargrave, look at the latest OSGi spec.
The OSGi Alliance has recently published the OSGi Service Platform Release 4 Core Specification Version 4.3. This update to the Core specification adds a number of new features to the OSGi Framework. Some of the new features are useful to application programmers while other new features are meant for systems programmers. Let’s take a look at these new features.
Release 1 of the OSGi specifications came out in 2000, well before Java 5 introduced a number of new and useful language features. The OSGi Framework API looks a bit old fashioned in the absence of the use of any of these new language features. But Java Micro Edition (CDC-1.1/Foundation-1.1), based upon the Java 1.4 language and class libraries, is still a target platform for OSGi development. So to update the OSGi Framework API for Java 5 language features would mean abandoning the Java ME users of OSGi. The OSGi Alliance did not want to do that.
Instead, we took advantage of the undocumented compiler option -target jsr14 to allow the use of Java 5 generics in the API while still supporting the Java ME platform. Both javac from the JDK and the Eclipse Compiler for Java support this option in their current releases. Since enums and annotations require class library support which does not exist in Java ME, the OSGi Framework API does not use these language features.
The main place in the OSGi Framework API where generics are used is in the Service Layer APIs for type safety. Several methods have been overridden to take Class objects and the service related interfaces have formal types. So now one can use the Service Layer in a type safe manner.
The changes provide type safe references to OSGi services and avoid the need for explicit casts.
Capabilities and Requirements
OSGi bundles have always been able to export packages to, and import package from, other bundles. The resolver in the OSGi Framework has to match exporters to importers in order to resolve bundles for execution. For the Core 4.3 release, we have generalized this into the concepts of bundles offering capabilities to other bundles and bundles expressing requirements for a capability from another bundle.
We define a capability as a set of attributes (key/value pairs) in a namespace and a requirement as a filter expression over the attribute set of a capability in a namespace. The specification defines the built-in namespaces: osgi.wiring.package, osgi.wiring.bundle and osgi.wiring.host, to represent the existing concepts of exporting and importing packages, requiring bundles and being a fragment respectively. For example, within the osgi.wiring.package namespace, an export package is represented by a capability and an import package is represented by a requirement.
The idea of generic capabilities and requirements is also added. This allows bundle programmers to define their own namespaces so a bundle can provide a capability with the new Provide-Capability manifest header and have a requirement for a capability with the new Require-Capability manifest header.
These requirements become additional constraints to the resolver in the OSGi Framework and are visible in the new Bundle Wiring API (Figure 1).
A bundle cannot be resolved unless its mandatory requirements can be satisfied by a capability from some bundle. The built-in namespaces cannot be used on the new Provide-Capability and Require-Capability manifest headers).
In addition to the built-in namespaces, the specification defines the new osgi.ee namespace to replace the org.osgi.framework.executionenvironment framework launching property and the Bundle-RequiredExecutionEnvironment manifest header. Now bundles can use the Require-Capability manifest header to declare a requirement in the osgi.ee namespace on the execution environment. Frameworks can be configured with the new org.osgi.framework.system capabilities and org.osgi.framework.system.capabilities.extra framework launching properties to provide capabilities in the osgi.ee namespace representing the current execution environment. This will result in the system bundle providing the specified capabilities which can then satisfy the requirement from a bundle.
Bundle Wiring API
The PackageAdmin framework service was introduced by Release 2 in 2001. Using a service to supply framework function was an important choice at the time. It enabled additional function to be added in an optional manner so resource constrained systems could implement the OSGi Framework without implementing the PackageAdmin service. It also avoided adding methods to the core framework types such as Bundle which would only be interesting to systems programmers. But the cost of this design choice was an API that is not very object oriented since you have to pass the Bundle objects to the methods. Ten years later, today’s resource constrained systems are much bigger, and we also find that the PackageAdmin service does not well model the actual wiring of bundles in the Framework.
In 4.3, we introduce the new Bundle Wiring API which replaces PackageAdmin and provides a much richer model of the wiring relationship between bundles. Instead of using a service to deliver the new API, the API is available via the new, type safe adapt method on Bundle. To learn about the wiring state of a bundle, adapt the Bundle object to the desired bundle wiring type. For example:
Whenever a bundle is installed or updated, a new revision of the bundle is created. A BundleRevision for a bundle contains information about a specific revision of the bundle such as the symbolic name, version and declared capabilities and requirements. The most recent revision of a bundle is called the current revision and can be obtained by adapting the bundle to the BundleRevision type.
Whenever a bundle is resolved, a new wiring of the bundle is created. A BundleWiring for a bundle contains information about a specific wiring of the bundle, such as the capabilities and requirements that are wired together. The wiring can also be used to scan the class path of a bundle and to obtain the class loader of a bundle. The most recent wiring of a bundle is called the current wiring and can be obtained by adapting the bundle to the BundleWiring type (Figure 2).
If a bundle has been updated, older revisions and wirings of the bundle may still be in use. All the available revisions of a bundle can be obtained by adapting the bundle to the BundleRevisions type (Figure 3).
The system bundle can be adapted to the FrameworkWiring type which provides access to framework wide wiring operations. Using the FrameworkWiring object, bundles can be resolved and refreshed. Refreshing a bundle will result in the non-current revisions and wirings to be released by the Framework. The removal pending bundles, that is, the bundles which have non-current revisions and wirings, can also be obtained. All of these operations are very useful for management code (Figure 4).
While PackageAdmin has been deprecated and replaced by the Bundle Wiring API, framework implementations will still implement the PackageAdmin service for some time to come to support existing bundles that use the PackageAdmin service.
Start Level API
The StartLevel framework service was introduced by Release 3 in 2003. Like the PackageAdmin service, it was also provided as a framework service as a way to add function to the Framework that could be optional and not complicate the core types. But this design, like PackageAdmin, was not very object oriented.
In 4.3, with the introduction of the adapt method to Bundle, we introduce the new Start Level API which replaces the StartLevel framework service. To inspect or modify the start level information for a bundle, the bundle can be adapted to the BundleStartLevel type. To inspect or modify the start level information for the Framework, the system bundle can be adapted to the FrameworkStartLevel type (Figure 5).
While StartLevel has been deprecated and replaced by the Start Level API, framework implementations will still implement the StartLevel service for some time to come to support existing bundles that use the StartLevel service.
Bytecode weaving is becoming very popular particularly in enterprise applications such as those using JPA. There has long been interest in bytecode weaving in OSGi but there has never been an OSGi standard way to do it. In 4.3, we introduce Weaving Hooks.
Weaving Hooks are services registered by bundles that are prepared to weave the class files loaded from other bundles. Whenever the Framework is preparing to load a class from a bundle, it first calls the weaving hooks to give them an opportunity to mutate the class file for the class. The Framework will create a WovenClass object for the class being loaded and call the WeavingHook services in service ranking order.
Each weaving hook will have the opportunity to mutate the byte array containing the class file of the class to be loaded. Since it is quite common for a weaver to add code which may call classes which are not originally used by the bundle, for example, tracing or logging APIs, the weaver needs to be able to modify the woven bundle’s wiring so the bundle can access these classes. So the WovenClass object lets the weaving hook add DynamicImport-Package entries to the bundle’s wiring. These entries can refer to the packages which contain the classes used by the newly woven class. Once all the weaving hooks have been called, the Framework will put the new DynamicImport-Package entries into effect and call the VM to define the woven class (Figure 6).
Since weaving hooks can modify classes from other bundles, care must be taken in developing and deploying weaver bundles to ensure the integrity and security of the system.
In previously released drafts for Core 4.3, there were specification proposals for Composite Bundles and virtual frameworks. These proposals were attempts at defining a grouping model for bundles such that bundles in a group would be able to share packages and services while bundles outside the group would have more limited access.
Both of these design proposals have been replaced in the final Core 4.3 specification with the introduction of Resolver Hooks and Bundle Hooks. The Resolver Hooks, along with Bundle Hooks and Service Hooks, provide low level primitives upon which grouping policies for bundles can be implemented. Using these hooks allows different grouping policies to be created rather than specifying a single grouping policy into the Framework.
A ResolverHookFactory is a service that is registered by bundles that wish to influence the Framework’s resolver and how it wires bundles together. For each resolve operation, the Framework will call the ResolverHookFactory services, in service ranking order, requesting a ResolverHook object that will be used for the duration of the resolve operation. Each resolver hook object will be called and given the opportunity to influence the resolve operation by restricting the bundles that can be resolved and which candidate capabilities can be used to satisfy a requirement (Figure 7).
So, while a resolver hook can’t actually make the decisions about how the bundles are wired together, it can influence the choices the resolver can make. This allows a resolver hook to create a grouping policy for bundles with respect to how the bundles can be wired. Using the new Resolver Hooks, the Service Hooks introduced by 4.2, and the new Bundle Hooks, a complete grouping model can be implemented.
Since resolver hooks can modify how, and even if, bundles are resolved, care must be taken in developing and deploying resolver hooks to ensure the integrity and reliability of the system.
The Service Hooks introduced by 4.2 and the new Resolver Hooks are important parts of implementing a grouping policy for bundles. With Resolver Hooks you can place limits on how bundles are wired together at resolve time. With Service Hooks you can place limits on what services are visible to a bundle. The missing piece is how to limit which bundles are visible to a bundle. This is the purpose of the newly introduced Bundle Hooks.
Bundle Hooks are services registered by bundles that are called by the Framework, in service ranking order, when the Framework must decide whether a bundle can observe another bundle. Two bundle hooks are defined. The Find Hooks are called by the Framework during the processing of the getBundle(long) and getBundles() methods of Bundle-Context. These methods are used by bundles to find other bundles. A FindHook is able to remove bundles from the result set thus preventing the calling bundle from observing the removed bundles.
The other bundle hook is the Event Hook. The Event Hooks are called by the Framework during the delivery of Bundle Events. An EventHook can remove bundles from the set of bundles whose listeners would receive the bundle event. This prevents the removed bundles from observing the Bundle Event (Figure 8).
Bundle Hook implementations must take care to ensure that bundles consistently observe or don’t observe other bundles. That is, with the Event Hook, a bundle should see either all or none of the life cycle events for a bundle. Seeing only a partial set of events can result in undefined behavior for the observing bundle.
Version 4.3 of the Core specification is another incremental improvement over past specifications. The basic abstractions of OSGi: modularity, dynamic life cycle and services, all continue as before. These new features add to the level of introspection and control of the Framework.
Some of the new features enable new and powerful capabilities to be built upon the Framework. Take care if you decide to use these low level features since they can have a significant effect on other bundles.
At the OSGi Alliance, we are continuing to work on new specifications and enhancements to the existing specifications. If you have input or want to participate in the effort, please check out the OSGi Alliance website for information on joining the OSGi Alliance or providing feedback.