Using the Factory service interface¶
When you declare a component either with @Component
or <component>
, you are instructing iPOJO to create a factory.
A factory is an entity managed by iPOJO and responsible for the instance creation and management. Indeed, every time you declare an instance with @Instantiate
, <instance>
or the configuration admin, you are asking the appropriate factory to create the instance an manage the instance.
Private vs. Public factories¶
When you are declaring the component (type), you have the choice between making this factory public or private. Private factories are not accessible, and can only create instances declared from the same bundle. Public factories are accessible from anywhere, and so are able to create instances declared by anyone. They also expose the org.apache.felix.ipojo.Factory
service.
By default, factories are public. However, you can hide them with:
@Component(publicFactory=false)
The Factory service¶
Every public factory is exposed as an OSGi service. The org.apache.felix.ipojo.Factory service interface allows you to create and manage instance programmatically.
Finding and using a Factory service¶
Let's imagine you have the following class:
@Component(name="my-factory") public class MyImplementation { public MyImplementation(@Property String message) { //... } //... }
Once packaged and deployed, iPOJO finds it and creates the factory object and registers it in the OSGi service registry (because this factory is public). However, to find the right Factory service from within the OSGi service registry, you must a filter to distinguish this factory from the other ones. Fortunately, iPOJO exposed a service property allowing this distinction: factory.name
. In the previous example you can retrieve the factory service with:
@Requires(filter="(factory.name=my-factory)") private Factory factory;
When no name is provided (in the @Component
annotation), the qualified class name is used as factory name.
Retrieving factory information¶
Once you have the factory
object, you can get a couple of additional metadata. The factory name can be retrieved with getName
. Additionally, the factory version is retrieved with the getVersion
method.
The current factory state is accessible with the getState
method. A factory can either be VALID
or INVALID
. A factory is valid only and only if all the required handlers are available (don't worry, you don't really need to know this definition). In other words, when a factory is not valid, it just means that a handler is not there (the bundle is not deployed).
By using the getComponentDescription()
method, you can retrieve additional information about the factory. Check the ComponentTypeDescription
API for more information.
To illustrate factory's state retrieval, let's look at the following code displaying the name, version and state of all the available factories.
@Requires private Factory[] factories; public void dump() { for (Factory factory : factories) { String version = factory.getVersion() != null ? factory.getVersion() : ""; String state = factory.getState() == Factory.VALID ? "valid" : "invalid"; System.out.println("Factory " + factory.getName() + " " + version + " " + state); } }
Creating an instance¶
Factory services are generally used to create instances. The createComponentInstance
method let you create instances. It receives a configuration object (potentially null
):
public void createMyInstance() throws Exception { ComponentInstance instance = factory.createComponentInstance(null); //... }
The createComponentInstance
method returns a ComponentInstance object. This object is representing the created instance, and let you manage it (to reconfigure it or to dispose it).
The configuration you can pass to the createComponentInstance
method let you configure the instance properties. For example, you can gives a value to the message
property (From the component example):
public void createMyInstance() throws Exception { Dictionary<String, Object> configuration = new Hashtable<String, Object>(); configuration.put("message", "hello"); ComponentInstance instance = factory.createComponentInstance(configuration); //...
}
To give a name to your instance, set the instance.name
property.
Getting created instances¶
The Factory service also allows you to retrieve the created instances:
public void listInstancesFromFactory(Factory factory) { for (ComponentInstance instance : factory.getInstances()) { System.out.println(instance.getInstanceName()); } }
Deleting instances¶
You can't delete an instance directly from the factory object. However, you can from the ComponentInstance
object. In the case you already have this object, just call the dispose()
method. Otherwise, retrieve the component instance from the factory object and then call the dispose()
method.
Observing the factory state¶
As stated above, factory objects have a state. We already saw how to retrieve the current state of the factory. You can also register a FactoryStateListener to be notified when the state changes. The methods addFactoryStateListener
and removeFactoryStateListener
let you respectively register and unregister the listener.
Declaration publication vs. Programmatic creation¶
This page has explained how to create an instance from the Factory service. However, when the factory leaves or become invalid your instance is disposed. The instance is not recreated when the factory comes back or is revalidated.
To avoid to have to listen and manage these events, you can use an InstanceDeclaration.
An instance declaration is a service you publish to instruct iPOJO to create an instance
and manage its lifecycle. InstanceDeclaration
are created through the DeclarationBuilderService
(since 1.12):
// Obtain the service through the service registry DeclarationBuilderService service = ... // Get a fresh instance builder InstanceBuilder builder = service.newInstance("my-factory"); DeclarationHandle handle = builder.name("a-unique-name") // Make sure name is unique for the expected type .configure() .property("a-property", "a-value") .property("another-property", "another-value") .build(); // Push the InstanceDeclaration service in the registry handle.publish();
In this snippet, we've registered a named InstanceDeclaration
service for the my-factory
component type, we've
created a handle though an InstanceBuilder
object. The builder helps to configure and produce an
InstanceDeclaration that is controllable through the handle. By default, the declaration service will be registered with
the BundleContext of the user (this is configurable with the context(BundleContext)
method). Once
created, call the publish
method to register the declaration service and let iPOJO do its usual work.
Call the retract
method on the handle to dispose the service.