7 Bundle Wiring API Specification

7.1 Introduction

A key aspect of the OSGi framework is managing the dependencies between the bundles. These dependencies are expressed as manifest headers that can be grouped into requirements and capabilities as defined in Resource API Specification. For example, an Export-Package clause is a capability and an Import-Package clause is a requirement. During the resolving phase the requirements are resolved to matching capabilities by creating a Bundle Wire. Some of the wires can influence how the classes are loaded from bundles during runtime.

This section outlines the API to introspect the wiring between the requirements and capabilities of resolved bundles.

7.1.1 Entities

  • Bundle Revision - Represents the class/resource container of an install or update (that is, the JAR, directory, or other form of archive). Each update creates a new Bundle Revision and an uninstall removes the Bundle Revisions. A Bundle Revision is modeled after a Resource.

  • Namespace - Bundle Requirements and Bundle Capabilities are defined in a namespace, namespaces define the semantics of the requirements and capabilities. The osgi.wiring.bundle, osgi.wiring.host and osgi.wiring.package from the Framework Namespaces are defined in Framework Namespaces Specification.

  • Bundle Requirement - Represents a requirement header, either the Require-Capability header or any of the manifest headers referred to in the Framework Namespaces Specification that map to a requirement.

  • Bundle Capability - A quality of a Bundle Revision that is provided when the revision is installed. Implemented as a set of attributes that are part of a namespace. A Bundle Capability represents either the Provide-Capability manifest header clauses, or any headers defined in the OSGi namespaces that map to a capability.

  • Bundle Wiring - Created each time when a Bundle Revision is resolved for holding the wires to other Bundle Wirings as well as maintaining the run time state. Used by the framework to control class loading depending on the semantics of the OSGi namespaces.

  • Bundle Wire - Connects a Bundle Requirement to a Bundle Capability as well as the requirer Bundle Wiring and provider Bundle Wiring.

  • Framework Wiring - Provides access to manage and initiate refresh and resolving.

Figure 7.1 Class Diagram org.osgi.framework.wiring (with relations to org.osgi.resource)

Class Diagram org.osgi.framework.wiring (with relations to org.osgi.resource)

7.2 Using the Wiring API

This section explains how the wiring API can be used without fully explaining all the concepts in depth. The next sections outline the formal specification.

7.2.1 Synopsis

The Bundle Context installBundle method installs a bundle and returns a Bundle object. This install provides the classes and resources in a JAR, directory or some other form, as an environment. This resource is represented as a Bundle Revision.

A Bundle Revision declares a number of Bundle Capabilities as well as a number of Bundle Requirements. A capability represents a set of attributes and a requirement is a filter on those attributes. For a requirement to be applicable to a capability, they must reside in the same namespace. The namespace groups requirements and capabilities and defines the semantics for a resolved requirement/capability pair. This pair is represented as a Bundle Wire.

Capabilities can be anything: certificates, screen size, the packages, the bundle itself or the capability to act as host for a fragment. Some capabilities and requirements are from the Provide-Capability and Require-Capability headers, others are defined by the OSGi headers defined in Module Layer, the namespaces for these OSGi specific headers are defined in Framework Namespaces Specification.

The framework wires the Bundle Requirements to Bundle Capabilities during the resolving operation. The framework must resolve all the requirements to matching capabilities according to the semantics of their namespaces before it can declare a bundle to be resolved. For generic namespaces it is sufficient to find a matching capability for each requirement. However, for the OSGi namespaces additional rules are implied. For example, the osgi.wiring.host namespace implies all the rules around OSGi fragment bundles.

Once a bundle is in the RESOLVED state it gets a Bundle Wiring, the Bundle Wiring represents the run time state of the Bundle Revision. The Bundle Wiring holds the Bundle Wires. A Bundle Wire ties a single Bundle Requirement to a single Bundle Capability as well as tying the Bundle Wiring that holds the requirement to the Bundle Wiring that holds the capability. The Bundle Wires that flow from a Bundle Wiring's Requirement to a capability are the required wires, they can be obtained with getRequiredWires(String). Bundle Wires that come from a Bundle Wiring's Capability to a requirement are the provided wires, they can be obtained with getProvidedWires(String). The same requirements and capabilities can be used in different wires.

