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(); ... }
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>
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();