User Tools

Site Tools


component_factory

Component Factory

Most of the time it is quite sufficient and probably should be so to use Singletons. In an OSGi environment this is the standard type of beans/services used in combination with DI (dependency injection).

But sometimes you need several instances of a component. The default pattern for this is a factory.

OSGi supports this in several ways.

Declarative Services

This service can be used to let the system create instances of components for you. These instances can be retrieve programmatically or via method injection.

Declarative Service also lets you define a factory for a component in a very simple way. You don't have to write the factory yourself. The system will create it for you.

One of the very good parts of OSGi Declarative Services is that your component class can be totally free of OSGi classes.

public class RestServiceClient {

    public Object get() {
        // retrieve object from server
    }
}

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" 
     factory="test.service.client" name="Rest Service Client">
   <implementation class="test.osgi.factory.RestServiceClient"/>
</scr:component>

Just by specifying a factory attribute in the component definition the system will create a factory for it.

Retrieve Component Instance

Most tutorials and articles describe how to write/create/declare a factory but most miss the part of how to retrieve an instance of this factory.

The system will create the factory for you. It is registered as a component itself and implements org.osgi.service.component.ComponentFactory. So you can get the factory by getting this service from the system. But as you can declare many factories there will be many factories registered with this class. You have to specify a criteria which identifies the factory you want. One criteria can be the factory element you specified in the component definition, component.factory=“test.service.client”.

Programmatically Retrieve Component Instance

These are the necessary steps to manually get an instance of a component from a factory.

Getting the service reference for the component factory:

ServiceReference[] serviceReferences = context.getServiceReferences(ComponentFactory.class.getName(), "(component.factory=test.service.client)");

Getting the component factory:

ComponentFactory factory = (ComponentFactory) context.getService(serviceReferences[0]);

Getting an instance from the factory:

ComponentInstance instance = factory.newInstance(null);

In this example null is passed to the newInstance() Method. The parameter specifies a java.util.Dictionary which can hold additional properties which are passed to the ComponentContext which can be accessed by the instantiated component in the activate Method by adding ComponentContext to the method as a parameter.

void activate(ComponentContext context) {
    Dictionary properties = context.getProperties();
    ...
}

I am using OSGi 4.2. More recent versions of the framework provide more elegant ways to work with services.

Component Instances with Properties

There are several ways to add properties to an OSGi component.

Static Properties

One way to add static properties to an OSGi component is to define them in the component definition xml file.

<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" factory="test.service.client" name="Rest Service Client">
   ...
   <property name="extra.property.key" type="String" value="this is the extra property value"/>
</scr:component>

Component Instances with Service References

Anything specified in the component definition xml file will be used on the component instance, not the factory, f. e. properties and service references.

So every service reference entered in the component definition will be used on each component instance.

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" 
   factory="test.service.client" name="Rest Service Client">
   ...
   <reference bind="bindRestClientProvider" cardinality="0..1" interface="test.osgi.factory.RestClientProvider" name="restClientProvider" policy="dynamic" unbind="unbindRestClientProvider"/>
   ...
</scr:component>

Component Instances with Managed Configuration

In an OSGi environment components can be provisioned with a configuration. This is also true for component instances created by a component factory.

public class RestServiceClient implements ManagedService {

    void activate(ComponentContext context) {
        Dictionary properties = context.getProperties();
        System.out.println("activating employee service client with the context: " + context.toString());
    }

    @Override
    public void updated(Dictionary properties) throws ConfigurationException {
        if (properties == null) {
            System.out.println("no configiuration");
        }
        else {
            System.out.println("configuration: " + properties);
        }
    }
}

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" factory="test.service.client" name="Rest Service Client">
   ...
   <service>
      <provide interface="org.osgi.service.cm.ManagedService"/>
   </service>
   ...
</scr:component>

If configured in this way every component instance gets the same configuration and will be updated if this configuration has been changed. It is also possible to create instances for each prepared configuration, see ManagedServiceFactory.

Disposing Component Instances

The created component instances are managed by the OSGi framework. To unregister a component instance the dispose() Method of the class ComponentInstance can be called.

  // get factory
  ComponentFactory factory = (ComponentFactory) context.getService(serviceReferences[0]);

  // get instance
  ComponentInstance instance = factory.newInstance(null);
  
  ...
  // do something
  ...
       
  // unregister component instance
  instance.dispose();       

component_factory.txt · Last modified: 2015/03/16 13:01 by mihael