Declarative Components
This section describes the JavaScript adoption of the OSGi Declarative Services Specification.
The goal of the Declarative Service Specification is to hide the complexity of the low level programming approach provided by the dynamic service layer to a more descriptive and easier way using metadata declarations. A lot of the dynamic complexity is hidden behind simple configuration statements.
The Declarative Service Specification introduces a new term "Component", or in other words a component-oriented approach to declaring dynamic services. Sometimes it is difficult to understand the difference between a component and a service. In the book "OSGi in Action" (Manning 2011 - ISBN 9781933988917, page 119) the author points out that both terms refer to the same thing, but from a different architectural perspective. When talking about components, the focus is on the view of the provider. This means, in turn, that when talking about services, the focus is on the customer’s point of view. In the following numerous JSON component declarations are listed. The purpose of these declarations is to declare a service to be registered with the JavaScript OSGi runtime, so that the focus is on the service provider view.
The main advantages of declarative services are:
-
Service/Component lifecycle management
-
Declarative expression of dependencies on services (dependency injection)
-
Simplified implementation of services (focus on functional tasks and not on managing dependencies)
-
Simple properties-based configuration model
The Declarative Component System can be imagined as an extension layer built upon the dynamic service layer.
Components
Terms used in following sections:
- Component
-
A component contains a description that is interpreted at runtime to create and dispose objects depending on the availability of other services, the need for such an object, and available configuration data. Such objects can optionally provide a service.
- Component Description
-
The declaration of a component. It is contained within the manifest.json of a bundle.
- Component Properties
-
A set of properties which can be specified by the component description, the Configuration Admin Service and from the component factory.
- Component Configuration
-
A component configuration represents a component description parameterized by component properties. It is the entity that tracks the component dependencies and manages a component instance. An activated component configuration has a component context.
- Component Instance
-
An instance of the component implementation class. A component instance is created when a component configuration is activated and discarded when the component configuration is deactivated. A component instance is associated with exactly one component configuration.
- Delayed Component
-
A component whose component configurations are activated when their service is requested.
- Immediate Component
-
A component whose component configurations are activated immediately upon becoming satisfied.
- Factory Component
-
A component whose component configurations are created and activated through the component’s component factory.
- Reference
-
A specified dependency of a component on a set of target services.
- Service Component Runtime (SCR)
-
The actor that manages the components and their lifecycle.
- Target Services
-
The set of services that is defined by the reference interface and filter property.
- Bound Services
-
The set of target services that are bound to a component configuration.
The life of a component is fully managed by a component environment which is called the Service Component Runtime (SCR). The SCR is provided and registered to the JS OSGi runtime by the system bundle. A component instance is a normal JavaScript Object, which can provide some lifecycle methods.
The SCR distinguishes between the following component types:
- Immediate Component
-
The component configuration of an immediate component is activated immediately after becoming satisfied. Immediate components can provide a service.
- Delayed Component
-
When a component configuration of a delayed component becomes satisfied, the SCR registers the service specified by the service element without creation of the component instance. If this service is requested, the SCR creates the component instance and returns it as the service object. If the serviceFactory attribute of the component description is true, for each distinct bundle that requests the service, a different component instance is created and returned as the service object.
- Factory Component
-
If a component’s description specifies the componentFactory attribute, the SCR registers a Component Factory service. This service allows client bundles to create and activate multiple component configurations and dispose of them. If the component’s description also specifies a service, as each component configuration is activated, the SCR registers it as a service.
Declaring a Component
A component description is provided inside the manifest.json in the special property "components"
.
{
"name" : "mybundle",
...
"components" : [
{
"name" : "MyComponent"
},
...
]
}
A full list of all available properties inside a component description can be found at the end of this chapter.
A component description must at least contain the name of the component. This name must be unique inside the bundle.
Specifying the implementation class
By default the name of the component is used as implementation class name.
If a different name should be used, the property "impl"
must be specified.
{
"name" : "MyComponent",
"impl" : "AClass"
}
The implementation class of a component is located exactly like described in the "Class Loading" section of the chapter Module layer, the SCR tries to resolve the class by looking into the object returned by the module.js.
Providing a Service
A component instance is registered as a service in the JS OSGi runtime if its component description declares the property "provides"
.
The property contains the names of the provided service interfaces, the value can be a string or an array.
{
"name" : "MyComponent",
"provides" : "myservice.Interface"
}
or
{
"name" : "MyComponent",
"provides" : ["myservice.Interface","asecondprovided.Interface"]
}
The properties specified in the components description are also important for service registration, see Component Properties.
It is important that each property which does not start with a _
prefix is treated as public service property and registered as service properties.
Instance Factory
Sometimes it is required to register a different instance as a service than the one that you want to configure as a component implementation.
This is often the case if you have a ready-to-use class which is implemented independent of the component model and you don’t wish to introduce component system-relevant code inside this class, for example configuration code.
A very typical use case is the use of Dijit widgets as components.
To support this case the component description has to be marked as "instanceFactory"
, which means that the component instance acts as factory for the service.
{
"name" : "MyComponentFactory",
"provides" : ["myservice.Interface"],
"instanceFactory": true
}
A component instance which was marked as an instance factory must implement the method createInstance
, which is responsible for creating the "real" service instance.
This service instance is registered at the JS OSGi runtime.
Optionally the component instance can provide a destroyInstance
method, which is responsible for cleaning up the service instance during shutdown.
import MyComponentWidget from "./MyComponentWidget";
export default class{
// responsible to create the service
createInstance(){
return new MyComponentWidget();
},
// responsible to destroy the service
destroyInstance(instance){
// destroys the service
instance.destroyRecursive();
}
}
Service Factory
The component system also supports the service factory pattern, as described in the Dynamic Services section.
If the serviceFactory
flag is set to true
in the component description, for each distinct bundle a new service component instance is created and returned to the using bundle.
Only delayed components are allowed to be service factories, because for all other types of components the SCR is not free to create the component instance on demand.
{
"name" : "MyComponentFactory",
"provides" : ["myservice.Interface"],
// this component should behave as service factory
"serviceFactory": true
}
Immediate Component
An immediate component is declared if the component description contains the property "immediate"
with value 'true' and is not a factory component.
A component is treated as immediate if its description does not declare a "provides"
property.
{
"name" : "MyComponent",
"provides" : ["myservice.Interface"],
// this component should immediatly created
"immediate": true
}
Immediate component instances are created immediately if all references are satisfied.
If the component has no references, you can consider the lifetime of the component as equal to BundleActivator, which are created on the start of a bundle and destroyed during the stop of the bundle.
Only immediate components are allowed to act asynchronously in their activation method.
This means a component instance is allowed to return a dojo.Deferred during its activate
method and the SCR waits until activate
finishes before it registers the component instance as a service at the JS OSGi runtime.
Delayed Component
A component description which declares a "provides"
property and which is not an immediate component and not a factory component is treated as delayed.
This means that delayed components are the most common kind of components, because this is the default case if no other special property is declared.
{
"name" : "MyComponent",
"provides" : ["myservice.Interface"]
}
Delayed components are so named because of the fact that service registration is made before the component instance is created. This means that the creation of the component instance is delayed until the first use of the registered service. A delayed component instance is destroyed if it becomes unused. As a result, delayed components can have a very dynamic life because construction and destruction might happen very often and are highly dependent on usage of the service provided by the component.
Factory Component
A component description which specifies the property "componentFactory"
is treated as factory component.
{
"name" : "MyComponent",
"provides" : ["myservice.Interface"],
"componentFactory" : true
}
A factory component is a special design pattern, because it acts more as a template for the dynamic creation of component configurations based on component descriptions.
The SCR parses the component description.
If it is satisfied, it registers a service with the interface ct.framework.api.ComponentFactory
for this configuration.
This service provides a method newInstance
, which can be used to create new component configurations during runtime.
The newInstance
method returns a ComponentInstance object, which provides the method getInstance
for accessing the real instance and the method dispose
for shutting down the component configuration.
The caller of newInstance
is responsible for the disposal of the created component configuration.
// Activator using a component factory
export default class{
start(bundleContext){
// search special component factory with name "MyComponentFactory"
let references = bundleContext.getServiceReferences("ct.framework.api.ComponentFactory","(Component-Name=MyComponent)");
try{
// get the component factory service
let factory = bundleContext.getService(references[0]);
// create a new component configuration
// here the default component properties defined in the declaration of the component factory can be overwritten.
let componentInstance = factory.newInstance({ "aproperty": "newValue" });
// get component/service implementation
// this feature should rarely be used, better define a reference
let impl = componentInstance.getInstance();
impl.doSomething();
// the created component configuration has to be cleaned up later
componentInstance.dispose();
}finally{
bundleContext.ungetService(references[0]);
}
}
}
Factory Components are a very powerful feature because the newInstance
method creates new component configurations.
This means that such a new dynamically created component configuration is managed by the SCR as if it were a new and independent component description in the manifest.json. Therefore if the property "provides"
was specified, the new component instance is registered as a service in the JS OSGi runtime.
Because of the ComponentInstance.getInstance
method, a component configuration created by a factory component is treated as an immediate component, therefore, regarding its using state, the component instances are directly created.
An important point is that a factory component is satisfied if all references of the component description are also satisfied.
In some circumstances this definition leads to unexpected behaviors especially if references are using placeholders in filter expressions.
// Other component using a component factory
// manifest.json
{
"name" : "OtherComponent",
"references" : [{
"name" : "_factory",
"providing" : "ct.framework.api.ComponentFactory",
"filter": "(Component-Name=MyComponent)"
}]
}
// implementation of OtherComponent
export default class{
activate(){
// create a new component configuration
// here the default component properties defined in the declaration of the component factory can be overwritten.
let componentInstance = this._factory.newInstance({ "aproperty": "newValue" });
// get component/service implementation
// this feature should rarely be used, better define a reference
let impl = componentInstance.getInstance();
impl.doSomething();
// the created component configuration has to be cleaned up later
componentInstance.dispose();
}
}
Component Properties
A component description can declare properties.
These properties are used for two purposes.
The first is to provide a configuration mechanism and the second to specify service properties (if the "provides"
property is defined).
{
"name" : "MyComponent",
"provides" : ["myservice.Interface"],
// properties of the component
"properties": {
// underscore properties are private properties
"_offset" : 125,
// all other properties are registered as service properties (if provides exist)
"url" : "http://locahost:8080/..."
}
}
The recommended way to access component properties within an implementation class is to use the provided property _properties
.
This is a plain JavaScript object which can be used to access the configuration properties.
export default class {
aServiceMethod(){
// accessing the injected '_properties' object
let properties = this._properties;
// the url property
let url = properties.url;
// the offset property
let offset = properties._offset;
...
}
}
An alternative way is using the Component Context provided by the activate
method (see component lifecycle).
The Component Context provides a getProperties
method, which returns a ct.Hash
instance containing the properties, which means that the access style is slightly different.
export default class {
activate(componentContext){
// get the properties from ComponentContext
var properties = componentContext.getProperties();
// the url property
var url = properties.get("url");
// the offset property
var offset = properties.get("_offset");
...
}
}
Another way to retrieve the properties is to specify the flag "propertiesConstructor"
on the component description.
This defines that the configuration properties are transported as constructor options into the implementation class.
Here the properties are exactly like the '_properties' property a plain JavaScript object.
Besides the transportation as constructor properties, the configuration properties are still injected as _properties
property.
{
"name" : "MyComponent",
"provides" : ["myservice.Interface"],
// the component implementation shall get the properties as constructor options
"propertiesConstructor": true,
"properties": {
"_offset" : 125,
"url" : "http://locahost:8080/..."
}
}
export default class {
constructor(properties){
// the url property
let url = properties.url;
// the offset property
let offset = properties._offset;
...
}
});
});
Configuration properties are read only. You are not allowed to change them.
Visibility of Properties
Properties can be public or private. Public properties are part of the component interface and registered as service properties if the component is registered as OSGi service.
By default all properties starting with an underscore "_" are treated as private properties, which mean that all other properties are registered as public service properties.
Following further declaration options are possible:
// 1) use +|- prefix declarations
{
"name": "MyComponent",
"provides" : "anInterface",
"properties": {
// public -- use the "+" prefix to declare a public property.
"+id": "test",
// private -- use the "-" prefix to declare a private property.
"-aConfigOption" : "optionvalue"
}
}
// 2) use publicProperties and/or privateProperties declarations
{
"name": "MyComponent",
"provides" : "anInterface",
"properties": {
"id": "test",
"aConfigOption" : "optionvalue"
},
"publicProperties" : ["id"],
"privateProperties" : ["aConfigOption"]
}
If you use the You are free to mix the styles. |
Accessing Internationalization (i18n) Properties
A component instance can access the i18n properties provided by the bundle using the "_i18n"
property, injected into it.
The "_i18n"
property is a ct.I18N
object.
The "_i18n"
property is also provided in the constructor if the flag 'propertiesConstructor' is set to 'true'.
export default class {
aServiceMethod(){
// get a i18n value of key 'i18nProperty'
let i18nValue = this._i18n.get().i18nProperty;
...
}
}
Accessing the BundleContext
Each component implementation is allowed to access the BundleContext.
This can be achieved by providing a activate
method (see component lifecycle).
This method is called with a single parameter called ComponentContext from which a component instance can get the BundleContext.
export default class {
activate(componentContext){
// retrieve the BundleContext from the component context
let bundleContext = componentContext.getBundleContext();
}
}
If a component instance stores the BundleContext or ComponentContext as member variable, it has to release these resources during destruction, therefore it has to implement a deactivate method!
|
References to Services
A very powerful feature of the component system is the declarative expression of references to other services registered in the JS OSGi runtime. The declarative component model significantly simplifies the handling of the dynamics of service dependencies.
A reference expression is a declarative wrapper of the method BundleContext.getServiceReferences
(described in the Dynamic Services chapter), which requires an interface as selector together with an optional filter expression.
Services which are selected by these conditions are called target services.
The properties "providing"
, "filter"
and "cardinality"
of a reference specify rules which are checked by the SCR.
If all rules of a reference are fulfilled, the reference is said to be satisfied.
A component configuration becomes satisfied when all references are satisfied.
If a reference becomes unsatisfied, the component configuration also becomes unsatisfied and is deactivated.
If a component configuration is activated by the SCR, some of the target services are bound to the configuration.
These services are called bound services.
References are expressed as part of the component description.
{
"name" : "mybundle",
...
"components" : [
{
"name" : "MyComponent",
...
"references" : [
{
"name" : "logService",
"providing" : "ct.framework.api.LogService"
},
...
]
},
...
]
}
A reference needs at least a "name"
and a "providing"
property.
The "name"
property must have a unique value inside the references of the component description and identifies the reference.
The "providing"
property defines the interface which the target service must provide.
Filtering of References
The property "filter"
at a reference is used to further reduce the target services selected by the providing property.
The value of "filter"
is a filter expression described in the section "Searching for Bundles" in chapter "Module layer".
{
"name" : "myFactory",
"providing" : "ct.framework.api.ComponentFactory",
"filter" : "(Component-Factory=MyComponentFactory)"
}
It is allowed to use property placeholders in filter expressions, like in the following sample.
properties: {
"storeId" : "sample-store"
},
references:[{
"name": "store",
"providing": "ct.api.Store",
// the string '{storeId}' is replaced by the value of the property 'storeId' of the component configuration
"filter": "(&(useIn=selection)(id={storeId}))"
}]
Placeholders in filter expressions allow the change of the target set of a reference based on configuration properties.
Reference Cardinality
By specifying the property "cardinality"
on a reference it is possible to declare target set conditions.
{
"name" : "myFactory",
"providing" : "myservice.Interface",
// optional reference, only one target service needed
"cardinality" : "0..1"
}
Following cardinality values are supported:
Expression | Description |
---|---|
|
Selects exactly one target service from the set of target services and a target service must be available to become satisfied. This is the default if no property is defined. |
|
The reference is satisfied if none or more target services are available. Selects one service from the set of target services, if available. |
|
Selects all target services from the set of target services and a target service must be available to become satisfied. |
|
The reference is satisfied if none or more target services are available. Selects all services from the set of target services, if available. |
If only one target service is selected then the first service returned by BundleContext.getServiceReferences
is bound, which is the one with the highest ranking or the one first registered.
Reference Policy
A special lifecycle property is the "policy"
property on a reference.
It can have the value "dynamic"
or "static"
and declares the capability of a component instance to react on runtime changes of the matching target services of a reference.
The policy is only important if changes in the target services still keep the reference satisfied.
For example, this is the case if a reference has a 1..n
cardinality and a new target service is registered at the system.
Value | Description |
---|---|
|
Defines that the component instance is able to react dynamically on changes in the bound service set itself. Therefore the SCR is not restarting the component configuration. This is the default value. |
|
Defines that the component instance is not able to react on dynamic changes in the bound service set. This means the SCR restarts the component configuration on such changes. |
{
"name" : "myFactory",
"providing" : "myservice.Interface",
"cardinality" : "0..n",
// defines that the component can't handle dynamic changes of that reference
"policy" : "static"
}
Accessing References
The bound service set of declared references are provided by the SCR to a component instance in different ways. The SCR distinguishes between following main strategies:
-
Injection Strategy: References are transparently injected into the component instance and access is made via member variables in the component instance.
-
Event Strategy: References are injected into the component instance by method calls. The SCR calls specific methods provided by the component instance to inform about new services and service removal.
-
Lookup Strategy: References are manually looked up by the component instance in the component context.
All strategies can be mixed inside a component instance, very common is a mix of Injection Strategy and Event Strategy. The Lookup Strategy is rarely used.
All these strategies are discussed in the following sections on this sample component description:
{
"name" : "mybundle",
...
"components" : [
{
"name" : "MyComponent",
"references" : [
{
"name" : "logService",
"providing" : "ct.framework.api.LogService"
},
{
"name" : "stores",
"providing" : "ct.api.Store",
"cardinality" : "0..n"
}
]
}
]
}
Accessing References via Injection Strategy
The injection strategy means that references are provided as member properties inside the component instance without any further effort on the side of the component instance.
The component instance can directly access the injected properties.
The injected properties are named exactly like the name property of the reference.
To access the service properties of an injected reference, the special property <name>_info
is injected.
export default class {
doSomething(){
// simply access the logService service instance,
// because of the default 1..1 cardinality it is guaranteed to be available
this.logService.info("DoSomething started!");
// read service properties
let logserviceProperties = this.logService_info;
// this.stores contains all stores in the system (0..n cardinality)
// if no stores are in the system, the array is still present but empty
let stores = this.stores;
// read service properties of stores, is an array, too
let storeProperties = this.stores_info;
stores.forEach((store,index)=>{
// get service properties of current store
let storeProps = storeProperties[index];
let ranking = storeProps["Service-Ranking"];
...
});
}
}
Injection/ejection happens before activate and after deactivate and of course in between if the policy is dynamic and the target service set changes.
For details, see component lifecycle.
|
Accessing References via Event Strategy
The event strategy is used by the SCR to inject services into the component instance if the instance provides event methods. The names of the event methods are calculated based on the multiplicity of the cardinality and the name of the reference.
Cardinality | Name Calculation Pattern | Sample |
---|---|---|
|
|
|
|
|
|
The name of the reference is capitalized to create the method name, so logService is converted into LogService .
|
export default class {
constructor(){
this.logger = null;
this.stores = [];
}
// called by SCR to bind the log service
setLogService(logservice, serviceproperties){
this.logger = logservice;
}
// called by SCR to inform about unregistration of the log service
unsetLogService(logservice, serviceproperties){
this.logger = null;
}
// called by SCR to inform about a new store
addStores(store, serviceproperties){
this.stores.push(store);
}
// called by SCR to inform about unregistration of a store
removeStores(store, serviceproperties){
var index = this.stores.indexOf(store);
this.stores.splice(index,1);
}
doSomething(){
this.logger.info("DoSomething started!");
this.stores.forEach((store)=>{
...
});
}
}
These default method names can be overwritten by defining the "bind"
and "unbind"
properties at the reference description.
{
"name" : "mybundle",
...,
"components" : [
{
"name" : "MyComponent",
"references" : [
{
"name" : "logService",
"providing" : "ct.framework.api.LogService",
// declare custom event methods (setLogging is used for bind and unbind)
"bind" : "setLogging",
"unbind" : "setLogging"
}
]
}
]
}
This results in an implementation like this:
export default class {
constructor(){
this.logger = null;
}
// called by SCR to bind and unbind the log service
setLogging(logservice, serviceproperties){
if (this.logger){
// unset
this.logger = null;
}else{
this.logger = logservice;
}
}
doSomething(){
this.logger.info("DoSomething started!");
}
}
The event methods are called (like injection) before activate and after deactivate and of course also between if the policy is dynamic and the target service set changes.
For details, see component lifecycle.
|
Accessing References via Lookup Strategy
A third variant for retrieving the services is to use the ComponentContext.
The ComponentContext is provided by the activate
method (see component lifecycle).
Reference-relevant methods of the ComponentContext are:
Method | Description |
---|---|
|
Returns an array of service references. The matching target services. |
|
Returns an array of service instances. The bound service set. |
|
Returns service instance selected by the service reference. |
The usage style is similar to working with the BundleContext.
Services, which are located by using locateService
or locateServices
, should not be stored in member variables. The 'locate*' methods should only be called between activate
and deactivate
.
For details, see component lifecycle.
export default class {
activate(componentContext){
// store component context in member variable
this.componentContext = componentContext;
}
deactivate(componentContext){
// clear member variable
this.componentContext = null;
}
doSomething(){
let componentContext = this.componentContext;
// cardinality is 1..1 so the result array has exactly 1 item, the log service
let logger = componentContext.locateServices("LogService")[0];
logger.info("DoSomething started!");
// get all stores
let stores = componentContext.locateServices("Stores");
stores.forEach((store)=>{
...
});
}
}
If the lookup style is used in a component instance, the special property "noInjection"
should be set on the reference description to ensure that the SCR does not inject the references into the component instance.
{
"name" : "mybundle",
...,
"components" : [
{
"name" : "MyComponent",
"references" : [
{
"name" : "logService",
"providing" : "ct.framework.api.LogService",
// disable injection behavior
"noInjection" : true
},
{
"name" : "stores",
"providing" : "ct.api.Store",
"cardinality" : "0..n",
// disable injection behavior
"noInjection" : true
}
]
}
]
}
Declarative Event Binding to References
A further feature of the component system is the declarative event binding. This is of interest if a component instance likes to listen on events on a reference and to execute a special method if that event is fired.
It can be distinguished between three kinds of event binding styles: "connect", "on", and "watch".
Style: on
This style uses the dojo/on
module to connect to events provided by services.
The "on"
property on a reference declares the binding.
{
"name" : "aTool",
"providing" : "ct.tools.Tool",
"filter" : "(id=atoolname)",
// on is an object with event names to event handle methods
"on" : {
// Click = event name fired by tool
// _handleOnClick = the method which should be called if the event fires
"Click" : "_handleOnClick"
}
}
The code of the component instance might look like this:
export default class {
_handleOnClick(event){
// do something...
console.debug("clicked..", event);
}
}
Style: connect
This style should be replaced by using the on style, when possible. |
This style uses the dojo/_base/connect
module to connect to events provided by services.
The "connect"
property on a reference declares the binding.
{
"name" : "aTool",
"providing" : "ct.tools.Tool",
"filter" : "(id=atoolname)",
// connect is an object with event names to event handle methods
"connect" : {
// onClick = event fired by tool
// _handleOnClick = the method which should be called if the event fires
"onClick" : "_handleOnClick"
}
}
The code of the component instance might look like this:
export default class {
_handleOnClick(event){
// do something...
console.debug("clicked..", event);
}
}
Style: watch
This style declares a property change listener.
It can be used at services which provides watch
methods, like defined by dojo/Stateful
.
All ct/Stateful
and dijit/_Widget
instances provide such a method.
The "watch"
property on a reference declares the binding.
{
"name" : "aStateful",
"providing" : "a.datamodel.Instance",
// watch is an object with property names to property change listener methods
"watch" : {
// selectedItem = name of the property about whose value changes should be informed
// _handleSelectedItemChange = the property change listener method
"selectedItem" : "_handleSelectedItemChange"
}
}
The code of the component instance might look like this:
export default class {
_handleSelectedItemChange(propertyname, oldvalue, newvalue){
// do something...
}
}
Component Lifecycle
Enabled
A component must be enabled before it can be used.
You can define the initial value of "enabled"
in the component description as property "enabled"
the default value is true
, so by default components are enabled.
{
"name" : "MyComponent",
"provides" : ["myservice.Interface"],
// disables the component
"enabled": false
}
You can programmatically control this state using the methods enableComponent
and disableComponent
of the ComponentContext.
A component can only enable/disable the components within its own bundle.
The next sample shows how a component can be used to enable/disable another component (ToolXY
).
export default class {
activate(componentContext){
this._componentContext = componentContext;
}
deactivate(){
this._componentContext = null;
}
enableToolXY(){
this._componentContext.enableComponent("ToolXY");
}
disableToolXY(){
this._componentContext.disableComponent("ToolXY");
}
}
Satisfied
The SCR activates a component configuration only if the component configuration is satisfied. The following conditions must be true for a component configuration to become satisfied:
-
The component is enabled.
-
All the references are satisfied. A reference is satisfied when the reference specifies optional cardinality or there is at least one target service for the reference.
As soon as any of the listed conditions are no longer true, the component configuration becomes unsatisfied and is deactivated.
Lifecycle States
The lifecycle state diagram of an immediate component looks like this:
If an immediate component becomes satisfied, it is activated, and if it provides a declaration, it is registered as a service in the JS OSGi runtime. It remains in ACTIVE state until it becomes unsatisfied, which triggers the unregistration and deactivation of the component.
The lifecycle state diagram of a delayed component looks like this:
If a delayed component becomes satisfied, it is first registered as service in the JS OSGi runtime. When the first client gets the registered service, the component is activated. The component remains in active state until it becomes unsatisfied or if the last client "ungets" the service instance, which triggers the deactivation of the component. If unget triggered the deactivation, the component remains registered. Otherwise it becomes unsatisfied.
The lifecycle state diagram of a factory component looks like this:
If a factory component becomes satisfied, the factory service is created and registered at the JS OSGi runtime.
The factory service is registered until it becomes unsatisfied.
If a client calls newInstance
on the factory, a new component configuration is created and activated.
The lifecycle of a component created by a factory is equal to the lifecycle of an immediate component.
Lifecycle Methods
A component instance can provide following lifecycle methods.
Name | Description |
---|---|
|
Signals the start of the injection phase of the component.
It is the first method called by the SCR after the construction of the instance.
Services are injected after this method is called, but the special properties |
|
Signals the end of the injection phase and that the component instance is ready for use. The SCR guarantees that service consumers can access the component instance only after activate is called on the instance. [NOTE]
Only immediate components are allowed to return a |
|
This method is called after |
|
Signals that the component properties(configuration) was changed.
This method is only called between |
|
This method is called directly before |
|
Signals the start of the shutdown process of the component instance. All injected services are still available and the ejection phase starts after this method. |
|
Signals the end of the life of the component instance.
All services are ejected before this method, but |
These methods are called in the life of a component in the order as listed.
Activation
-
constructor: properties are handed over (if
"propertiesConstructor"
istrue
) -
init: is called before references are injected,
_properties
and_i18n
are available -
set<ReferenceName>, add<ReferenceName>: SCR injects services
-
activate: is called after references are injected
-
createInstance: called if
"instanceFactory"
istrue
and must return the service instance
During Runtime (Component is activated)
-
modified: called if configuration was modified;
_properties
contains the new configuration properties. -
set<ReferenceName>, add<ReferenceName>: SCR injects new registered services, if
"policy": "dynamic"
. -
unset<ReferenceName>, remove<ReferenceName>: SCR ejects unregistered services, if
"policy": "dynamic"
Deactivation
-
destroyInstance: called if
"instanceFactory"
istrue
and should clean up the service instance -
deactivate: is called before references are ejected from the component
-
unset<ReferenceName>, remove<ReferenceName>: SCR ejects services
-
destroy or destroyRecursive: is called after references have been removed,
_properties
and_i18n
are still available
Configuration Reference
Following properties can be declared on a component description.
Property | Sample | Description |
---|---|---|
|
|
required The name of the component. Used as "impl" value if "impl" is not declared. Must be unique inside a bundle. |
|
|
optional (default: value of name) The implementation class. An instance of this class is created if the component has to create a new service. |
|
|
optional (default: Specifies if the component should be enabled during the bundle start. |
|
|
optional (default: Specifies the name of the Factory Component, this name is available as service property "Component-Factory" |
|
|
optional (default: Specifies if this component shall be newly created for each requesting bundle. |
|
|
optional (default: Specifies if a configuration element must be available at the Configuration Admin Service. |
|
|
optional (default: Specifies if this is an immediate component. |
|
|
optional (default: Specifies the method name to signal if the component is activated, it’s only called if present in the instance. |
|
|
optional (default: Specifies the method name to signal if component is deactivated, it’s only called if present in the instance. |
|
|
optional (default: Specifies the method name to signal if the component configuration was changed, it’s only called if present in the instance. |
|
|
optional (default: Marks that the component instance is a factory for a service instance.
The component instance must provide a |
|
|
optional (default: Specifies the method name to call for instance creation if this component is an |
|
|
optional (default: Specifies the method name to call for instance dispose if this component is an |
|
|
optional (default: Declares all interface names for which this component is registered as service at the JS OSGi runtime. If this is not specified, this component is an immediate component. |
|
|
optional (default: If |
|
|
optional (default: Declares configuration properties and service properties.
Properties starting with '_' underscore are treated as private and are only visible by this component.
If |
|
|
optional (default: Array of reference declarations. |
A reference declaration can have following properties.
Property | Sample | Description |
---|---|---|
|
|
required The internal name of this reference (used for injection and error logging). |
|
|
required The interface name under which the target service must be registered in the service registry. |
|
|
optional (default: A filter expression which expresses further service metadata requirements of the reference. It is needed if the providing interface is not sufficient to find the correct target service. |
|
|
optional (default: The binding style policy, possible values are "static" or "dynamic". The value "static" means that the component is restarted if the reference becomes unsatisfied but another matching reference is available. The value "dynamic" means that the component is able to handle the dynamic unbinding/binding and keeps running during reference updates. |
|
|
optional (default: Specifies the cardinality of the reference.
Possible values are |
|
|
optional (default: Specifies the method called during the injection process.
If the method is not present, the reference is injected as property |
|
|
optional (default: Specifies the method called during the ejection process. |
|
|
optional (default: Signals that this reference should not be injected and the component uses a programmatic resolving by applying the |
|
|
optional (default: {}) Defines that the SCR should manage the connects to the reference.
In the sample the SCR connects to the event |