Namespace rules can be complex. For example, in the case of fragments they imply that any capabilities from the fragment are actually available from its hosts. In the case of exported packages that are also imported the resolver can choose to pick either. These examples demonstrate that the resolver must be able to differentiate between the Bundle Revision's declared requirements and capabilities and the run time state, the Bundle Wiring, of the corresponding Bundle Revision. A Bundle Revision's Bundle Wiring therefore provides the actual run time requirements and capabilities as chosen by the resolver with the Bundle Wiring's getRequirements(String) and getCapabilities(String) methods. Any optional declared requirements that were not satisfied are not in the list of requirements. All dynamic requirements that can potentially be satisfied at run time are in this requirements list.

The BundleWiring objects are therefore not necessarily associated with the same Bundle Revisions that originate the declared Bundle Requirement and the declared Bundle Capability. It is therefore that the diagramming technique used in Figure 7.2 uses dotted lines for the Bundle Wiring connection. That is, the connections from the Bundle Wire to the requirer BundleWiring object and to the provider BundleWiring object. It then uses solid lines for the connection to the declared requirement and capability in their Bundle Revisions. This technique makes it possible to depict fragments where a capability in the fragment is actually available from the host's Bundle Wiring.

Figure 7.2 Requirements and Capabilities Diagramming

Requirements and Capabilities Diagramming

BundleWiring objects can continue to exist and operate as long as there are wires from other BundleWiring objects, even after a bundle is updated. The only way to break this non-current wiring is to refresh the bundles that are involved. The refresh operation computes a transitive closure of all dependent bundles, and re-resolves them. Any active bundles in this closure will be stopped and restarted. This operation can be activated on the Framework Wiring.

The wiring API is based on the Bundle.adapt() method, see Adaptations. This method allows the Bundle object to be adapted to another type. For example:

BundleWiring current = bundle.adapt(BundleWiring.class);
if ( current != null ) {
  ...
}

For this API, the following adaptations of the Bundle object are supported:

  • BundleRevision - Provides access to the current revision at the time of the adapt method call. A Bundle will always have a current Bundle Revision until it is uninstalled.

  • BundleWiring - Provides access to the current Bundle Wiring at the time of the adapt method call. A current Bundle Wiring object only exists (the adapt method returns non-null) when the bundle is resolved.

  • BundleRevisions - Provides access to all the BundleRevision objects that are still in use. A Bundle always has a BundleRevisions object, the adapt method must never return null.

  • FrameworkWiring - Can only be adapted from the system bundle with bundle id 0. Provides access to the management methods like refresh and resolve, and information about bundles that are pending removal, and the dependency closure of a set of bundles.

The Bundle Wiring API is usable during launching after the init method has returned.

After an uninstall the adapt method will always return null for BundleRevision or BundleWiring. However, it is possible that the Bundle Revision and/or its Bundle Wiring are reachable through other bundles.

7.2.2 Finding the Imported Packages

Packages are reflected in the osgi.wiring.package namespace. An Import-Package clause is mapped to an osgi.wiring.package requirement and an Export-Package clause is mapped into the corresponding capability. For example:

Import-Package: com.acme.foo;version=1
Export-Package: com.acme.foo;version=1

In the Requirements/Capabilities model this is depicted as in Figure 7.3.:

Figure 7.3 Example Import/Export Package with Requirements/Capabilities Model

Example Import/Export Package with Requirements/Capabilities Model

The following code prints the bundles that bundle A is wired to through Import-Package statements:

void printImports( Bundle A ) {
    BundleWiring wA = A.adapt( BundleWiring.class );
    for ( BundleWire wire :
     wA.getRequiredWires(PACKAGE_NAMESPACE)) {
     String pack = (String) wire.getCapability().getAttributes()
         .get(PACKAGE_NAMESPACE);
     Bundle bundle = wire.getProviderWiring()
          .getBundle());
     System.out.println(pack + " - " + bundle.getLocation());
    }
}

