The Feature Service Specification defines a model to design and declare Complex Applications and reusable Sub-Components that are composed of multiple bundles, configurations and other metadata. These models are, however, only descriptive and have no standard mechanism for installing them into an OSGi framework.
This specification focuses on turning these Features into a running system, by introducing the Feature Launcher and Feature Runtime. The Feature Launcher takes a Feature definition, obtains a framework instance for it and then starts the Feature in that environment. The Feature Runtime extends this capability to a running system, enabling one or more Features to be installed, updated, and later removed from a running OSGi framework.
The Launcher and Runtime also interact with the Configuration Admin Service, that is, they provide configuration to the system if it is present in the Feature being launched or installed.
-
Dynamic - The Feature Runtime dynamically adds, updates and removes Features in a running system.
-
Parameterizable - Feature installation may be customised using local parameters if the Feature supports it.
-
Zero code - The Feature Launcher can launch a framework containing an installed Feature in an implementation independent way without a user writing any code .
The following entities are used in this specification:
-
Feature - A Feature as defined by the Feature Service Specification
-
Artifact Repository - A means of accessing the installable bytes for bundles in a Feature
-
Feature Launcher - A Feature Launcher obtains an OSGi Framework instance and installs a Feature into it.
-
Framework - A running implementation of the OSGi core specification.
-
Launch Properties - Framework launching properties defined in a Feature.
-
Feature Parameters - Key value pairs that can be used to customise the installation of a Feature.
-
Configuration - A configuration for the Configuration Admin service.
-
Feature Runtime - A Feature Runtime is an OSGi service capable of installing Features into the running OSGi framework, removing installed Features from the OSGi framework, and updating an installed Feature with a new Feature definition.
-
Installed Feature - A representation of a Feature installed by the Feature Runtime.
-
Installed Configuration - A representation of a Configuration installed by the Feature Runtime.
OSGi Features exist either as JSON documents, or as runtime objects created by the Feature Service API. The primary purpose of a Feature is to define a list of bundles and configurations that should be installed, however the Feature provides no information about the location of the bundle artifacts. A key challenge with installing a Feature is therefore finding the appropriate artifacts to install.
The ArtifactRepository interface is designed to be implemented by users
of the Feature Launcher Service to provide a way for the Feature Launcher
Service to find an installable InputStream
of bytes for a
given bundle artifact using the getArtifact(ID) method. Artifact Repository implementations are free
to use any mechanism for locating the bundle artifact data. If no artifact
can be found for the supplied ID
then the implementation of
the Artifact Repository should return null
. If the Artifact
Repository throws an exception then this must be logged by the Feature
Launcher Service and then treated in the same manner as a null
return value.
In order to support the Zero Code objective of this specification, and to simplify usage for most users, the ArtifactRepositoryFactory provides a factory for commonly used repository types.
The Artifact Repository Factory is useful both for the Feature Launcher and the Feature Runtime, and as such it must be easy to access both inside and outside an OSGi framework. The Feature Launcher Service implementation must provide an implementation of the Artifact Repository Factory interface. A user of the Artifact Repository Factory service may use the following ways to find an instance.
When outside OSGi:
-
Using the Java ServiceLoader API to find instances of
org.osgi.service.featurelauncher.repository.ArtifactRepositoryFactory
-
From configuration, and then using
Class.forName
,getConstructor()
andnewInstance()
-
By hard coding the implementation and using the
new
operator.
When inside an OSGi framework:
-
Using the OSGi service registry to find instances of
org.osgi.service.featurelauncher.repository.ArtifactRepositoryFactory
-
Using the Java ServiceLoader API and the OSGi Service Loader Mediator to find instances of
org.osgi.service.featurelauncher.repository.ArtifactRepositoryFactory
-
By hard coding the implementation type and using the
new
operator.
A Local Repository is one that exists on a locally accessible file system. Note that this does not require that the file system is local, and technologies such as NFS or other network file systems would still be considered as Local Repositories. The key aspects of a Local Repository are that:
-
The root of the repository can be accessed and resolved as a
java.nio.file.Path
orfile:
URI. -
The repository uses [1] The Maven 2 Repository Layout
An Artifact Repository representing a Local Repository can be
created using the createRepository(Path) method, passing in the path to the root of the repository.
A NullPointerException
must be thrown if the path is null
and an IllegalArgumentException
must be thrown if the path does not
exist, or represents a file which is not a directory.
An Artifact Repository representing a Local Repository can also
be created using the createRepository(URI,Map) method, passing a URI using the file
scheme
which points to the root of the repository. A NullPointerException
must be thrown if the URI is null
and an IllegalArgumentException
must be thrown if the path does not exist, or represents a file which is not a
directory.
Once created this Artifact Repository will search the supplied repository for any requested artifact data. Implementations are free to optimise checks using repository metadata.
A Remote Repository is one that exists with an accessible
http
or https
endpoint for retrieving artifact data.
Note that this does not require that the repository is on a remote machine,
only that the means of accessing data is via HTTP
requests.
The key aspects of a Remote Repository are that:
-
The root of the repository can be accessed and resolved as a
http
orhttps
URI
-
The repository uses [1] The Maven 2 Repository Layout
An Artifact Repository representing a Remote Repository can be
created using the createRepository(URI,Map) method, passing in the uri to the root of the repository.
A NullPointerException
must be thrown if the uri is null
and an IllegalArgumentException
must be thrown if the uri does not
use the http
or https
scheme.
In addition to the repository URI
the user may pass
configuration properties in a Map
. Implementations may support custom
configuration properties, but those properties should use Reverse Domain Name keys.
Keys not using the reverse DNS naming scheme are reserved for OSGi use. Implementations
must ignore any configuration property keys that they do not recognise. All
implementations must support the following properties:
-
ARTIFACT_REPOSITORY_NAME - The name for this repository
-
ARTIFACT_REPOSITORY_USER - The user name to use for authenticating with this repository
-
ARTIFACT_REPOSITORY_PASSWORD - The password to use for authenticating with this repository
-
ARTIFACT_REPOSITORY_BEARER_TOKEN - A bearer token to use when authenticating with this repository
-
ARTIFACT_REPOSITORY_SNAPSHOTS_ENABLED - A
Boolean
indicating that SNAPSHOT versions are supported. Defaults totrue
-
ARTIFACT_REPOSITORY_RELEASES_ENABLED - A
Boolean
indicating that release versions are supported. Defaults totrue
-
ARTIFACT_REPOSITORY_TRUST_STORE - A trust store to use when validating a server certificate. May be a file system path or a
data
URI as defined by [2] The Data URI scheme. -
ARTIFACT_REPOSITORY_TRUST_STORE_FORMAT - The format of the trust store to use when validating a server certificate.
-
ARTIFACT_REPOSITORY_TRUST_STORE_PASSWORD - The password to use when validating the trust store integrity.
Once created this Artifact Repository will search the supplied repository for any requested artifact data. Implementations are free to optimise checks using repository metadata.
This specification includes support for bootstrapping an OSGi runtime, for ongoing management of an OSGi runtime, and for merging features. There are many concepts that apply across more than one of these scenarios, and so they are described here.
Some Feature definitions include variables which can be used to customise
their deployment. These variables are intended to be set at the point where
a Feature is installed, and may contain default values. To enable these
variables to be overridden there are overloaded versions of methods which
permit a Map
of variables to be provided. The keys in this
map must be strings and the values must be one of the types permitted
by the Feature Service Specification
If a Feature declares a variable with no default value then this variable must be provided. If no value is provided then the method must fail to launch by throwing a LaunchException
An OSGi framework contains a number of bundles which collaborate to produce a functioning application. There are times when some bundles require the system to have reached a certain state before they can be started. To address this use case the OSGi framework has the concept of start levels as described in the Start Level API Specification chapter of OSGi Core Release 8..
Setting the initial start level for the OSGi framework when bootstrapping
can easily be achieved using the framework launch property
org.osgi.framework.startlevel.beginning
as defined by the
OSGi core specification.
Controlling the start levels assigned to the bundles in a feature is
managed through the use of Feature Bundle metadata. Specifically the Feature
Launcher will look for a Feature Bundle metadata property named BUNDLE_START_LEVEL_METADATA which is of type integer and has a value between
1
and Integer.MAX_VALUE
inclusive. If the property does
not exist then the default start level will be used. If the property does
exist and is not a suitable integer then launching must fail with a LaunchException.
Setting the default start level for the bundles, and the minimum start
level required for an installed Feature is accomplished by using a Feature Extension named BUNDLE_START_LEVELS with Type
JSON
. The JSON
contained in this extension is used to configure the default start level for
the bundles, and the target start level for the framework.
The schema of this JSON is as follows:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "http://www.osgi.org/json.schema/featurelauncher/v1.0.0/bundle-start-levels.json",
"title": "bundle-start-levels",
"description": "The definition of the bundle-start-levels feature extension",
"type": "object",
"properties": {
"version": {
"description": "The version of the Feature Launcher extension",
"const": "1.0.0"
},
"defaultStartLevel": {
"description": "The default start level to use for bundles in this feature.",
"type": "integer",
"minimum": 1,
"maximum": 2147483647
},
"minimumStartLevel": {
"description": "The minimum required framework start level after installing this feature",
"type": "integer",
"minimum": 1,
"maximum": 2147483647
}
},
"required": [ "version" ]
}
Setting the default start level for bundles installed by the feature is
achieved using the defaultStartLevel
property of the JSON
extension. This must be an integer greater than zero and less than
Integer.MAX_VALUE
. If the defaultStartLevel
property is not
present then the current framework start level is used as the default start level for newly
installed bundles, or 1
if the
current framework start level is 0
. If the value of defaultStartLevel
is not valid then a LaunchException
must be thrown when attempting to use the feature.
The minimum start level required to start the feature
can be set using the minimumStartLevel
property of the JSON
extension. This must be an integer greater than zero and less than Integer.MAX_INT
.
If the minimumStartLevel
property is not set then the minimum required start level
is set to the current framework start level, meaning that the framework start level will not
change when the feature is started.
If the value of the minimumStartLevel
property is not valid then a LaunchException
must be thrown when attempting to use the feature.
Finally the version
property defines the version of the extension
schema being used. This can be used by the implementation to determine whether the
Feature is targeting a newer version of the specification. If the version is missing,
or not understood by the implementation then a LaunchException
must be thrown when attempting to use the feature.
Feature Decoration is a process by which features can be pre-processed before they are installed or updated. This gives users an opportunity to modify the feature, accept it as is, or block the operation from proceeding. There are two types of decorator:
-
Feature Decorators - called for all operations. Can re-write the bundles, configurations, variables and extensions present in a feature.
-
Feature Extension Handlers - called operations where the feature defines the named extension. Can re-write the bundles, configurations and variables present in a feature, but not the extensions.
Both types of decorator may pass through the feature unchanged by returning the feature object passed into them. This will cause the operation to continue as normal. Decorators may also block an operation from proceeding by throwing an AbandonOperationException. This will cause the operation to be immediately halted, but must be treated as an error when logging. Abandoned operations must result in an exception being thrown to the caller who requested the operation.
Feature objects are expected to be immutable, and therefore a decorator cannot, and should not, change the feature object that is passed to them. Instead the decorator must create a new feature object which includes the decorated content.
To enable this both types of decorator are passed two builders, the first of which implements BaseFeatureDecorationBuilder and the second of which implements DecoratorBuilderFactory.
The former builder is similar to a FeatureBuilder but with three important differences:
-
The builder is pre-populated with the information from the existing feature, such that immediately calling build() would create a feature with a different
ID
but otherwise identical content to the original. -
Except where explicitly stated the builder configuration methods replace content rather than adding to it
-
Only a limited subset of the feature content can be changed.
The latter builder is similar to a BuilderFactory but it cannot create FeatureBuilder instances.
By using these two builders a decorated feature can be configured and created.
This decorated feature can then be returned from the decorator. The ID of a decorated
feature will always share the groupId
, artifactId
, version
and type
of the original feature, however the classifier
may be
changed using the setClassifier(String) method, and will default to DEFAULT_DECORATED_CLASSIFIER. Note that the
only valid way to create a decorated feature is by using the builder.
Any attempt to return a feature object which is not either:
-
The original feature object.
-
The object returned by build()
is an error and will result in the operation being abandoned.
Decorators may be included using one of the relevant builder methods for a launch or runtime operation:
When registering a FeatureExtensionHandler the name of the extension to be handled must be passed, and cannot be null
.
This defines the name of the extension that the Feature Extension Handler will be used to process.
If multiple FeatureDecorator instances are registered then they will be called in the order that they were added.
If multiple FeatureExtensionHandler instances are registered for the same extension name then the earlier instances will be discarded. It is not possible to register more than one Feature Extension Handler for a single extension.
The FeatureLauncher is the main entry point for creating a running OSGi framework containing the bundles and configurations defined in a Feature. As such the Feature Launcher is primarily designed for use outside of an OSGi framework.
To support usage in a non-OSGi environment implementations of the Feature Launcher Service must register the following implementation classes with the Java ServiceLoader API, and any necessary module metadata.
-
org.osgi.service.featurelauncher.FeatureLauncher
-
org.osgi.service.featurelauncher.repository.ArtifactRepositoryFactory
A Feature Launcher Service implementation must provide an implementation of the Feature Launcher interface. A user of the Feature Launcher service may use the following ways to find this class and create an instance:
-
Using the Java ServiceLoader API to find instances of
org.osgi.service.featurelauncher.FeatureLauncher
-
From configuration, and then using
Class.forName
,getConstructor()
andnewInstance()
-
By hard coding the implementation type and using the
new
operator.
Once instantiated the Feature Launcher may be supplied with a
Feature, either as a Reader
providing access to the JSON text of
a Feature document or a parsed Feature to create a FeatureLauncher.LaunchBuilder. The Launch Builder can be configured in a fluent
manner using the withConfiguration(Map), withVariables(Map), withFrameworkProperties(Map) and withRepository(ArtifactRepository) methods. Configuration properties for the Feature
Launcher are implementation specific, and any unrecognised property names
should be ignored. Artifact Repository instances may be created by the user
using as described in The Artifact Repository Factory, or using custom implementations.
The withConfiguration
, withVariables
and withFrameworkProperties
builder methods encapsulate a single element within the builder, meaning that calling any of those
methods again will replace the previous value. The withRepositor
, withDecorator
and withExtensionHandler
methods encapsulate a multiple element within the builder, meaning
that calling any of those methods multiple times can result in multiple values being registered.
Once a configured Launch Builder instance has been created the launchFramework() method can be used to launch an OSGi framework
containing the supplied Feature.
The Feature Launcher will then return a running Framework
instance
representing the launched OSGi framework and the Feature that it contains. If
an error occurs creating the framework, or locating and installing any of the
feature bundles, then a LaunchException must be thrown.
Once the caller has received their framework instance they may carry on
with other work, or they may wait for the OSGi framework to stop using the
waitForStop()
method.
Framework launch properties are key value pairs which are passed to the OSGi framework as it is created. They can control many behaviours, including operations which happen before the framework starts, meaning that is not always possible to set them after startup.
Feature definitions that require particular framework launch properties
can define them using a Feature Extension named FRAMEWORK_LAUNCHING_PROPERTIES. The Type of this Feature Extension must be
JSON
, where the value is a single JSON
object.
Each JSON
property in this object represents a single Framework Launch Property.
The name of each JSON
property must be used as the name of a
Framework Launch Property, unless the name starts with a single underscore _
.
The value of each property is used as the value of the Framework Launch Property,
and must be a scalar type, that is a JSON
string
,
number
or boolean
. If the value is a different JSON
type then this must be treated as an error.
If the JSON
property starts with a single underscore then it may be
used for implementation specific behaviour, with the prefix _osgi
reserved for future specifications. Implementation specific behaviours may
permit JSON
values to be any value JSON
type.
If users require their Framework Launch Property name to start with an underscore
then they must use two underscores __
in the JSON
property
name. When the implementation detects more than one underscore at the beginning of
a JSON
property defined in this extension the leading underscore must
be removed, and the remaining string used as the Framework Launch Property name.
All implementations of the Feature Launcher must support this extension,
and use it to populate the Framework Launch Properties. The version of
this extension is 1.0.0
, and may be declared in the extension
JSON
using the property FRAMEWORK_LAUNCHING_PROPERTIES_VERSION.
In addition to Framework Launch properties defined inside the Feature,
users of the Feature Launcher can add and override Framework Launch Properties
using one of the withFrameworkProperties
method that permits a
Map
of framework properties to be provided. Any key value pairs
defined in this map must take precedence over those defined in the Feature. A
key with a null
value must result in the removal of a key value
pair if it is defined in the Feature.
When defining a feature it is not always possible to be framework independent.
Sometimes specific framework APIs, or licensing restrictions, will require that a
particular implementation is used. In this case a Feature Extension named LAUNCH_FRAMEWORK with Type
ARTIFACTS
can be used
to list one or more artifacts representing OSGi framework implementations.
The list of artifacts is treated as a preference order, with the first listed
artifact being used if available, and so on, until a framework is found. If a
listed artifact is not an OSGi framework implementation then the Feature Launcher
must log a warning and continue on to the next artifact in the list. If the
Kind
of the feature is MANDATORY
and none of
the listed artifacts are available then launching must fail with a LaunchException.
The Feature Launcher implementation may identify that an artifact is an OSGi framework implementation in any way that it chooses, however it must recognise framework implementations that provide the Framework Launch API using the service loader pattern, as described in the Launching and Controlling a Framework section of OSGi Core Release 8.
The following code snippet demonstrates a simple example of using the Feature Launcher to start an OSGi framework containing one or more bundles.
// Load the Feature Launcher
ServiceLoader<FeatureLauncher> sl = ServiceLoader.load(FeatureLauncher.class);
FeatureLauncher launcher = sl.iterator().next();
// Set up a repository
ArtifactRepository localRepo = launcher.createRepository(Paths.get("bundles"));
// Launch the framework
Framework fw = launcher
.launch(Files.newBufferedReader(Paths.get("myfeature.json")))
.withRepository(localRepo)
.launchFramework();
fw.waitForStop(0);
In order to support the Zero Code goal of the Feature Launcher
Service it is not sufficient to provide a Java API, it must also be possible to launch a
feature from the command line in a standard way. To support this implementations of the
Feature Launcher must provide an executable JAR
file which allows a Feature
to be launched from the command line. The general syntax for this command is:
java -jar <impl jar> [opts] [<feature json>]
In the above definition <feature json>
is the JSON representation of
the Feature to be launched, and opts
are command line options.
The following options must be supported:
Table 160.1 Command line options for the Feature Launcher
Option Name | Option Value | Description |
---|---|---|
|
file path |
Specifies the location of a file containing the feature JSON.
If used then the |
|
|
Specifies an artifact repository |
|
A class name[,<class name>] |
Provides the name of a decorator class that should be used when launching the feature. The decorator class must be public, available on the classpath, and have a public zero-argument constructor. This property may be repeated to add more than one decorator. |
|
<extension name>= <class name>[,<extension name>= <class name>] |
Provides the name of an extension, and the extension handler class that should be used to handle the extension when launching the feature. The extension handler class must be public, available on the classpath, and have a public zero-argument constructor. This property may be repeated to add more than one extension handler. |
|
key=value[,key=value] |
Provides one or more launch properties that should be passed to the framework when it is launched. |
|
key=value[,key=value] |
Provides one or more variables that should be used to set or override variables defined in the feature. |
|
key=value[,key=value] |
Provides one or more configuration properties that should be used to control implementation specific behaviour. |
If implementations wish to support any additional options these must only use long-form option
names starting with --impl-
to ensure that users know the launch command is not portable,
and to avoid possible name clashes in future versions of the specification.
The following section defines the process through which the Feature Launcher must locate, initialize and populate an OSGi framework when launching a feature. Unless explicitly stated implementations may perform one or more parts of this process in a different order to that described in the specification.
The first stage of launching is to determine the feature that should be launched by running the configured feature decoration handlers.
First the Feature Launcher must execute any registered FeatureDecorator instances in the order that they were registered. The feature returned by each decorator is used as input to the next.
Once the decoration is complete the Feature Launcher must iterate through the Feature Extensions defined by the feature. For each Feature Extension the launcher must:
-
Identify the Feature Extension Handler for the named extension.
-
If no Feature Extension Handler can be found, and the extension name is one of:
then create an empty Feature Extension Handler which may validate the FeatureExtension.Type of the extension and must return the original feature.
-
If no Feature Extension Handler has been found or created then check the FeatureExtension.Kind of the extension. If it is MANDATORY then the launch fails with a
LaunchException
-
Otherwise call the Feature Extension Handler, and use its result as input when calling any subsequent Feature Extension Handlers.
If any of the decorators throws an AbandonOperationException then the launch operation must immediately fail.
Before a framework instance can be created the Feature Launcher must identify a suitable implementation using the following search order:
-
If any provider specific configuration has been given to the Feature Launcher implementation then this should be used to identify the framework.
-
If the Feature declares an Extension LAUNCH_FRAMEWORK then the Feature Launcher implementation must use the first listed artifact that can be found in any configured Artifact Repositories, as described in Selecting a framework implementation.
-
If no framework implementation is found in the previous steps then the Feature Launcher implementation must search the classpath using the Thread Context Class Loader, or, if the Thread Context Class Loader is not set, the Class Loader which loaded the caller of the Feature Launcher's
launch
method. The first suitable framework instance located is the instance that will be used. -
In the event that no suitable OSGi framework can be found by any of the previous steps then the Feature Launcher implementation may provide a default framework implementation to be used.
If no suitable OSGi framework implementation can be found then the Feature Launcher
implementation must throw a LaunchException
.
Once a suitable framework implementation has been located the Feature Launcher
implementation must create and initialize a framework instance. Implementations are
free to use implementation specific mechanisms for framework implementations that
they recognise. The result of this initialization must be the same as if the
Feature Launcher used the org.osgi.framework.launch.FrameworkFactory
registered by the framework implementation to create the framework instance.
When creating the framework any framework launch properties defined in the Feature must be used. These are defined as described in Providing Framework Launch Properties and must include any necessary variable replacement as defined by Overriding Feature variables.
Once instantiated the framework must be initialised appropriately so that it
has a valid BundleContext
. Once initialised the framework is ready for
the Feature Launcher implementation to begin populating the framework.
The Feature Launcher must iterate through the list of bundles in the feature, installing them in the same order that they are declared in the feature. If bundle start levels have been defined, as described in Setting the bundle start levels, then the Feature Launcher must ensure that the start level is correctly set for each installed bundle. If no start level metadata or extension is defined then all bundles are installed with the framework default start level.
If the installation of a bundle fails because it is determined by the framework to be a duplicate of an existing bundle then the Feature Launcher must treat the installation as a success. The start level of such a bundle must be set to the lower of its current value and the start level defined for the feature bundle that failed to install.
If a Feature defines one or more Feature Configurations then these cannot be
guaranteed to be made available until the ConfigurationAdmin service
has been registered. A Feature Launcher implementation may provide an implementation
specific way to pre-register configurations, however in general the Feature Launcher
should listen for the registration of the ConfigurationAdmin
service
and immediately create the defined configurations when it becomes available.
As Configuration Admin is asynchronous configurations may be created and delivered in any order.
If the CONFIGURATION_TIMEOUT configuration property is set to 0
, and one or more
Feature Configurations are defined in the Feature being installed, then the implementation
must throw a LaunchException
unless it is capable of pre-registering those configurations in an implementation specific
way.
Once all of the the bundles listed in the feature are installed, and any necessary configuration listener is registered, the implementation must start the OSGi framework. This action will automatically start the installed bundles as defined by the initial start level of the framework, and the start levels of the installed bundles.
The Feature Launcher implementation must delay returning control to the caller
until all configurations have been created, subject to the timeout defined by CONFIGURATION_TIMEOUT. The default timeout is 5000
milliseconds,
and it determines the maximum length of time that the Feature Launcher implementation
should wait to begin creating the configurations. A value of -1
indicates
that the Feature Launcher implementation must not wait, and must continue immediately,
even if the configurations have not yet been created. If it is not possible to begin before
the timeout expires then a LaunchException must be thrown.
Finally, if the minimumStartLevel
has been set by the BUNDLE_START_LEVELS extension then the Feature Launcher implementation must check
the current start level of the framework. If the current start level is less than the
value of minimumStartLevel
then the framework's start level must be set
to this value.
Once the start process is complete the Framework instance must be returned to the caller.
The following failure modes must all result in a LaunchException being thrown:
-
A bundle fails to resolve. If one of the installed bundles fails to resolve this is an error unless the Feature is not complete. For Features that are not complete resolution failures must be logged, but not cause a failure.
-
A resolved bundle fails to start. If one of the resolved bundles fails to start this is an error unless the bundle is a fragment or an extension bundle, which the Feature Launcher should not attempt to start.
-
A configuration cannot be created. If a configuration cannot be created then this must result in a start failure
If a launching failure is triggered by an exception, for example a
BundleException
then this must be
set as the cause
of the LaunchException
that is thrown.
The Feature Runtime Service can be thought of as an equivalent of the Feature Launcher for an existing, running OSGi framework. The Feature Runtime Service therefore does not offer any mechanism for launching a framework, but instead allows one or more features to be installed into the running framework. As an OSGi framework is a dynamic environment the Feature Runtime Service also provides snapshots describing the currently installed Features, allows installed Features to be updated, and allows Features to be removed from the system.
An important difference between the Feature Launcher and Feature Runtime Service is that because the Feature Runtime Service allows multiple Features to be installed it must be able to identify and resolve simple conflicts. For example if two Features include the same bundle at different versions then the resolution may be to install only the higher version, or both versions.
The Feature Runtime must be registered as a service in the service registry. Management agents that wish to install, manage or introspect Features in the framework must obtain this service. The Feature Service Runtime service must advertise the FeatureRuntime interface.
Instances of the Feature Runtime are Thread Safe, regardless of whether the service is implemented as a singleton or otherwise. Any FeatureRuntime.OperationBuilder instances created by the Feature Runtime are not thread safe and must not be shared between threads.
Despite the Operation Builders not being Thread Safe the underlying Feature Runtime must remain Thread Safe, specifically if two Operation Builders complete at the same time then these calls should be handled sequentially such that there are never partially deployed Features present when installing, updating or removing a Feature.
An important role for any management agent is being able to introspect the system to discover its current state. The Feature Runtime enables this through the getInstalledFeatures() method, which returns a snapshot of the current state of the system.
The returned list of snapshots contains one InstalledFeature entry for each installed Feature, in the order
that they were installed, and may be empty if no Features have been
installed. If the framework was started using a Feature Launcher from the
same implementation as the Feature Runtime then the Feature Runtime may
choose to represent the launched Feature in the snapshot list. If the launched
Feature is included in the snapshot list then it must set isInitialLaunch() to true
. Launch features cannot be
removed or updated by the Feature Runtime, and any attempt to do so must
throw a FeatureRuntimeException
Each Installed Feature provides:
-
The installed Feature from getFeature(). This will include any decoration as described in Feature Decoration
-
The original undecorated Feature from getOriginalFeature()
-
Whether this installed feature was decorated using isDecorated()
-
The
List
of InstalledBundle from getInstalledBundles() listing the bundles installed by the Runtime on behalf of the Feature. -
The
List
of InstalledConfiguration from getInstalledConfigurations() listing the configurations installed by the feature.
The InstalledBundle snapshots each represent a bundle installed by the Feature Runtime on behalf of the Feature. The Installed Bundle contains the following information:
-
getBundleId() - The
ID
of the bundle that was installed. -
getAliases() - A
Collection
of one or moreID
s that are known to correspond to this bundle. This list will always contain thebundleId
and may contain additional IDs if their attempted installation resulted in a collision. -
getBundle() - The actual bundle that was installed into the runtime.
-
getStartLevel() - The calculated start level for this bundle. Note that this start level may have been affected by other features.
-
getOwningFeatures() - A
List
of the ids of the features which own the installed bundle. Ownership of a bundle is tracked by the Feature Runtime, and it is used to identify when the same bundle forms part of more than one Feature. Bundles that are owned by more than one Feature will not be removed until all of the Features that own them are removed.In the case where a bundle was not installed by the Feature Runtime, but later became owned by an installed Feature, that bundle will also be owned by the EXTERNAL_FEATURE_ID to indicate that they will not be removed if the other owning Feature is removed.
In addition to bundles Features can contain configurations. The InstalledConfiguration snapshots each represent a configuration created by the Feature Runtime on behalf of the Feature. The Installed Configuration contains the following information:
-
getPid() - The configuration pid of this configuration.
-
getFactoryPid() - The factory pid of this configuration, or an empty
Optional
if the configuration is not a factory configuration. -
getProperties() - The merged configuration properties that result from the full set of installed Features contributing to this configuration. Note that there is no dynamic link to Configuration Admin and so any configuration changes made outside the Feature Runtime will not be reflected.
-
getOwningFeatures() - A
List
of the ids of the features which own the configuration. Ownership of a configuration is tracked by the Feature Runtime, and it is used to identify when the same configuration, as defined by its pid, forms part of more than one Feature. Configurations that are owned by more than one Feature will not be removed until all of the Features that own them are removed.In the case where a configuration was not installed by the Feature Runtime, but later became owned by an installed Feature, that configuration will also be owned by the EXTERNAL_FEATURE_ID to indicate that they will not be deleted if the other owning Feature is removed.
Installing a Feature uses one of the install
methods present
on the Feature Runtime. These methods allow the caller to provide the Feature
to be installed and return an FeatureRuntime.InstallOperationBuilder to allow the caller to configure their installation
operation. Configuration of operations includes:
Once the operation is fully configured then the caller uses the install() method to begin the installation. The end result of installing a Feature is that all of the bundles listed in the Feature are installed, all of the Feature Configurations have been created, all bundles have been marked as persistently started, and the framework start level is at least the minimum level required by the Feature.
Start levels for the bundles in the Feature may be controlled as described in Setting the bundle start levels. If any bundles are installed with a start level higher than the current framework start level then they will be marked persistently started but will not start until the framework start level is changed.
In more complex cases, where multiple features are installed with overlapping bundles or configurations then Merging strategies will be applied to determine which bundles are installed, and what configuration properties will be used when creating or updating a configuration.
If a failure occurs during the installation of a Feature then the Feature Runtime must make every effort to return the system to its pre-existing state. After a failure no new bundles should be installed, any altered configurations returned to their previous states, and the framework start level should be the same as it was prior to the failed installation.
As with the Feature Launcher, in order to successfully locate the bundles listed in a feature the Feature Runtime must have access to one or more Artifact Repositories capable of locating the bundles. These Artifact Repositories are configured into each Operation Builder by the user.
A configured Feature Runtime will typically include one or more pre-defined Artifact Repositories. These pre-defined repositories are available to view via the getDefaultRepositories(). By default all Operation Builders will have access to these repositories when completing. This behaviour can be changed using the useDefaultRepositories(boolean) method.
Additional Artifact Repositories can be added to an Operation Builder by calling the addRepository(String,ArtifactRepository) method. The supplied name
is used to identify
the repository. If the supplied name is already used for an existing
Artifact Repository then it will be replaced or, if the supplied Artifact Repository
is null
, removed. A named Artifact Repository added in this way will
override a default Artifact Repository with the same name.
As described in Overriding Feature variables a feature may define zero or more overridable properties
which can be used to alter the deployment of the feature. These properties may be
configured into each Operation Builder by calling the withVariables(Map) method. The supplied Map
contains the
keys and values that will override the variables in the Feature.
Merge operations occur when two or more features reference the same, or similar, items to be installed. The purpose of a merge operation is to avoid unnecessary duplication, and to resolve conflicts.
Merging potentially applies whenever a Feature is installed, updated or removed, and may result in different outcomes depending on the strategy used. All runtime merge functions therefore receive a MergeOperationType indicating which type of operation is currently running.
Features may define bundles to be installed by including Feature Bundle
entries. If two or more Features include Feature Bundles which have IDs with
the same group id and artifact id, but which are not the same, then this
situation requires a merge to resolve the possible conflict. Determining
whether two IDs are the same is accomplished by checking whether they
return equal strings from toString()
.
When a possible conflict is detected the Feature Runtime must call a RuntimeBundleMerge to identify the correct actions to take. These actions include:
-
Whether to install the candidate Feature Bundle or not
-
Whether to re-designate the ownership of any existing Installed Bundles
-
Whether to remove any existing Feature Bundles
Although the obvious time for a bundle merge operation to occur is during
an INSTALL
operation, merges may also occur during UPDATE
and REMOVE
operations. During an UPDATE
the existing
bundles from the Feature being updated will remain available so that the updated
Feature may be merged into the existing runtime. During a REMOVE
a merge will occur to allow Feature ownership to be re-allocated if a shared bundle
is being removed.
Merges are resolved by the mergeBundle
method which receives:
-
The type of the operation, one of
INSTALL
,UPDATE
orREMOVE
. -
The Feature being operated on
-
The Feature Bundle which requires merging
-
A
Collection
of Installed Bundles representing the currently installed bundles which have an overlappinggroupId
andartifactId
. Note that in the case of anUPDATE
orREMOVE
operation the Feature being updated or removed will not be present in the collection of owning features for any of the Installed Bundles. -
A
List
of RuntimeBundleMerge.FeatureBundleDefinition representing the existing Features which form part of the merge operation. Note that in the case of anUPDATE
orREMOVE
operation the Feature Bundle being updated or removed will not be present in the list. Entries in the list are present in the order that the Features were installed into the runtime.
The result of the merge function is a Stream
of RuntimeBundleMerge.BundleMapping. Each Bundle Mapping links a bundle ID
to List
of feature
ID
s. The Bundle Mapping's bundle id must only be a bundleId
found in the list of Installed
Bundles or, in the case of an INSTALL
or UPDATE
operation, the id
of the
Feature Bundle being merged. The mapped Feature ids must contain the id
of every Feature in the supplied
Feature Bundle Definitions, and, in the case of an INSTALL
or UPDATE
operation, the id
of the
Feature being merged. If the id
of any Installed Bundle is not present in the returned Stream
then that bundle will be removed as part of the ongoing operation. If the same bundle id is present more than once
the the two mappings will be combined using the union of the mapped Feature ids.
A simple example of a merge strategy which combines configurations by upgrading Features to the highest compatible version could be implemented as follows:
public Stream<BundleMapping> mergeBundle(MergeOperationType operation,
Feature feature, FeatureBundle toMerge,
Collection<InstalledBundle> installedBundles,
List<FeatureBundleDefinition> existingFeatureBundles) {
Stream<BundleMapping> result;
if (operation == MergeOperationType.REMOVE) {
// Just keep everything the same
result = installedBundles.stream()
.filter(i -> !i.getOwningFeatures().isEmpty())
.map(i -> new BundleMapping(i.getBundleId(),
i.getOwningFeatures()));
} else {
// Find the Installed bundles we might replace
Version v = RuntimeMerges.getOSGiVersion(toMerge.getID());
List<InstalledBundle> sameMajor = new ArrayList<>();
List<InstalledBundle> differentMajor = new ArrayList<>();
installedBundles.forEach(i -> {
if (i.getBundle().getVersion().getMajor() == v.getMajor()) {
sameMajor.add(i);
} else {
differentMajor.add(i);
}
});
// Bundles with a different major version stay the same
result = differentMajor.stream()
.filter(i -> !i.getOwningFeatures().isEmpty())
.map(i -> new BundleMapping(i.getBundleId(),
i.getOwningFeatures()));
// Find the biggest existing version and see if it's bigger than v
Optional<InstalledBundle> max = sameMajor.stream()
.max((a, b) -> a.getBundle()
.getVersion()
.compareTo(b.getBundle().getVersion()))
.filter(m -> m.getBundle().getVersion().compareTo(v) >= 0);
// Use the old version if it's bigger, or the new if not
ID key = max.isPresent() ? max.get().getBundleId()
: toMerge.getID();
List<ID> featureIds = sameMajor.stream()
.flatMap(i -> i.getOwningFeatures().stream())
.collect(Collectors.toList());
result = Stream.concat(result,
Stream.of(new BundleMapping(key, featureIds)));
}
return result;
}
Features may define configurations by including Feature Configuration entries. If two or more Features include properties for the same configuration PID then this situation requires a merge to resolve the conflict.
Merges are resolved by a RuntimeConfigurationMerge which receives:
-
The type of the operation, one of
INSTALL
,UPDATE
orREMOVE
. -
The Feature being operated on
-
The Feature Configuration which requires merging
-
The Installed Configuration representing the current state of the configuration. Note that in the case of an
UPDATE
orREMOVE
operation the Feature being updated or removed will not be present in the list of owning features. -
A
List
of RuntimeConfigurationMerge.FeatureConfigurationDefinition representing the existing Features which form part of the merge operation. Note that in the case of anUPDATE
orREMOVE
operation the Feature Configuration being updated or removed will not be present in the list. Entries in the list are present in the order that the Features were installed into the runtime.
The result of the merge function is a map of configuration properties that should be used to
update the configuration. If the map is null
then the configuration should be deleted.
A simple example of a merge strategy which combines configurations by overlaying each in turn
and ignoring null
configurations could be implemented as follows:
public Map<String,Object> mergeConfiguration(MergeOperationType operation,
Feature feature, FeatureConfiguration toMerge, InstalledConfiguration configuration,
List<FeatureConfigurationDefinition> existingFeatureConfigurations) {
boolean addedSomething = false;
Map<String,Object> result = new HashMap<>();
for (FeatureConfigurationDefinition fcd : existingFeatureConfigurations) {
FeatureConfiguration fc = fcd.getFeatureConfiguration();
if(fc.getValues() != null) {
result.putAll(fc.getValues());
addedSomething = true;
}
}
if(operation != MergeOperationType.REMOVE && toMerge.getValues() != null) {
result.putAll(toMerge.getValues());
addedSomething = true;
}
return addedSomething ? result : null;
}
Removing a feature from the Feature Runtime Service uses the remove(ID) method to uninstall and remove a feature from the framework. Removing a feature is a comparatively simple operation, and therefore does not require the configuration of an FeatureRuntime.OperationBuilder.
Once the remove
method returns the feature will have been removed
from the Feature Runtime, and any links to installed bundles and configurations will
have been removed. If this leaves any installed bundles or installed configurations
with no owners then these will be uninstalled or deleted from the system as appropriate.
If a failure occurs during the removal of a feature then the
Feature Runtime must make every effort to fully remove the feature, for example
by continuing to remove installed bundles that no longer have any owners.
Exceptions that occur must be logged, and upon completion the Feature Runtime must
throw a FeatureRuntimeException
which indicates the incomplete removal.
It is not an error to remove a feature which does not exist in the Feature Runtime
and this must return without error, and without altering the state of the system. It is
an error to attempt to remove any feature that returns true
for isInitialLaunch(), and any attempt to do so must result in a
FeatureRuntimeException
.
Updating a Feature uses one of the update
methods present
on the Feature Runtime. These methods allow the caller to indicate which feature
should be updated, and provider the new Feature definition to replace it with.
The methods return an FeatureRuntime.UpdateOperationBuilder to allow the caller to configure their update
operation. Configuration of operations includes:
Once the operation is fully configured then the caller uses the update() method to begin the update. The end result of updating a Feature is that all of the bundles listed in the new Feature are installed, all of the Feature Configurations in the new Feature have been created, all bundles have been marked as persistently started, and the framework start level is at least the minimum level required by the new Feature. In addition, any bundles and configurations from the old Feature that are not present in the new Feature will have been removed, and any configurations present in both the old and new Features will have been updated with new any new content.
At a high level an update operation is therefore superficially similar to
performing a remove
operation followed by an install
operation. The key difference, however, is that any bundles and configurations
shared by both features, or identified by a merge strategy, will not be removed,
and instead will become owned by the new Feature.
As for installation, start levels for the bundles in the new Feature will be determined as described in Setting the bundle start levels. If any bundles are installed with a start level higher than the current framework start level then they will be marked persistently started but will not start until the framework start level is changed.
Where the feature update includes overlapping bundles or configurations then Merging strategies will be applied to determine which bundles are installed, and what configuration properties will be used when creating or updating a configuration.
If a failure occurs during the update of a Feature then the Feature Runtime must make every effort to return the system to its pre-existing state. After a failure no new bundles should be installed, any altered configurations returned to their previous states, and the framework start level should be the same as it was prior to the failed installation.
The following section provides normative requirements for the behaviour of the Feature Runtime when it is used. This includes the necessary end states after installation, update and removal of Features.
The Feature Installation process has four main phases:
-
The feature decoration phase, where the Feature is decorated and validated
-
The bundle installation phase, where Feature bundles are installed
-
The configuration creation phase, where Feature Configurations are created
-
The Feature Start phase, where Bundles are started.
The feature decoration phase must complete before any other phases can begin. The the bundle installation phase and the configuration creation phase may happen in any order, or even with interleaved steps, however the Feature Start phase must not begin until the bundle installation and configuration creation phases are complete.
The first stage of the operation is to determine the feature that should be used by running the configured feature decoration handlers.
First the Feature Runtime must execute any registered FeatureDecorator instances in the order that they were registered. The feature returned by each decorator is used as input to the next.
Once the decoration is complete the Feature Runtime must iterate through the Feature Extensions defined by the feature. For each Feature Extension the Feature Runtime must:
-
Identify the Feature Extension Handler for the named extension.
-
If no Feature Extension Handler can be found, and the extension name is one of:
then create an empty Feature Extension Handler which may validate the FeatureExtension.Type of the extension and must return the original feature.
-
If no Feature Extension Handler has been found or created then check the FeatureExtension.Kind of the extension. If it is MANDATORY then the operation fails with a
FeatureRuntimeException
-
Otherwise call the Feature Extension Handler, and use its result as input when calling any subsequent Feature Extension Handlers.
If any of the decorators throws an AbandonOperationException then the operation must immediately fail.
When a feature is being installed the Feature Runtime identifies the bundles to be installed. The Feature Runtime also gathers the set of bundles that are already installed, and then computes the overlap between these. Bundles are deemed to overlap if they have the same group id, artifact id, type and classifier but they may differ in version.
If the overlap list contains entries which overlap exactly, that is they have the same version in the runtime and the Feature being installed, then those bundles are removed from the list of bundles to be installed and the existing bundles are marked as owned by the Feature being installed. If the marked bundles were not previously owned by any other feature then they also marked as owned by the EXTERNAL_FEATURE_ID to indicate that they will not be removed if the Feature being installed is removed.
Any remaining overlap entries are processed according to the merge strategy for the feature, as described in Merging Bundles. The final list of bundles to install, which excludes any already installed bundles, is then installed in the same order as it was defined by the feature. Each bundle in the feature, including bundles that were already installed, is then marked as owned by the installing feature.
If the installation of a bundle fails because it is determined by the framework to be a duplicate of an existing bundle then the Feature Runtime must treat the installation as a success and add the ID as an alias for the existing Installed Bundle. The start level of such a bundle must be set to the lower of its current value and the start level defined for the feature bundle that failed to install.
Once the installation of bundles is complete the Feature Runtime must uninstall any bundles which were identified for removal as part of any merge processes.
As part of the initial Feature installation the Feature Runtime
must also process and create any Feature Configurations that are defined in
the Feature. Feature Configurations cannot be guaranteed to be made available
until a ConfigurationAdmin
service has been registered. A Feature Runtime implementation should therefore
listen for the registration of a ConfigurationAdmin
service
and immediately create or update any pending configurations when it becomes available.
Configurations must be created or updated in the same order as they are defined in the
Feature.
If the same configuration, as identified by its configuration pid,
is defined in one or more existing installed Features then the configuration
properties to be used are determined by merging the previous configuration
properties with the new properties defined in the Feature, as described in Merging Configurations.
If at the point where the FeatureRuntime attempts to create or update a
Feature Configuration there are already configuration properties defined in
ConfigurationAdmin
then these must be ignored and replaced using
updateIfDifferent(Dictionary) unless the Configuration
is marked as READ_ONLY. If a READ_ONLY
configuration does exist
then the Feature Runtime must log a warning and skip that configuration.
Once all of the bundles listed by the feature are installed then the bundles' start levels are assigned as described in Setting the bundle start levels. This includes any pre-existing bundles and the results of any merge operations. If no start level configuration is defined in the feature for a particular bundle then the start level for that bundle is set to the current start level of the framework.
The Feature Runtime must then identify the lowest start level referenced in the Feature, and repeatedly run through the list of bundles, in the order that they are defined in the Feature, looking for bundles which match the identified start level. For each bundle the Feature Runtime must:
-
If the bundle was installed in the Bundle Installation phase then set the start level for the bundle.
-
If the bundle was already installed then update the start level for the bundle if, and only if, the new start level is lower than the existing start level.
-
Mark the bundle as persistently started unless it is a fragment bundle.
The Feature Runtime must then identify the next lowest start level referenced in the Feature and repeat this process until all bundles have been persistently started. Once this process is complete then the framework start level must be increased to the minimum start level required by the Feature, or returned to the original framework start level if this is higher and was decreased as part of Merging Bundles.
The following is a non-exhaustive list of possible failure scenarios that must be handled.
-
The feature being installed is already known to the Feature Runtime. This must be treated as a failure as the configuration of the
InstallOperationBuilder
may not be the same as the previous installation. The Feature Runtime must make no changes and immediately throw aFeatureRuntimeException
. -
A Feature Bundle cannot be found by any configured ArtifactRepository.
-
A
BundleException
is thrown during Bundle Installation. -
A
BundleException
is thrown during Feature Start. -
A Feature Configuration cannot be created by the ConfigurationAdmin service.
-
An
Exception
is thrown by any configured ArtifactRepository, RuntimeBundleMerge or RuntimeConfigurationMerge.
In all cases the first exception must be treated as a failure, with the installation process
halting immediately. The feature must then be removed from the runtime in a similar manner to
calling remove
for the feature id. Once the feature removal is complete the failure
may be used in creating the FeatureRuntimeException
that must be thrown by this method.
The Feature removal process has four main phases:
-
The feature removal phase, where the feature is removed from the Feature Runtime.
-
The bundle stop phase, where Installed Bundles without owners are stopped.
-
The configuration deletion phase, where Installed Configurations without owners are removed
-
The bundle removal phase, stopped bundles are uninstalled
The the feature removal and bundle stop phases may happen in any order, or even with interleaved steps. The same is true for the configuration deletion phase and the bundle removal phase, however these phases must not begin until the bundle stop phase is complete.
Feature removal is a simple operation which removes any reference to
the Installed Feature from the Feature Runtime. This includes the list of
installed features, and the ownership lists of any Installed Bundles or
Installed configurations in the Feature Runtime. After removal is complete
the ID
of the removed feature should not appear anywhere in
the Feature Runtime.
Installed Bundles and Installed Configurations which have zero owners after the removal of the feature are now considered eligible for removal. Their removal processes are described in the next phases.
The Feature Runtime must identify the highest start level set by the list of Installed Features, excluding the Feature being removed. If no start level is defined by this list of features then no action is taken, otherwise the framework start level is set to the newly identified start level.
The list of bundles eligible to be stopped, as determined in Feature Removal, is used to peristently stop any remaining bundles. Bundles that are eligible for removal are stopped in the reverse order in which they were started by Feature Start. This is accomplished by stopping the bundles with the highest start level first, using the reverse order of declaration in the feature where the start level is the same. If an eligible bundle is already stopped due to its start level then it must still be persistently stopped.
Once the Bundle Stop phase has completed the Feature Runtime may begin removing configurations that are eligible. As with bundles, configurations become eligible for removal if they are no longer owned by any feature. Eligible configurations must be removed in the reverse order of creation, that is the reverse order that they were listed in the feature being removed.
Once the Bundle Stop phase has completed the Feature Runtime may begin uninstalling bundles from the OSGi framework. These bundles must only be eligible bundles identified and stopped as part of the previous phase. Bundles are uninstalled in reverse installation order, that is the reverse of the order in which they are listed in the feature.
If one or more bundles have been uninstalled, and once all eligible
bundles have been uninstalled, the Feature Runtime must refresh the framework
wiring by calling FrameworkWiring.refreshBundles
, passing the
list of uninstalled bundles. This will cause the framework to completely
remove the uninstalled bundles, and any wirings that link to them.
The following is a non-exhaustive list of possible failure scenarios that must be handled.
-
The feature being removed is not known to the Feature Runtime. This must not be treated as a failure, and should simply return immediately.
-
One or more
BundleExceptions
are thrown during Bundle Stop. These exceptions should be logged when they occur, but then ignored. -
One or more
BundleExceptions
are thrown during Bundle Removal. These exceptions should be logged when they occur, with the Feature Runtime continuing despite the errors. Once the feature removal is complete the failures may be used in creating theFeatureRuntimeException
that must be thrown by this method. -
One or more Installed Configurations are missing from the ConfigurationAdmin service. These missing configurations should be logged with a warning, but not treated as an error.
-
One or more Installed Configurations cannot be deleted missing from the ConfigurationAdmin service. These exceptions should be logged when they occur, with the Feature Runtime continuing despite the errors. Once the feature removal is complete the failures may be used in creating the
FeatureRuntimeException
that must be thrown by this method.
The Feature Update Process
The Feature Update Process can be viewed as an interleaved remove and installation operation, following the phases present in both.
-
The feature decoration phase, where the new Feature is decorated and validated
-
The feature removal phase, where the existing feature is removed from the Feature Runtime.
-
The bundle installation phase, where the new Feature bundles are installed
-
The bundle stop phase, where Installed Bundles without owners are stopped.
-
The configuration creation and update phase, where the new Feature Configurations are created or updated
-
The configuration deletion phase, where Installed Configurations without owners are removed
-
The Feature Start phase, where Bundles in the new feature are started.
-
The bundle removal phase, stopped bundles are uninstalled
Decorating the new feature proceeds exactly as if a new feature is being installed, as described in Feature Decoration.
Removing the existing feature proceeds exactly as if a new feature is being removed, as described in Feature Removal.
Installing the bundles from the new feature proceeds as if a new feature is being removed, as described in Bundle Installation, but with two important differences.
The first important difference is that bundles being installed must be prevented from wiring to bundles that are eligible for removal. This may be accomplished through the use of a Resolver Hook. As the resolver may attempt to resolve bundles at any time this restriction must be enforced by the Feature Runtime until after all of the eligible bundles are uninstalled.
The second important difference is that any Installed Bundles that are eligible for removal are still available in the runtime. This means that they must be considered when determining whether bundles are already installed, or whether they need to be merged. This may lead to one or more Installed Bundles that were eligible for removal becoming ineligible for removal as they become owned by the new feature. Any Installed Bundles for which this is the case must be removed from the list of eligible bundles, and immediately become available for wiring by newly installed bundles.
Stopping the eligible bundles proceeds exactly as described in Bundle Stop. Note that if the existing feature used start levels then this process will likely result in one or more bundles shared between the old and new features being stopped temporarily.
Care must be taken in this phase to persistently stop all eligible bundles. Failing to do so may result in eligible bundles being accidentally restarted in later phases.
Creating and updating configurations proceeds as described in Configuration Creation, but with one important difference.
Any Installed Configurations that are eligible for removal are still available in the runtime. This means that they must be considered when determining whether they need to be merged. This may lead to one or more Installed Configurations that were eligible for removal becoming ineligible for removal as they become owned by the new feature. Any Installed Configurations for which this is the case must be removed from the list of eligible configurations.
Removing eligible configurations proceeds exactly as described in Configuration Removal.
Starting the new feature proceeds exactly as described in Feature Start. As all bundles eligible for removal were persistently stopped in an earier phase they will remain stopped during this phase, and must not be started again.
Until the Feature Runtime reaches this phase of an update it must fail by attempting to roll back to the previous feature. Once this phase has been reached this failure mode changes, and the Feature Runtime must retain the new Feature, attempting to continue despite failures.
Removing the eligible bundles proceeds exactly as described in Bundle Removal.
The following is a non-exhaustive list of possible failure scenarios that must be handled.
-
The feature being updated is not known to the Feature Runtime. This must not make any changes and should immediately throw a
FeatureRuntimeException
. -
A Feature Bundle cannot be found by any configured ArtifactRepository.
-
A
BundleException
is thrown during Installing the new bundles. This should result in the imediate failure of the operation, rolling back to the pre-update state, with aFeatureRuntimeException
thrown to the caller. -
A
BundleException
is thrown during Starting the new feature. This should result in the imediate failure of the operation, rolling back to the pre-update state, with aFeatureRuntimeException
thrown to the caller. -
A Feature Configuration cannot be created by the ConfigurationAdmin service. This should result in the imediate failure of the operation, rolling back to the pre-update state, with a
FeatureRuntimeException
thrown to the caller. -
An
Exception
is thrown by any configured ArtifactRepository, RuntimeBundleMerge or RuntimeConfigurationMerge. This should result in the imediate failure of the operation, rolling back to the pre-update state, with aFeatureRuntimeException
thrown to the caller. -
One or more
BundleExceptions
are thrown during Stopping the eligible bundles. These exceptions should be logged when they occur, but then ignored. -
One or more
BundleExceptions
are thrown during Uninstalling the eligible bundles. These exceptions should be logged when they occur, with the Feature Runtime continuing despite the errors. Once the feature removal is complete the failures may be used in creating theFeatureRuntimeException
that must be thrown by this method. -
One or more Installed Configurations are missing from the ConfigurationAdmin service. These missing configurations should be logged with a warning, but not treated as an error.
-
One or more Installed Configurations cannot be deleted missing from the ConfigurationAdmin service. These exceptions should be logged when they occur, with the Feature Runtime continuing despite the errors. Once the feature removal is complete the failures may be used in creating the
FeatureRuntimeException
that must be thrown by this method.
The Feature Launcher must provide the following capabilities.
The bundle providing the Feature Runtime service must provide
capabilities in the osgi.service
namespace representing the services it is required to register. This
capability must also declare uses constraints for the relevant service
packages:
Provide-Capability: osgi.service;
objectClass:List<String>="org.osgi.service.featurelauncher.runtime.FeatureRuntime";
uses:="org.osgi.service.featurelauncher.runtime",
osgi.service;
objectClass:List<String>="org.osgi.service.featurelauncher.repository.ArtifactRepositoryFactory";
uses:="org.osgi.service.featurelauncher.repository"
This capability must follow the rules defined for the osgi.service Namespace.
When Java permissions are enabled, the following security procedures apply.
Bundles that need to make use of the Feature Runtime or Artifact Repository Factory
services must be granted permission to get the relevant service, for example
ServicePermission[ org.osgi.service.featurelauncher.runtime.FeatureRuntime,
GET]
so that they may retrieve the service and use it.
Only a bundle that provides a Feature Runtime implementation should be
granted ServicePermission[
org.osgi.service.featurelauncher.runtime.FeatureRuntime, REGISTER]
and
ServicePermission[
org.osgi.service.featurelauncher.repository.ArtifactRepositoryFactory, REGISTER]
to register the services defined by this specification.
The Feature Runtime implementation must also be granted
ServicePermission[org.osgi.service.cm.ConfigurationAdmin,
GET]
,
AdminPermission[*, execute]
,
AdminPermission[*, lifecycle]
,
AdminPermission[*, metadata]
,
AdminPermission[*, resolve]
,
AdminPermission[*, startlevel]
,
AdminPermission[*, context]
,
as these actions are all required to implement the
specification.
Feature Launcher Package Version 1.0.
Bundles wishing to use this package must list the package in the Import-Package header of the bundle's manifest. This package has two types of users: the consumers that use the API in this package and the providers that implement the API in this package.
Example import for consumers using the API in this package:
Import-Package: org.osgi.service.featurelauncher; version="[1.0,2.0)"
Example import for providers implementing the API in this package:
Import-Package: org.osgi.service.featurelauncher; version="[1.0,1.1)"
-
FeatureLauncher
- The Feature launcher is the primary entry point for launching an OSGi framework and set of bundles. -
FeatureLauncher.LaunchBuilder
- A builder for configuring and triggering the launch of an OSGi framework containing the supplied feature -
FeatureLauncherConstants
- Defines standard constants for the Feature Launcher specification. -
LaunchException
- A LaunchException is thrown by the FeatureLauncher if it is unable to:-
Locate or start an OSGi Framework instance
-
Locate the installable bytes of any bundle in a Feature
-
Install a bundle in the Feature
-
Determine a value for a Feature variable that has no default value defined
-
The Feature launcher is the primary entry point for launching an OSGi framework and set of bundles. As it is a means for launching a framework it is designed to be used from outside OSGi and therefore should be obtained using the ServiceLoader.
Consumers of this API must not implement this type
the feature to launch
Begin launching a framework instance based on the supplied feature
A running framework instance.
LaunchException
–
A builder for configuring and triggering the launch of an OSGi framework containing the supplied feature
LaunchBuilder instances are single use. Once they have been used to launch a framework instance they become invalid and all methods will throw IllegalStateException
Launch a framework instance based on the configured builder
A running framework instance.
LaunchException
–
IllegalStateException
– if the builder has been launched
the configuration for this implementation.
Configure this LaunchBuilder with the supplied properties.
this
IllegalStateException
– if the builder has been launched
the decorator to add
Add a FeatureDecorator to this LaunchBuilder that will be used to decorate the feature being launched. If called multiple times then the supplied decorators will be called in the same order that they were added to this builder.
this
NullPointerException
– if the decorator is null
IllegalStateException
– if the builder has been launched
the name of the extension to handle
the extensionHandler to add
Add a FeatureExtensionHandler to this LaunchBuilder
that will be used to process the named FeatureExtension if it
is found in the Feature being launched. If called multiple
times for the same extensionName
then later calls will
replace the extensionHandler
to be used.
this
NullPointerException
– if the extension name or decorator is
null
IllegalStateException
– if the builder has been launched
the launch properties to use when starting the
framework. All framework properties must have
String
keys and String
values.
Configure this LaunchBuilder with the supplied Framework Launch Properties.
this
IllegalStateException
– if the builder has been launched
the repository to add
Add a repository to this LaunchBuilder that will be used to locate installable artifact data. If called multiple times then the supplied repositories will be searched in the same order that they were added to this builder.
this
NullPointerException
– if the repository is null
IllegalStateException
– if the builder has been launched
the variable placeholder overrides for this launch.
All variable overrides must have String
keys
and the values must be String
,
Boolean
or BigDecimal
for
numbers. The value null
is not permitted in the
map.
Configure this LaunchBuilder with the supplied variables.
this
IllegalStateException
– if the builder has been launched
Defines standard constants for the Feature Launcher specification.
The name of the metadata property used to indicate the start level of the
bundle to be installed. The value must be an integer between
0
and Integer.MAX_VALUE.
The name for the FeatureExtension of Type.JSON which defines the start level configuration for the bundles in the feature
The configuration property used to set the timeout for creating configurations from FeatureConfiguration definitions.
The value must be a Long indicating the number of milliseconds
that the implementation should wait to be able to create configurations
for the Feature. The default is 5000
.
A value of 0
means that the configurations must be created
before the bundles in the feature are started. In general this will
require the ConfigurationAdmin
service to be available from
outside the feature.
A value of -1
means that the implementation must not wait to
create configurations and should return control to the user as soon as
the bundles are started, even if the configurations have not yet been
created.
The name of the implementation capability for the Feature specification.
The version of the implementation capability for the Feature specification.
The name for the FeatureExtension of Type.JSON which defines the framework properties that should be used when launching the feature.
The property name for the JSON
property which defines the
version of the framework properties extension used in this feature.
The name for the FeatureExtension which defines the framework that should be used to launch the feature. The extension must be of Type.ARTIFACTS and contain one or more ID entries corresponding to OSGi framework implementations. This extension must be processed even if it is Kind.OPTIONAL or Kind.TRANSIENT.
If more than one framework entry is provided then the list will be used as a priority order when determining the framework implementation to use. If none of the frameworks are present then an error is raised and launching will be aborted.
A LaunchException is thrown by the FeatureLauncher if it is unable to:
-
Locate or start an OSGi Framework instance
-
Locate the installable bytes of any bundle in a Feature
-
Install a bundle in the Feature
-
Determine a value for a Feature variable that has no default value defined
Create a LaunchException with the supplied error message
Feature Annotations Package Version 1.0.
This package contains annotations that can be used to require the Feature Service implementation.
Bundles should not normally need to import this package as the annotations are only used at build-time.
-
RequireFeatureLauncherService
- This annotation can be used to require the Feature implementation.
Feature Launcher Decorator Package Version 1.0.
Bundles wishing to use this package must list the package in the Import-Package header of the bundle's manifest. This package has two types of users: the consumers that use the API in this package and the providers that implement the API in this package.
Example import for consumers using the API in this package:
Import-Package: org.osgi.service.featurelauncher.decorator; version="[1.0,2.0)"
Example import for providers implementing the API in this package:
Import-Package: org.osgi.service.featurelauncher.decorator; version="[1.0,1.1)"
-
AbandonOperationException
- An AbandonOperationException is thrown by a FeatureDecorator or FeatureExtensionHandler if it needs to prevent the operation from continuing. -
BaseFeatureDecorationBuilder
- The BaseFeatureDecorationBuilder is used to allow a user to customize a Feature. -
DecoratorBuilderFactory
- The Builder Factory can be used to obtain builders for the various entities. -
FeatureDecorator
- A FeatureDecorator is used to pre-process a Feature before it is installed or updated. -
FeatureDecorator.FeatureDecoratorBuilder
- A reified builder which adds the ability to replace the extensions for the decorated feature -
FeatureExtensionHandler
- A FeatureExtensionHandler is used to check and pre-process a Feature based on its FeatureExtensions before the feature is installed or updated. -
FeatureExtensionHandler.FeatureExtensionHandlerBuilder
- A reified builder which does not permit extensions to be modified
An AbandonOperationException is thrown by a FeatureDecorator or FeatureExtensionHandler if it needs to prevent the operation from continuing. This may be because of a problem detected in the feature, or because an extension has determined that the feature cannot be used in the current environment.
Create an AbandonOperationException with the supplied error message
the type of the FeatureDecorator, used to parameterize the builder return values
The BaseFeatureDecorationBuilder is used to allow a user to customize a Feature. It is pre-populated with data from the original Feature, and calling any of the setXXX methods will replace the data in that section.
Consumers of this API must not implement this type
The default classifier for decorated features created by this BaseFeatureDecorationBuilder
Build the Feature. Can only be called once. After calling this method the current builder instance cannot be used any more. and all methods will throw IllegalStateException.
The Feature.
The Bundles to add.
Replace the bundles in the Feature, discarding the current values.
This builder.
- the classifier to use for the decorated feature.
Set the classifier for this feature. This will replace any previously set classifier. If not set then the value DEFAULT_DECORATED_CLASSIFIER will be used.
When the Feature is built using the build() method then this classifier will be used in the ID of the built Feature.
This builder.
The Configurations to add.
Replace the Configurations in the Feature, discarding the current values.
This builder.
The key.
The default value.
Set or replace a single variable in the Feature. If a variable with the specified key already exists it is replaced with this one. Variable values are of type: String, Boolean or BigDecimal for numbers.
This builder.
IllegalArgumentException
– if the value is of an invalid type.
to be added.
Replace all the variables in the Feature, discarding the current values. Variable values are of type: String, Boolean or BigDecimal for numbers.
This builder.
IllegalArgumentException
– if a value is of an invalid type.
The Builder Factory can be used to obtain builders for the various entities.
This is similar to BuilderFactory but does not permit the creation of FeatureBuilder instances.
Consumers of this API must not implement this type
The artifact ID for the artifact object being built.
Obtain a new builder for Artifact objects.
The builder.
The ID for the bundle object being built. If the ID has no
type
specified, a default type of @{code jar} is
assumed.
Obtain a new builder for Bundle objects.
The builder.
The persistent ID for the Configuration being built.
Obtain a new builder for Configuration objects.
The builder.
The factory persistent ID for the Configuration being built.
The name of the configuration being built. The PID for the configuration will be the factoryPid + '~' + name
Obtain a new builder for Factory Configuration objects.
The builder.
The extension name.
The type of extension: JSON, Text or Artifacts.
The kind of extension: Mandatory, Optional or Transient.
Obtain a new builder for Feature objects.
The builder.
A FeatureDecorator is used to pre-process a Feature before it is installed or updated. This allows the caller to potentially add or remove extensions, alter feature bundles, or edit configurations before the feature is installed or updated.
Note that a FeatureDecorator is always called for all features and may change the feature extensions, as well as bundles, configurations and variables.
the feature to be installed or updated
a builder that can be used to produce a decorated feature with updated values
- a factory allowing users to create values for use with
decoratedFeatureBuilder
Provides an opportunity to transform a feature before it is installed or updated
The Feature to be installed. This must either be the same
instance as feature
or a new object created by
calling decoratedFeatureBuilder.build()
. Returning
any other object is an error that will cause the install or
update operation to fail
AbandonOperationException
– if the feature installation or update
operation must not continue
A reified builder which adds the ability to replace the extensions for the decorated feature
Consumers of this API must not implement this type
A FeatureExtensionHandler is used to check and pre-process a Feature based on its FeatureExtensions before the feature is installed or updated. This allows the caller to potentially alter feature bundles, or edit configurations before the feature is installed or updated.
Note that a FeatureExtensionHandler is only called for features with a matching extension called and may only change the feature bundles or feature configurations.
the feature to be installed or updated
the feature extension which caused this handler to be called
a builder that can be used to produce a decorated feature with updated values
- a factory allowing users to create values for use with
decoratedFeatureBuilder
Provides an opportunity to transform a feature before it is installed or updated
The Feature to be installed. This must either be the same
instance as feature
or a new object created by
calling decoratedFeatureBuilder.build()
. Returning
any other object is an error that will cause the install or
update operation to fail
AbandonOperationException
– if the feature installation or update
operation must not continue
Feature Launcher Repository Package Version 1.0.
Bundles wishing to use this package must list the package in the Import-Package header of the bundle's manifest. This package has two types of users: the consumers that use the API in this package and the providers that implement the API in this package.
Example import for consumers using the API in this package:
Import-Package: org.osgi.service.featurelauncher.repository; version="[1.0,2.0)"
Example import for providers implementing the API in this package:
Import-Package: org.osgi.service.featurelauncher.repository; version="[1.0,1.1)"
-
ArtifactRepository
- An ArtifactRepository is used to get hold of the bytes used to install an artifact. -
ArtifactRepositoryConstants
- Defines standard constants for creating ArtifactRepository instances using the ArtifactRepositoryFactory -
ArtifactRepositoryFactory
- A ArtifactRepositoryFactory is used to create implementations of ArtifactRepository for one of the built in repository types:-
Local File System
-
HTTP repository
-
An ArtifactRepository is used to get hold of the bytes used to install an artifact. Users of this specification may provide their own implementations for use when installing feature artifacts. Instances must be Thread Safe.
Thread-safe
Defines standard constants for creating ArtifactRepository instances using the ArtifactRepositoryFactory
The configuration property key used to set the bearer token when creating an ArtifactRepository using ArtifactRepositoryFactory.createRepository(URI, Map)
The configuration property key used to set the repository name when creating an ArtifactRepository using ArtifactRepositoryFactory.createRepository(URI, Map)
The configuration property key used to set the repository password when creating an ArtifactRepository using ArtifactRepositoryFactory.createRepository(URI, Map)
The configuration property key used to set that release versions are enabled for an ArtifactRepository using ArtifactRepositoryFactory.createRepository(URI, Map)
The configuration property key used to set that SNAPSHOT release versions are enabled for an ArtifactRepository using ArtifactRepositoryFactory.createRepository(URI, Map)
The configuration property key used to set the trust store to be used when accessing a remote ArtifactRepository using ArtifactRepositoryFactory.createRepository(URI, Map)
The configuration property key used to set the trust store format to be used when accessing a remote ArtifactRepository using ArtifactRepositoryFactory.createRepository(URI, Map)
The configuration property key used to set the trust store password to be used when accessing a remote ArtifactRepository using ArtifactRepositoryFactory.createRepository(URI, Map)
The configuration property key used to set the repository user when creating an ArtifactRepository using ArtifactRepositoryFactory.createRepository(URI, Map)
A ArtifactRepositoryFactory is used to create implementations of ArtifactRepository for one of the built in repository types:
-
Local File System
-
HTTP repository
Consumers of this API must not implement this type
a path to the root of a Maven Repository Layout containing installable artifacts
Create an ArtifactRepository using the local file system
an ArtifactRepository using the local file system
IllegalArgumentException
– if the path does not exist, or exists
and is not a directory
NullPointerException
– if the path is null
the URI for the repository. The http
,
https
and file
schemes must be
supported by all implementations.
the configuration properties for the remote repository. See FeatureLauncherConstants for standard property names
Create an ArtifactRepository using a remote Maven repository.
an ArtifactRepository using the local file system
IllegalArgumentException
– if the uri scheme is not supported by
this implementation
NullPointerException
– if the path is null
Feature Launcher Runtime Package Version 1.0.
Bundles wishing to use this package must list the package in the Import-Package header of the bundle's manifest. This package has two types of users: the consumers that use the API in this package and the providers that implement the API in this package.
Example import for consumers using the API in this package:
Import-Package: org.osgi.service.featurelauncher.runtime; version="[1.0,2.0)"
Example import for providers implementing the API in this package:
Import-Package: org.osgi.service.featurelauncher.runtime; version="[1.0,1.1)"
-
FeatureRuntime
- The Feature runtime service allows features to be installed and removed dynamically at runtime. -
FeatureRuntime.InstallOperationBuilder
- The OperationBuilder for a FeatureRuntime.install(Feature) operation. -
FeatureRuntime.OperationBuilder
- An OperationBuilder is used to configure the installation or update of a Feature by the FeatureRuntime. -
FeatureRuntime.UpdateOperationBuilder
- The OperationBuilder for a FeatureRuntime.install(Feature) operation. -
FeatureRuntimeConstants
- Defines standard constants for the Feature Runtime. -
FeatureRuntimeException
- A FeatureRuntimeException is thrown by the FeatureRuntime if it is unable to:-
Locate the installable bytes of any bundle in a Feature
-
Install a bundle in the Feature
-
Determine a value for a Feature variable that has no default value defined
-
Successfully merge a feature with the existing environment
-
-
InstalledBundle
- An InstalledBundle represents a configuration that has been installed as a result of one or more feature installations. -
InstalledConfiguration
- An InstalledConfiguration represents a configuration that has been installed as a result of one or more feature installations. -
InstalledFeature
- An InstalledFeature represents the current state of a feature installed by the FeatureRuntime. -
MergeOperationType
- An MergeOperationType represents the type of operation that is in flight -
RuntimeBundleMerge
- Merge operations occur when two or more features reference the same (or similar) items to be installed. -
RuntimeBundleMerge.BundleMapping
- A BundleMapping is used to define that a bundle should be (or remain) installed and which Features should own it -
RuntimeBundleMerge.FeatureBundleDefinition
- A FeatureBundleDefinition is used to show which FeatureBundle(s) are being merged, and the Feature that they relate to. -
RuntimeConfigurationMerge
- Merge operations occur when two or more features reference the same (or similar) items to be installed. -
RuntimeConfigurationMerge.FeatureConfigurationDefinition
- A FeatureConfigurationDefinition is used to show which FeatureConfiguration(s) are being merged, and the Feature that they relate to. -
RuntimeMerges
- Merge operations occur when two or more features reference the same (or similar) items to be installed.
The Feature runtime service allows features to be installed and removed dynamically at runtime.
Thread-safe
Consumers of this API must not implement this type
Get the default repositories for the FeatureRuntime service. These are the repositories which are used by default when installing or updating features.
This method can be used to select a subset of the default repositories when using an OperationBuilder, or to query for instances manually.
the default repositories
Get the features that have been installed by the FeatureRuntime service
a list of installed features
the feature to launch
Install a feature into the runtime
An OperationBuilder that can be used to set up the installation of this feature
LaunchException
– if installation fails
a Reader for the input Feature JSON
Install a feature into the runtime based on the supplied feature JSON
An installedFeature representing the results of installing this feature
LaunchException
– if installation fails
the id of the feature to update
the feature to launch
Update a feature in the runtime
An installedFeature representing the results of updating this feature
the id of the feature to update
a Reader for the input Feature JSON
Update a feature in the runtime based on the supplied feature JSON
An installedFeature representing the results of updating this feature
The OperationBuilder for a FeatureRuntime.install(Feature) operation. Instances are not thread safe and must not be shared.
An alias for the complete() method
the installed feature
An OperationBuilder is used to configure the installation or update of a Feature by the FeatureRuntime. Instances are not thread safe and must not be shared.
Once the complete() method is called the operation will be run by the feature runtime and the operation builder will be invalidated, with all methods throwing IllegalStateException.
the name to use for this repository
the repository
Add an ArtifactRepository for use by this
OperationBuilder instance. If an ArtifactRepository
is already set for the given name then it will be replaced. Passing a
null
ArtifactRepository will remove the
repository from this operation.
ArtifactRepository instances are stored in the order that they are added, and this order defines the order in which they will be queried.
this
IllegalStateException
– if the builder has been completed
Complete the operation by installing or updating the feature
An InstalledFeature representing the result of the operation
FeatureRuntimeException
– if an error occurs
IllegalStateException
– if the builder has been completed
already
Include the default repositories when completing this operation. This
value defaults to true
. If any
ArtifactRepository added using
addRepository(String, ArtifactRepository) has the same name
as a default repository then the added repository will override the
default repository.
Default repositories are always added after any added by addRepository(String, ArtifactRepository), and are therefore the last to be queried.
this
IllegalStateException
– if the builder has been completed
Use The supplied RuntimeBundleMerge to resolve any bundle merge operations that are required to complete the operation
this
Use The supplied RuntimeConfigurationMerge to resolve any configuration merge operations that are required to complete the operation
this
the decorator to add
Add a FeatureDecorator to this OperationBuilder that will be used to decorate the feature. If called multiple times then the supplied decorators will be called in the same order that they were added to this builder.
this
NullPointerException
– if the decorator is null
IllegalStateException
– if the builder has been launched
the name of the extension to handle
the extensionHandler to add
Add a FeatureExtensionHandler to this
OperationBuilder that will be used to process the named
FeatureExtension if it is found in the Feature. If
called multiple times for the same extensionName
then
later calls will replace the extensionHandler
to be
used.
this
NullPointerException
– if the extension name or decorator is
null
IllegalStateException
– if the builder has been launched
the variable placeholder overrides for this launch
Configure this OperationBuilder with the supplied variables.
this
IllegalStateException
– if the builder has been completed
The OperationBuilder for a FeatureRuntime.install(Feature) operation. Instances are not thread safe and must not be shared.
Defines standard constants for the Feature Runtime.
A FeatureRuntimeException is thrown by the FeatureRuntime if it is unable to:
-
Locate the installable bytes of any bundle in a Feature
-
Install a bundle in the Feature
-
Determine a value for a Feature variable that has no default value defined
-
Successfully merge a feature with the existing environment
Create a LaunchException with the supplied error message
An InstalledBundle represents a configuration that has been installed as a result of one or more feature installations.
This type is a snapshot and represents the state of the runtime when it was created. It may become out of date if additional features are installed or removed.
Consumers of this API must not implement this type
Get any known IDs which correspond to the same bundle
an immutable collection of aliases for this bundle. Always includes the id returned by getBundleId()
The actual bundle installed in the framework
the Bundle installed for this getBundleId()
Get the ID of the bundle that has been installed
the id of the bundle that was installed
The features responsible for this bundle being installed, in installation order
A list of Feature IDs
An InstalledConfiguration represents a configuration that has been installed as a result of one or more feature installations.
This type is a snapshot and represents the state of the runtime when it was created. It may become out of date if additional features are installed or removed.
Consumers of this API must not implement this type
Get the factory PID of the configuration
the factory PID of this configuration, or an empty optional if this is not a factory configuration
The features responsible for creating this configuration, in installation order
A list of Feature IDs
Get the PID of the configuration
the full PID of this configuration
An InstalledFeature represents the current state of a feature installed by the FeatureRuntime.
This type is a snapshot and represents the state of the runtime when it was created. It may become out of date if additional features are installed or removed.
Consumers of this API must not implement this type
The Feature that was installed. This will be identical to
getOriginalFeature() unless the feature was decorated
during installation. If decoration did occur then
isDecorated() will return true
and this
method will return the decorated feature.
Get the bundles installed by this feature
A List of the bundles installed by this feature, in the order they were declared by the feature
Get the configurations installed by this feature
A List of the configurations installed by this feature, in the order they were declared by the feature
The undecorated Feature that was originally used in the
operation. If no decoration occurred then isDecorated()
will return false
and this method will return the
same value as getFeature();
true
if the original feature was decorated by one or
more decorators. If true
then the undecorated
feature will be available from getOriginalFeature() and
the actual feature used will be available from
getFeature().
Is this a feature installed by FeatureLauncher
true
If this feature was installed as part of a
FeatureLauncher launch operation. false
if
it was installed by the FeatureRuntime
An MergeOperationType represents the type of operation that is in flight
Merge operations occur when two or more features reference the same (or similar) items to be installed.
The purpose of a RuntimeBundleMerge is to resolve possible conflicts between FeatureBundle entries and determine which bundle(s) should be installed as a result.
Merge operations happen in one of three scenarios, indicated by the MergeOperationType:
-
INSTALL - a feature is being installed
-
UPDATE - a feature is being updated
-
REMOVE - a feature is being removed
When any merge operation occurs the merge function will be provided with the Feature being operated upon, the FeatureBundle which needs to be merged, a List of the InstalledBundles representing the currently installed bundles applicable to the merge, and a List of FeatureBundleDefinitions representing the FeatureBundles and installed features participating in the merge. All Installed Bundle and Feature Bundle objects will have the same group id and artifact id.
If an UPDATE
or REMOVE
operation is underway then
the Feature being updated or removed will already have been removed from any
Installed Bundles and from the list of Feature Bundle Definitions. For an
UPDATE
this may result in one or more Installed Bundles having
an empty list of owning features, and the list of existing installed Feature
Bundle Definitions being empty.
The returned result from the merge function must be a full mapping of
installed Bundle IDs to Lists of owning Feature ids. This is returned
as a Stream of BundleMappings. The combined
BundleMapping.owningFeatures in the stream must contain all of the
Feature ids from the list of Feature Bundle Definitions, and in the case of
an INSTALL
or UPDATE
operation also the Feature
being operated upon. The entries in the returned stream must only contain
BundleMapping.bundleIds from the list of Installed Bundles, and in
the case of an INSTALL
or UPDATE
operation the
Feature Bundle being merged
.
It is an error for any value in the returned stream to be null
,
or to have fields set to null
or an empty list. In the case of a
REMOVE
operation it is an error to include the Feature id being
operated upon in the returned Bundle Mappings.
- the type of the operation triggering the merge.
The feature being operated upon
The FeatureBundle in feature
that
requires merging
A read only list of bundles that have been installed as part of previous installations. This list will always contain at least one entry.
An immutable list of FeatureBundleDefinitions which are part of this merge operation. The entries are in the same order as the Features were installed.
This list may be empty in the case of an UPDATE
operation. Note that multiple Feature Bundle Definitions may
refer to the same bundle ID, or aliases of a single
InstalledBundle.
Calculate the bundles that should be installed at the end of a given operation.
Bundle Merge operations occur when two or more features reference a bundle with the same group id and artifact id, and the purpose of this method is to identify which bundles should be/remain installed, and which features they should be owned by.
The returned result from the merge function must be a full mapping of
installed Bundle IDs to Lists of owning Features. It is an error
for the stream to contain a BundleMapping.bundleId which is not
the ID of an entry in in the installedBundle
list
or, in the case of an INSTALL
or UPDATE
operation, the ID of the toMerge
Feature Bundle.
The combined BundleMapping.owningFeatures in the stream must
contain all of the Features from the List of Feature Bundle Definitions,
and in the case of an INSTALL
or UPDATE
operation also the Feature being operated upon. In the case of a
REMOVE
operation it is an error to include the Feature being
operated upon in the returned stream.
It is an error for any entry in the returned stream to be, or contain,
null
or an empty list.
An unordered Stream of BundleMapping entries
linking a bundle id to List of owning Feature ids. Each Bundle
Mapping represents a bundle that should be installed as a result
of this operation. Note that every Feature id must
appear in the combined BundleMapping.owningFeatures and
that the BundleMapping.bundleId may only contain IDs from
toMerge
or one of the keys from the
installedBundles
list. If two BundleMapping
entries use the same bundle id, or alias, then this is not an
error and these mappings will be combined by the implementation.
A BundleMapping is used to define that a bundle should be (or remain) installed and which Features should own it
A FeatureBundleDefinition is used to show which FeatureBundle(s) are being merged, and the Feature that they relate to.
The Feature containing the FeatureBundle
The FeatureBundle being merged
Merge operations occur when two or more features reference the same (or similar) items to be installed.
The purpose of a RuntimeConfigurationMerge is to resolve possible conflicts between FeatureConfiguration entries and determine what configuration should be created as a result.
Merge operations happen in one of three scenarios, indicated by the MergeOperationType:
-
INSTALL - a feature is being installed
-
UPDATE - a feature is being updated
-
REMOVE - a feature is being removed
When any merge operation occurs the merge function will be provided with the Feature being operated upon, the FeatureConfiguration which needs to be merged, the InstalledConfiguration representing the current configuration, and a list of FeatureConfigurationDefinitions representing the installed features participating in the merge. All Feature Configurations will have the same PID.
If an UPDATE
or REMOVE
operation is underway then
the Feature being updated or removed will already have been removed from the
Installed Configuration and the list of existing Feature Configuration
Definitions. For an UPDATE
this may result in the
InstalledConfiguration.getOwningFeatures() being an empty list, and
the map of existing installed Feature Configurations being empty.
The returned result from the merge function is a map of configuration properties that should be used to complete the operation. This may be null if the configuration should be deleted.
- the type of the operation triggering the merge.
The feature being operated upon
The FeatureConfiguration in feature
that requires merging
The existing configuration that has been installed
as part of previous installations. This will represent a
configuration with the same PID as toMerge
.
Note that this value will be null
if the
configuration does not exist to differentiate it from an empty
configuration dictionary
An immutable list of FeatureConfigurationDefinitions which are part of this merge operation. The entries are in the same order as the Features were installed.
This list may be empty in the case of an UPDATE
operation. Note that all Feature Configuration Definitions
will refer to the same PID, and this will match the PID of
toMerge
. An immutable map of existing Feature
Configurations which are part of this merge operation. The
keys in the map are the Feature Configurations involved in the
merge and the values are the Features which contain the
Feature Configuration.
Calculate the configuration that should be used at the end of a given operation.
Configuration merge operations occur when two or more features define the same configuration, where configuration identity is determined by the PID of the configuration. The purpose of this function is to determine what configuration properties should be used after the merge has finished.
A map of configuration properties to use. Returning
null
indicates that the configuration should be
deleted.
A FeatureConfigurationDefinition is used to show which FeatureConfiguration(s) are being merged, and the Feature that they relate to.
The Feature containing the FeatureConfiguration
The FeatureBundle being merged
Merge operations occur when two or more features reference the same (or similar) items to be installed.
The purpose of a RuntimeMerges is to provide common merge strategies in an easy to construct way.
Attempts to turn the version String from an ID into an OSGi version
Note that this parsing is more lenient than
Version.parseVersion(String). It treats the first three segments
separated by .
characters as possible integers. If they are
integers then they represent the major, minor and micro segments of an
OSGi version. If any non-numeric segments are encountered, or the end of
the string, then the remaining version segments are 0
. Any
remaining content from the input version string is used as the qualifier.
An OSGi version which attempts to replicate the version from the ID
The preferExistingBundles() merge strategy tries to reduce the number of new installations by applying semantic versioning rules. The new bundle is only installed if it has:
-
A different major version from all installed bundles
-
A higher minor version than all other installed bundles with the same major version
the prefer existing merge strategy
The replaceExistingProperties() merge strategy simply replaces any existing configuration values with the new values from the new FeatureConfiguration.
Removal is more complex and relies on the fact that the
existingFeatureConfigurations
are in installation order.
This means that we can descend the list looking for the previous
configuration properties and apply them
the replace existing merge strategy
[1] The Maven 2 Repository Layouthttps://maven.apache.org/repository/layout.html#maven2-repository-layout
[2] The Data URI schemehttps://en.wikipedia.org/wiki/Data_URI_scheme