Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3
Include Page
apache-felix-ipojo-header
apache-felix-ipojo-header

HTML

<div class="content">

...

iPOJO Factories Principles

...

iPOJO defines a factory for each declared component type. These factories are used to create component instances. This document presents

...

how to declare instances and configure them. The API to create, dispose and reconfigure instances is also explained.

Div
classtoc
Table of Contents
maxLevel4
minLevel2

Preliminary Concepts

Component Type

A component type is a kind of instance template. If we compare component concepts with object oriented programming, component types are classes and component instances are objects. A component type is declared inside a metadata file (generally named 'metadata.xml'). The next snippet shows you a component type declaration:

Code Block
xml
xml
 
<component className="..." 
factory
name="MyFactory">
    ...
    <!
â€"handler
--component type configuration -
->
    ...
</component>

A component type

...

declaration begins generally by '<component>' and is composed by:

  • An implementation class ('className', mandatory). The 'className' attribute indicates the qualified name of the class implementing the component.
  • A

...

  • name (

...

  • 'name')

...

  • Handler configuration (see handler guide)

The 'name' attribute contains the factory

...

name. If not specified, the 'className' attribute is used as the factory name. This factory name is used to refer to the factory (and consequently to the component type).

...


A factory

...

can

...

be public or private. A public factory allows creating instances dynamically and from other bundles. A private factory can only be used to create instances by declaring them in the same metadata.xml file that defines the instances' component type (i.e. in the same bundle). By default, factories are public. To set the factory to private add the 'public="false"'' attribute in the '<component>' element, such as:

Code Block
xml
xml
 
<component className="..." name="MyPrivateFactory" public="false">
    ...
    <!--component type configuration -->
    ...
</component>

Public factories offer different ways to create instances:

  • Instances can be declared in iPOJO descriptor in any bundle (i.e. from any metadata.xml file)
  • Instances can be created dynamically by using the API. Two APIs are provided the iPOJO Factory API and the OSGi Configuration Admin API
  • The factory attribute contains a name: this name will be used as factory name.
  • The factory attribute contains "no" : the factory will be private
  • The factory attribute is not declared: the class name is used as factory name.

...

Indeed, iPOJO will publish two services to access

...

the factory through the API:

  • org.apache.felix.ipojo.Factory : iPOJO Factory Interface
  • org.osgi.service.cm.ManagedServiceFactory : Config Admin Interface

The factory name will be used

...

as the service.pid property for

...

those services. The service.pid is unique and persists between

...

framework restarts. The service.pid of the factory equals the factory name. This allows identifying factories exposed in the service registry.
Factories are either valid or invalid. You can't create instances until a factory becomes valid. A factory is invalid if required handlers are missing. This issue occurs only if the component type uses external handlers.

Component Instance

A component instance is an instance of a component type. For example, if a component type declares 'providing' and 'requiring' services, then the component instances

...

will actually expose and require those services.

...

Several instances can be created from one factory, but all these instances will be managed as different entities, and

...

so are independent. A component instance is characterized by:

  • a component type (the factory name)
  • an instance name (used to identify the instance, is unique)
  • a configuration : a set of

...

  • <key, value>

...

  • couple

A factory keeps a reference

...

to each created instance

...

. If the factory stops

...

, goes away, or becomes invalid, all created instances

...

are stopped and

...

are destroyed.
To create an instance,

...

the creation request (describing the instance to create) must refer to the factory (by using the factory name), and provide the instance configuration. This configuration can specify the instance name ('instance.name' property). Be aware that this name must be unique. If not specified, iPOJO will generate a unique name.
A factory can refuse the creation if the configuration is not acceptable or if the factory is invalid. An unacceptable configuration is a

...

not suitable

...

configuration in regard to component type. Reasons for unacceptable configuration are:

...

  • The instance name is not set or not unique

...

  • A property required by the component type is missing inside the configuration

...

  • A pushed property has a wrong type