7.2.3 Attached Fragments

Fragments use the osgi.wiring.host namespace to control their attachment. A fragment has a requirement for a host capability, this is a capability with the bundle symbolic name and version. If a fragment is attached then there is a wire from the fragment's Bundle Wiring to the host's Bundle Wiring.

The following snippet finds the attached fragments of a bundle:

Set<BundleWiring> attachedFragments( BundleA ) {
    Set<BundleWiring> result = new HashSet<BundleWiring>();
    BundleWiring wA = A.adapt( BundleWiring.class );

    for ( BundleWire wire : wA.getProvidedWires(HOST_NAMESPACE)) {
        result.add( wire.getRequirerWiring() );
    }
    return result;
}

7.3 Bundle Wiring

A bundle provides a simplified view of the state of the framework: it is either resolved or not. If it is resolved, bundles can become active and collaborate with other resolved bundles. During the time a bundle is resolved, and thus can see the environment, it will see a consistent stable state with respect to its code dependencies. Other bundles can be started and stopped, installed, updated, and uninstalled during the life cycle of a bundle. However, as long as a bundle is resolved it will continue to load classes from the bundle revisions it was wired to when it was initially resolved, even if those bundles are updated or uninstalled.

The consequence of this model is that each bundle can have multiple revisions, and each revision can have an optional wiring at any moment in time. Management agents have the need to see this more complex state to be able to predict the impact of management actions and to help diagnose problems.

There are two important event types that complicate the overall state. The install and update events provide a new Bundle Revision for a bundle and the uninstall event disconnects any Bundle Revisions from the bundle. The Bundle Revision contains the resources and the metadata defining, among other things, what type of bundle it is and what its dependencies are. An update can therefore change every aspect of a bundle. For example, an update could turn a non-fragment bundle into a fragment.

The other event types that is of interest here are the RESOLVED and UNRESOLVED events. Resolving a bundle creates a Bundle Wiring based on the then current Bundle Revision. During resolving, a Bundle Wiring uses the requirements from the Bundle Revision to create wires to other Bundle Revisions; the wires are used to control the class loading process. Once a Bundle Wiring is required by another Bundle Wiring, or it is the current wiring, it is said to be in use. This model is depicted in Figure 7.4.

Figure 7.4 Relationship between events, revisions, and wirings

Relationship between events, revisions, and wirings

The framework never eagerly disconnects the wires between Bundle Wirings, a disconnect happens only under control of the management agent when the refreshBundles(Collection,FrameworkListener...) method is called or when all requiring bundles become uninstalled. When a bundle is updated, its existing BundleWiring objects will continue to serve classes and resources to bundles that use it. The update, even though it provides a new revision, has no effect on resolved bundles with respect to class loading. Also, the installation of a new bundle could allow new wires but they must not affect the existing wiring until refreshBundles(Collection,FrameworkListener...) is called (with the exception for dynamic imports). Though the class loading wires remain in place, proper bundles should react to the changes in the dynamic state. That is, when a bundle is updated it will be stopped, allowing others to remove any references they have to that bundle. However, even in those cases the wirings will remain until the bundle is unresolved during a refresh cycle.

After an update, the existing Bundle Wiring is no longer current for the bundle.

Bundle Wirings that are not in use (no other Bundle Wiring is wired to it) can be removed immediately but in-use Bundle Wirings must remain in place until they become no longer in use. These non-current in-use Bundle Wirings are called pending for removal.

To forcefully remove all these non-current in use Bundle Wirings the framework can refresh a set of bundles at the request of a management agent. The refresh will create a transitive dependency graph on an initial set of bundles and then unresolves each bundle in the graph, which will make any of the stale Bundle Wirings no longer in use so they can be cleaned up. After this refresh, any previously active bundles will be restored to their previous state.

The purpose of this non-eager behavior is to allow for efficient handling of multiple updates/installs/uninstalls. Refreshing the wires after each such event requires the start and stop of the dependent bundles, disrupting the operations of the system unnecessary often. It is therefore better to batch up a number of such operations and then refresh the system once. However, the implication of this optimization is that the actual wiring between bundles can quickly become an intricate web of connections between revisions of bundles.

