This section describes the API for the generic Requirement-Capability model as introduced in the Dependencies. This API is not used directly by the Framework, the purpose of specifying this base API is to allow it to be used as building block for other specifications.
-
Resource - An entity that can be installed in an Environment where it will provide its Capabilities when all its Requirements are satisfied.
-
Environment - A framework or container that contains one or more Resources.
-
Namespace - Defines the semantics of the Requirements and Capabilities; a Requirement can only match a Capability when they are in the same Namespace. A Namespace is similar to a type in an object oriented language.
-
Requirement - An assertion on a Capability in a Namespace. The assertion uses the OSGi filter language to match the attributes of a Capability. The filter is specified as a directive; a Requirement can also have other directives and attributes.
-
Capability - An attribute based description of a quality of a Resource when installed in an Environment.
-
Wiring - The wired state of a Resource.
-
Wire - Connects a Requirement to a matching Capability.
The OSGi dependency model is introduced in Dependencies ; it is based on a generic Requirements/Capability model. In this model a Resource models something that can be installed in an Environment but has Requirements that must be satisfied by Capabilities available in that the Environment. However, once it is installed and resolved into the Environment it provides its declared Capabilities to that Environment. Bundles are Resources, an example of a Requirement is an Import-Package clause, and an example of a Capability is an Export-Package clause.
The org.osgi.resource
package contains a base API that
defines a number of interfaces that model this generic model.
The org.osgi.framework.wiring
package provides an API
for reflecting the wiring of an OSGi Framework. The purpose of the
separation is to allow the management agents to treat the system in a
uniform way. That is, it allows a wide array of resources and environments
to be modeled, and thus managed, in a uniform way. The Resource API is
therefore primarily a building block for other specifications.
The Capability and Requirement class are almost identical in their signature. They both provide the following methods:
-
getAttributes()
- Return a map with the attributes -
getDirectives()
- Return a map with the directives -
getNamespace()
- Return the Namespace -
getResource()
- Return the Resource
The key difference between a Requirement and a Capability is that a
Capability provides attributes that are matched by an OSGi filter
specified in a Requirement's filter
directive.
The Namespace acts as the type of a Capability and Requirement. A Capability and a Requirement can only match when they are in the same Namespace. The Namespaces in use by the OSGi Core specification are defined in Framework Namespaces Specification. Other specifications can, however, define their own Namespaces.
A Namespace defines:
-
Requirement Attributes - Any attributes that are allowed to be used on a Requirement declaration.
-
Requirement Directives - Any directives that are allowed to be used on a Requirement declaration.
-
Capability Attributes - Any attributes that are allowed to be used on a Capability, these attributes are available for matching.
-
Capability Directives - Any defined directives that are allowed to be used on the Capability
-
Semantics - The Namespace definition defines what the meaning is of a Capability. This can include actions in the Environment like for example being wired with certain rules.
A Requirement matches a Capability when:
-
They have the same Namespace, and
-
The Requirement's filter matches the Capability's attributes, and
-
If the Namespace is an
osgi.wiring.*
Namespace then themandatory
directive must be supported.
Other Namespaces must not introduce additional matching rules.
Before a Resource can provide its functionality it must be resolved against the Environment. The Environment can provide a number of Capabilities of its own but in general the Capabilities come from the of the installed Resources. The resolver must find a set of Wires between Requirements and Capabilities in an Environment such that each mandatory Requirement is satisfied with at least one Capability, and the constraints of the involved Namespaces are all met.
Resolving is an NP-complete problem since there are many solutions, it is easy to test if a solution is right, but there is no algorithm to calculate a solution. In practice, for the OSGi resolvers it is possible to find solutions in a reasonable amount of time. However, the nature of NP-complete problems makes it difficult to set exact rules: many solutions are correct. Constraining the resolvers too much would stifle innovation and likely cause performance problems.
In general the Environment has an existing Wiring state for already installed Resources. The resolver then calculates a resolution, which is a set of Wires that should be added to the existing Wiring state by installing the Resources. A Wire is a connection from a Requirement to a Capability. A Requirement or Capability is declared in a Resource. This is depicted in Figure 6.2.
Though each Capability and Requirement is declared in a Resource, it can however be hosted by another Resource. For example, when a Fragment has an Export-Package header it is in reality its host that will provide that package. There is therefore a clear distinction between the Resource that declares the Capability/Requirement and the run time Wiring state that hosts that Capability. For this reason, a Wire connects a Requirement and a Capability but links separately to the Resources that host the Requirement and the Capability. Figure 6.3 depicts a hosted Capability. The Capability from the Fragment bundle is hosted by Host A and Host B.
For this reason, the Wire class provides the following methods:
-
getRequirement() - The Requirement wired from.
-
getRequirer() - The Resource that hosts the Requirement.
-
getCapability() - The Capability that is wired to.
-
getProvider() - The Resource that hosts the Capability.
Requirements can be optional
or
mandatory
, as specified in the resolution
directive, which is only available on the Requirement. Optional
Requirements do not have to be satisfied by the Resolver. Environments
can be eager or relaxed in finding Resources to resolve optional
Requirements. All mandatory Requirements of a Resource must be satisfied
before that Resource's Capabilities can be provided to the
Environment.
The syntax of the resolution directive is therefore:
resolution ::= 'optional' | 'mandatory'
The default is mandatory
.
Both Requirements and Capabilities support the
effective
directive. This directive provides a name that
can be used by the Environment to control the resolving process. During
a resolve process, the Environment can then decide one or more names
that must match the effective
directive.
For the OSGi Framework, the name resolve
is reserved,
this is also the default. The syntax is therefore:
effective ::= <name>
If a Capability has declared a mandatory
directive
and the Namespace starts with osgi.wiring
then it mandates
that the names listed directive are used in the filter and must match.
The syntax for the mandatory attribute is:
mandatory ::= extended ( ',' extended )*
The cardinality directive defines if a Requirement can be wired to multiple Capabilities or must be wired to at most one. The syntax for the directive is:
cardinality ::= 'single' | 'multiple'
The default is single
.
Though the Requirement/Capability model is generic it is linked
closely with the class loading architecture of OSGi frameworks,
particularly class space consistency, see Constraint Solving. For this reason, each
Capability can specify its uses constraints with
the uses
Capability directive. The uses directive always
contains a comma separated list of package names. The resolver must
ensure that any resolution does not violate the class space consistency
based on these constraints. Uses constraints can be specified on any
Capability, not just Capabilities related to class loading, and are
always about Java packages. The syntax of the directive is:
uses ::= package-name ( ',' package-name )*
A resolver calculates a set of Wires between Requirements and Capabilities based on an existing, potentially empty, state. The existing state in the Environment is represented in a set of Wiring objects. Such an object represents the Wiring state of a Resource in an Environment. It provides access to all hosted Requirements and Capabilities as well as existing Wires. It has the following methods to conveniently provide access to the state:
-
getResource() - The related Resource.
-
getProvidedResourceWires(String) - Get any Wires, in the given Namespace, where the related Resource is the provider.
-
getRequiredResourceWires(String) - Get any Wires, in the given Namespace, where the related Resource is the requirer.
-
getResourceCapabilities(String) - Get the hosted Capabilities of the related Resource.
-
getResourceRequirements(String) - Get the hosted Requirements of the related Resource.