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.