For example, assume a bundle A is installed. At installation, it will have a single Bundle Revision, called A.0. Next, bundle B is installed, it will have a Bundle Revision B.0. Assuming Bundle Revision A.0 requires a capability in bundle B, resolving bundle A and bundle B will create a Bundle Wiring for Bundle Revision A.0 linking to a Bundle Wiring for Bundle Revision B.0. If bundle B is now updated, it will create a second Bundle Revision, B.1.However, the current Bundle Wiring for bundle A (Bundle Revision A.0) will remain wired to Bundle Revision B.0 as long as bundle A and bundle B remain resolved, even though the current Bundle Revision for bundle B has now become B.1. As long as Bundle Revision A.0 remains resolved, bundle B's resolved state has no impact.

Bundles are only actually unresolved when they are refreshed, the UNRESOLVED event only indicates that a Bundle is updated or uninstalled. Refreshing happens on a per bundle basis but takes any Bundle Wirings into account that depend on the refreshed bundle. In the previous example, if bundle B is refreshed, it will automatically refresh bundle A because A is wired to B. That is, bundle B is in use by A. The refresh will stop bundles A and B and then unresolve both of them. Unresolving basically means removing any reference from the framework to the Bundle Wirings of the involved bundles. This unreferencing will allow the garbage collector to remove any remains, like for example the class loader and the activator, unless some bundles illegally hold on to references. Once a Bundle Wiring is no longer required by the framework, it is set to be not in use, regardless of stale references.

Normally, after unresolving, the bundles are started again in their original state, forcing them to resolve again. In the previous example, Bundle Revision A.0 will then be connected to the Bundle Revision B.1 through newly created BundleWiring objects. The old Bundle Wiring for B.0 will no longer be in use and will thus be garbage collected.

This example is depicted in Figure 7.5. This picture shows when the different objects are created and discarded. In this picture bundle B is not started.

Figure 7.5 The Bundle Revisions and Bundle Wirings over time

The Bundle Revisions and Bundle Wirings over time

The resolver is responsible for wiring Bundle Requirements and Bundle Capabilities to each other while adhering to the semantics defined in their namespace. For each paired Bundle Requirement and Bundle Capability the resolver creates a Bundle Wire that links the Bundle Requirement, the requiring Bundle Wiring, the providing Bundle Wiring, and the Bundle Capability. The relationships between a bundle A and bundle B, where A requires some capability in B, is depicted in Figure 7.6.

Figure 7.6 Bundle A requires Bundle B Wiring Instances

Bundle A requires Bundle B Wiring Instances

The OSGi framework can add wires and new requirements and capabilities after resolving during run time. This mechanism is for example used in DynamicImport-Package, dynamic attaching of fragments, and byte code weaving.

7.4 Fragments

The type of a bundle is available on the Bundle Revision because a Bundle can change from a fragment to a normal bundle or vice versa after an update. The getTypes() method is used to obtain a bitmap that is either 0 or has the following bit set:

  • TYPE_FRAGMENT - If this bit is set the Bundle Revision is a fragment.

The type is a bitmap allowing future versions of this specification to add new types that can be a combination of existing and new types. The following example displays how a Bundle is checked to be a fragment:

BundleRevision rev = aBundle.adapt(BundleRevision.class);
if ( rev != null && (rev.getTypes() & TYPE_FRAGMENT)!= 0 ){
  ... // do the fragment thing
}

A fragment bundle will show all its declared capabilities and requirements on its Bundle Revision but during resolving the resolver only considers the osgi.wiring.host and osgi.ee requirements and the osgi.identity capability and requirements.

The osgi.wiring.host requirement represents the Fragment-Host header. A fragment can be attached to different hosts and each attachment creates a wire from the fragment's Bundle Wiring to the host's Bundle Wiring. The osgi.ee requirement is also never hosted.

