The standard Java platform provides only limited support for packaging, deploying, and validating Java-based applications and components. Because of this, many Java-based projects, such as JBoss and NetBeans, have resorted to creating custom module-oriented layers with specialized class loaders for packaging, deploying, and validating applications and components. The OSGi Framework provides a generic and standardized solution for Java modularization.
The Framework defines a unit of modularization, called a bundle. A bundle is comprised of Java classes and other resources, which together can provide functions to end users. Bundles can share Java packages among an exporter bundle and an importer bundle in a well-defined way. In the OSGi framework, bundles are the only entities for deploying Java-based applications.
A bundle is deployed as a Java ARchive (JAR) file. JAR files are
used to store applications and their resources in a standard ZIP-based
file format. This format is defined by [9] Zip File Format. Bundles
normally share the Java Archive extension of .jar
. However,
there is a special MIME type reserved for OSGi bundles that can be used to
distinguish bundles from normal JAR files. This MIME type is:
application/vnd.osgi.bundle
The type is defined in [14] OSGi IANA Mime Type.
A bundle is a JAR file that:
-
Contains the resources necessary to provide some functionality. These resources may be class files for the Java programming language, as well as other data such as HTML files, help files, icons, and so on. A bundle JAR file can also embed additional JAR files that are available as resources and classes. This is however not recursive.
-
Contains a manifest file describing the contents of the JAR file and providing information about the bundle. This file uses headers to specify information that the Framework needs to install correctly and activate a bundle. For example, it states dependencies on other resources, such as Java packages, that must be available to the bundle before it can run.
-
Can contain optional documentation in the
OSGI-OPT
directory of the JAR file or one of its sub-directories. Any information in this directory is optional. For example, theOSGI-OPT
directory is useful to store the source code of a bundle. Management systems may remove this information to save storage space in the OSGi framework. -
Can be a multi-release JAR. See Multi-release JAR.
Once a bundle is resolved, its functionality is provided to other bundles installed in the OSGi framework.
A bundle can carry descriptive information about
itself in the manifest file that is contained in its JAR file under the
name of META-INF/MANIFEST.MF
.
The Framework defines OSGi manifest headers such as Export-Package and Bundle-ClassPath, which bundle developers use to supply descriptive information about a bundle. Manifest headers must strictly follow the rules for manifest headers as defined in [10] Manifest Format.
A Framework implementation must:
All specified manifest headers are listed in the following sections. All headers are optional, unless specifically indicated.
The Bundle-ActivationPolicy specifies how the framework should activate the bundle once started. See Activation Policies.
The Bundle-Activator header specifies the name of the class used to start and stop the bundle. See Starting Bundles.
The Bundle-Category header holds a comma-separated list of category names.
The Bundle-ClassPath header defines a comma-separated list of
JAR file path names or directories (inside the bundle) containing
classes and resources. The full stop ('.' \u002E
)
specifies the root directory of the bundle's JAR. The full stop is
also the default. See Bundle Class Path.
The Bundle-ContactAddress header provides the contact address of the vendor.
The Bundle-Copyright header contains the copyright specification for this bundle.
The Bundle-Description header defines a short description of this bundle.
The Bundle-Developers header provides an optional machine readable form of information about the developers of the bundle. This header is purely informational and must not be processed by the OSGi Framework.
The syntax for this header is as follows:
Bundle-Developers ::= developer ( ',' developer ) *
developer ::= identifier ( ';' developer-attr ) *
identifier ::= argument
developer-attr ::= email | name | url | organization |
organization-url | roles | timezone
email ::= 'email' '=' <email>
name ::= 'name' '=' argument
url ::= 'url' '=' <url>
organization ::= 'organization' '=' argument
organization-url ::= 'organizationUrl' '=' <url>
roles ::= 'roles' '=' argument
timezone ::= 'timezone' '=' argument
This header has the following attributes:
-
email
- (optional) The email address of the developer. -
name
- (optional) The name of the developer. -
url
- (optional) The URL of the developer. -
organization
- (optional) The name of the organization of the developer. -
organization-url
- (optional) The URL of the organization of the developer. -
roles
- (optional) The roles of the developer. This is a comma separated list of role names. -
timezone
- (optional) The time zone of where the developer resides. The time zone can be an id such asAmerica/New_York
or a numerical offset in hours from UTC.
See [25] Maven POM Reference, Developers for more information. Tools can use the information in this header for POM generation.
The Bundle-DocURL headers must contain a URL pointing to documentation about this bundle.
The optional Bundle-Icon header provides a list of URLs to icons representing this bundle in different sizes. The following attribute is permitted:
-
size
- (integer) Specifies the size of the icon in pixels horizontal. It is recommended to always include a 64x64 icon.
The URLs are interpreted as relative to the bundle. That is, if a URL with a scheme is provided, then this is taken as an absolute URL. Otherwise, the path points to an entry in the JAR file, taking any attached fragments into account. Implementations that want to use this header should at least support the Portable Network Graphics (PNG) format, see [17] Portable Network Graphics (PNG) Specification (Second Edition).
The Bundle-License header provides an optional machine readable form of license information. The purpose of this header is to automate some of the license processing required by many organizations like for example license acceptance before a bundle is used. The header is structured to provide the use of unique license naming to merge acceptance requests, as well as links to human-readable information about the included licenses. This header is purely informational for management agents and must not be processed by the OSGi Framework.
The syntax for this header is as follows:
Bundle-License ::= '<<EXTERNAL>>' |
( license ( ',' license ) * )
license ::= license-identifier ( ';' license-attr ) *
license-attr ::= description | link
description ::= 'description' '=' string
link ::= 'link' '=' <url>
This header has the following attributes:
-
license-identifier
- Provides a globally unique identifier for this license, preferably world wide, but it should at least be unique with respect to the other clauses. The magic identifier<<EXTERNAL>>
is used to indicate that this artifact does not specify any license information but that licensing information is provided in some other way. This is also the default value of this header.This identifier should be one of the identifiers defined by [24] Software Package Data Exchange (SPDX) License List. Clients of this bundle can assume that licenses with the same identifier refer to the same license. This can for example be used to minimize the click through licenses.
Alternatively, the identifier can be the canonical URL of the license, it must not be localized by the translator. This URL does not have to exist but must not be used for later versions of the license. It is recommended to use URLs from [18] Open Source Initiative. Other licenses should use the following structure, but this is not mandated:
http://<domain-name>/licenses/ <license-name>-<version>.<extension>
-
description
- (optional) Provide the description of the license. This is a short description that is usable in a list box on a UI to select more information about the license. -
link
- (optional) Provide a URL to a page that defines or explains the license. If this link is absent, the name field is used for this purpose. The URL is relative to the root of the bundle. That is, it is possible to refer to a file inside the bundle.
If the Bundle-License statement is absent, then this does not
mean that the bundle is not licensed. Licensing could be handled
outside the bundle and the <<EXTERNAL>>
form
should be assumed. This header is informational and may not have any
legal bearing. Consult a lawyer before using this header to automate
licensing processing.
The Bundle-Localization header contains the location in the
bundle where localization files can be found. The default value is
OSGI-INF/l10n/bundle
. Translations are by default
therefore OSGI-INF/l10n/bundle_de.properties
,
OSGI-INF/l10n/bundle_nl.properties
, etc. See Manifest Localization.
The Bundle-ManifestVersion header defines that the bundle follows the rules of this specification. The Bundle-ManifestVersion header determines whether the bundle follows the rules of this specification. It is 1 (the default) for Release 3 Bundles, 2 for Release 4 and later. Future version of the OSGi framework can define higher numbers for this header.
The Bundle-Name header defines a readable name for this bundle. This should be a short, human-readable name that can contain spaces.
The Bundle-NativeCode header contains a specification of native code libraries contained in this bundle. See Loading Native Code Libraries.
The Bundle-RequiredExecutionEnvironment contains a comma-separated list of execution environments that must be present on the OSGi framework. See Execution Environment. This header is deprecated.
The Bundle-SCM header provides an optional machine readable form of information about the source code of the bundle. This header is purely informational and must not be processed by the OSGi Framework.
The syntax for this header is as follows:
Bundle-SCM ::= scm-attr ( ',' scm-attr ) *
scm-attr ::= url | connection | developer-connection | tag
url ::= 'url' '=' <url>
connection ::= 'connection' '=' <uri>
developer-connection ::= 'developerConnection' '=' <uri>
tag ::= 'tag' '=' argument
This header has the following attributes:
-
url
- (optional) The URL to browse the source code repository. -
connection
- (optional) Thescm:
URI for read access to the source code repository. -
developer-connection
- (optional) Thescm:
URI for write access to the source code repository. -
tag
- (optional) The tag within the source code repository.
See [26] Maven POM Reference, SCM for more information. Tools can use the information in this header for POM generation.
The Bundle-SymbolicName header specifies a non-localizable name for this bundle. The bundle symbolic name together with a version must identify a unique bundle though it can be installed multiple times in a framework. The bundle symbolic name should be based on the reverse domain name convention, see Bundle-SymbolicName. This header must be set.
The Bundle-UpdateLocation header specifies a URL where an update for this bundle should come from. If the bundle is updated, this location should be used, if present, to retrieve the updated JAR file.
The Bundle-Vendor header contains a human-readable description of the bundle vendor.
The Bundle-Version header specifies the version of this bundle.
See Version. The default value is
0.0.0
The DynamicImport-Package header contains a comma-separated list of package names that should be dynamically imported when needed. See Dynamic Import Package.
The Export-Package header contains a declaration of exported packages. See Export-Package.
The Fragment-Host header defines the host bundles for this fragment. See Fragment-Host
The Import-Package header declares the imported packages for this bundle. See Import-Package.
Specifies that a bundle provides a set of Capabilities, see Dependencies.
The Require-Bundle header specifies that all exported packages from another bundle must be imported, effectively requiring the public interface of another bundle. See Require-Bundle
Specifies that a bundle requires other bundles to provide a capability, see Dependencies.
The manifest is an excellent place to provide metadata belonging
to a bundle. This is true for the OSGi Working Group but it is also
valid for other organizations. For historic reasons, the OSGi Working
Group claims the default namespace, specifically headers that indicate
OSGi related matters like names that contain Bundle, Import, Export,
etc. Organizations that want to use headers that do not clash with OSGi
Working Group defined names or bundle header names from other
organizations should prefix custom headers with x-
, for
example x-LazyStart
.
Organizations external to the OSGi Working Group can request header names in the OSGi namespace. The OSGi Working Group maintains a registry of such names at [15] OSGi Header Registry.
The Header annotation can be used on a class or package to specify a header for a bundle. This annotation can be processed by bundle assembly tools to generate the specified header information in the bundle's manifest.
Each Manifest header has its own syntax. In all descriptions, the syntax is defined with [11] W3C EBNF. These following sections define a number of commonly used terminals.
Many Manifest header values share a common syntax. This syntax consists of:
header ::= clause ( ',' clause ) *
clause ::= path ( ';' path ) *
( ';' parameter ) * // See 1.3.2
A parameter can be either a directive or an attribute. A directive is an instruction that has some implied semantics for the Framework. An attribute is used for matching and comparison purposes.
Version specifications are used in several places. A version has the following grammar:
version ::=
major( '.' minor ( '.' micro ( '.' qualifier )? )? )?
major ::= number // See 1.3.2
minor ::= number
micro ::= number
qualifier ::= ( alphanum | '_' | '-' )+
A version must not contain any white space. The default value for
a version is 0.0.0
.
Versions are supported in the API with the Version class.
The Version annotation can be used in
package-info.java
source files to document the version of a
package. This annotation can be processed by bundle assembly tools when
generating the version information for a bundle's Export-Package
manifest header.
A version range describes a range of versions using a mathematical interval notation. See [12] Interval (Mathematics). The syntax of a version range is:
version-range ::= interval | atleast
interval ::= ( '[' | '(' ) floor ',' ceiling ( ']' | ')' )
atleast ::= version
floor ::= version
ceiling ::= version
If a version range is specified as a single version, it must be
interpreted as the range [version,
∞)
. The default for a non-specified
version range is 0, which maps to [0.0.0,
∞)
.
Note that the use of a comma in the version range requires it to be enclosed in double quotes. For example:
Import-Package: com.acme.foo;version="[1.23,2)", «
com.acme.bar;version="[4.0, 5.0)"
In the following table, for each specified range in the left-hand column, a version x is considered to be a member of the range if the predicate in the right-hand column is true.
Table 3.1 Examples of version ranges
Example | Predicate |
---|---|
|
|
|
|
|
|
|
|
|
|
Version Ranges are supported in the API with the VersionRange class.
The OSGi specifications use filter expressions extensively. Filter expressions allow for a concise description of a constraint. The syntax of a filter string is based upon the string representation of LDAP search filters as defined in [5] A String Representation of LDAP Search Filters. It should be noted that RFC 2254: A String Representation of LDAP Search Filters supersedes RFC 1960, but only adds extensible matching and is not applicable to this OSGi Framework API.
The string representation of an LDAP search filter uses a prefix format and is defined by the following grammar:
filter ::= '(' filter-comp ')'
filter-comp ::= and | or | not | operation
and ::= '&' filter-list
or ::= '|' filter-list
not ::= '!' filter
filter-list ::= filter | filter filter-list
operation ::= simple | present | substring
simple ::= attr filter-type value
filter-type ::= equal | approx | greater-eq | less-eq
equal ::= '='
approx ::= '~='
greater-eq ::= '>='
less-eq ::= '<='
present ::= attr '=*'
substring ::= attr '=' initial any final
initial ::= () | value
any ::= '*' star-value
star-value ::= () | value '*' star-value
final ::= () | value
value ::= <see text>
attr ::= <see text>
attr
is a string representing an attribute key or
name. Attribute names are not case sensitive; that is, cn
and CN
both refer to the same attribute. attr
must not contain the characters '=', '>', '<', '~', '(' or ')'.
attr
may contain embedded spaces but leading and trailing
spaces must be ignored.
value
is a string representing the value, or part of
one, which will be compared against a value in the filtered
properties.
If value
must contain one of the characters reverse
solidus ('\' \u005C
), asterisk ('*' \u002A
),
parentheses open ('(' \u0028
) or parentheses close
(')' \u0029
), then these characters should be preceded with
the reverse solidus ('\' \u005C
) character. Spaces are
significant in value
. Space characters are defined by
Character.isWhiteSpace()
.
Although both the substring
and present
productions can produce the attr=*
construct, this
construct is used only to denote a presence filter.
The substring
production only works for attributes
that are of type String
, Collection
of
String
or String[]
. In all other cases the
result must be false
.
The evaluation of the approximate match ('~='
) filter
type is implementation specific but should at least ignore case and
white space differences. Codes such as Soundex or other smart
closeness comparisons could be used.
Values specified in the filter are compared to values in the properties against which the filter is evaluated. The comparison of these values is not straightforward. Strings compare differently than numbers, and it is also possible for a property to have multiple values.
Property keys are case insensitive. The object class of the property's value defines the comparison type. The properties values should be of the following types:
Figure 3.1 Primary Property Types
type ::= scalar | collection | array
scalar ::= String | Integer | Long | Float
| Double | Byte | Short
| Character | Boolean
primitive ::= int | long | float | double | byte
| short | char | boolean
array ::= <Array of primitive>
| <Array of scalar>
collection ::= <Collection of scalar>
The following rules apply for comparison:
-
String - Use String comparison
-
Integer, Long, Float, Double, Byte, Short, Character objects and primitives - Use numerical comparison. The
value
should be trimmed of any extraneous white space before the comparison. -
Boolean objects - Use comparison defined by
Boolean.valueOf(value).booleanValue()
. Thevalue
should be trimmed of any extraneous white space before this conversion is applied. -
Array or Collection elements - Comparison is determined by the object type of the element
Array and Collection elements may be a mix of scalar types. Array
and Collection elements may also be null
. If the type of
the property value is not one of the above types, then it could be
possible to create an instance of the above type. The following
conversions must be tried in the given order:
-
A public static method on the required type called
valueOf
that returns an instance of the given type and takes a singleString
argument. -
A public constructor taking a single
String
argument.
If one of these methods is available then the Framework must
construct a temporary object by passing the value
as the
String
argument. If the constructor/function is not
directly accessible then the invocation should use the
setAccessible
method to make it accessible.
The resulting object must be used to compare with the property value according to the following comparison rules:
-
Comparable objects - Comparison through the
Comparable
interface -
Other objects - Equality comparison
If none of the above comparison rules apply, then the result of
the comparison is false
.
A filter matches a property with multiple values if it matches at least one of those values. For example:
Dictionary dict = new Hashtable();
dict.put( "cn", new String[] { "a", "b", "c" } );
The dict
will match against a filter with
(cn=a)
as well as (cn=b)
.
Service properties are often defined to take a type, a collection
of that type, or an array of that type. In those cases, a simple + will
be suffixed to the type name. For example String+
,
indicates that a String
, a String[]
, and a
Collection<String>
are all valid forms.
Filters are supported in the API with the Filter type. Filters can be created with the FrameworkUtil.createFilter(String) method or the BundleContext.createFilter(String) method.
A bundle JAR file can be a multi-release JAR. See [27] Multi-release JAR File. When a bundle is a multi-release JAR, that is, the manifest contains
Multi-Release: true
then the Framework must look for a supplemental manifest file,
OSGI-INF/MANIFEST.MF
, in the versioned directories. For
example:
META-INF/versions/9/OSGI-INF/MANIFEST.MF
The Framework must first look in the versioned directory for the major version of the current Java platform and then prior versioned directories in descending order. The first supplemental manifest file found must be used and the Framework must replace the values of the following manifest headers in the manifest with the values of these headers, if present, in the supplemental manifest file.
-
Import-Package
-
Require-Capability
Any other headers in the supplemental manifest file must be ignored.
The Framework APIs which provide access to the bundle metadata, such as getHeaders() and BundleRevision and BundleWiring, must present the supplemented manifest information. That is, the main manifest with the replacement values from a supplemental manifest, if any, for the running Java platform version.
Both fragment bundles as well as non-fragment bundles can be multi-release JARs.
Support for multi-release JARs must only be active on Java 9 and later. On Java 8 and earlier, the JAR must be treated as a non-multi-release JAR.
OSGi dependency handling is based on a very general model that describes the dependency relationships. This model consists of a small number of primitive concepts:
-
Environment - A container or framework that installs Resources.
-
Resource - An abstraction for an artifact that needs to become installed in some way to provide its intended function. A Bundle is modeled by a Resource but for example a display or secure USB key store can also be Resources.
-
Namespace - Defines what it means for the Environment when a requirement and capability match in a given Namespace.
-
Capability - Describing a feature or function of the Resource when installed in the Environment. A capability has attributes and directives.
-
Requirement - An assertion on the availability of a capability in the Environment. A requirement has attributes and directives. The
filter
directive contains the filter to assert the attributes of the capability in the same Namespace.
The relations between these entities are depicted in Figure 3.2.
A Resource in general has dependencies on other Resources or can
satisfy other Resource's dependencies. Dependency types can vary wildly, a
Bundle can require packages from another Bundle (Import-Package), or a
Fragment needs a host Bundle (Fragment-Host), or a Bundle requires access
to a high resolution display. The OSGi Core specification demonstrates
that it is possible to describe these varying types of dependencies with
dedicated headers, optimized for each case. However, this model requires
that every type of dependency will go through a specification process,
limiting the usefulness for parties not participating in this process.
Therefore, this specification provides a generic dependency model based on
Namespaces. A Namespace is the
type of a dependency. For example, the
osgi.wiring.package
Namespace defines the semantics for
Import-Package and Export-Package headers by specifying a number of
attributes and directives.
Attributes are used for matching and directives provide information about
the semantics of the Namespace. For example, in the case of the
osgi.wiring.host
Namespace (Fragments) the capability's
attributes are:
-
osgi.wiring.host
- (String
) The host's name. -
bundle-version
- (Version
) The host's version. -
*
- Any other attributes are allowed.
The OSGi Framework Namespaces are defined in classes, see Framework Namespaces Specification.
The purpose of a Namespace is to create an attribute/directive based
language that describes a dependency in a generic way unrelated to the
specific dependency type. A number of Namespaces have been defined by the
OSGi Working Group in this and other specifications. OSGi namespaces start
with the reserved osgi.
prefix. For example, the
osgi.ee
namespace defines a capability for specifying an
execution environment. A Namespace can also be defined by other
organizations and individuals. To minimize name clashes it is recommended
to use the reverse domain name rule used for packages and bundle symbolic
names. It is also recommended to register these Namespaces with the OSGi
Working Group, see [16] OSGi Namespace Registry
to prevent clashes.
Given a Namespace, it is possible to declare a
capability of that Namespace. A capability provides
the values for the attributes and directives defined in the Namespace. For
example, it is possible to translate the Export-Package header to a
capability in the osgi.wiring package
Namespace.
Given a capability, it is possible to specify a
requirement. A requirement has a filter that can
match the attributes of the capability, if so, the requirement is
satisfied. Requirements are always associated with a
given Namespace, like the capability, and can therefore only be
satisfied by Capabilities in the same Namespace. A
requirement is satisfied when its filter, as specified with the
filter
directive, matches a capability's attributes. The
filter language specification can be found in Filter Syntax. A requirement can be
mandatory or optional, as set
with its resolution
directive. A requirement can have
single
or multiple
cardinality, indicating it
requires at least one or more Capabilities.
A Resource that declares requirements can only provide its intended functionality when its mandatory requirements are satisfied by one or more Capabilities, which in general means they come from other Resources. A Resource that has all its mandatory requirements satisfied is said to be resolved and must provide the functionality described by its Capabilities. A capability can only satisfy a requirement if its Resource is resolved.
The process of matching up requirements to capabilities is called resolving. In this process, the resolver must create Wires that link requirements to Capabilities. Both the Wire and the requirement/ Capability have a reference to a Resource. In certain cases the requirement/ capability can be declared in one Resource but wired from another Resource. Therefore, a requirement/ capability can have a declared Resource, which is the Resource that declares it. However, when a Resource is wired the Wire has a provider or requirer Resource which can differ from the corresponding declared Resource. When the declared Resource differs from the provider/requirer it is called hosting. This separation is caused by Fragments; with Fragments some requirements and Capabilities are hosted and others remain part of the Fragment.
Only requirements that are effective must be
wired. Each requirement is intended for a certain state of the system. For
example, the OSGi Framework only resolves requirements when the
requirement has the effective
directive set to
resolve
.
Once a set of Resources are resolved the Environment, for example the OSGi Framework for bundles, creates a Wiring per Resource to hold the resolved state. This state includes the Wires as well as all Capabilities and requirements, regardless if they are wired or not.
The Wires between a requirement and a capability must be created
according to the semantics implied by their Namespace. The Wires coming
out of a resolve operation can be used during run time as specified in
their Namespace. For example, the osgi.wiring.*
Namespaces
are used to control the Class Loading, see Bundle Wiring API Specification. However, they can also serve other purposes
as outlined by their Namespace. For example, a Wire could specify a
Dependency Injection source and target.
Interfaces for the generic model are defined in Resource API Specification. The Bundle Wiring API Specification chapter describes the Wiring API that is based on this generic package. The generic API is intended for other specifications that want to be compatible with the generic OSGi Core framework's Capability /Requirement model.
All bundles depend on one or more external entities and this is
expressed as requirements and Capabilities. Once a bundle is resolved,
it assumes that those dependencies are satisfied.
The Require-Capability and Provide-Capability headers are manifest
headers that declare generic requirements and
Capabilities in any Namespace. However, a number of manifest headers in
the OSGi specifications are actually requirements on Capabilities
specified by other OSGi manifest headers. For example, an Import-Package
clause is a requirement on the capability specified by an Export-Package
clause. The attributes on an Import-Package clause are treated as
assertions on the attributes of the corresponding Export-Package clause.
This specification therefore contains a number of Namespaces for these
OSGi manifest headers: osgi.wiring.bundle
,
osgi.wiring.package
, and osgi.wiring.host
.
These namespaces influence the resolver and define the class loading
process. For example, a Require-Bundle clause is a requirement that
ensures that the exported packages of the required bundle are available
to the requirer's class loader.
The OSGi resolving process is described in Resolving Process. The diagramming technique of the Requirement/Capability model is depicted in Figure 3.3, the details of the wiring are further explained in Bundle Wiring API Specification.
A bundle has Windows 7 specific Java code and requires a display that has a resolution of at least 1000x1000 pixels. It also relies on a bundle providing an IP-number-to-location table.
These dependencies on the environment and another bundle can be expressed with the requirement header in the bundle as follows:
Require-Capability:
com.microsoft; filter:="(&(api=win32)(version=7))",
com.acme.display; filter:="(&(width>=1000)(height>=1000))",
com.acme.ip2loc
Each clause lives in a namespace, for example
com.microsoft
. A namespace defines the semantics of the
attributes as well as optional rules.
The deployer of the environment sets the following launching property when starting the framework:
org.osgi.framework.system.capabilities.extra= «
com.acme.display; width:Long=1920; height:Long=1080; interlace=p, «
com.microsoft; edition=home; version:Version=7; api=win32
The framework will be able to satisfy the display requirement but
it cannot satisfy the ip2loc
table requirement. The
deployer can then install the bundle with the ip2loc
table.
This bundle specifies the following header:
Provide-Capability: com.acme.ip2loc; version:Version=1.2
After installing and resolving this bundle, the framework can
resolve the original bundle because there is now a provider of the
ip2loc
table.
A generic capability for a Bundle is defined with the Provide-Capability header. This header has the following syntax:
Provide-Capability ::= capability (',' capability )*
capability ::= name-space
( ';' directive | typed-attr )*
name-space ::= symbolic-name
typed-attr ::= extended ( ':' type )? '=' argument
type ::= scalar | list
scalar ::= 'String' | 'Version' | 'Long'
| 'Double'
list ::= 'List' ( '<' scalar '>' )?
The Capability annotation can be used on a class or package to specify a capability to be provided by the bundle. This annotation can be processed by bundle assembly tools to add the capability to the Provide-Capability header in the bundle's manifest.
The header has the following directives architected:
-
effective
-(resolve)
Specifies the time a capability is available, eitherresolve
(default) or another name. The OSGi framework resolver only considers Capabilities without aneffective
directive oreffective:=resolve
. Capabilities with other values for theeffective
directive can be considered by an external agent. -
uses
- The uses directive lists package names that are used by this capability. This information is intended to be used for uses constraints, see Package Constraints. -
deprecated
- The deprecated directive indicates the capability has been deprecated. The value is the human-readable reason to developers for the deprecation. This reason should include the version in which the capability was deprecated and any removal plan.
Namespaces can define additional directives and attributes.
Attributes can be typed. Typing is important because it defines how attributes compare. Comparing two versions as strings does not provide the proper comparison semantics for versions. In similar vein, lexicographical ordering is different than numeric ordering.
Types are specified between the attribute name and the equal sign
('=' \u003D
), the separator is a colon (':'
\u003A
). For example:, for a Long:
attr:Long=24
If no type is specified, the String
type is
assumed.
The parsing rules of the corresponding type's String
constructor are then used to create a new instance that is placed in the
capability's map. Numeric types must trim white space around the value,
for other types spaces around the argument are not ignored, however,
white space is skipped by the manifest parsing rules. That is:
attr:Long= 23 , //ok
attr:Version=" 23 ", // error
attr:Long=" 23 ", // ok, because nummeric
Multi-valued attributes can be constructed with the
List
type. The List
type can specify a
scalar
type for the list elements. If no element type is
specified, String
is assumed. Parsing of the corresponding
argument
list must be done according to the following
rules:
-
Element values in the list are separated by commas (
',' \u002C
). -
White space around an element value must be trimmed for non-
String
element types. -
Commas or reverse solidi (
'\' \u005C
) that are part of an element value must be escaped by prefixing them with a reverse solidus. In practice, this requires escaping with two reverse solidi because a reverse solidus must already be escaped in strings.
The whole argument
must be surrounded by quotes
because the comma is a significant token in the manifest grammar.
The version attribute requires the Version type to be specified to be compared as a Version rather than as a String:
version:Version=1.23
For example:
Provide-Capability: «
com.acme.dictionary; from:String=nl; to=de; version:Version=3.4, «
com.acme.dictionary; from:String=de; to=nl; version:Version=4.1, «
com.acme.ip2location;country:List="nl,be,fr,uk";version:Version=1.3, «
com.acme.seps; tokens:List<String>="\\,,;,\\\""
Capabilities can also be provided by the system bundle by specifying the following launch properties:
org.osgi.framework.system.capabilities
org.osgi.framework.system.capabilities.extra
The format for these system properties is identical to the Provide-Capability header. A framework must parse these properties and use them in the resolving process as if provided by the system bundle.
There are two properties so that the framework can specify its
default Capabilities in
org.osgi.framework.system.capabilities
while the deployer
can specify specific deployment Capabilities in the
org.osgi.framework.system.capabilities.extra
system
property. Frameworks can often deduce many Capabilities from their
environment.
The following is an example capability header for the system bundle as defined by the deployer:
map.put("org.osgi.framework.system.capabilities.extra",
"com.acme.screen; width:Long=640; height:Long=480; card=GeForce");
The Bundle's Require-Capability header has the following grammar:
Require-Capability ::= requirement ( ',' requirement )*
requirement ::= name-space ( ';' directive | typed-attr )*
Requirements have attributes that can be set with the
Require-Capability header. The purpose of these attributes are to
provide further information about the requirement; they are not
assertions as they are in the Import-Package, Require-Bundle, and
Fragment-Host headers. The attributes of these headers are mapped to the
filter
directive in their corresponding namespaces.
The Requirement annotation can be used on a class or package to specify a capability to be required by the bundle. This annotation can be processed by bundle assembly tools to add the requirement to the Require-Capability header in the bundle's manifest.
The following directives are architected for the Require-Capability header:
-
effective
-(resolve)
Specifies the time a requirement is considered, eitherresolve
(default) or another name. The OSGi framework resolver only considers requirements without aneffective
directive oreffective:=resolve
. Other requirements can be considered by an external agent. Additional names for theeffective
directive should be registered with the OSGi Working Group. -
resolution
- (mandatory|optional
) A mandatory requirement forbids the bundle to resolve when the requirement is not satisfied; an optional requirement allows a bundle to resolve even if the requirement is not satisfied. No wirings are created when this requirement cannot be resolved, this can result in Class Not Found Exceptions when the bundle attempts to use a package that was not resolved because it wasoptional
. The default ismandatory
. -
filter
- (Filter
) A filter expression that is asserted on the Capabilities belonging to the given namespace. The matching of the filter against the capability is done on one capability at a time. A filter like(&(a=1)(b=2))
matches only a capability that specifies both attributes at the required value, not two capabilities that each specify one of the attributes correctly. A filter is optional, if no filter directive is specified the requirement always matches. The attribute names in the filter expression are used to locate capability attributes in a case sensitive manner. -
cardinality
- (single|multiple
) Indicates if the requirement can be wired a single time or multiple times. The default is single.
Additional directives are ignored during resolving. Attributes on
the requirement
clause are also ignored.
The Java environment provides all packages in the
java.*
namespace. Prior to Java SE 9, this namespace was not
well defined and could be different for different runtime environments.
For example, Java SE 5 is not equal to Java SE 7 and an Android
environment has substantial differences to a Java SE environment. However,
Java SE 6 is backward compatible for Java SE 5, Java SE 1.4, Java SE 1.3
and Java SE 1.2. That is, applications written for Java SE 1.3 must run
unchanged on a Java SE 5 environment.
These differences and backward compatibilities can not be captured
using versions because they are variations. For
example, [21] Google Android is a variation of a Java SE 5
environment, as is [22] Google App Engine and [23] Google Web Toolkit. All these variations have a different set of
packages, types, and methods in the java.*
namespace.
Starting in Java SE 9, the platform has been modularized and its
packages organized into a set of modules. Depending upon how the platform
is configured for execution, some modules and their contained packages may
not be present at runtime. However, Java SE 9 provides a standard way to
interrogate the Java platform for the packages currently available at
runtime. Therefore, the Framework must set the
org.osgi.framework.system.packages
launching property, see
Launching Properties, to the list of
Java platform packages currently available at runtime.
For Java platform versions prior to Java SE 9, the Framework must
also set the org.osgi.framework.system.packages
launching
property to the list of Java platform packages generally known to be
available at runtime. Since there is no standard way to interrogate the
Java platform for the packages currently available at runtime, a Framework
implementation may use a predefined list of packages for the Java platform
version.
In additional to specifying the version of the execution environment
via an osgi.ee
requirement, see osgi.ee Namespace, a Bundle may also import
java.*
packages using the Import-Package header. Imports of
java.*
packages by a bundle will be used during the
resolution process, see Resolving Process, to ensure the bundle has
visibility to the requested packages by wiring the bundle's import of a
java.*
package to the export of the java.*
package by the Framework's system bundle. However, a bundle always loads
classes in java.*
packages via parent delegation. See Parent Delegation. Therefore, a bundle is not
required to import a java.*
package to have visibility to the
classes in the package, if the package is present in the current runtime.
It only needs to import a java.*
package to have the
resolution process ensure the package is available from the execution
environment. This can avoid a NoClassDefFoundError
during
execution of the bundle due to a missing java.*
package
required by the bundle.
The Bundle-RequiredExecutionEnvironment manifest header provides
the same function as the osgi.ee Namespace.
It allows a bundle to depend on the execution environment. This header
is deprecated but must be fully supported by a compliant framework.
Bundles should not mix these headers but use either an
osgi.ee
requirement or this header. If both are used, both
constraints must be met to resolve.
The syntax of this header is a list of comma-separated names of execution environments.
Bundle-RequiredExecutionEnvironment ::= ee-name ( ',' ee-name )*
ee-name ::= bree | <ee name>
bree ::= token ( '-' version )? ( '/' token ( '-' version )? )?
For example:
Bundle-RequiredExecutionEnvironment: CDC-1.0/Foundation-1.0, «
OSGi/Minimum-1.1
If a bundle includes this header in the manifest then the bundle must only use methods with signatures that are contained within a proper subset of all mentioned execution environments. Bundles should list all (known) execution environments on which it can run the bundle.
A bundle can only resolve if the framework is running on a VM which implements one of the listed required execution environments. Frameworks should recognize that the current VM can implement multiple execution environments. For example, Java 6 is backward compatible with Java 5 and a bundle requiring the Java 6 execution environment must resolve on a Java 6 VM.
The Bundle-RequiredExecutionEnvironment header can not prevent a bundle from installing.
The org.osgi.framework.executionenvironment
launching
property defines the current execution environment with a comma
separated list of execution environment names. If not set, the framework
must provide an appropriate value. This property is also deprecated, its
function is replaced with
org.osgi.framework.system.capabilities[.extra]
.
An example:
org.osgi.framework.executionenvironment =
JavaSE-1.5, J2SE-1.4, JavaSE-1.4, JavaSE-1.3, OSGi/Minimum-1.1
Frameworks must convert a Bundle-RequiredExecutionEnvironment
header to a requirement in the osgi.ee
namespace when used
in the Wiring API, see Bundle Wiring API Specification. Since the
header uses opaque names for the execution environments there is no
guaranteed algorithm to map the ee-name
to a
Require-Capability header. However, the suggested names so far for
popular execution environments do have a structure that can be used to
create such a header, this pattern was reflected in the
bree
term. The structure of the bree
term for
the existing recommendations is:
n1 ( '-' v )? ( '/' n2 ( '-' v )? )?
For example:
CDC-1.0/Foundation-1.0
OSGi/Minimum-1.2
J2SE-1.4
JavaSE-1.4
Each bree
term that matches this pattern can thus be
converted into an equivalent osgi.ee
Require-Capability
filter. First variable n1
must be replaced with
JavaSE
when it is J2SE
since the
Require-Capability header uses a single name for the Java Standard
Edition. The filter
directive can then be constructed from
n1
, v
, and n2
. If n2 is not
defined or v is not defined then the parenthesized parts in which they
participate are not used in the expansion.
bree-filter ::= '(&(osgi.ee=' n1 ( '/' n2 )? ')' ( '(version=' v ')' )? ')'
If the bree
term cannot be parsed into the given
constituents then the filter must look like:
filter ::= '(osgi.ee=' <ee name> ')'
Some examples:
CDC-1.0/Foundation-1.0 (&(osgi.ee=CDC/Foundation)(version=1.0))
OSGi/Minimum-1.2 (&(osgi.ee=OSGi/Minimum)(version=1.2))
J2SE-1.4 (&(osgi.ee=JavaSE)(version=1.4))
JavaSE-1.6 (&(osgi.ee=JavaSE)(version=1.6))
AA/BB-1.7 (&(osgi.ee=AA/BB)(version=1.7))
V1-1.5/V2-1.6 (osgi.ee=V1-1.5/V2-1.6)
MyEE-badVersion (osgi.ee=MyEE-badVersion)
Each element of the Bundle-RequiredExecutionEnvironment is ORed
together in the final osgi.ee
requirement's
filter
directive. For example:
Bundle-RequiredExecutionEnvironment:
CDC-1.0/Foundation-1.0,
OSGi/Minimum-1.2,
J2SE-1.4,
JavaSE-1.6,
AA/BB-1.7,
V1-1.5/V2-1.6,
MyEE-badVersion
This must be converted into the following Require-Capability:
Require-Capability:osgi.ee; filter:="(|
(&(osgi.ee=CDC/Foundation)(version=1.0))
(&(osgi.ee=OSGi/Minimum)(version=1.2))
(&(osgi.ee=JavaSE)(version=1.4))
(&(osgi.ee=JavaSE)(version=1.6))
(&(osgi.ee=AA/BB)(version=1.7))
(osgi.ee=V1-1.5/V2-1.6)
(osgi.ee=MyEE-badVersion)
)"
Every org.osgi.resource.Resource
representing a
Bundle which has a Bundle-RequiredExecutionEnvironment header must have
the converted osgi.ee
requirement in the list returned by
getRequirements(String) for the osgi.ee
namespace. In cases
where the bundle already has a requirement for the osgi.ee
namespace no merging is done, the bundle will simply have an additional
osgi.ee
requirement added.
Many bundles can share a single virtual machine (VM), see [1] Java Virtual Machine Specification. Within this VM, bundles can hide packages and classes from other bundles, as well as share packages with other bundles.
The key mechanism to hide and share packages is the Java class loader that loads classes from a sub-set of the bundle-space using well-defined rules. Each bundle has a single class loader. That class loader forms a class loading delegation network with other bundles as shown in Figure 3.4.
The class loader can load classes and resources from:
-
Boot class path - The boot class path contains the
java.*
packages and its implementation packages. -
Framework class path - The Framework usually has a separate class loader for the Framework implementation classes as well as key service interface classes.
-
Bundle Space - The bundle space consists of the JAR file that is associated with the bundle, plus any additional JAR that are closely tied to the bundle, like fragments, see Fragment Bundles.
A class space is then all classes reachable from a given bundle's class loader. Thus, a class space for a given bundle can contain classes from:
-
The parent class loader (normally
java.*
packages from the boot class path) -
Imported packages
-
Required bundles
-
The bundle's class path (private packages)
-
Attached fragments
A class space must be consistent, such that it never contains two classes with the same fully qualified name (to prevent Class Cast Exceptions). However, separate class spaces in an OSGi Platform may contain classes with the same fully qualified name. The modularization layer supports a model where multiple versions of the same class are loaded in the same VM.
Figure 3.5 shows the class space for a Bundle A. The right top of Bundle A is not in the class space because it illustrates that sometimes packages inside a bundle are not accessible to the Bundle itself when an export is substituted.
The Framework therefore has a number of responsibilities related to class loading. Before a bundle is used, it must resolve the constraints that a set of bundles place on the sharing of packages. Then select the best possibilities to create a wiring. See Resolving Process for further information. The runtime aspects are described in Runtime Class Loading.
The Framework must resolve bundles. Resolving is the process where any external dependencies are satisfied and then importers are wired to exporters. Resolving is a process of satisfying constraints; constraints that are provided by the Dependencies section and constraints by the different manifest headers like Import/Export Package, Require-Bundle, and Fragment-Host. The resolving process must take place before any code from a bundle can be loaded or executed.
A wire is an actual connection between an exporter and an importer, which are both bundles. A wire is associated with a number of constraints that are defined by its importer's and exporter's manifest headers. A valid wire is a wire that has satisfied all its constraints. Figure 3.6 depicts the class structure of the wiring model. Not all constraints result in a wire.
The following sections define the manifest headers that provide the metadata for the resolver.
A bundle manifest must express the version of the OSGi manifest
header syntax in the Bundle-ManifestVersion
header. Bundles
exploiting this version of the Framework specification (or later) must
specify this header. The syntax of this header is as follows:
Bundle-ManifestVersion ::= number //See 1.3.2
The Framework bundle manifest version must be '2'. Bundle
manifests written to previous specifications' manifest syntax are taken
to have a bundle manifest version of '1', although there is no way to
express this in such manifests. Therefore, any other value than
2
for this header is invalid unless the Framework
explicitly supports such a later version.
OSGi Framework implementations should support bundle manifests without a Bundle-ManifestVersion header and assume Framework 1.2 compatibility at the appropriate places.
Version 2 bundle manifests must specify the bundle symbolic name. They need not specify the bundle version because the version header has a default value.
The Bundle-SymbolicName manifest header is a mandatory header. The bundle symbolic name and bundle version identify a unique bundle. This does not always imply that this pair is unique in a framework, in certain cases the same bundle can be installed multiple times in the same framework, see Bundle Identifiers.
A bundle gets its unique Bundle-SymbolicName from the developer. The Bundle-Name manifest header provides a human-readable name for a bundle and is therefore not replaced by this header.
The Bundle-SymbolicName manifest header must conform to the following syntax:
Bundle-SymbolicName ::= symbolic-name
( ';' parameter ) * // See 1.3.2
The framework must recognize the following directives for the Bundle-SymbolicName header:
-
singleton
- Indicates that the bundle can only have a single version resolved in an environment. A value oftrue
indicates that the bundle is a singleton bundle. The default value isfalse
. The Framework must resolve at most one bundle when multiple versions of a singleton bundle with the same symbolic name are installed. Singleton bundles do not affect the resolution of non-singleton bundles with the same symbolic name. -
fragment-attachment
- Defines how fragments are allowed to be attached, see the fragments in Fragment Bundles. The following values are valid for this directive:-
always
- (Default) Fragments can attach at any time while the host is resolved or during the process of resolving. -
never
- No fragments are allowed. -
resolve-time
- Fragments must only be attached during resolving.
-
-
mandatory
- Provide a list of mandatory attributes. If these attributes are not specifically used in the requirement (Require-Bundle, Fragment-Host) then this bundle must not match. See Mandatory Attributes. -
deprecated
- The deprecated directive indicates the bundle has been deprecated. The value is the human-readable reason to developers for the deprecation. This reason should include the version in which the capability was deprecated and any removal plan.
The header allows the use of arbitrary attributes that can be required by the Require-Bundle and Fragment-Host headers. The following attribute is predefined:
-
bundle-version
- The value of the Bundle-Version header or 0 if no such header is present. Explicitly setting this attribute is an error.
For example:
Bundle-SymbolicName: com.acme.foo;singleton:=true
Bundle-Version is an optional header; the default value is
0.0.0
.
Bundle-Version ::= version // See 3.2.5
If the minor
or micro
version components
are not specified, they have a default value of 0
. If the
qualifier component is not specified, it has a default value of the
empty string (""
).
Versions are comparable. Their comparison is done numerically and
sequentially on the major
, minor
, and
micro
components and lastly using the String class
compareTo
method for the qualifier
.
A version is considered equal to another version if the major,
minor, micro, and the qualifier components are equal (using
String
method compareTo
).
Example:
Bundle-Version: 22.3.58.build-345678
The Import-Package header defines the constraints on the imports of shared packages. The syntax of the Import-Package header is:
Import-Package ::= import ( ',' import )*
import ::= package-names ( ';' parameter )*
package-names ::= package-name
( ';' package-name )* // See 1.3.2
The header allows many packages to be imported. An import definition is the description of a single package for a bundle. The syntax permits multiple package names, separated by semi-colons, to be described in a short form.
Import package directives are:
-
resolution
- Indicates that the packages must be resolved if the value is mandatory, which is the default. If mandatory packages cannot be resolved, then the bundle must fail to resolve. A value ofoptional
indicates that the packages are optional. See Optional Packages.
The developer can specify arbitrary matching attributes. See Attribute Matching. The following arbitrary matching attributes are predefined:
-
version
- Aversion-range
to select the exporter's package version. The syntax must follow Version Ranges. For more information on version selection, see Semantic Versioning. If this attribute is not specified, it is assumed to be[0.0.0,
∞)
. -
specification-version
- This attribute is an alias of the version attribute only to ease migration from earlier versions. If theversion
attribute is present, the values must be equal. -
bundle-symbolic-name
- The bundle symbolic name of the exporting bundle. In the case of a fragment bundle, this will be the host bundle's symbolic name. -
bundle-version
- Aversion-range
to select the bundle version of the exporting bundle. The default value is[0.0.0,
∞)
. See Semantic Versioning. In the case of a fragment bundle, the version is from the host bundle.
In order to be allowed to import a package, a bundle must have
PackagePermission[<package-name>, IMPORT]
. See
PackagePermission
for more information.
An error aborts an installation or update when:
-
A directive or attribute appears multiple times, or
-
There are multiple import definitions for the same package, or
-
The version and specification-version attributes do not match.
Example of a correct definition:
Import-Package: com.acme.foo;com.acme.bar; «
version="[1.23,1.24]"; «
resolution:=mandatory
The syntax of the Export-Package header is similar to the Import-Package header; only the directives and attributes are different.
Export-Package ::= export ( ',' export)*
export ::= package-names ( ';' parameter )*
package-names ::= package-name // See 1.3.2
( ';' package-name )*
The header allows many packages to be exported. An export definition is the description of a single package export for a bundle. The syntax permits the declaration of multiple packages in one clause by separating the package names with a semi-colon. Multiple export definitions for the same package are allowed for example, when different attributes are needed for different importers. The Export annotation can be applied to a package to specify the package is to be exported. This annotation can be processed by bundle assembly tools to add the annotated package to the Export-Package header in the bundle's manifest.
Export directives are:
-
uses
- A comma-separated list of package names that are used by the exported package. Note that the use of a comma in the value requires it to be enclosed in double quotes. If this exported package is chosen as an export, then the resolver must ensure that importers of this package wire to the same versions of the package in this list. See Package Constraints. -
mandatory
- A comma-separated list of attribute names. Note that the use of a comma in the value requires it to be enclosed in double quotes. A bundle importing the package must specify the mandatory attributes, with a value that matches, to resolve to the exported package. See Mandatory Attributes. -
include
- A comma-separated list of class names that must be visible to an importer. Note that the use of a comma in the value requires it to be enclosed in double quotes. For class filtering, see Class Filtering. -
exclude
-A comma-separated list of class names that must be invisible to an importer. Note that the use of a comma in the value requires it to be enclosed in double quotes. For class filtering, see Class Filtering. -
deprecated
- The deprecated directive indicates the package has been deprecated. The value is the human-readable reason to developers for the deprecation. This reason should include the version in which the capability was deprecated and any removal plan.
The following attributes are part of this specification:
-
version
- The version of the named packages with syntax as defined in Version. It defines the version of the associated packages. The default value is0.0.0
. -
specification-version
- An alias for the version attribute only to ease migration from earlier versions. If theversion
attribute is present, the values must be equal.
Additionally, arbitrary matching attributes may be specified. See Attribute Matching.
The Framework will automatically associate each package export definition with the following attributes:
-
bundle-symbolic-name
- The bundle symbolic name of the exporting bundle. In the case of a fragment bundle, this is the host bundle's symbolic name. -
bundle-version
- The bundle version of the exporting bundle. In the case of a fragment bundle, this is the host bundle's version.
An installation or update must be aborted when any of the following conditions is true:
-
a directive or attribute appears multiple times
-
the
bundle-symbolic-name
orbundle-version
attribute is specified in the Export-Package header.
An export definition does not imply an automatic import definition. A bundle that exports a package and does not import that package will get that package via its bundle class path. Such an exported only package can be used by other bundles, but the exporting bundle does not accept a substitution for this package from another bundle.
In order to export a package, a bundle must have
PackagePermission[<package>, EXPORTONLY]
.
Example:
Export-Package: com.acme.foo;com.acme.bar;version=1.23
Bundles that collaborate require the same class loader for types used in the collaboration. If multiple bundles export packages with collaboration types then they will have to be placed in disjoint class-spaces, making collaboration impossible. Collaboration is significantly improved when bundles are willing to import exported packages; these imports will allow a framework to substitute exports for imports.
Though substitution is recommended to increase collaboration, it is not always possible. Importing exported packages can only work when those packages are pure API and not encumbered with implementation details. Import of exported packages should only be done when:
-
The exported package does not use private packages. If an exported package uses private packages then it might not be substitutable and is therefore not clean API.
-
There is at least one private package that references the exported package. If no such reference exist, there is no purpose in importing it.
In practice, importing exported packages can only be done with clean API-implementation separation. OSGi services are carefully designed to be as standalone as possible. Many libraries intertwine API and implementation in the same package making it impossible to substitute the API packages.
Importing an exported package must use a version range according to its compatibility requirements, being either a consumer or a provider of that API. See Semantic Versioning for more information.
Bundles that are not marked with a Bundle-ManifestVersion that equals 2 or more must treat the headers according the definitions in the Release 3. More specifically, the Framework must map the Release 3 headers to the appropriate Release 4 headers:
-
Import-Package - An import definition must change the
specification-version
attribute to theversion
attribute. An import definition without a specification version needs no replacement since the default version value of0.0.0
gives the same semantics as Release 3. -
Export-Package - An export definition must change the
specification-version
attribute to theversion
attribute. The export definition must be appended with theuses
directive. The uses directive must contain all imported and exported packages for the given bundle. Additionally, if there is no import definition for this package, then an import definition for this package with the package version must be added. -
DynamicImport-Package - A dynamic import definition is unmodified.
A bundle manifest which mixes legacy syntax with bundle manifest version 2 syntax is in error and must cause the containing bundle to fail to install.
The specification-version
attribute is a deprecated
synonym for the version attribute in bundle manifest version 2
headers.
The OSGi Framework package resolver provides a number of mechanisms to match imports to exports. The following sections describe these mechanisms in detail.
Wires create a graph of nodes. Both the wires as well as nodes (bundles) carry a significant amount of information. In the next sections, the following conventions are used to explain the many details.
Bundles are named A
, B
,
C
,... That is, uppercase characters starting from the character
A
. Packages are named p
, q
,
r
, s
, t
,... In other words, lower
case characters starting from p
. If a version is important,
it is indicated with a dash followed by the version: q-1.0
.
The syntax A.p
means the package definition (either import
or export) of package p
by bundle A
.
Import definitions are graphically shown by a white box. Export definitions are displayed with a black box. Packages that are not exported or imported are called private packages. They are indicated with diagonal lines.
Bundles are a set of connected boxes. Constraints are written on the wires, which are represented by lines.
For example:
A: Import-Package: p; version="[1,2)"
Export-Package: q; version=2.2.2; uses:=p
Require-Bundle: C
B: Export-Package: p; version=1.5.1
C: Export-Package: r
Figure 3.8 shows the same setup graphically.
Version constraints are a mechanism whereby an import definition can declare a precise version or a version range for matching an export definition.
An import definition must specify a version range as the value for its version attribute, and the exporter must specify a version as the value for its version attribute. Matching is done with the rules for version range matches as described in Version Ranges.
For example, the following import and export definition resolve correctly because the version range in the import definition matches the version in the export definition:
A: Import-Package: p; version="[1,2)"
B: Export-Package: p; version=1.5.1
Figure 3.9 graphically shows how a constraint can exclude an exporter.
Version ranges encode the assumptions about binary compatibility. Though the OSGi frameworks do not enforce a specific encoding for a compatibility policy, it is strongly recommended to use the following semantics.
Traditionally, compatibility has always been between two parties. One is the consumer of the code and the other is the provider of the code. API based design introduces a third party in the compatibility policy:
-
The API itself
-
The provider of the API
-
The consumer of the API
A provider of an API is closely bound to that API. Virtually any change to that API makes a provider implementation incompatible with the new version of the API. However, API changes have more leeway from the perspective of a consumer of that API. Many API changes can be made backward compatible for consumers but hardly any API change can be made backward compatible for a provider of that API.
A provider of an API should therefore import that API with a smaller range than a consumer of that API. This policy can be encoded in a version range. The rules are summarized as follows:
-
major
- Changes for an incompatible update for both a consumer and a provider of an API. -
minor
- Changes for a backward compatible update for a consumer but not for a provider. -
micro
- A change that does not affect the API, for example, a typo in a comment or a bug fix in an implementation.
Both consumers and providers should use the version they are
compiled against as their base version. It is recommended to ignore the
micro part of the version because systems tend to become very rigid if
they require the latest bug fix to be deployed all the time. For
example, when compiled against version 4.2.1.V201007221030
,
the base version should be 4.2
.
A consumer of an API should therefore import a range that starts
with the base version and ends with the next major change, for example:
[4.2,5)
. A provider of an API should import a range that
starts with the base version up to the next minor change, for example:
[4.2,4.3)
.
As mentioned in Semantic Versioning, there are two roles for clients of an API package: API consumers and API providers. API consumers use the API and API providers implement the API. For the types in an API, it is important that the API clearly document which of those types are only to be implemented or extended by API providers and which of those types can be implemented or extended by API consumers. For example, listener interfaces are generally implemented by API consumers and instances of them passed to API providers.
API providers are sensitive to changes in types implemented or extended by both API consumers and API providers. An API provider must implement any new changes in API provider types and must understand and likely invoke any new changes in API consumer types. An API consumer can generally ignore compatible changes in API provider types unless it wants to invoke the new function. But an API consumer is sensitive to changes in API consumer types and will probably need modification to implement the new function. For example, in the org.osgi.framework package, the BundleContext type is implemented by the Framework which is the API provider. Adding a new method to BundleContext will require all Framework implementations to be updated to implement the new method. Bundles, which are the API consumers, do not have to change unless they wish to call the new method. However, the BundleActivator type is implemented by bundles and adding a new method to BundleActivator will require all bundles implementing this interface to be modified to implement the new method and will also require all Framework implementations to be modified to utilize the new method. Thus the BundleContext type has an API provider role and the BundleActivator type has an API consumer role in the org.osgi.framework API package.
Since there are generally many API consumer and few API providers, API evolution must be very careful when considering changes to API consumer types while being more relaxed about changes to API provider types. This is because you will need to change the few API providers to support an updated API but you do not want to require the many existing API consumers to change when an API is updated. API consumers should only need to change when the API consumer wants to take advantage of new API.
The ProviderType and ConsumerType annotations can be used in source files to document the roles of types in a package. API types marked ProviderType must not be implemented or extended by API consumers. These annotations can be processed by bundle assembly tools that support Semantic Versioning when generating the version range information for a bundle's Import-Package manifest header.
A bundle can indicate that it does not require a package to resolve correctly, but it may use the package if it is available. For example, logging is important, but the absence of a log service should not prevent a bundle from running.
Optional imports can be specified in the following ways:
-
Dynamic Imports - The DynamicImport-Package header is intended to look for an exported package when that package is needed. The key use case for dynamic import is the
Class forName
method when a bundle does not know in advance the class name it may be requested to load. -
Resolution Directive - The resolution directive on an import definition specifying the value
optional
. A bundle may successfully resolve if a suitable optional package is not present.
The key difference between these two mechanisms is when the wires
are made. An attempt is made to establish a wire for a dynamic import
every time there is an attempt to load a class in that package, whereas
the wire for a resolution optional
package may only be
established when the bundle is resolved.
The resolution
directive of the import definition can
take the value mandatory
or optional
.
-
mandatory
- (Default) Indicates that the package must be wired for the bundle to resolve. -
optional
- Indicates that the importing bundle may resolve without the package being wired. If the package is not wired, the class loading will treat this package as if it is not imported.
The following example will resolve even though bundle
B
does not provide the correct version (the package will
not be available to the code when bundle
A
is resolved).
A: Import-Package: p; «
resolution:=optional; «
version=1.6
B: Export-Package: p; «
q; «
version=1.5.0
The implementation of a bundle that uses optional packages must be prepared to handle the fact that the packages may not be available: that is, an exception can be thrown when there is a reference to a class from a missing package. This can be prevented by including a fallback package on the bundle's class path. When an optional package cannot be resolved, any attempts by the bundle to load classes from it will follow normal bundle class loading search order as if the import never existed. It will load it from the bundle's class path or in the end through dynamic class loading when set for that bundle and package.
Classes can depend on classes in other packages. For example, when
they extend classes from another package, or these other classes appear
in method signatures. It can therefore be said that a package
uses other packages. These inter-package
dependencies are modeled with the uses
directive on the
Export-Package header.
For example, org.osgi.service.http
depends on the
package javax.servlet
because it is used in the API. The
export definition of the org.osgi.service.http
must
therefore contain the uses
directive with the
javax.servlet
package as its value.
Class space consistency can only be ensured if a bundle has only
one exporter for each package. For example, the Http Service
implementation requires servlets to extend the
javax.servlet.http.HttpServlet
base class. If the Http
Service bundle would import version 2.4 and the client bundle would
import version 2.1 then a class cast exception is bound to happen. This
is depicted in Figure 3.11.
If a bundle imports a package from an exporter then the export
definition of that package can imply constraints on a number of other
packages through the uses
directive. The uses
directive lists the packages that the exporter depends upon and
therefore constrains the resolver for imports. These constraints ensure
that a set of bundles share the same class loader for the same
package.
When an importer imports a package with uses constraints, the resolver must wire the import to the exporter named in the uses constraint. This exporter may in turn imply additional constraints, and so on. The act of wiring a single import of a package to an exporter can therefore imply a large set of constraints. The term implied package constraints refers to the complete set of constraints constructed from recursively traversing the wires. Implied package constraints are not automatic imports; rather, implied package constraints only constrain how an import definition must be wired.
For example, in Figure 3.12, bundle A
imports package p
. Assume this import definition is wired
to bundle B
. Due to the uses
directive (the
ellipse symbols indicates the uses
directive) this implies
a constraint on package q
.
Further, assuming that the import for package q
is
wired to bundle C
, then this implies a constraint on the
import of package r
and s
. Continuing,
assuming C.s
and C.r
are wired to bundle
D
and E respectively. These bundles both add package
t
to the set of implied packages for bundle A.
To maintain class space consistency, the Framework must ensure that none of its bundle imports conflicts with any of that bundle's implied packages.
For the example, this means that the Framework must ensure that
the import definition of A.t
is wired to package
D.t
. Wiring this import definition to package
F.t
violates the class space consistency. This violation
occurs because bundle A could be confronted with objects with the same
class name but from the class loaders of bundle D
and
F
. This would potentially create
ClassCastExceptions
. Alternatively, if all bundles are
wired to F.t
, then the problem also goes away.
Another scenario with this case is depicted in Figure 3.11. Bundle A
imports the Http Service
classes from bundle B
. Bundle B
has grouped
the org.osgi.service.http
and the
javax.servlet
and bundle A
is therefore
constrained to wire javax.servlet
to the same exporter as
bundle B
.
As an example of a situation where the uses
directive
makes resolving impossible consider the following setup that is
correctly resolved:
A: Import-Package: q; version="[1.0,1.0]"
Export-Package: p; uses:="q,r",r
B: Export-Package: q; version=1.0
C: Export-Package: q; version=2.0
These specific constraints can be resolved because the import
A.q
can be wired to the export B.q
but not
C.q
due to the version constraint.
Adding a bundle D
will now not be possible:
D: Import-Package: p, q; version=2.0
Package D.p
must be wired to package A.p
because bundle A
is the only exporter. However, this
implies the use of package q
due the uses
directive in the package A.q
import. Package
A.q
is wired to B.q-1.0
. However, import
package D.q
requires version 2.0 and can therefore not be
resolved without violating the class space constraint.
This scenario is depicted in Figure 3.13.
Attribute matching is a generic mechanism to allow the importer
and exporter to influence the matching process in a declarative way. In
order for an import definition to be resolved to an export definition,
the values of the attributes specified by the import definition must
match the values of the attributes of the export definition. By default,
a match is not prevented if the export definition contains attributes
that do not occur in the import definition. The mandatory
directive in the export definition can reverse this by listing all
attributes that the Framework must match in the import definition. Any
attributes specified in the DynamicImport-Package is ignored during the
resolve phase but can influence runtime class loading.
For example, the following statements will match.
A: Import-Package: com.acme.foo;company=ACME
B: Export-Package: com.acme.foo; «
company="ACME"; «
security=false
Attribute values are compared string wise except for the version and bundle-version attributes which use version range comparisons. Leading and trailing white space in attribute values must be ignored.
Attribute matching also works for the Require-Bundle and Fragment-Host headers; the attributes to be matched are specified on the Bundle-SymbolicName header.
There are two types of attributes: mandatory and optional. Mandatory attributes must be specified in the import definition to match. Optional attributes are ignored when they are not referenced by the importer. Attributes are optional by default.
The exporter can specify mandatory attributes with the
mandatory
directive in the export definition. This
directive contains a comma-separated list of attribute names that must
be specified by the importer to match.
For example, the following import definition must not match the
export definition because security
is a mandatory
attribute:
A: Import-Package: com.acme.foo;company=ACME
B: Export-Package: com.acme.foo; «
company="ACME"; «
security=false; «
mandatory:=security
An exporter can limit the visibility of the classes in a package
with the include
and exclude
directives on the
export definition. The value of each of these directives is a
comma-separated list of class names. Note that the use of a comma in the
value requires it to be enclosed in double quotes.
Class names must not include their package name and do not end
with .class
. That is, the class
com.acme.foo.Daffy
is named Daffy
in either
list. The class name can include multiple wildcard asterisks ('*'
\u002A
).
The default for the include
directive is an asterisk
('*' \u002A
) (wildcard matching all names), and for the
exclude
directive, so that no classes are excluded, an
empty list that matches no names. If include
or
exclude
directive are specified, the corresponding default
is overridden.
A class is only visible if it is:
-
Matched with an entry in the
included
list, and -
Not matched with an entry in the
excluded
list.
In all other cases, loading or finding fails, and a Class Not
Found Exception is thrown for a class load. The ordering of
include
and exclude
is not significant.
The following example shows an export statement, and a list of files with their visibility status.
Export-Package: com.acme.foo; include:="Qux*,BarImpl"; «
exclude:=QuxImpl
com/acme/foo
QuxFoo visible
QuxBar visible
QuxImpl excluded
BarImpl visible
Care must be taken when using filters. For example, a new version of a module that is intended to be backward compatible with an earlier version should not filter out classes that were not filtered out by the earlier version. In addition, when modularizing existing code, filtering out classes from an exported package may break users of the package.
For example, packages defined by standard bodies often require an implementation class in the standardized package to have package access to the specification classes.
package org.acme.open;
public class Specified {
static Specified implementation;
public void foo() { implementation.foo(); }
}
package org.acme.open;
public class Implementation {
public void initialize(Specified implementation) {
Specified.implementation = implementation;
}
}
The Implementation
class must not be available to
external bundles because it allows the implementation to be set. By
excluding the Implementation
class, only the exporting
bundle can see this class. The export definition for this header could
look like:
Export-Package: org.acme.open; exclude:=Implementation
Provider selection allows the importer to select which bundles can be considered as exporters. Provider selection is used when there is no specification contract between the importer and the exporter. The importer tightly couples itself to a specific exporter, typically the bundle that was used for testing. To make the wiring less brittle, the importer can optionally specify a range of bundle versions that will match.
An importer can select an exporter with the import attributes
bundle-symbolic-name
and bundle-version
. The
Framework automatically provides these attributes for each export
definition. These attributes must not be specified in an export
definition.
The export definition bundle-symbolic-name
attribute
will contain the bundle symbolic name as specified in the
Bundle-SymbolicName
header without any parameters. The
export definition bundle-version
attribute is set to the
value of the Bundle-Version
header or its default of 0.0.0
when absent.
The bundle-symbolic-name
is matched as an attribute.
The bundle-version
attribute is matched using the version
range rules as defined in Version Ranges. The import
definition must be a version range and the export definition is a
version.
For example, the following definitions will match:
A: Bundle-SymbolicName: A
Import-Package: com.acme.foo; «
bundle-symbolic-name=B; «
bundle-version="[1.41,2.0.0)"
B: Bundle-SymbolicName: B
Bundle-Version: 1.41
Export-Package: com.acme.foo
The following statements will not match because bundle B does not specify a version and thus defaults to 0.0.0:
A: Bundle-SymbolicName: A
Import-Package: com.acme.foo; «
bundle-symbolic-name=B; «
bundle-version="[1.41,2.0.0)"
B: Bundle-SymbolicName: B
Export-Package: com.acme.foo;version=1.42
Selecting an exporter by symbolic name can result in brittleness because of hard coupling of the package to the bundle. For example, if the exporter eventually needs to be refactored into multiple separate bundles, all importers must be changed. Other arbitrary matching attributes do not have this disadvantage as they can be specified independently of the exporting bundle.
The brittleness problem of the bundle symbolic name in bundle refactoring can be partly overcome by writing a façade bundle using the same bundle symbolic name as the original bundle.
Resolving is the process that creates a wiring between bundles. Constraints on the wires are statically defined by:
-
Any mandatory requirement must be matched to at least one capability in the same namespace provided by any of the resolved bundles, including itself and the system bundle.
-
The required execution environments as defined by the Bundle-RequiredExecutionEnvironment header.
-
Native code
-
Import and export packages (the DynamicImport-Package header is ignored in this phase)
-
Required bundles, which import all exported packages from a bundle as defined in Requiring Bundles.
-
Fragments, which provide their contents and definitions to the host as defined in Fragment Bundles
A bundle can only be resolved when a number of constraints are satisfied:
-
Execution Environment - The underlying VM implements at least one of the execution environments listed in the Bundle-RequiredExecutionEnvironment header. See osgi.ee Namespace.
-
Native code - The native code dependencies specified in the Bundle-NativeCode header must be resolved. See Loading Native Code Libraries.
The resolving process is then a constraint-solving algorithm that can be described in terms of requirements on wiring relations. The resolving process is an iterative process that searches through the solution space.
A bundle can be resolved if the following conditions are met:
-
All its mandatory requirements are satisfied
-
All its mandatory imports are wired
-
All its mandatory required bundles are available and their exports wired
A wire is only created when the following conditions are met:
-
The importer's version range matches the exporter's version. See Semantic Versioning.
-
The importer specifies all mandatory attributes from the exporter. See Mandatory Attributes.
-
All the importer's attributes match the attributes of the corresponding exporter. See Attribute Matching
-
Implied packages referring to the same package as the wire are wired to the same exporter. See Package Constraints.
-
The wire is connected to a valid exporter.
The following list defines the preferences, if multiple choices are possible, in order of decreasing priority:
-
A resolved exporter must be preferred over an unresolved exporter.
-
An exporter with a higher version is preferred over an exporter with a lower version.
-
An exporter with a lower bundle ID is preferred over a bundle with a higher ID.
If a bundle has both import and export definitions for the same package, then the Framework needs to decide which to choose.
It must first try to resolve the overlapping import definition. The following outcomes are possible:
-
External - If this resolves to an export statement in another bundle, then the overlapping export definition in this bundle is discarded.
-
Internal - If it is resolved to an export statement in this bundle, then the overlapping import definition in this bundle is discarded.
-
Unresolved - There is no matching export definition. In this case the framework is free to discard either the overlapping export definition or overlapping import definition in this bundle. If the export definition is discarded and the import definition is not optional then the bundle will fail to resolve.
The above only applies to the import and export package
definitions of a bundle. For namespaces other than
osgi.wiring.package
, a requirement definition of a bundle
may be wired to a capability definition of that same bundle.
Each bundle installed in the Framework must not have an associated class loader until after it is resolved. After a bundle is resolved, the Framework must create one class loader for each bundle that is not a fragment. The framework may delay creation of the class loader until it is actually needed.
One class loader per bundle allows all resources within a bundle to have package level access to all other resources in the bundle within the same package. This class loader provides each bundle with its own namespace, to avoid name conflicts, and allows resource sharing with other bundles.
This class loader must use the wiring as calculated in the resolving process to find the appropriate exporters. If a class is not found in the imports, additional headers in the manifest can control the searching of classes and resources in additional places.
The following sections define the factors that influence the runtime class loading and then define the exact search order the Framework must follow when a class or resource is loaded.
JAR, ZIP, directories, etc. are called containers. Containers contain entries organized in hierarchical paths. During runtime, an entry from a bundle can actually come from different containers because of attached fragments. The order in which an entry can be found is significant because it can shadow other entries. For a bundle, the search order for a named entry is:
-
First the container of the (host) bundle
-
Then the (optional) fragment containers in ascending id order
This search order is called the entry path. A resource (or class) is not loaded via the entry path, but it is loaded through the bundle class path. The bundle class path provides an additional indirection on top of the entry path. It defines an ordered list of container paths. Each container path can be found on the entry path.
Each container, independent of any other containers, can be a multi-release container. See Multi-release Container.
The full stop ('.' \u002E
) container path is a
synonym for the solidus ('/' \u002F
) or the root of a
container. The full stop is the default value for a bundle or fragment
if no Bundle-ClassPath header is specified.
The Bundle-ClassPath manifest header must conform to the following syntax:
Bundle-ClassPath ::= entry ( ',' entry )*
entry ::= target ( ';' target )* ( ';' parameter ) *
target ::= path | '.' // See 1.3.2
The Framework must ignore any unrecognized parameters.
The content of the effective bundle class path is constructed from the bundle's Bundle-ClassPath header, concatenated with the Bundle-ClassPath headers of any fragments, in ascending bundle id order. The effective Bundle-ClassPath is calculated during resolve time, however, a dynamically attached fragment can append elements at the end if the Framework supports dynamically attached fragments.
An element from the bundle's Bundle-ClassPath header refers to the first match when searched through the entry path, while a fragment's Bundle-ClassPath can refer only to an entry in its own container.
An example can illustrate this:
A: Bundle-ClassPath: .,resource.jar
B: Fragment-Host: A
The previous example uses an effective bundle class path of:
/, resource.jar, B:/
The first element / is the root of a container. The bundle always
has a root and can therefore always be found in the (host) bundle. The
second element is first looked up in the host bundle's container, and if
not found, the entry is looked up in the container of B
.
The Framework must use the first entry that matches. The last element in
the effective bundle class path is the / from fragment B
;
the / is the default because there is no Bundle-ClassPath specified.
However, a fragment can only refer to an internal entry. This full stop
therefore refers to the root of the container of fragment
B
. Assuming, fragment B
contains an entry for
resource.jar
and bundle A
does not, then the
search order must be:
A:/
B:resource.jar
B:/
The Framework must ignore a container path in the bundle
class-path if the container cannot be located when it is needed, which
can happen at any time after the bundle is resolved. However, the
Framework should publish a Framework Event of type INFO
once with an appropriate message for each entry that cannot be located
at all.
An entry on the Bundle-ClassPath can refer to a directory in the container. However, it is not always possible to establish the directory's existence. For example, directories can be omitted in JAR/ZIP files. In such a case, a Framework must probe the directory to see if any resources can be found in this directory. That is, even if the directory construct is absent in the container, if resources can be found assuming this directory, than it must still be chosen for the Bundle-ClassPath.
A host bundle can allow a fragment to insert code ahead of its own code by naming a container in its Bundle-ClassPath that will be provided by a fragment. Fragments can never unilaterally insert code ahead of their host's bundle class path. The following example illustrates the possibilities of the bundle class path in more detail:
A: Bundle-SymbolicName: A
Bundle-ClassPath: /,required.jar,optional,default.jar
content ...
required.jar
default.jar
B: Bundle-SymbolicName: B
Bundle-ClassPath: fragment.jar
Fragment-Host: A
content ...
optional/
content ...
fragment.jar
The names of the bundle class path elements indicate their
intention. The required.jar
is a container that provides
mandatory functionality, it is packaged in bundle A. The
optional
container is a directory containing optional
classes, and the default.jar
is a JAR entry with backup
code. In this example, the effective bundle class path is:
A:/
A:required.jar
B:optional
A:default.jar
B:fragment.jar
This will expand to the following (logical) search order for a
resource X.class
:
A:/X.class
A:required.jar!X.class
B:optional/X.class
A:default.jar!X.class
B:fragment.jar!X.class
The exclamation mark (!) indicates a load from a JAR resource.
A container can be a multi-release
container . A multi-release container supports versioned
directories as specified in [27] Multi-release JAR File. When a container is
a multi-release container, that is, the container has a
META-INF/MANIFEST.MF
manifest which specifies
Multi-Release: true
then the Framework must search the container's versioned directories when attempting to locate an entry in the container.
Support for multi-release containers must only be active on Java 9 and later. On Java 8 and earlier, the container must be treated as a non-multi-release container.
Dynamic imports are matched to export definitions (to form package wirings) during class loading, and therefore do not affect module resolution. Dynamic imports apply only to packages for which no wire has been established and no definition could be found in any other way. Dynamic import is used as a last resort.
DynamicImport-Package ::= dynamic-description
( ',' dynamic-description )*
dynamic-description ::= wildcard-names ( ';' parameter )*
wildcard-names ::= wildcard-name ( ';' wildcard-name )*
wildcard-name ::= package-name
| ( package-name '.*' ) // See 1.3.2
| '*'
No directives are architected by the Framework for DynamicImport-Package. Arbitrary matching attributes may be specified. The following matching attributes are architected by the Framework:
-
version
-- A version range to select the version of an export definition. The default value is 0.0.0 . -
bundle-symbolic-name
- The bundle symbolic name of the exporting bundle. -
bundle-version
- a version range to select the bundle version of the exporting bundle. The default value is0.0.0
.
Packages may be named explicitly or by using wild-carded
expressions such as org.foo.*
and *
. The
wildcard can stand for any suffix, including multiple sub-packages. If a
wildcard is used, then the package identified by the prefix must
not be included. That is, org.foo.*
will include all sub-packages of org.foo
but it must not
include package org.foo
itself.
Dynamic imports must be searched in the order in which they are specified. The order is particularly important when package names with wildcards are used. The order will then determine the order in which matching occurs. This means that the more specific package specifications should appear before the broader specifications. For example, the following DynamicImport-Package header indicates a preference for packages supplied by ACME:
DynamicImport-Package: *;vendor=acme, *
If multiple packages need to be dynamically imported with identical parameters, the syntax permits a list of packages, separated by semicolons, to be specified before the parameters.
During class loading, the package of the class being loaded is
compared against the specified list of (possibly wild-carded) package
names. Each matching package name is used in turn to attempt to wire to
an export using the same rules as Import-Package. If a wiring attempt is
successful (taking any uses
constraints into account), the
search is forwarded to the exporter's class loader where class loading
continues. The wiring must not subsequently be modified, even if the
class cannot be loaded. This implies that once a package is dynamically
resolved, subsequent attempts to load classes or resources from that
package are treated as normal imports.
In order for a DynamicImport-Package to be resolved to an export statement, all attributes of the dynamic import definition must match the attributes of the export statement. All mandatory arbitrary attributes (as specified by the exporter, see Mandatory Attributes ) must be specified in the dynamic import definition and match.
Once a wire is established, any uses
constraints from
the exporter must be obeyed for further dynamic imports.
Dynamic imports are very similar to optional packages, see Optional Packages, but differ in the fact that they are handled after the bundle is resolved.
The Framework must always delegate any package that starts with
java.
to the parent class loader.
Certain Java virtual machines, also Oracle's VMs, appear to make
the erroneous assumption that the delegation to the parent class loader
always occurs. This implicit assumption of strictly hierarchical class
loader delegation can result in NoClassDefFoundErrors
. This
happens if the virtual machine implementation expects to find its own
implementation classes from any arbitrary class loader, requiring that
packages loaded from the boot class loader not be restricted to only the
java.*
packages.
Other packages that must be loaded from the boot class loader can therefore be specified with the System property:
org.osgi.framework.bootdelegation
This property must contain a list with the following format:
org.osgi.framework.bootdelegation ::= boot-description
( ',' boot-description )*
boot-description ::= package-name // See 1.3.2
| ( package-name '.*' )
| '*'
The .*
wildcard means deep matching, that is,
com.acme.*
, matches any sub-package of package
com.acme
, however, it does not match com.acme
.
Packages that match this list must be loaded from the parent class
loader. The java.*
prefix is always implied; it does not
have to be specified.
The single wildcard means that the Framework must always delegate to the parent class loader first, which is the same as the Release 3 behavior.
For example, when running on an OpenJDK, it may be necessary to specify a value like:
org.osgi.framework.bootdelegation=sun.*,com.sun.*
With such a property value, the Framework must delegate all
java.*
, sun.*
, and com.sun.*
packages to the parent class loader.
Frameworks must adhere to the following rules for class or resource loading. When a bundle's class loader is requested to load a class or find a resource, the search must be performed in the following order:
-
If the class or resource is in a
java.*
package, the request is delegated to the parent class loader; otherwise, the search continues with the next step. If the request is delegated to the parent class loader and the class or resource is not found, then the search terminates and the request fails. -
If the class or resource is from a package included in the boot delegation list (
org.osgi.framework.bootdelegation
), then the request is delegated to the parent class loader. If the class or resource is found there, the search ends. -
If the class or resource is in a package that is imported using Import-Package or was imported dynamically in a previous load, then the request is delegated to the exporting bundle's class loader; otherwise the search continues with the next step. If the request is delegated to an exporting class loader and the class or resource is not found, then the search terminates and the request fails.
-
If the class or resource is in a package that is imported from one or more other bundles using Require-Bundle, the request is delegated to the class loaders of the other bundles, in the order in which they are specified in this bundle's manifest. This entails a depth-first strategy; all required bundles are searched before the bundle class path is used. If the class or resource is not found, then the search continues with the next step.
-
Search the bundle's embedded class path, see Bundle Class Path. If the class or resource is not found, then continue with the next step.
-
If the class or resource is in a package that is exported by the bundle or the package is imported by the bundle (using Import-Package or Require-Bundle), then the search ends and the class or resource is not found.
-
Otherwise, if the class or resource is in a package that is imported using DynamicImport-Package, then a dynamic import of the package is now attempted. An exporter must conform to any implied package constraints. If an appropriate exporter is found, a wire is established so that future loads of the package are handled in step 3. If a dynamic wire is not established, then the request fails.
-
If the dynamic import of the package is established, the request is delegated to the exporting bundle's class loader. If the request is delegated to an exporting class loader and the class or resource is not found, then the search terminates and the request fails.
When delegating to another bundle class loader, the delegated request enters this algorithm at step 4.
The following non-normative flow chart illustrates the search order described above:
The set of implicitly visible packages are all java.*
packages, since these packages are required by the Java runtime, and
using multiple versions at the same time is not easy. For example, all
objects must extend the same Object class.
A bundle must not declare exports for java.*
packages; doing so is an error and any such bundle must fail to install
or update. A bundle may declare imports for java.*
packages; but this is for resolution purposes only. All other packages
visible through the parent class loader must be hidden from executing
bundles.
However, the Framework must explicitly export relevant packages from the parent class loader. The system property
org.osgi.framework.system.packages
contains the export packages descriptions for the system bundle. This property employs the standard Export-Package manifest header syntax:
org.osgi.framework.system.packages ::= package-description
( ',' package-description )*
Some classes on the boot class path assume that they can use any class loader to load other classes on the boot class path, which is not true for a bundle class loader. Framework implementations should attempt to load these classes from the boot class path.
The system bundle (bundle ID zero) is used to export packages from the parent class loader. Export definitions from the system bundle are treated like normal exports, meaning that they can have version numbers, and are used to resolve import definitions as part of the normal bundle resolving process. Other bundles may provide alternative implementations of the same packages.
The set of export definitions for the parent class loader can
either be set by this property or calculated by the Framework. The
export definitions must have the bundle-symbolic-name
and
bundle-version
attributes with the implementation-specific
bundle symbolic name and bundle version value of the system
bundle.
Exposing packages from the parent class loader in this fashion
must also take into account any uses
directives of the
underlying packages. For example, the definition of
javax.crypto.spec
must declare its usage of
javax.crypto.interfaces
and
javax.crypto
.
A resource in a bundle can be accessed through the class loader of
that bundle but it can also be accessed with the getResource(String), getEntry(String), findEntries(String,String,boolean) and other methods or the methods on the Bundle Wiring API Specification. All these methods return a
URL
object or an Enumeration
object of
URL
objects. The URLs are called bundle entry
URLs. The schemes for the URLs returned by these methods can
differ and are implementation dependent.
Bundle entry URLs are normally created by the Framework, however, in certain cases bundles need to manipulate the URL to find related resources. The Framework is therefore required to ensure that:
-
Bundle entry URLs must be hierarchical (See [13] RFC 2396 Uniform Resource Identifiers URI: Generic Syntax )
-
Usable as a context for constructing another URL.
-
The
java.net.URLStreamHandler
class used for a bundle entry URL must be available to thejava.net.URL
class to setup a URL that uses the protocol scheme defined by the Framework. -
The
getPath
method for a bundle entry URL must return an absolute path (a path that starts with '/') to a resource or entry in a bundle. For example, the URL returned fromgetEntry("myimages/test.gif")
must have a path of/myimages/test.gif
.
For example, a class can take a URL to an index.html
bundle resource and map URLs in this resource to other files in the same
JAR directory.
public class BundleResource extends ServletContextHelper {
URL root; // to index.html in bundle
URL getResource( String resource ) {
return new URL( root, resource );
}
...
}
Multiple required bundles can export the same package. Bundles which export the same package involved in a require bundle cycle can lead to lookup cycles when searching for classes and resources from the package. Consider the following definitions:
A: Require-Bundle: B, C
C: Require-Bundle: D
These definitions are depicted in Figure 3.15.
Each of the bundles exports the package p
. In this
example, bundle A
requires bundle B
, and
bundle C
requires bundle D
. When bundle
A
loads a class or resource from package p
,
then the required bundle search order is the following: B
,
D
, C
, A
. This is a depth first
search order because required bundles are searched before the bundle
class path is searched (see step 4 ). The
required bundles are searched in the order that they appear in the
Require-Bundle header. The depth first search order can introduce
endless search cycles if the dependency graph has a cycle in it.
Using the previous setup, a cycle can be introduced if bundle
D
requires bundle A
as depicted in Figure 3.16.
D: Require-Bundle: A
When the class loader for bundle A
loads a class or
resource from package p
then the bundle search order would
be the following: B, B, B,... if cycles were not taken into
account.
Since a cycle was introduced each time bundle D is reached the search will recurs back to A and start over. The framework must prevent such dependency cycles from causing endless recursive lookups.
To avoid endless looping, the Framework must mark each bundle upon
first visiting it and not explore the required bundles of a previously
visited bundle. Using the visited pattern on the dependency graph above
will result in the following bundle search order: B
,
D
, C
, A
.
Packages exported from a bundle are exposed to other bundles as soon as the bundle has been resolved. This condition could mean that another bundle could call methods in an exported package before the bundle exporting the package is started.
There are scenarios where a bundle is required in code that has no access to a Bundle Context. For this reason, the framework provides the following Framework Util methods:
-
getBundle(Class)
- The getBundle(Class) method allows code to find the bundle of a class. The method returnsnull
when the class does not originate from a bundle. -
getBundle(ClassLoader)
- The getBundle(ClassLoader) method allows code to find the bundle of a class loader. The method returns an empty Optional when the class loader is not associated with a specific bundle.
In an OSGi system, not all objects belong to types loaded from a bundle class loader. For example, the boot class path loader.
Dependency on native code is expressed in the Bundle-NativeCode header. The framework must verify this header and satisfy its dependencies before it attempts to resolve the bundle. However, a bundle can be installed without an exception if the header is properly formatted according to its syntax. If the header contains invalid information, or can not be satisfied, errors will be reported during resolving.
A Java VM has a special way of handling native code. When a class
loaded by a bundle's class loader attempts to load a native library, by
calling System.loadLibrary
, the findLibrary
method of the bundle's class loader must be called to return the file path
in which the Framework has made the requested native library available.
The parameter to the findLibrary
method is the name of the
library in operating system independent form, like http
. The
bundle class loader can use the mapLibraryName
method from
the VM to map this name to an operating system dependent name, like
libhttp.so
.
The bundle's class loader must attempt to find the native library by
examining the selected native code clauses, if any, of the bundle
associated with the class loader and each attached fragment. Fragments are
examined in ascending bundle ID order. If the library is not referenced in
any of the selected native code clauses then null
must be
returned which allows the parent class loader to search for the native
library.
The bundle must have the required
RuntimePermission
[loadLibrary.
<
library name
>] in order to load native code in the OSGi
framework.
The Bundle-NativeCode manifest header must conform to the following syntax:
Bundle-NativeCode ::= nativecode
( ',' nativecode )* ( ',' optional ) ?
nativecode ::= path ( ';' path )* // See 1.3.2
( ';' parameter )*
optional ::= '*'
When locating a path in a bundle the Framework must attempt to locate the path relative to the root of the bundle that contains the corresponding native code clause in its manifest header.
The following attributes are architected:
-
osname
- Name of the operating system. The value of this attribute must be the name of the operating system upon which the native libraries run. A number of canonical names are defined in Table 4.3. -
osversion
- The operating system version. The value of this attribute must be a version range as defined in Version Ranges. -
processor
- The processor architecture. The value of this attribute must be the name of the processor architecture upon which the native libraries run. A number of canonical names are defined in Table 4.2. -
language
- The ISO code for a language. The value of this attribute must be the name of the language for which the native libraries have been localized. -
selection-filter
- A selection filter. The value of this attribute must be a filter expression that indicates if the native code clause should be selected or not.
If a selection-filter attribute contains an invalid filter, then the bundle must fail to install with a Bundle Exception of type NATIVECODE_ERROR. The following is a typical example of a native code declaration in a bundle's manifest:
Bundle-NativeCode: lib/http.dll ; lib/zlib.dll; «
osname = Windows95 ; «
osname = Windows98 ; «
osname = WindowsNT ; «
processor = x86 ; «
selection-filter = «
"(com.acme.windowing=win32)"; «
language = en ; «
language = se , «
lib/solaris/libhttp.so ; «
osname = Solaris ; «
osname = SunOS ; «
processor = sparc, «
lib/linux/libhttp.so ; «
osname = Linux ; «
processor = mips; «
selection-filter = «
"(com.acme.windowing=gtk)"
If multiple native code libraries need to be installed on one platform, they must all be specified in the same clause for that platform.
If a Bundle-NativeCode clause contains duplicate parameter entries,
the corresponding values must be OR
'ed together. This feature
must be carefully used because the result is not always obvious. This is
highlighted by the following example:
// The effect of this header has probably
// not the intended effect!
Bundle-NativeCode: lib/http.DLL ; «
osname = Windows95 ; «
osversion = "3.1" ; «
osname = WindowsXP ; «
osversion = "5.1" ; «
processor = x86
The above example implies that the native library will load on Windows XP 3.1 and later, which was probably not intended. The single clause should be split in two clauses:
Bundle-NativeCode: lib/http.DLL ; «
osname = Windows95 ; «
osversion = 3.1; «
processor = x86, «
lib/http.DLL ; «
osname = WindowsXP ; «
osversion = 5.1; «
processor = x86
Any paths specified in the matching clause must be present in the
bundle or any of its attached fragments for a bundle to resolve. The
framework must report a Bundle Exception with the
NATIVECODE_ERROR
as error code when the bundle can not be
resolved due to a native code problem.
If the optional '*' is specified at the end of the Bundle-NativeCode manifest header, the bundle will still resolve even if the Bundle-NativeCode header has no matching clauses.
The following is a typical example of a native code declaration in a bundle's manifest with an optional clause:
Bundle-NativeCode: lib/win32/winxp/optimized.dll; «
lib/win32/native.dll ; «
osname = WindowsXP ; «
processor = x86 , «
lib/win32/native.dll ; «
osname = Windows95 ; «
osname = Windows98 ; «
osname = WindowsNT ; «
osname = Windows2000; «
processor = x86 , «
*
Frameworks must convert a Bundle-NativeCode header to a requirement
in the osgi.native
namespace when used in the Wiring API, see
Bundle Wiring API Specification. Each native code clause specified in a
Bundle-NativeCode header is converted into a filter component for the
osgi.native
requirement filter directive using the following
architected matching attributes:
-
osgi.native.osname
- Uses the approximate equals (~=) filter type to evaluate the value specified by theosname
Bundle-NativeCode attribute. -
osgi.native.osversion
- Create aVersionRange
using the value specified by the osversion Bundle-NativeCode attribute and then create a filter string out of theVersionRange
. -
osgi.native.processor
- Uses the approximate equals (~=) filter type to evaluate the value specified by theprocessor
Bundle-NativeCode attribute. -
osgi.native.language
- Uses the approximate equals (~=) filter type to evaluate the value specified by thelanguage
Bundle-NativeCode attribute.
In cases where the same Bundle-NativeCode attribute is specified multiple times within the same clause then the filter components for each value for that attribute are ORed together. For example, if osname attribute is specified as both "Windows95" and "Windows7" then the resulting filter will contain:
(|
(osgi.native.osname~=Windows95)
(osgi.native.osname~=Windows7)
)
If the selection-filter
Bundle-NativeCode attribute is
specified then the specified filter is included as a component of the
native code clauses AND filter type. Consider the following
Bundle-NativeCode header which contains a single clause:
Bundle-NativeCode: «
lib/http.dll; lib/zlib.dll; «
osname=Windows95; «
osname=Windows98; «
osname=WindowsNT; «
processor=x86; «
selection-filter="(com.acme.windowing=win32)"; «
language=en; «
language=se
This clause would get translated into the following AND filter type:
Require-Capability: «
osgi.native; «
filter:=" «
(& «
(| «
(osgi.native.osname~=Windows95) «
(osgi.native.osname~=Windows98) «
(osgi.native.osname~=WindowsNT) «
) «
(osgi.native.processor~=x86) «
(| «
(osgi.native.language~=en) «
(osgi.native.language~=se) «
) «
(com.acme.windowing=win32) «
)"
The Bundle-NativeCode header may specify multiple clauses, each
having their own list of native code paths and set of matching attributes.
Instead of using a separate osgi.native
requirement for each
Bundle-NativeCode clause, the complete Bundle-NativeCode header is
specified as a single osgi.native
requirement. This is done
by using an OR filter type using all of the individual Bundle-NativeCode
clause filter components (as specified above) as components of a single
filter directive. Consider the following Bundle-NativeCode header which
contains three clauses:
Bundle-NativeCode: «
lib/http.dll; lib/zlib.dll; «
osname=Windows95; «
osname=Windows98; «
osname=WindowsNT; «
processor=x86; «
selection-filter = "(com.acme.windowing=win32)"; «
language=en; «
language=se, «
lib/solaris/libhttp.so; «
osname=Solaris; «
osname=SunOS; «
processor=sparc, «
lib/linux/libhttp.so; «
osname=Linux; «
processor=mips; «
selection-filter="(com.acme.windowing=gtk)"
This Bundle-NativeCode header would get translated into the
following osgi.native
filter directive:
(|
(&
(|
(osgi.native.osname~=Windows95)
(osgi.native.osname~=Windows98)
(osgi.native.osname~=WindowsNT)
)
(osgi.native.processor~=x86)
(|
(osgi.native.language~=en)
(osgi.native.language~=se)
)
(com.acme.windowing=win32)
)
(&
(|
(osgi.native.osname~=Solaris)
(osgi.native.osname~=SunOs)
)
(osgi.native.processor~=sparc)
)
(&
(osgi.native.osname~=Linux)
(osgi.native.processor~=mips)
(com.acme.windowing=gtk)
)
)
If the optional '*' is specified at the end of the Bundle-NativeCode
manifest header, then the native code for the bundle is considered to be
optional. When the Framework converts a Bundle-NativeCode header into an
osgi.native
requirement which is designated as optional then
the requirement resolution directive must be set to
optional
In the description of this algorithm, [x] represents the value of the launching property x (see Launching Properties) and ~= represents the match operation. The match operation is a case insensitive comparison. Certain properties can be aliased. In those cases, the manifest header should contain the generic name for that property but the Framework should attempt to include aliases when it matches.
The Framework must select the native code clause using the following algorithm:
-
Only select the native code clauses for which the following expressions all evaluate to true.
-
osname ~= [org.osgi.framework.os.name]
orosname
is not specified -
processor ~= [org.osgi.framework.processor]
orprocessor
is not specified -
osversion
range includes[org.osgi.framework.os.version]
orosversion
is not specified -
language ~= [org.osgi.framework.language]
orlanguage
is not specified -
selection-filter
evaluates totrue
when using the values of the launching properties orselection-filter
is not specified
-
-
If no native clauses were selected in step 1, this algorithm is terminated. A Bundle Exception is thrown if the optional clause is not present.
-
The selected clauses are now sorted in the following priority order:
-
osversion
: floor of theosversion
range in descending order,osversion
not specified -
language
: language specified, language not specified -
Position in the Bundle-NativeCode manifest header: lexical left to right.
-
-
The first clause of the sorted clauses from step 3 must be used as the selected native code clause.
If a native code library in a selected native code clause cannot
be found within the bundle then the bundle is still allowed to resolve.
A missing native code library will result in an error being thrown at
runtime when the bundle attempts to load the native code (for example,
by invoking the method System.loadLibrary
).
If the selected clause contains multiple libraries with the same
base file name then only the lexically left most library with that base
file name will be used. For example, if the selected clause contains the
libraries lib1/http.dll; lib2/http.dll; lib3/foo.dll;
a/b/c/http.dll
then only http.dll
in
lib1
and foo.dll
will be used.
Designing a bundle native code header can become quickly complicated when different operating systems, libraries, and languages are used. The best practice for designing the header is to place all parameters in a table. Every targeted environment is then a row in that table. See Table 3.2 for an example.
Table 3.2 Native code table
Libraries | osname | osversion | processor | language | filter |
---|---|---|---|---|---|
nativecodewin32.dll, delta.dll |
|
|
|
||
nativecodegtk.so |
|
|
|
|
|
nativecodeqt.so |
|
|
|
|
This table makes it easier to detect missing combinations. This table is then mapped to the Bundle-NativeCode header in the following code example.
Bundle-NativeCode: nativecodewin32.dll; «
delta.dll; «
osname=win32; «
processor=x86; «
language=en, «
nativecodegtk.so; «
osname=linux; «
processor=x86; «
language=en; «
selection-filter= «
"(com.acme.windowing=gtk)", «
nativecodeqt.so; «
osname=linux; «
processor=x86; «
language=en; «
selection-filter = «
"(com.acme.windowing=qt)"
There are some restrictions on loading native libraries due to the nature of class loaders. In order to preserve namespace separation in class loaders, only one class loader can load a native library as specified by an absolute path. Loading of a native library file by multiple class loaders (from multiple bundles, for example) will result in a linkage error.
Care should be taken to use multiple libraries with the same file
name but in a different directory in the JAR. For example,
foo/http.dll
and bar/http.dll
. The Framework
must only use the first library and ignore later defined libraries with
the same name. In the example, only foo/http.dll
will be
visible.
A native library is unloaded only when the class loader that loaded it has been garbage collected.
When a bundle is uninstalled or updated, any native libraries loaded by the bundle remain in memory until the bundle's class loader is garbage collected. The garbage collection will not happen until all references to objects in the bundle have been garbage collected, and all bundles importing packages from the updated or uninstalled bundle are refreshed. This implies that native libraries loaded from the system class loader always remain in memory because the system class loader is never garbage collected.
It is not uncommon that native code libraries have dependencies on other native code libraries. This specification does not support these dependencies, it is assumed that native libraries delivered in bundles should not rely on other native libraries.
A bundle contains a significant amount of information that is human-readable. Some of this information may require different translations depending on the user's language, country, and any special variant preferences, a.k.a. the locale. This section describes how a bundle can provide common translations for the manifest and other configuration resources depending on a locale.
Bundle localization entries share a common base name. To find a
potential localization entry, an underscore ('_' \u005F
) is
added plus a number of suffixes, separated by another underscore, and
finally appended with the suffix .properties
. The suffixes
are defined in java.util.Locale
. The order for the suffixes
this must be:
-
language
-
country
-
variant
For example, the following files provide manifest translations for English, Dutch (Belgium and the Netherlands) and Swedish.
OSGI-INF/l10n/bundle_en.properties
OSGI-INF/l10n/bundle_nl_BE.properties
OSGI-INF/l10n/bundle_nl_NL.properties
OSGI-INF/l10n/bundle_sv.properties
The Framework searches for localization entries by appending
suffixes to the localization base name according to a specified locale and
finally appending the .properties
suffix. If a translation is
not found, the locale must be made more generic by first removing the
variant, then the country and finally the language until an entry is found
that contains a valid translation. For example, looking up a translation
for the locale en_GB_welsh
will search in the following
order:
OSGI-INF/l10n/bundle_en_GB_welsh.properties
OSGI-INF/l10n/bundle_en_GB.properties
OSGI-INF/l10n/bundle_en.properties
OSGI-INF/l10n/bundle.properties
This allows localization files for more specific locales to override localizations from less specific localization files.
Localization entries can be contained in the bundle or delivered in fragments. The framework must search for localization entries using the following search rules based on the bundle type:
-
fragment bundle
- If the bundle is a resolved fragment, then the search for localization data must delegate to the attached host bundle with the highest version. If the fragment is not resolved, then the framework must search the fragment's JAR for the localization entry. -
other bundle
- The framework must first search in the bundle's JAR for the localization entry. If the entry is not found and the bundle has fragments, then the attached fragment JARs must be searched for the localization entry.
The bundle's class loader is not used to search for localization
entries. Only the contents of the bundle and its attached fragments are
searched. The bundle will still be searched for localization entries
even if the full stop ('.' \u002E
) is not in the bundle
class path.
Localized values are stored in property resources within the
bundle. The default base name of the bundle localization property files
is OSGI-INF/l10n/bundle
. The Bundle-Localization manifest
header can be used to override the default base name for the
localization files. This location is relative to the root of the bundle
and bundle fragments.
A localization entry contains key/value entries for localized information. All headers in a bundle's manifest can be localized. However, the Framework must always use the non-localized versions of headers that have Framework semantics.
A localization key can be specified as the value of a bundle's manifest header using the following syntax:
header-value ::= '%'text
text ::= < any value which is both a valid manifest headervalue
and a valid property key name >
For example, consider the following bundle manifest entries:
Bundle-Name: %acme bundle
Bundle-Vendor: %acme corporation
Bundle-Description: %acme description
Bundle-Activator: com.acme.bundle.Activator
Acme-Defined-Header: %acme special header
User-defined headers can also be localized. Spaces in the localization keys are explicitly allowed.
The previous example manifest entries could be localized by the following entries in the manifest localization entry OSGI-INF/l10n/bundle.properties.
# bundle.properties
acme\ bundle=The ACME Bundle
acme\ corporation=The ACME Corporation
acme\ description=The ACME Bundle provides all of the ACME\ services
acme\ special\ header=user-defined Acme Data
The above manifest entries could also have French localizations in
the manifest localization entry
OSGI-INF/l10n/bundle_fr_FR.properties
.
If the Bundle-ManifestVersion is not specified, then the bundle manifest version defaults to 1, and certain Release 4 syntax, such as a new manifest header, is ignored rather than causing an error. Release 3 bundles must be treated according to the Release 3 specification.
The following (non-exhaustive) list of errors causes a bundle to fail to install:
-
Missing Bundle-SymbolicName.
-
Duplicate attribute or duplicate directive (except in the Bundle-Native code clause).
-
Multiple imports of a given package.
-
Export of
java.*
packages. -
Export-Package, Bundle-SymbolicName, or Fragment-Host with a mandatory attribute that is not defined.
-
Installing or updating a bundle to a bundle that has the same symbolic name and version as another installed bundle (unless this is allowed, see Bundle Identifiers).
-
Any syntactic error (for example, improperly formatted version or bundle symbolic name, unrecognized directive value, etc.).
-
Specification-version and version specified together (for the same package(s)) but with different values on manifest headers that treat them as synonyms. For example:
Import-Package p;specification-version=1;version=2
would fail to install, but:
Import-Package p;specification-version=1,q;version=2
would not be an error.
-
The manifest lists a
OSGI-INF/permissions.perm
file but no such file is present. -
Bundle-ManifestVersion value not equal to 2, unless the Framework specifically recognizes the semantics of a later release.
-
Requiring the same bundle symbolic name more than once.
The Framework supports a mechanism where bundles can be directly wired to other bundles. The following sections define the relevant headers and then discuss the possible scenarios. At the end, some of the (sometimes unexpected) consequences of using Require-Bundle are discussed.
The Require-Bundle manifest header contains a list of required bundle symbolic names, with optional attribute assertions. These bundles are searched after the imports are searched but before the bundle's class path is searched. Fragment or extension bundles cannot be required.
The framework must take all exported packages from a required bundle, including any packages exported by attached fragments, and wire these packages to the requiring bundle.
The Require-Bundle manifest header must conform to the following syntax:
Require-Bundle ::= bundle-description
( ',' bundle-description )*
bundle-description ::= symbolic-name // See 1.3.2
(';' parameter )*
The following directives can be used in the Require-Bundle header:
-
visibility
- If the value isprivate
(default), then all visible packages from the required bundles are not re-exported. If the value isreexport
then bundles that require this bundle will transitively have access to these required bundle's exported packages. That is, if bundleA
requires bundleB
, and bundleB
requires bundleC
withvisibility:=reexport
then bundleA
will have access to all bundleC
's exported packages as if bundleA
had required bundleC
. -
resolution
- If the value ismandatory
(default) then the required bundle must exist for this bundle to resolve. If the value isoptional
, the bundle will resolve even if the required bundle does not exist.
The following matching attribute is architected by the Framework:
-
bundle-version
- The value of this attribute is a version range to select the bundle version of the required bundle. See Version Ranges. The default value is[0.0.0,
∞)
.
The Bundle-SymbolicName header can specify further arbitrary attributes that must be matched before a bundle is eligible.
A specific symbolic name can only be required once, listing the same symbolic name multiple times must be regarded as an install error.
Requiring bundles must get wired to all exported packages of all their required bundles including exported packages from their attached fragments. This means that any mandatory attributes on these exports must be ignored. However, if a required bundle's exported package is substituted for an imported package, then the requiring bundles must get wired to the same exported package that the required bundle is wired to ensure class space consistency.
For example, assume that bundle A
exports and imports
package p
and bundle B
requires bundle
A
:
Bundle A
Export-Package: p;x=1;mandatory:=x
Import-Package: p
Bundle B
Require-Bundle: A
In this constellation, bundle B
will get package
p
from the same source as bundle A. Bundle A
can get the package from itself if it is chosen as an exporter for p,
but it can also get the package from another bundle because it also
imports it. In all cases, bundle B
must use exactly the
same exporter for package p
as bundle
A
.
A given package may be available from more than one of the required bundles. Such packages are named split packages because they derive their contents from different bundles. If these different bundles provide the same classes unpredictable shadowing of classes can arise, see Issues With Requiring Bundles. However, split packages without shadowing are explicitly permitted.
For example, take the following setup:
A: Require-Bundle: B
Export-Package: p
B: Export-Package: p;partial=true;mandatory:=partial
If bundle C
imports package p
, it will
be wired to package A.p
, however the contents will come
from B.p
> A.p
. The mandatory attribute on
bundle B
's export definition ensures that bundle
B
is not accidentally selected as exporter for package
p
. Split packages have a number of drawbacks that are
discussed in Issues With Requiring Bundles.
Resources and classes from a split package must be searched in the
order in which the required bundles are specified in the
Require-Bundle
header.
As an example, assume that a bundle requires a number of required bundles and a number of language resources (also bundles) that are optional.
Require-Bundle: com.acme.facade;visibility:=reexport, «
com.acme.bar.one;visibility:=reexport, «
com.acme.bar.two;visibility:=reexport, «
com.acme.bar._nl;visibility:=reexport;resolution:=optional, «
com.acme.bar._en;visibility:=reexport;resolution:=optional
A bundle may both import packages (via Import-Package) and require one or more bundles (via Require-Bundle), but if a package is imported via Import-Package, it is not also visible via Require-Bundle: Import-Package takes priority over Require-Bundle, and packages which are exported by a required bundle and imported via Import-Package must not be treated as split packages.
In order to be allowed to require a named bundle, the requiring
bundle must have BundlePermission[<bundle symbolic name>,
REQUIRE]
, where the bundle symbolic name is the name of the
bundle that is required. The required bundle must be able to provide the
bundle and must therefore have BundlePermission[<bundle
symbolic name>, PROVIDE]
, where the name designates the
requiring bundle. In the case a fragment bundle requires another bundle,
the Bundle Permission must be checked against the fragment bundle's
Protection Domain.
A package is a split package whenever there are multiple sources for the package; only bundles using the Require-Bundle header can have split packages.
A source is a bundle that provides the given package. Both the required bundles as well as the requiring bundle can act as a source. The required bundles and the requiring bundle can only contribute their exported packages.
Exported split packages from two bundles are compatible if the package sources for one are a subset of the other.
The preferred way of wiring bundles is to use the
Import-Package
and Export-Package
headers
because they couple the importer and exporter to a much lesser extent.
Bundles can be refactored to have a different package composition
without causing other bundles to fail.
The Require-Bundle header provides a way for a bundle to bind to all the exports of another bundle, regardless of what those exports are. Though this can seem convenient at first, it has a number of drawbacks:
-
Split Packages - Classes from the same package can come from different bundles with Require bundle, such a package is called a split package. Split packages have the following drawbacks:
-
Completeness - Split packages are open ended, it is difficult to guarantee that all the intended pieces of a split package have actually been included.
-
Ordering - If the same classes are present in more than one required bundle, then the ordering of Require-Bundle is significant. A wrong ordering can cause hard to trace errors, similar to the traditional class path model of Java.
-
Performance - A class must be searched in all providers when packages are split. This potentially increases the number of times that a
ClassNotFoundException
must be thrown which can potentially introduce a significant overhead. -
Confusing - It is easy to find a setup where there is lots of potential for confusion. For example, the following setup is non-intuitive.
A: Export-Package: p;uses:=q Import-Package: q B: Export-Package: q C: Export-Package: q D: Require-Bundle: B, C Import-Package: p
In this example, bundle
D
merges the split packageq
from bundlesB
and bundleC
, however, importing packagep
from bundleA
puts a uses constraint on packagep
for packageq
. This implies that bundleD
can see the valid packageq
from bundleB
but also the invalid packageq
from bundleC
. This wiring is allowed because in almost all cases there will be no problem. However, the consistency can be violated in the rare case when packageC.q
contains classes that are also in packageB.q
. -
-
Mutable Exports - The feature of
visibility:=reexport
that the export signature of the requiring bundle can unexpectedly change depending on the export signature of the required bundle. -
Shadowing - The classes in the requiring bundle that are shadowed by those in a required bundle depend on the export signature of the required bundle and the classes the required bundle contains. (By contrast, Import-Package, except with
resolution:=optional
, shadows whole packages regardless of the exporter.)
Fragments are bundles that can be attached to one or more host bundles by the Framework. Attaching is done as part of resolving: the Framework appends the relevant definitions of the fragment bundles to the host's definitions before the host is resolved. Fragments are therefore treated as part of the host, including any permitted headers; they must not have their own class loader though fragments must have their own Protection Domain.
Fragments can be attached to multiple hosts with the same symbolic name but different versions. If multiple fragments with the same symbolic name match the same host, then the Framework must only select one fragment, this must be the fragment with the highest version.
A key use case for fragments is providing translation files for different locales. This allows the translation files to be treated and shipped independently from the main application bundle.
When an attached fragment is updated, the content of the previous fragment must remain attached to its host bundles. The new content of the updated fragment must not be allowed to attach to the host bundles until the Framework is restarted or the host bundle is refreshed. During this time, an attached fragment will have two versions: the old version, attached to the old version of the host, and a new fragment bundle that can get attached to a new version or to a different host bundle. The exact configuration can be discovered with the Bundle Wiring API Specification.
When attaching a fragment bundle to a host bundle the Framework must perform the following steps:
-
Append the import definitions for the Fragment bundle that do not conflict with an import definition of the host to the import definitions of the host bundle. A Fragment can provide an import statement for a private package of the host. The private package in the host is hidden in that case.
-
Append the Require-Bundle entries of the fragment bundle that do not conflict with a Require-Bundle entry of the host to the Require-Bundle entries of the host bundle.
-
Append the export definitions of a Fragment bundle to the export definitions of the host bundle unless the exact definition (directives and attributes must match) is already present in the host. Fragment bundles can therefore add additional exports for the same package name.
-
Append the Provide-Capability clauses of the fragment to the Provide-Capability clauses of the host
-
Append the Require-Capability clauses of the fragment to the Require-Capability clauses of the host
A host and a fragment conflict when they cannot resolve to provide a consistent class space. If a conflict is found, the Fragment bundle is not attached to the host bundle.
A Fragment bundle must enter the resolved state only if it has been successfully attached to at least one host bundle.
During runtime, the fragment's JAR is searched after the host's bundle class path as described in Fragments During Runtime.
A Fragment bundle can not be required by another bundle with the Require-Bundle header.
The Fragment-Host manifest header links the fragment to its potential hosts. It must conform to the following syntax:
Fragment-Host ::= bundle-description
bundle-description ::= symbolic-name
( ';' parameter )* // See 1.3.2
The following directives are architected by the Framework for Fragment-Host:
-
extension
- Indicates this extension is a system or boot class path extension. It is only applicable when the Fragment-Host is the System Bundle. This is discussed in Extension Bundles. The following values are supported:-
framework
- The fragment bundle is a Framework extension bundle.
The fragment must be the bundle symbolic name of the implementation specific system bundle or the alias
system.bundle
. The Framework should fail to install an extension bundle when the bundle symbolic name is not referring to the system bundle. -
The following attributes are architected by the Framework for Fragment-Host:
-
bundle-version
- The version range to select the host bundle. If a range is used, then the fragment can attach to multiple hosts. See Semantic Versioning. The default value is[0.0.0,
∞)
.
The Fragment-Host header can assert arbitrary attributes that must be matched before a host is eligible.
When a fragment bundle is attached to a host bundle, it logically
becomes part of it. All classes and resources within the fragment bundle
must be loaded using the class loader (or Bunde
object) of
its host bundle. The fragment bundles of a host bundle must be attached
to a host bundle in the order that the fragment bundles are installed,
which is in ascending bundle ID order. If an error occurs during the
attachment of a fragment bundle then the fragment bundle must not be
attached to the host. A fragment bundle must enter the resolved state
only if it has been successfully attached to one or more host
bundles.
In order for a host bundle to allow fragments to attach, the host
bundle must have BundlePermission[<bundle symbolic
name>,HOST]
. In order to be allowed to attach to a host
bundle, a fragment bundle must have BundlePermission[<bundle
symbolic name>,FRAGMENT]
.
All class or resource loading of a fragment is handled through the
host's class loader or Bundle
object, a fragment must never
have its own class loader, it therefore fails the class and resource
loading methods of the Bundle
object. Fragment bundles are
treated as if they are an intrinsic part of their hosts.
Though a fragment bundle does not have its own class loader, it still must have a separate Protection Domain when it is not an extension fragment. Each fragment can have its own permissions linked to the fragment bundle's location and signer.
A host bundle's class path is searched before a fragment's class path. This implies that packages can be split over the host and any of its fragments. Searching the fragments must be done in ascending bundle ID order. This is the order that the fragment bundles were installed.
Figure 3.18 shows a setup with two fragments.
Bundle B
is installed before bundle C
and both
bundle B
and bundle C
attach to bundle
A
. The following table shows where different packages
originate in this setup. Note that the order of the append (>) is
significant.
Table 3.3 Effect of fragments on searching
Package Requested | From | Remark |
---|---|---|
p |
A.p > B.p |
Bundle |
q |
D.q |
The import does not handle split packages and
package |
r |
A.r > B.r |
Package |
s |
C.s |
|
t |
B.t > C.t |
In the example above, if package p
had been imported
from bundle D
, the table would have looked quite different.
Package p would have come from bundle D
, and bundle
A
's own contents as well as the contents of bundle
B
would have been ignored.
If package q
had bundle D
, then the
class path would have to be searched, and A.q
would have
consisted of A.q > C.q
.
Fragments must remain attached to a host as long as the host remains resolved. When a host bundle becomes unresolved, then all its attached Fragment bundles must be detached from the host bundle. When a fragment bundle becomes unresolved the Framework must:
-
Detach it from the host
-
Re-resolve the host bundles
-
Reattach the remaining attached fragment bundles.
A Fragment bundle can become unresolved by calling the refreshBundles(Collection,FrameworkListener...) method.
Extension bundles can deliver optional parts of the Framework implementation. The contents of extension bundles cannot be provided by the normal bundles because they need to be loaded by the framework implementation.
Framework extensions are necessary to provide implementation aspects of the Framework. For example, a Framework vendor could supply the optional services like Permission Admin service and Start Level API with Framework extension bundles.
An extension bundle should use the bundle symbolic name of the
implementation system bundle, or it can use the alias of the system
bundle, which is system.bundle
.
The following example uses the Fragment-Host manifest header to specify an extension bundle for any Framework implementation.
Fragment-Host: system.bundle
The following example uses the Fragment-Host manifest header to specify an extension bundle for a specific Framework implementation.
Fragment-Host: com.acme.impl.framework
The following describe the life cycle of an extension bundle:
-
When an extension bundle is installed it enters the
INSTALLED
state. -
The extension bundle is allowed to enter the
RESOLVED
state at the frameworks discretion. For example, a framework may choose to immediately resolve the extension after it has entered theINSTALLED
state. If all requirements of an extension bundle are satisfied then resolving the extension bundle must not require a framework restart. -
If a
RESOLVED
extension bundle is refreshed then the framework must shutdown and the framework must be re-launched. In this case a Framework Event is fired of type STOPPED_SYSTEM_REFRESHED. -
When a
RESOLVED
extension bundle is updated orUNINSTALLED
, it is not allowed to re-enter theRESOLVED
state. If the extension bundle is refreshed then the Framework must shutdown and the framework must be re-launched. In this case a Framework Event is fired of type STOPPED_SYSTEM_REFRESHED.
It is valid to update an extension bundle to a bundle of another type. If the old extension bundle is resolved then it must be attached as a fragment to the system bundle. When this bundle is updated the old content of the bundle must remain attached to the system bundle until the system bundle is refreshed or the extension bundle is refreshed (using the Wiring API). This must initiate and Framework shutdown and restart. When the framework comes back up the new content of the bundle may be resolved.
All Bundle events should be dispatched for extension bundles as for ordinary bundles.
An extension bundle must throw a Bundle Exception if it is installed or updated and it specifies any of the following headers.
-
Require-Bundle
-
Bundle-NativeCode
-
DynamicImport-Package
-
Bundle-Activator
Extension bundles are permitted to specify an Export-Package header. Any exported packages specified by an extension bundle must be hosted (exported) by the System Bundle when the extension bundle is resolved.
Extension bundles are permitted to specify Import-Package and Require-Capability headers to declare dependencies on packages and capabilities.
Extension bundles may specify requirements on packages (Import-Package) and capabilities (Require-Capability). An extension bundle is allowed to become resolved when all of its mandatory requirements are wired to valid packages or capabilities.
Extension bundle requirements may only be wired to packages and capabilities provided by the system bundle or another extension bundle that is also attached to the system bundle. All other capabilities are considered invalid for resolving an extension bundle. This ensures that the system bundle wiring never hosts a requirement from an extension bundle which is wired to a capability hosted by another bundle other than the system bundle.
A extension bundle's JAR is appended to the class path of the Framework. Extension bundles that are resolved together must have their JAR appended in the order in which the extension bundles are installed: that is, ascending bundle ID order.
A extension may hook into the Framework initialization and shutdown process by specifying an Extension Bundle Activator. The BundleActivator interface defines methods that the Framework invokes when the Framework is initialized and shutdown.
To inform the OSGi environment of a fully qualified class name
serving as its Extension Bundle Activator, a framework extension
developer must declare an ExtensionBundle-Activator
manifest header in the framework extension bundle's manifest file. The
following is an example of an ExtensionBundle-Activator:
ExtensionBundle-Activator: com.acme.Activator
The class acting as an Extension Bundle Activator must implement
the BundleActivator
interface, be declared public, and have
a public default constructor so an instance of it may be created with
Class.newInstance
.
Supplying an Extension Bundle Activator is optional and only valid
for Extension Bundles. For normal Bundles and Fragments, the
ExtensionBundle-Activator
must be ignored.
An Extension Bundle Activator allows a framework extension to
hook into the Framework initialization and shutdown process. Initializing the Framework describes how the
start
method for Extension Bundle Activators is called
during Framework initialization. Stopping a Framework describes how the
stop
method for Extension Bundle Activators is called
during Framework shutdown.
When an extension bundle is installed, the Framework must allow
the extension to become resolved dynamically, without a Framework
restart. When an extension bundle is resolved dynamically after
Framework initialization, then the Extension Bundle Activator start
method must be called as soon as the extension bundle is resolved.
This must happen before the Bundle Event of type RESOLVED
is fired for the extension bundle.
Unlike normal bundles, updating or uninstalling an extension bundle does not take effect until the Framework is shutdown and restarted. The original content of the extension bundle must remain attached to the system bundle and the Extension Bundle Activator must not have its stop method called until the Framework is shutdown.
In an environment that has Java security enabled the Framework must perform an additional security check before allowing an extension bundle to be installed. In order for an extension bundle to successfully install, the Framework must check that the extension bundle has All Permission assigned to it. This means that the permissions of an extension bundle must be setup before the extension bundle is installed.
All Permission must be granted to extension bundles because they will be loaded under the Protection Domain of either the boot class path or the Framework implementation. Both of these Protection Domains have All Permission granted to them. Attempting to install an extension bundle that has not already been granted All Permission must result in a Bundle Exception.
The installer of an extension bundle must have
AdminPermission[<extension
bundle>,EXTENSIONLIFECYCLE]
to install an extension
bundle.
Most package sharing permissions are based on Package Permission. However, fragments and required bundles use the bundle symbolic name to handle sharing. The Bundle Permission is used to control this type of package sharing.
The name parameter of the Bundle Permission is a bundle symbolic
name. The symbolic name is used as the identifier for the
target bundle. A wild card (".*"
\u002E,\u002A
) is permitted at the end of the name.
For example, for fragment bundle A to attach to its host bundle
B
then fragment bundle A requires
BundlePermission("B", "fragment")
so that A is permitted to
target host bundle B
. The direction of the actions is
depicted in Figure 3.19.
The following actions are architected:
-
provide
- Permission to provide packages to the target bundle. -
require
- Permission to require packages from the target bundle. -
host
- Permission to attach to the target fragment bundle. -
fragment
- Permission to attach as a fragment to the target host bundle.
When a fragment contains a Require-Bundle header, the Framework must check the permission against the domain of the fragment.
Bundles can only import and export packages for which they have
the required permission. A PackagePermission
must be valid
across all versions of a package.
A PackagePermission
has two parameters:
-
The name, either the name of the target package (with a possible wildcard character at the end) or a filter expression that can verify the exporting bundle. A filter expression can test for the package name with the
package.name
key. A filter can only be used for anIMPORT
action. Filters are described in Filter Based Permissions. -
The action, either
IMPORT
orEXPORTONLY
.
For example, the following Package Permission permits to import any package from a bundle downloaded from ACME:
PackagePermission("(location=http://www.acme.com/*",IMPORT)
When a fragment adds imports and exports to the host, the framework must check the protection domain of the fragment and not of the related host.
A Framework must always give a bundle the RESOURCE,
METADATA, and CLASS AdminPermission
actions to access the
resources contained within:
-
Itself
-
Any attached fragments
-
Any resources from imported packages
A resource in a bundle may also be accessed by using certain
methods on Bundle
. The caller of these methods must have
AdminPermission[bundle, RESOURCE]
.
If the caller does not have the necessary permission, a resource
is not accessible and null
must be returned. Otherwise, a
URL
object to the resource must be returned. These URLs are
called bundle resource URLs. Once the
URL
object is returned, no further permission checks are
performed when the contents of the resource are accessed. The
URL
object must use a scheme defined by the Framework
implementation.
Bundle resource URLs are normally created by the Framework, however, in certain cases bundles need to manipulate the URL to find related resources. For example, a URL can be constructed to a resource that is in the same directory as a given resource.
URLs that are not constructed by the Framework must follow
slightly different security rules due to the design of the
java.net.URL
class. Not all constructors of the URL class
interact with the URL Stream Handler classes (the implementation
specific part). Other constructors call at least the
parseURL
method in the URL Stream Handler where the
security check can take place. This design makes it impossible for the
Framework check the permissions during construction of a bundle resource
URL.
The following constructors use the parseURL
method
and are therefore checked when a bundle resource URL is
constructed.
URL(String spec)
URL(URL context, String spec)
URL(URL context, String spec, URLStreamHandler handler)
When one of these constructors is called for a bundle resource
URL, the implementation of the Framework must check the caller for the
necessary permissions in the parseURL
method. If the caller
does not have the necessary permissions then the parseURL
method must throw a Security Exception. This will cause a Malformed URL
Exception to be thrown by the URL constructor. If the caller has the
necessary permissions, then the URL object is setup to access the bundle
resource without further checks.
The following java.net.URL constructors do not call the
parseURL
method in the URL Stream Handler, making it
impossible for the Framework to verify the permission during
construction.
URL(String protocol, String host, int port,String file)
URL(String protocol, String host, int port, String file, URLStreamHandlerhandler)
URL(String protocol, String host, String file)
Bundle resource URLs that are created with these constructors
cannot perform the permission check during creation and must therefore
delay the permission check. When the content of the URL is accessed, the
Framework must throw a Security Exception if the caller does not have
AdminPermission[bundle, RESOURCE]
for the bundle referenced
by the URL.
The Capability Permission provides a means to limit access to certain Capabilities when security is on. A Capability Permission is a Filter based Permission, as described in Filter Based Permissions, giving access to the following additional property:
-
capability.namespace
- The namespace of the requirement or provided capability.
The filter can also assert information from the target bundle. The target bundle is always the bundle that provides the capability. This means that a requirer can be restricted to receive a capability from a specific bundle.
Capabilities in a namespace for which the resolving bundle has no permission are not available to other bundles. Requirements in a namespace for which a bundle has no permission can never be satisfied.
The Capability Permission has the following actions:
The Capability Permission has the following constructors:
-
CapabilityPermission(String,String) - Constructor to set the filter and actions. This constructor is also used to verify the
provide
action. -
CapabilityPermission(String,Map,Bundle,String) - Special constructor to verify the permission against the namespace. The bundle is the bundle providing the capability.
Since multiple bundles can export permission classes with the same class name, the Framework must make sure that permission checks are performed using the correct class. For example, a bundle that calls the checkPermission method provides an instance of the Permission class:
void foo(String name) {
checkPermission(new FooPermission(name,"foo"));
}
This class of this Permission instance comes from a particular source. Permissions can only be tested against instances that come from the same source.
Therefore, the Framework needs to look up permissions based on class rather than class name. When it needs to instantiate a permission it must use the class of the permission being checked to do the instantiation. This is a complication for Framework implementers; bundle programmers are not affected.
Consider the following example:
Bundle A
Import-Package: p
Export-Package: q
Bundle B
Import-Package: p
-
Bundle
A
uses ap.FooService
. Usage of this class checksq.FooPermission
whenever one of its methods is invoked. -
Bundle
B
has aFooPermission
in its Protection Domain in a (Conditional) Permission Info object. -
Bundle
B
invokes a method in theFooService
that was given by bundleA
. -
The
FooService
calls thecheckPermission
method with a newFooPermission
instance. -
The Framework must use a
FooPermission
object that is from the same class loader as the givenFooPermission
object before it can call theimplies
method. In this case, theFooPermission
class comes from packageA.q
.
After the permission check, bundle B
will have a
FooPermission
instantiated using a class from a package it
does not import. It is therefore possible that the Framework has to
instantiate multiple variations of the FooPermission
class
to satisfy the needs of different bundles.
[1]Java Virtual Machine Specificationhttps://docs.oracle.com/javase/specs/
[2]The Standard for the Format of
ARPA Internet Text MessagesSTD 11, RFC 822, UDEL, August
1982
https://www.ietf.org/rfc/rfc822.txt
[3]The Hypertext Transfer Protocol -
HTTP/1.1RFC 2068 DEC, MIT/LCS, UC Irvine, January
1997
https://www.ietf.org/rfc/rfc2068.txt
[4]Java Language Specification: Java SE 8 Editionhttps://docs.oracle.com/javase/specs/jls/se8/html/index.html
[5]A String Representation of LDAP
Search FiltersRFC 1960, UMich, 1996
https://www.ietf.org/rfc/rfc1960.txt
[6]Java Security Architecturehttps://docs.oracle.com/javase/8/docs/technotes/guides/security/spec/security-spec.doc.html
[7]Java Package Versioning Specificationhttps://docs.oracle.com/javase/8/docs/technotes/guides/versioning/index.html
[8]Codes for the Representation of
Names of LanguagesISO 639, International Standards
Organization
https://www.loc.gov/standards/iso639-2/langhome.html
[11]W3C EBNFhttps://www.w3c.org/TR/REC-xml#sec-notation
[12]Interval (Mathematics)https://en.wikipedia.org/wiki/Interval_(mathematics)
[13]RFC 2396 Uniform Resource Identifiers URI: Generic Syntaxhttps://www.ietf.org/rfc/rfc2396.txt
[14]OSGi IANA Mime Typehttps://www.iana.org/assignments/media-types/application/vnd.osgi.bundle
[15]OSGi Header Registryhttps://docs.osgi.org/reference/bundle-headers.html
[16]OSGi Namespace Registryhttps://docs.osgi.org/reference/capability-namespaces.html
[17]Portable Network Graphics (PNG) Specification (Second Edition)https://www.w3.org/TR/2003/REC-PNG-20031110/
[18]Open Source Initiativehttps://www.opensource.org/
[19]OSGi Semantic Versioninghttps://docs.osgi.org/whitepaper/semantic-versioning/
[20]Specification Referenceshttps://docs.osgi.org/reference/
[21]Google Androidhttps://developer.android.com/
[22]Google App Enginehttps://cloud.google.com/appengine/docs/standard/java
[23]Google Web Toolkithttps://www.gwtproject.org/
[24]Software Package Data Exchange (SPDX) License Listhttps://spdx.org/licenses/
[25]Maven POM Reference, Developershttps://maven.apache.org/pom.html#Developers
[26]Maven POM Reference, SCMhttps://maven.apache.org/pom.html#SCM
[27]Multi-release JAR Filehttps://docs.oracle.com/en/java/javase/17/docs/specs/jar/jar.html#multi-release-jar-files
-
Added new CAPABILITY_DEPRECATED_DIRECTIVE directive for Namespace. The
deprecated
directive is also added to Bundle-SymbolicName and Export-Package.