Creating instances

Component types and instances

iPOJO is a component model enforcing strictly the distinction between component types and instances. The relationship between types and instances is the same as the one between classes and objects in OOP. You can instantiate as many instances as you want from a component type. These instances can be configured with different properties. As a consequence, just using @Component or declares a component type, not an instance. So, at runtime, nothing will happen until you actually declare or create an instance.

iPOJO provides several ways to create instances, this page presents these possibilities:

@Instantiate

The @Instantiate annotation is the simplest way to declare an instance. In a class annotated with @Component, you just add the @Instantiate annotation. It instructs iPOJO to declare an instance of the component type described by the current class.

import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Instantiate;

@Component
@Instantiate
public class MyComponent {
    //...
}

Optionally, you can set the instance name, using the name attribute.

import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Instantiate;

@Component
@Instantiate(name="myInstance")
public class MyComponent {
    //...
}

The @Instantiate annotation is easy to use but, has a couple of limitations:

Declaring a singleton instance

The @Instance annotation is particularly useful to declare singleton instances, i.e. a component type with only one instance. To create a singleton instance, combine the @Instantiate annotation and the publicFactory attribute of the @Component annotation:

import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Instantiate;

@Component(publicFactory=false)
@Instantiate
public class MySingleton {
    //...
}

The publicFactory=false attribute makes the component type private, so invisible from other bundles. No one else would be able to create another instance of the component type.

@Configuration

This second annotation is used on classes to create one or several instances using a fluent API.

import org.apache.felix.ipojo.configuration.Configuration;
import org.apache.felix.ipojo.configuration.Instance;


import static org.apache.felix.ipojo.configuration.Instance.instance;
@Configuration
public class ConfigureOneInstance {

    Instance myInstance = instance().of(MyComponent.class)
            .with("property").setto("value");
}

The class is annotated with @Configuration. All fields of type Instance (org.apache.felix.ipojo.configuration.Instance) are read and declares an instance. In the previous example, an instance of the MyComponent component type is declared with a property property set to value. The instance is named myInstance (the field name).

You can declare several instances in the same @Configuration class:

@Configuration
public class ConfigureTwoInstances {

  // Declare an instance of MyComponent named myInstance
  Instance myInstance1 = instance().of(MyComponent.class)
          .with("property").setto("value");

  // Declare an instance of MyComponent
  Instance myInstance2 = instance().of(MyComponent.class)
          .with("property").setto("value2");

  // Declare an instance of AnotherComponent
  Instance myInstance2 = instance().of(AnotherComponent.class);
}

By default, the instance name is set to the field name. However, you can also set the instance name:

// Declare an instance of MyComponent named hello
Instance myInstance2 = instance().of(MyComponent.class)
        .named("hello")
                    .with("property").setto("value");

Using this configuration DSL allows creating a set of instances that you can configure easily.

Configuration including lists and maps

The setto method accepts any object. To ease creating collections, the API proposed two methods to handle lists and maps:

instance()
        .of(Mycomponent.class)
        // Lists    
    .with("list").setto(list(1, 2, 3))
    .with("list2").setto(list().with(1).with(2).with(3))
    // Maps
    .with("map").setto(map().with(pair("entry", "value")))
            .with("map2")
                .setto(map()
                    .with(entry("key", 1), entry("key2", 2)));

Methods returning Instance objects

The class annotated with @Configuration does not only handle fields, but also handles methods returning Instance object. These methods can have either no arguments or the BundleContext as unique argument.

Instance instance1() {
    return instance().of(MyComponent.class);
}

Instance instance2(BundleContext bc) {
    return instance().of(MyComponent.class);
}

As for fields, the method name is used as instance name except if the instance already received a name.

Note: the injected BundleContext is the BundleContext of the bundle containing the annotated class.

Declaring instances in XML

You can declare instances using the iPOJO XML descriptor. If you use XML to describe you component type, you probably want to use this way to create your instances.

    <instance component="factory.name">
    <property name="property" value="value"/>
      <property name="another property" value="another value"/>
    </instance>

The component attribute specifies the targeted component type. Generally it's the qualified classname of the component class, but can also be the name of the factory if one is specified.

The property elements have a mandatory name attribute to set the property name, and a value attribute to specify the String form of the property's value.

You can declare as many as you want instances in the XML descriptor. They can targets component types declared within the same bundles or not.

Setting the instance name

To set the instance name you can use the name attribute of the instance element or the instance.name property:

<instance component="…MyComponent" name="my-instance"/>
<instance component="…MyComponent">
    <property name="instance.name" value="my-instance-2"/>
</instance>

Describing complex properties in XML

The property element can be used to configure complex types such as arrays, lists and maps.

<!--Creates a string array-->
<property name="array" type="array"> 
    <property value="a"/>
      <property value="b"/>
</property>
<!--Creates a list containing string-->
<property name="list" type="list"> 
    <property value="a"/>
      <property value="b"/>
</property>
<!--Creates a dictionary containing string-->
<property name="dict" type="dictionary">
    <property name="a" value="a"/>
      <property name="b" value="b"/>
</property>
<!--Creates a map containing string-->
<property name="map" type="map"> 
    <property name="a" value="a"/>
      <property name="b" value="b"/>
</property>
<!--A complex type can contain other complex objects:-->
<property name="complex-array" type="array">
    <property type="list">
        <property value="a"/>
          <property value="b"/>
    </property>
    <property type="list">
        <property value="c"/>
          <property value="d"/>
    </property>
  </property>