The osgi.identity capability of a fragment is part of the fragment's Bundle Wiring and is not part of a host bundle's Bundle Wiring. That is, each Bundle Wiring has exactly one osgi.identity capability. However, osgi.identity requirements declared by a fragment are not part of the fragment's Bundle Wiring and are instead hosted by the host bundle's Bundle Wiring.

Any other requirements and capabilities in a fragment bundle never become part of the fragment's Bundle Wiring; they are treated as part of the host's requirements and capabilities when the fragment is attached to that host.

To find the attached fragment for a host bundle it is necessary to find the wires for the osgi.wiring.host capability. The requiring end of such a wire is the attached fragment and the providing end is the attaching host.

For example, bundle A is a host and bundle B is a fragment as depicted in Figure 7.7 on page .

Figure 7.7 Fragments and Wiring

Fragments and Wiring

Then, to find the attached fragments for Bundle Revision A0:

List<BundleWiring> attached = new ArrayList<BundleWiring>();
for ( BundleWire wire : A0.getBundleWiring().getProvidedWires(HOST_NAMESPACE)) 
    attached.add( wire.getRequirerWiring() );

It is also possible to calculate the reverse dependency for finding the hosts of a fragment. For the previous example, the bundles that attach fragment B can be found with:

List<BundleWiring> hosts = new ArrayList<BundleWiring>();
for ( BundleWire wire :  B0.getBundleWiring().getRequiredWires(HOST_NAMESPACE)) 
    hosts.add( wire.getProviderWiring() );

The osgi.wiring.host namespace mandates that the resolver moves the Bundle Requirements and Bundle Capabilities from the fragment in all other namespaces than the osgi.wiring.host, osgi.identity and osgi.ee namespaces to the host bundle. For example, if the fragment exports a package p, then this package is exported by the host. In such a case, the BundleRequirement and BundleCapability objects remain associated with the Bundle Revision of the fragment. However, the Bundle Wire has the appropriate Bundle Wiring of the host. This is depicted in Figure 7.8 on page . Package p is declared a capability in fragment B.0 but when wired the Bundle Wiring of host A.0 will be the provider.

Figure 7.8 Exporting a Package from a Fragment

Exporting a Package from a Fragment

The previous example is also depicted as an instance diagram in Figure 7.9 on page .

Figure 7.9 Fragments Wiring Instances

Fragments Wiring Instances

7.5 Framework Actions

There are a number of actions that are global in a framework and not associated with a specific bundle. These actions are associated with the framework; this is the reason for the Framework Wiring adaptation. The system bundle (bundle 0) can be adapted to a FrameworkWiring object:

FrameworkWiring fw = systemBundle.adapt(FrameworkWiring.class);

The Framework Wiring provides the following actions:

  • findProviders(Requirement) - The find providers method returns capabilities available in the framework that match the given requirement. This method can be used to search for capabilities provided by bundles in the framework. For example, an exported package with a specific package name.

  • getDependencyClosure(Collection) - The dependency closure method takes a seed of bundles for a dependency closure and then add any bundles that depend a bundle in the dependency closure, recursively. The result can be used to calculate the impact of a refresh operation. If the framework is refreshed the result of this method provides the bundles that will be affected.

  • getRemovalPendingBundles() - Bundles that have a Bundle Wiring that is in use but not current. Such bundles are pending removal.

  • refreshBundles(Collection,FrameworkListener...) - See Refreshing.

  • resolveBundles(Collection) - Attempt to resolve all the bundles in the given collection. This action can also cause bundles to become resolved outside the given collections.

7.5.1 Refreshing

The update of bundles will create new Bundle Revisions while the existing Bundle Wirings remain wired to their previous Bundle Revisions. This stale wiring must be cleaned up and the refreshBundles(Collection,FrameworkListener...) method achieves this.

The refreshBundles method works from an initial collection of bundles that is used to seed the calculation of the dependency closure. The dependency closure is calculated by expanding the seed dependency closure to include any bundle that has a Bundle Wiring that depends on any bundle in the dependency closure. This is a recursive definition so the dependency closure contains the list of transitive dependencies on the initial seed collection.