How-to declare instances inside metadata files

...

The main way to create instances is to declare those instances inside the iPOJO descriptor file (i.e. 'metadata.xml'). Those declarations can use either public factories from any bundle, or private factories from the same bundle. Private factories are generally used to guaranty singleton instance as instances can only be created inside the same bundle.
When a instance declaration targets an external public factory, it will wait until the factory becomes available. So, the instance will be created only when the factory appears and is

...

valid.

...

If the factory

...

disappears after the instance

...

creation, the instance is disposed and will be recreated as soon as the factory comes back.
The next snippet shows how to declare an instance in the metadata:

Code Block
xml
xml

<instance component="component factory name" name = "instance name" >
   <property name="a property name" value="a string form of the value"/>
   <property name="another 
property
prop name" value="the string form 
of the wanted
value 
value
"/>
</instance>
The component type attribute contains the targeted factory name. The name attribute declares the instance name. The instance configuration is set by declaring a set of properties. A property is composed by a name and a value. The value is the string form of the wanted value. The iPOJO runtime will create the property object with this string.

An instance declaration must contain the 'component' attribute. This attribute specifies the factory name (i.e. the component type). It can use either the factory name or the class name. The 'name' attribute allows setting the instance name. If not set, iPOJO will generate a unique name for you. Then, instances can declare properties. Those property are mostly key-value pair. The key refer to a property name from the component type declaration such as in:

Code Block
xml
xml

<component className="..." name="my-factory">
    <properties>
        <property name="foo" field="m_foo"/>
    </properties>
</component>
<instance component="my-factory ">
   <property name="foo" value="bla bla bla"/>
</instance>

The string-form of the property value will be use to create the real object at runtime. If an unacceptable configuration is set, the instance is not created, and an error message appears to the console (and in the Log Service if present).
Instance declaration properties can be more complex than only 'key-value'.

...

It can contains structure like list, map, dictionary and arrays such as in:

Code Block
xml
xml

<instance component="a.factory" name="complex-props">
        <!--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 contains any 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"/>
</instance>
Info
titleInstance name and factory name

The instance.name and factory.name property should not be set directly. iPOJO will manage those properties. The instance.name is simply created from the instance.name attribute of the instance declaration or from the factory-name-X where X is an integer.

Creating, disposing and reconfiguring instances with the API

How-to use iPOJO factory service

...

A public factory is accessible through an exposed service (

org.apache.felix.ipojo.Factory

...

). This service is accessible as any other

...

OSGi service, and could be an iPOJO dependency

...

using a LDAP filter or the 'from' attribute such as in:

java public interface Factory {
     <!-- These two requirement descriptions are equivalent -->
	<requires field="a_field" filter="(factory.name=factory-name)"/>
	<requires field="another_field" from="another-factory"/>
</component>
Code Block
xml
xml

<component classname="...">
   
java
titleFactory.java
borderStylesolid

Creating instances

Once you have a reference on the factory you can create instance with the 'createComponentInstance' method.

Code Block

ComponentInstance createComponentInstance(java.util.Dictionary configuration)
 
/** * Create an instance manager (i.e. component type instance).
     
*
  
@param
 
configuration
 
:
 
the
 
configuration
 
properties
 
for
 
this
 
component.
     
*
 
@return
 
the
 
created
 
instance
 
manager.
     
*
 
@throw
 
UnacceptableConfiguration
 
if
 
the
 
given
 
configuration
 
is
 
not
 
consistent
 
for
 
the
throws 
factory
UnacceptableConfiguration,
     
*/
       
ComponentInstance
 
createComponentInstance(Dictionary
 
configuration)
 
throw
 
UnacceptableConfiguration;
    
/**
     
*
 
Create
 
an
 
instance
 
manager
 
(i.e.
 
component
 
type
 
instance).
     
*
 
This
 
has
 
these
 
service
 
interaction
 
in
 
the
 
scope
 
given
 
in
 
argument.
MissingHandlerException,
            
* @param configuration : the configuration properties for this component.
                     
*
 
@param
 
serviceContext
 
:
 
the
 
service
 
context
 
of
 
the
 
component.
     
*
 
@return the created instance manager. * @throw UnacceptableConfiguration if the given configuration is not consistent for the factory */ ComponentInstance createComponentInstance(Dictionary configuration, ServiceContext serviceContext) throw UnacceptableConfiguration; /** * Get the component type information containing provided service, configuration properties ... * @return the component type information. */ ComponentDescription getComponentDescription(); /** * Check if the given configuration is acceptable as a configuration of a component instance. * @param conf : the configuration to test * @return true if the configuration is acceptable */ boolean isAcceptable(Dictionary conf); /** * @return the name of the factory. */ String getName(); }