<!--Empty structures will create empty objects-->
<property name="empty-array" type="array"/>
<property name="empty-list" type="list"/>
<property name="empty-map" type="map"/>

Creating instances using the Factory service

In previous technics to create instances were declarative. You declare an instance. This instance is going to be created as soon as the component type becomes available, and disappears as soon as the component type leaves. The technic presented here is a programatic way.

Each (non private) component types are exposed as an OSGi service. You can use this OSGi service to create, reconfigure and dispose instances from your code.

The Factory service

The published service interface is [org.apache.felix.ipojo.Factory](http://felix.apache.org/ipojo/api/1.12.1/org/apache/felix/ipojo/Factory.html) and provides the following methods:

/**
 * Creates an instance manager (i.e. component type instance).
 * @param configuration the configuration properties for this component.
 * @return the created instance manager.
 * @throws UnacceptableConfiguration if the given configuration is not valid.
 * @throws MissingHandlerException if an handler is missing.
 * @throws ConfigurationException if the instance configuration failed.
 */
ComponentInstance createComponentInstance(Dictionary configuration) throws UnacceptableConfiguration, MissingHandlerException, ConfigurationException;

/**
 * Reconfigures an instance already created. This configuration needs to
 * have the name property to identify the instance.
 * @param conf the configuration to reconfigure the instance. The instance.name property must be set to identify the instance to reconfigure.
 * @throws UnacceptableConfiguration  if the given configuration is not consistent for the targeted instance.
 * @throws MissingHandlerException if an handler is missing.
 */
void reconfigure(Dictionary conf) throws UnacceptableConfiguration, MissingHandlerException;

You can identify the factory using the factory.name. So target a specific component type, use the following filter:

    (factory.name=...MyComponent)

If you grab all factories, you can check their names using the getName() method.

Creating instances

Once you have the right Factory service, you can create instances using createComponentInstance method. This method returns a reference on the created instance. This method receives an optional configuration containing key-value pairs. Values are either objects (of the adequate type) or Strings used to create objects. This configuration can be 'null' if no properties have to be pushed.

You can set the instance name using the 'instance.name' property can be used to specify the instance name.

Instances are automatically started when created. However, the instance can be invalid, if at least one handler is not valid.

The instance creation process can fail. Three exceptions can be thrown during the creation:

If an error occurs, a comprehensive message is reported in order to solve the issue.

The next snippet shows an example of instance creation:

// Assume we get a Factory in the `fact` field
Properties props = new Properties();
props.put("instance.name","instance-name");
props.put("foo", "blablabla");
try {
    instance = fact.createComponentInstance(props);
} catch(Exception e) {
   fail("Cannot create the instance : " + e.getMessage());
}

Disposing created instance

You can only disposed instances that you created. To dispose an instance, just call the dispose method on the ComponentInstance object (returned by the createComponentInstance method).

instance.dispose();

Reconfiguring instance

To reconfigure an instance, call the 'reconfigure' method on the ComponentInstance object. This method receives the new set of properties. Be aware that the 'instance.name' property cannot be changed.

Properties props2 = new Properties();
props2.put("foo", "abc");
instance.reconfigure(props2);

Following the factory state

Factories can becomes invalid if one of the handler they require is not available. Basically, handlers are pieces of iPOJO containers.

You can check the factory state using the Factory.getState() method. This method returns 1 if the factory is valid, 0 if not.

You can also register a org.apache.felix.ipojo.FactoryStateListener object on the factory to be notified of the changes.

Creating instances using the OSGi Configuration Admin

The configuration admin service is a standard service specified by the OSGi Alliance to handle configurations. It allows an operator to configured the deployed bundles, and so iPOJO instances.

iPOJO supports the configuration admin and you can create, reconfigure and dispose instanced using this service.

Creating instances

Creating an instance is done by creating a factory configuration:

ConfigurationAdmin admin = ...// Let's assume with have the configuration admin
Configuration conf = admin.createFactoryConfiguration("...MyComponent", "?");

// Build the instance configuration
Dictionary dict = new Hashtable();
//...

// Push the configuration to the configuration admin
conf.update(dict);

To create the factory configuration, use the createFactoryConfiguration method on the Configuration Admin object. The first argument is the factory name. The second is the location binding. Using "?" is a wildcard, for more details, check the configuration admin specification.

You populate this configuration with a dictionary. The configuration is actually created using the update method.

Reconfiguring instances

If the instance was created using the Configuration Admin and you own the Configuration object used for the creation, the reconfiguration is done by calling the update method with the new properties.

If the instance was already created, you can configure it using a regular configuration. The pid given to this configuration is the instance name.

Disposing instances

To dispose an instance, just call the delete method on the configuration object you used to configure the instance.

Creating instances with declarations

Declarations offer a nice way to declares instances in a programmatic way. If not retracted by hand, they're bound to the declaring bundle lifecycle (i.e. are unregistered when the bundle is not ACTIVE anymore).

Declarations can be build using the DeclarationBuilderService (see interface below). Instances (of components), types (components) and iPOJO extensions can also be build using this service.

public interface DeclarationBuilderService {
    InstanceBuilder newInstance(String type);
    InstanceBuilder newInstance(String type, String name);
    InstanceBuilder newInstance(String type, String name, String version);
    DeclarationHandle newExtension(String name, FactoryBuilder builder);
    DeclarationHandle newType(Element description);
}

Instances created through declaration can indeed be configured.

// 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();

The builder ultimately produces handles to declarations. Handles are the live link to the underlying declarations: service publication and un-registration are done through the handle.publish() and handle.retract() methods. Declaration status (is it bound or not) is also accessible with handle.getStatus().