Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

  • Simple / Aggregate : the component can require one or several service providers
  • Mandatory / Optional : a component can declare an optional dependency
  • Filtered : a component can filter available providers
  • Dynamic / Static / Dynamic-Priority : the component can specify the binding policy

Dynamism & Instance Lifecycle

In OSGi?OSGi™, services can appear and disappear dynamically. This implies dependencies can target a provdier which can appear or disappear dynamically.  So, dependencies need to manage this dynamism by tracking every time available services. At any moment, a dependency can be unresolved (i.e. no more provider can fulfill the requirement).  In the case of a mandatory requirement, the instance becomes invalid (an invalid instance is no more accessible externally, for example provided service are unpublished). If a service, resolving the unfilled dependency appears, the instance becomes valid. In consequence, dependencies affect directly the instance state, and must manage correctly OSGi dynamism to allow a complete unloading when a service goes away. As soon a mandatory dependency cannot be fulfilled, the instance is invalidated.

Service Requirement Injection Mechanisms

The handler manages two types of injections:

  • Field injection: a field contains the service object. As soon as the field is used, a consistent service object is injected. This injection type fully hides the dynamism
  • Method invocation: when a service appears, or disappears a method in the component is invoked. For each dependency, bind and unbind methods are invoke to notify the component of the event.

Moreover, the two injections type can be merged. A component can declare a requirement containing both a field and 'binding'.

Field injection

Imagine a Hello service with one method 'getMessage' returning a "Hello Message". The following component implementation can use this service by attaching this service to a field and by using the field:

By default, dependencies are managed dynamically (as previously explained). However, iPOJO supports two other types of binding policies : 

  • Static : if a bound service disappears, the instance is invalidated and cannot be revalidated (binding broken)
  • Dynamic-Priority : at each injection, the best provider is injected, or the providers array is sorted according to the OSGi Ranking policy.

Service Requirement Injection Mechanisms

The handler manages two types of injections:

  • Field injection: a field contains the service object. As soon as the field is used, a consistent service object is injected. This injection type fully hides the dynamism
  • Method invocation: when a service appears, or disappears a method in the component is invoked. For each dependency, bind and unbind methods are invoke to notify the component of the event.

Moreover, the two injections type can be merged. A component can declare a requirement containing both a field and 'binding'.

Field injection

Imagine a Hello service with one method 'getMessage' returning a "Hello Message". The following component implementation can use this service by attaching this service to a field and by using the field:

Code Block

public class HelloConsumer {
Code Block

public class HelloConsumer {
                private Hello m_hello;
                   private Hello m_hello;
                public doSomething() {
                               System.out.println(m_hello.getMesage());
                }
}

...

Code Block
<requires aggregate="true" optional="true">
     <callback type="bind" method="bindHello">
     <callback type="unbind" method="unbindHello">
</requires>

In this case, you need to add the 'aggregate' attribute and the 'optional' attribute. The bindHello and unbindHello will be called each time a Hello service appears or disappears.

Filtered Requirement

A filtered dependency applies an LDAP filter on service provider. IPOJO reuses OSGi LDAP filter ability. The following metadata illustrates how to use filters:

Code Block

<component classname="...HelloConsumer">
<requires filter="(language=fr)">
     <callback type="bind" method="bindHello">
     <callback type="unbind" method="unbindHello">
</requires>
...
</component>

To add a filter, just add a 'filter' attribute in your dependency containing the LDAP filter. IPOJO will select only provider matching with this filter.

Requires Metadata

A requires element metadata can contains :

  • Field : name of the field representing the dependency in the component class
  • Interface : type of the Field
  • Optional: is the dependency an optional dependency?
  • Aggregate: is the dependency an aggregate dependency?
  • Filter : filter selecting service provider
  • Callback : allow to call a method on the component instances when a service provider (matching with the dependency) appears or disappears

Some of these attributes are optional. Indeed, either the component implementation contains the information; either iPOJO uses default behavior.

The field attribute is optional if callbacks are specified. The field attribute contains the name of the field of the component attached with this dependency.

The interface attribute describes the required service. This element is optional because the component implementation contains the same information (the type of the field). If the interface attribute has a value, the consistency of the information is checked. However, sometimes this attribute is mandatory if iPOJO cannot discover the type of the dependency.

The optional attribute describes if the dependency is optional or not. An optional dependency is always valid, and does not interact with the instance state. On the other hand, when a mandatory is no more valid the component state becomes INVALID.

The aggregate attribute determines if the dependency needs to inject several providers on just one. This attribute is optional if the dependency point on a field array.

The filter attribute contains the LDAP request on the service provider. By default, iPOJO use no filter, but you can specify a filter. For further information, see the OSGi specification.

The callback elements contain the name of method to call when a service provider matching with the dependency appears or disappears. This allows the component to be notified when a service provider arrives or goes away.

Technical details on service discovery and service invocation

The handler of the registers an OSGi service event listener and listens for interesting events. When an interesting service arrives, it stores the service reference. When the service is used, it obtains the service object and injects the object in the instance. If it is an aggregate dependencies, it returns an array of service object or/and call the bind method for each provider.
When a service goes away, the handler removes the reference from its list (and call the unget method): the manager does not keep any reference on the old service. This allows a complete unloading of the service.
Two calls to a service can use two different service providers if the used provider has gone. IPOJO tries to return always the same service object if possible, since the service goes away.

Note about nullable object

The instance implementation can use an optional dependency without any checking. Indeed, when an instance declares an optional dependency using field injection, iPOJO create on the fly a Nullable class implementing the service specification but doing nothing (mock object). Therefore, iPOJO cannot return a service to the instance, for an optional dependency, it returns a nullable object.

A nullable object returns:

  • Null when the method returns an object
  • 0 when the method returns an int, log, byte, short, char, float or a double
  • False when the method return a boolean
unbindHello">
</requires>

In this case, you need to add the 'aggregate' attribute and the 'optional' attribute. The bindHello and unbindHello will be called each time a Hello service appears or disappears.

Filtered Requirement

A filtered dependency applies an LDAP filter on service provider. IPOJO reuses OSGi LDAP filter ability. The following metadata illustrates how to use filters:

Code Block

<component classname="...HelloConsumer">
<requires filter="(language=fr)">
     <callback type="bind" method="bindHello">
     <callback type="unbind" method="unbindHello">
</requires>
...
</component>

To add a filter, just add a 'filter' attribute in your dependency containing the LDAP filter. IPOJO will select only provider matching with this filter.

Binding Policies 

Three binding policies are supported inside iPOJO.

  • Dynamic policy (default) : the binding are managed dynamically. At each injection, the same provider is injected if the provider is always available. Else a new one is chosen. For aggregate dependency, the array order does not change, new providers are placed at the end of the array.
  • Static policy : the binding is static. So, once bound a provider cannot disappear. If it disappears, the instance is invalidated and cannot be revalidated without stopping and restarting the instance.
  • Dynamic-priority policy : the binding is managed dynamically but the injected provider is selected by using the OSGi ranking policy. Two injections can return two different providers, is a new provider is 'better' than the previous one, despite the first one is alway available. For aggregate dependency, the array is sorted.
    A static binding is declared as following :
    Code Block
    
    <component classname="...HelloConsumer">
    <requires field="m_hellos" policy="static"/>
    ...
    </component>
    
    A dynamic-priority binding is declared as following :
    Code Block
    
    <component classname="...HelloConsumer">
    <requires field="m_hellos" policy="dynamic-priority"/>
    ...
    </component>
    

Requires Metadata

A requires element metadata can contains :

  • Field : name of the field representing the dependency in the component class
  • Interface : service interface
  • Optional: is the dependency an optional dependency?
  • Aggregate: is the dependency an aggregate dependency?
  • Filter : filter selecting service provider
  • Callback : allow to call a method on the component instances when a service provider (matching with the dependency) appears or disappears

Some of these attributes are optional. Indeed, either the component implementation contains the information; either iPOJO uses default behavior.

The field attribute is optional if callbacks are specified. The field attribute contains the name of the field of the component attached with this dependency.

The interface attribute describes the required service. This element is optional because the component implementation contains the same information (the type of the field). If the interface attribute has a value, the consistency of the information is checked. However, sometimes this attribute is mandatory if iPOJO cannot discover the type of the dependency.

The optional attribute describes if the dependency is optional or not. An optional dependency is always valid, and does not interact with the instance state. On the other hand, when a mandatory is no more valid the component state becomes INVALID.

The aggregate attribute determines if the dependency needs to inject several providers on just one. This attribute is optional if the dependency point on a field array.

The filter attribute contains the LDAP request on the service provider. By default, iPOJO use no filter, but you can specify a filter. For further information, see the OSGi specification.

The callback elements contain the name of method to call when a service provider matching with the dependency appears or disappears. This allows the component to be notified when a service provider arrives or goes away.

Technical details on service discovery and service invocation

The handler of the registers an OSGi service event listener and listens for interesting events. When an interesting service arrives, it stores the service reference. When the service is used, it obtains the service object and injects the object in the instance. If it is an aggregate dependencies, it returns an array of service object or/and call the bind method for each provider.
When a service goes away, the handler removes the reference from its list (and call the unget method): the manager does not keep any reference on the old service. This allows a complete unloading of the service.
Two calls to a service can use two different service providers if the used provider has gone. IPOJO tries to return always the same service object if possible, since the service goes away.

Note about nullable object & default-implementation

The instance implementation can use an optional dependency without any checking. Indeed, when an instance declares an optional dependency using field injection, iPOJO create on the fly a Nullable class implementing the service specification but doing nothing (mock object). Therefore, iPOJO cannot return a service to the instance, for an optional dependency, it returns a nullable object.

A nullable object returns:

  • Null when the method returns an object
  • 0 when the method returns an int, log, byte, short, char, float or a double
  • False when the method return a boolean

You can check if the returned object is a nullable object with the test: _m_myservice instanceof Nullable.

However, you can indicates a default-implementation for your optional service. In this case, if no providers are found, iPOJO creates an instance of the default-implementation and injects it. The default-implementation attribute describes the class name of your implementation. The given class MUST implement the required service interface.

For example, the following componentr uses a default implementation for a Log Service dependency :

Code Block

<component classname="...LogExample">
<requires field="m_log" optional="true" default-implementation="org.apache.felix.ipojo.example.default.MyLogService"/>
...
</component>

If the log service is not available, iPOJO creates an object of the 'org.apache.felix.ipojo.example.default.MyLogService' class. This object is injected instead of a Nullable object. For instance, the default implementation can print messages on the System.err stream. The nullable object does no display anythingYou can check if the returned object is a nullable object with the test: _m_myservice instanceof Nullable.

Note about Callbacks

Dependency manages two type of callback: bind and unbind. A callback with a type "bind" is called each type that a service provider arrives and the binding is necessary. According to the cardinality of the dependency it means:

...

The method can receive in argument the service object or the service reference (in order to obtain service properties). The bind methods are delayed since a component instance is created.

Note on service

...

interface discovery

The 'interface' attribute is generally optional except when iPOJO cannot discover the type of the service. IPOJO cannot infer the type when the dependency has no field and no callbacks receive the service object in parameter. In this case, you need to declare the service interface.