The factory service contains two methods to create instances:

  • With a service context: this method is only used with composite, and should not be used.
  • Without the service context: the method takes in argument a Dictionary containing the configuration. One of the contained properties must be the instance name (the property key must be "name").
An instance is started automatically when created. However, the instance can be invalid, if handlers are not valid). To destroy an instance, invoke the stop method on the instance
 ConfigurationException

This method returns a reference on the created instance. As you see, the method receives a dictionary containing the instance configuration. This configuration contains key-value pairs. However, values are either object (of the adequate type) of String used to create objects. This configuration can be 'null' if no properties have to be pushed.

Info
titleInstance Name

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:

  • UnacceptableConfiguration means that mandatory properties are missing in the instance configuration
  • MissingHandlerException means that the factory is not valid (i.e. an external handler is missing)
  • ConfigurationException means that the instance configuration has failed. The cause can be either an issue in the component type description or an invalid property type.

If an error occurs, a comprehensive message is reported in order to solve the issue.
The next snippet shows an example of instance creation:

Code Block

  	// 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).

Code Block

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.

Code Block

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

Accessing services exposed by created instances

You can obviously access services exposed by an instance that you create.
To do this just use the OSGi API and the bundle context in order to query service references in the service registry such as in

Code Block

    ComponentInstance instance = ...
    // ... 
    try {
        ServiceReference[] refs = 
              context.getServiceReferences(YourServiceInterface.class.getName(), 
              "(instance.name=" + instance.getInstanceName() +")");
        if (refs != null) {
            Foo your_object = (Foo) context.getService(refs[0]);
        }
    } catch (InvalidSyntaxException e) {
        // Should not happen
    }

The LDAP filter allows selecting the service provided by your instance. Be care that this service can be not accessible if the instance is not valid. Once you get the service reference, you can ask the service registry to get the service object (i.e. the object contained in your instance).

If your instance does not provide services, you can access to the instance by following principles illustrated in the next snippet:

Code Block

    if (instance.getState() == ComponentInstance.VALID) {
       ImplementationClass object = 
          (ImplementationClass) ((InstanceManager) instance).getPojoObject();
    } else {
       System.out.println("Cannot get an implementation object from an invalid instance");
    }

Take care to check the instance state before accessing the object. Indeed, the behavior of an invalid instance is not guaranty. The 'getPojoObject' method will return an already created implementation (pojo) object or create a new one (if none already created).

How to use the ManagedServiceFactory to create, disposed and reconfigure instances

The principle of the ManagedServiceFactory is the same as the iPOJO Factory Service.

...

So, you can create, dispose and reconfigure instances with the Configuration Admin. For further information

...

, read the OSGi R4.x Compendium - Configuration Admin chapter.

Conclusion

This document has presented how to use iPOJO factories. If you have remarks or comments, do not hesitate do send me en email to clement.escoffier@gmail.com

Be aware that the updated method is used both for instance creation (if the given configuration is new) and to reconfigure an existing instance. The deleted method is used to dispose instances.

...

width20%

...

Include Page
apache-felix-ipojo-

...

footer

...

apache-felix-ipojo-

...

footer