Versions Compared

Key

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

The dependency handler manages OSGi service dependencies/requirements. It allows a component to consume service without managing service discovery, tracking and binding. The handler manages all this interaction and injects required service in the component.

Service Requirement

What's a service requirement?

A requirement represents a required service. Therefore, it manages the service lookup and the service binding. When an instance requires a service, the handler injects directly a service object inside a field, or invokes a method when a consistent service appears (or disappears). Service requirements can be:

  • 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

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:

...

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:

...

The metadata contains a 'requires' element (representing the service dependency). This element has a field attribute. This attribute is the name of the field representing the service dependency in the implementation class. The implementation uses the field as a normal field without managing service interactions.

Method invocation

The second injection mechanism uses methods in the implementation class. By this way, the dynamics can be managed directly by the developer. Each dependency declares two methods:

...

(see note on type discovery)

Field injections and Method invocations

The two mechanisms can be merged together. In this case, the field receives the value before the bind method invocation.

...

Code Block
<component classname="...HelloConsumer">
<requires  field="m_hello">
    <callback type="bind" method="bindHello">
    <callback type="unbind" method="unbindHello">
</requires>
...
</component>

Injection mechanisms & lazzy object creation

IPOJO creates objects only when required. When needed, iPOJO invokes the constructor of the implementation class. The implementation class can use field requirement because values are already injected. However, method dependencies are called just after the constructor. If the service already presents, the invocation of the methods are delayed after the constructor invocation.

Some Examples

Simple Requirement

By default, a requirement is mandatory, non-filtered and simple (non-aggregate). The two previous examples illustrate this kind of dependency. When services goes away and appears, the service substitution is hidden.

Field attached to simple requirement points always a consistent service object. For a simple dependency, the bind method is called once time at the beginning. If the service disappear the unbind method is called. The bind method is re-invoked as soon as another service provider is available. If another service provider is presents when the used one disappears, the instance is not invalidated.

Aggregate Requirement

When a component requires several providers of the same service, it declares an aggregate dependency.

Aggregate Dependency with field injection

Code Block
public class HelloConsumer {
     private Hello m_hellos[];
     public doSomething() {
             for(int I = 0; I < m_hellos.length; i++) { System.out.println(m_hellos[i].getMessage());}
       }
}

...

Note: The synchronization is managed by iPOJO. As soon as you are 'touching' a dependency in a method, iPOJO ensure that you will keep these objects until the end of the method. Imbricated methods will share the same service object set.

Aggregate Dependency with method invocation

Code Block
public class HelloConsumer {
      private List m_hellos= new ArrayList();
      private void bindHello(Hello h) { m_hellos.add(h); }
      private void unbindHello(Hello h) { m_hellos.remove(h); }
      public synchronized doSomething() {
                     for(int I = 0; I < m_hellos.size(); i++) { System.out.println(m_hellos.get(i).getMessage());}
                }
        }
}

...

Note: To avoid the list modification during the loop, you need synchronized the block. Indeed, as the field is not an iPOJO requirement, iPOJO will not manage the synchronization.

Optional Dependency (non-aggregate)

An optional requirement does not invalidate the instance if it is not resolved.

Optional Requirement with field injection

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

...

To declare an optional requirement, you need to add the 'optional' attribute. To avoid null pointer exception, iPOJO injects a Nullable object in the field when no service provider is available. The nullable object implements the service interface, but does nothing. For further information see the note about nullable object.

Optional Dependency with method invocation

Code Block
public class HelloConsumer {
     private Hello m_hello;
     public void bindHello(Hello h) { m_hello = h; }
     public void unbindHello() { m_hello = null; }
     public doSomething() { if(m_hello != null) { System.out.println(m_hello.getMesage()); } }
}

...

As for field requirement, the dependency metadata needs to contain the optional attribute. IPOJO invokes the method only when a 'real' service is available, so you need to test if m_hello is null before to use it.

Aggregate & Optional Dependency

A dependency can be both aggregate and optional.

Aggregate & Optional Dependency with field injection

Code Block
public class HelloConsumer {
     private Hello m_hellos[];
     public doSomething() {
           for(int I = 0; I < m_hellos.length; i++) { System.out.println(m_hellos[i].getMessage());}
     }
}

...

To declare an optional & aggregate field requirement you need to write the optional attribute in the dependency metadata and to point on a field array. If no service available, iPOJO injects an empty array.

Aggregate & Optional Requirement with method invocation

Code Block
public class HelloConsumer {
     private List m_hellos<Hello> = new ArrayList<Hello>();
     private void bindHello(Hello h) { m_hellos.add(h); }
     private void unbindHello(Hello h) { m_hellos.remove(h); }
     public doSomething() {
        synchronized(m_hellos) {
               for(int I = 0; I < m_hellos.size(); i++) { System.out.println(m_hellos.get(i).getMessage());}
        }
     }
}

...

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:

...

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 :

...

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.

...

You 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 type 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.