This dependency closure can be obtained separately with the getDependencyClosure(Collection) method providing it with the same seed. If no seed is provided, that is a null argument is given, the refreshBundles method will be identical to calling it with the result of the getRemovalPendingBundles() method as the seed collection. This default will ensure that all stale Bundle Wirings will be cleaned up.

The refresh process will stop any bundles in the considered collection while recording their state and, if active, their starting option (for example START_TRANSIENT). Stopping must take any start level rules into account.

The refresh must then unresolve all the bundles in the considered collection. Unresolving will cause all the removal pending Bundle Wirings to become no longer in use because there are no longer any bundles requiring them. This will make the Bundle Wirings available for garbage collection because they are then no longer reachable from the framework API.

The framework must then attempt to restore the state as it was before the refresh taking all the framework rules into account, including start levels, start options, and activation options.

The actual refresh operation will take place in the background because it can be a long running operation. The refresh operation will send out a global framework event PACKAGES_REFRESHED. However, catching this event properly is non-trivial. For this reason, the refreshBundle method also allows a callback by specifying an optional Framework Listener in the method invocation that will only be called when the method is finished. For example:

fw.refreshBundles( null, new FrameworkListener(){
  public void frameworkEvent(FrameworkEvent ev) {
     System.out.println("Refresh finished");
  }
});

7.6 Container Scanning

A resolved bundle can consist of a number of containers: the basic bundle container (usually a JAR), embedded JARs or directories, and fragments. Containers contain entries but the Bundle-ClassPath header turns these entries into a single namespace, called resources. These concepts are fully defined in Bundle Class Path.

The wiring API provides two different ways to iterate over the contents in the containers that constitute a resolved bundle:

  • Bundle Class Path Order - Scan the bundle class path containers.

  • Entry Order - Scan all the entries that constitute a bundle and its attached fragments.

These two different ways are outlined in the following sections.

7.6.1 Bundle Class Path Order

Once a bundle is resolved all its container namespaces are flattened to a single namespace that is then used by the class loader. Flattening has as a consequence that certain resources will disappear from the view, which resource remains and which disappear depends on the order of the flattening. The OSGi specification defines exactly what this order is. However, the rules for this ordering are many and non-trivial. For this reason, a Bundle Wiring allows the iteration over the resources of a bundle in the bundle class path order, reflecting the same flattening as that what the class loader will do. A bundle must be resolved to be able to iterate over its resources.

The method used to iterate over the resources in bundle class path order is listResources(String,String,int). This method takes a starting path in the namespace, a pattern to match (for example *.class for class resources) and a flag to indicate if the scan should recurs into directories or not.

When the bundle class path has a multi-release container, see Multi-release Container, and an argument to the listResources(String,String,int) method would include a resource name in the method result if the resource was not available from the root directory but is available from a versioned directory visible on the current Java version, then the method result must include the resource name from the root directory. For example, if the multi-release container has the following entry

META-INF/versions/9/com/foo/resource.txt

and the call listResources(“/com/foo”, “*.txt”, 0) is made when running on Java 9, or later, the result must include

com/foo/resource.txt

The listResources(String,String,int) method has no counterpart in the standard class loader API.

7.6.2 Entry Order

A Bundle Wiring reflects a resolved bundle. This wiring constitutes the bundle and any attached fragments. The findEntries(String,String,int) method is similar to the Bundle.findEntries(String,String,boolean) method. The Bundle's method will be identical when the bundle can be resolved, if the bundle cannot resolve the Bundle's findEntries method has a fallback that allows iteration without attached fragments. The Bundle Wiring's findEntries(String,String,int) is always against a resolved bundle because it is on a Bundle Wiring.

7.6.3 Class Loader Access

The class loader can also be obtained from the BundleWiring class with the getClassLoader() method.

7.7 Security

The Bundle Wiring API requires Adapt Permission with action ADAPT for the following types:

  • org.osgi.framework.wiring.BundleWiring

  • org.osgi.framework.wiring.BundleRevision

  • org.osgi.framework.wiring.BundleRevisions

  • org.osgi.framework.wiring.FrameworkWiring

The Framework Wiring methods that mutate state require an additional Admin Permission with the action:

7.8 Changes