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">

...

How to write your iPOJO Handler

This document explains how developers can use iPOJO extensibility mechanism to extend the (primitive) component instance container. Such extensibility mechanism does not require to modify the iPOJO core.

Div
classtoc
Table of Contents
maxLevel4
minLevel2

First, iPOJO concepts are briefly explained. The second section explains the steps to create a handler. The two last sections describes the implementation and the usage of two small example handlers : a Log Handler, logging messages inside the OSGi log service, and a Property Handler, injecting properties values inside fields.

iPOJO Concepts

iPOJO is a service oriented component model aiming to simplify OSGi applications development. iPOJO is based on the POJO concepts. A POJO is a simple Java class without any dependency on its runtime environment. In iPOJO, POJO are encapsulated in a container managing the relation between the POJO and the external world. This container keeps separated the POJO from the external "wild" world. Moreover, this container can be extended, using handlers.
Basically, iPOJO contains two main concepts: component type and component instance. A component type is a type of component. A component type defines its implementation class, its creation policy, and its container. A component instance is a configured instance of a component type. This instance is created with the component type factory. A component instance inherits of all component type characteristics but has a unique name and its own configuration (set of <key, value>).
Above these concepts, iPOJO runtime will manage component type factories and component instances. Each component instance is managed separately (but the factory can delete them).

A component type declares its container configuration. Each component instance owns its container conform to the component type container configuration. An iPOJO container is composed by an InstanceManager, encapsulating the POJO, on which are plugged handlers. A handler manages one non functional concern. Handlers participate to the component instance lifecycle; can interact with the POJO; can manage relations with external entity like database, or other POJOs... For example, a persistence handler may interact with a database to store and inject POJO state, while an administration handler may use JMX to allow remote configuration of instance properties.

Image Added

iPOJO is an extensible model allowing developer to manage other non functional concerns. Indeed, handlers can be developed singly, without modifying the iPOJO core. At runtime, iPOJO looks for each handler needed by a component instance and plugs an instance of each (required) handler on the container. So iPOJO containers are flexible, light and adaptable to each component. When a needed handler cannot be found, the component instance cannot be created.
An external handler is identified by a namespace. This namespace will be used by developers to refer to the external handler (when he configures its component type) and by iPOJO to instantiate the handler object.

Note : iPOJO core contains 6 "core" handlers managing service providing, service dependencies, lifecycle callbacks, lifecycle controller, instance dynamic configuration, and component instance architecture. Theses handlers follow the same rules than external handlers, except that they use the iPOJO default namespace (i.e. org.apache.felix.ipojo).

...

Handler development basis

Fundamentals

As explain above, the handler interacts with the POJO, with the component's container and with the external world (e.g.

...

:

...

other

...

components,

...

services,

...

bundles,

...

the

...

OSGi

...

framework,

...

...).

...

The

...

skeleton

...

of

...

such

...

an

...

agent

...

is

...

defined

...

in

...

iPOJO

...

is

...

defined

...

by

...

the

...

PrimitiveHandler

...

(abstract)

...

class

...

that

...

can

...

be

...

found

...

in

...

the

...

org.apache.felix.ipojo

...

package.

...

You

...

need

...

to

...

implement

...

the

...

three

...

basic

...

lifecycle

...

methods

...

of

...

this

...

class,

...

but

...

you

...

can

...

extends

...

this

...

model

...

by

...

redefining

...

some

...

other

...

methods

...

(e.g.

...

:

...

to

...

intercept

...

POJO

...

method

...

calls,

...

field

...

accesses,

...

...).

Anchor
description
description

Declaring your handler

You first need to declare your handler, so iPOJO will be able to initialize, configure and use it when needed. First, you must give a name and an XML namespace to your handler. By doing that, iPOJO can recognize that a certain component uses your handler, so it can initialize it. You need to declare, in the metadata.xml of the bundle containing your handler, the class name of your handler and its name and XML namespace. You can, of course, declare several handlers, and even declare components using these handlers, in the same bundle.
Then, you must know that a handler is a component (almost) like standard iPOJO components : it can use other handlers (like core handlers : service requirements, provided services, ...). You can consequently describe your handler's required services, provided services, etc. in its metadata.xml, as for classic iPOJO components.

Code Block
xml
xml


{anchor:description}
h3.  Declaring your handler 
You first need to declare your handler, so iPOJO will be able to initialize, configure and use it when needed. First, you must give a name and an XML namespace to your handler. By doing that, iPOJO can recognize that a certain component uses your handler, so it can initialize it. You need to declare, in the {{metadata.xml}} of the bundle containing your handler, the class name of your handler and its name and XML namespace. You can, of course, declare several handlers, and even declare components using these handlers, in the same bundle.
Then, you must know that a handler is a component (almost) like standard iPOJO components : it can use other handlers (like core handlers : service requirements, provided services, ...). You can consequently describe your handler's required services, provided services, etc. in its metadata.xml, as for classic iPOJO components.
{code:xml}
<ipojo>
 ... 
    <handler className="your.handler.class" 
        name="HandlerName" 
        namespace="the.namespace.of.your.handler">
        ...
        <provides interface="a.provided.Service">        
            <property field="a_field" 
                name="a.property" value="a.value"/>
        </provides>
        ...
    </handler>
</ipojo>
{code}

_Note :_ In order to use iPOJO annotations processing, the namespace must be a valid package name and the name must be a valid annotation name (without the 

Note : In order to use iPOJO annotations processing, the namespace must be a valid package name and the name must be a valid annotation name (without the '@').

...

Refer

...

to

...

the

...

#annotations

...

section.

...

Hint

...

:

...

It

...

is

...

a

...

good

...

idea

...

to

...

provide

...

a

...

documented

...

XML

...

schema

...

description

...

(XSD)

...

with

...

your

...

handler

...

to

...

help

...

users

...

to

...

configure

...

your

...

handler

...

and

...

to

...

validate

...

their

...

configurations.

...

Refer

...

to

...

the

...

#xsd

...

section.

...

Handler lifecycle

A handler lifecycle is composed of four different states.

Image Added

  • First, when iPOJO parses the metadata.xml of a bundle, it detects that a certain component type use your handler (using XML qualified names, see the following ìUsing your handlerî section). When it finds such a reference, it initializes the handler by calling the initializeComponentFactory() method. This method should be static but actually can't be so for some technical reasons. Consequently, a ìmockî instance of the handler is created, the initializeComponentFactory() method is called, and this instance is destroyed. This method aims to check the validity of the component type description, avoiding starting invalid factories.If you override this method, you should here set up the component description (e.g. : common properties, exported services, ...) and check the handler configuration. The parameters passed to this method are the ComponentTypeDescription and the component's Metadata (i.e. : the structure of the component type declaration).
  • Once your handler has been initialized, iPOJO configures it for each created instance of the using components. The ComponentTypeDescription and the instance specific properties are passed to the configure() method of your handler.This method is mandatory so you have to implement it. Here you should check the handler configuration (if not already done in the initializeComponentFactory() method) and configure the handler with given instance specific properties.
  • Then, iPOJO starts the handler, following the component instance lifecycle, by calling the start() method. You have to put in this method the activation code of your handler. A freshly started handler is by default in an active state (if all its used handlers, like required services, are in an active state).
  • Once started, the handler state can be either in a valid or in an invalid state, depending on its used handlers (a handler is an iPOJO component, so it can depend on other handlers, like service dependencies, provided services, ... See the ìHandler extends Componentî section). The validity of your handler depends on used handlers status, but it also can be changed in your handler code by using the setValidity() method.
  • Finally, when the component instance is stopped (generally just before being destroyed), the stop method of the handler is called. Place here the inactivation code of your handler.

Note : Keep in mind that the stop() method of your handler is called when the component instance is stopped (not necessarily destroyed). This instance can be restarted later, so the same instance of your handler must have to ability to restart too.

Reading handler and instance configurations

Your handler need to read how it is configured in the using component type description. The configuration is written in the metadata.xml of the using bundle, but is passed to the initializeComponentFactory() and configure() methods as an Element object.

The Element type (placed in the org.apache.felix.ipojo.metadata

...

package

...

),

...

coupled

...

with

...

the

...

Attribute

...

type,

...

is

...

used

...

to

...

retrieve

...

the

...

structure

...

and

...

the

...

content

...

of

...

the

...

component

...

configuration.

...

The

...

Element

...

parameter,

...

passed

...

to

...

the

...

initialization

...

and

...

configuration

...

methods,

...

represents

...

the

...

root

...

of

...

the

...

component

...

type

...

description

...

(i.e.

...

the

...

root

...

of

...

the

...

tree

...

is

...

the

...

component

...

XML

...

tag).

...

Several

...

methods

...

allows

...

to

...

browse

...

the

...

entire

...

configuration

...

from

...

the

...

root

...

Element :

  • The getElement()

...

  • methods

...

  • let

...

  • you

...

  • access

...

  • the

...

  • content

...

  • of

...

  • an

...

  • Element

...

  • (i.e.

...

  • the

...

  • children

...

  • elements)

...

  • The getAttribute()

...

  • methods

...

  • allows

...

  • you

...

  • to

...

  • access

...

  • the

...

  • attributes

...

  • of

...

  • an

...

  • Element

...

  • .

...

  • The containsElement()

...

  • and

...

  • containsAttribute()

...

  • methods

...

  • test

...

  • the

...

  • presence

...

  • of

...

  • a

...

  • child-element

...

  • or

...

  • an

...

  • attribute

...

  • in

...

  • an

...

  • Element

...

  • .

...

Note

...

:

...

As

...

described

...

in

...

the

...

#description

...

section,

...

a

...

name

...

and

...

a

...

namespace

...

are

...

associated

...

to

...

each

...

handler.

...

To

...

safely

...

retrieve

...

the

...

configuration

...

of

...

this

...

handler

...

from

...

the

...

component

...

metadata,

...

you

...

can

...

take

...

inspiration

...

from

...

the

...

following

...

snippet

...

(the

...

componentMetadata

...

variable

...

is

...

the

...

component

...

root

...

Element

...

passed

...

to

...

the

...

initializeComponentFactory()

...

and

...

configure()

...

methods)

...

:

{
Code Block
}
 Element[] handlerConfig = 
           componentMetadata.getElements("HandlerName",
                               "the.namespace.of.your.handler");
{code}

You

...

will

...

also

...

need

...

to

...

read

...

specific

...

instance

...

configuration

...

(properties

...

defined

...

in

...

the

...

instance

...

XML

...

tag).

...

The

...

instance

...

properties

...

are

...

directly

...

passed,

...

as

...

a

...

Dictionary,

...

to

...

the

...

configure()

...

method.

...

With

...

these

...

properties,

...

you

...

can

...

easily

...

allow

...

instances

...

to

...

override

...

some

...

component

...

fixed

...

configuration.

Interacting with the POJO

One of the most interesting features of an handler is the ability to interact with the component's POJO. Indeed, you can intercept method calls and returns, inject values in the POJO's fields...

The getPojoMetadata() method of the PrimitiveHandler class lets you access the structure of the POJO (represented by the PojoMetadata type) without having to use (slow) reflection. It allows you to list all fields and methods of the POJO, and get informations about implemented interfaces and the super-class. The PojoMetadata class implements the following operations :

  • The getInterfaces() method returns the list of implemented interfaces, while the isInterfaceImplemented() methods test if a given interface is implemented by the POJO.
  • The getSuperClass() method returns the name of the class extended by the POJO (or null instead of java.lang.Object).
  • The getField() methods lets you access the fields of the POJO. The returned object is a FieldMetadata that provides informations about a particular field inside the POJO.
  • The getMethod() methods lets you access the methods of the POJO. The returned object is a MethodMetadata that provides informations about a particular method in the POJO.

Once you've retrieved informations about the POJO structure, you can interact with it, via the InstanceManager, accessible in your handler by the getInstanceManager() method. It allows you to register interceptors, that are called before and after POJO method calls or field accesses.

Note : The InstanceManager manages the component instance attached to your handler instance. Thus, it can't be available in the initializeComponentFactory() because this method is run before the creation of any component instance.

You need to implement some of the following methods to intercept fields accesses :

  • The void onSet(Object pojo, String fieldName, Object value) method: This method is called each time a field of the POJO is assigned. The first parameter is the instance of the concerned POJO, the second is the name of the accessed field and the third is the value assigned to the POJO's field. If the field type is a primitive type, this method receives the boxed object.
  • The Object onGet(Object pojo, String fieldName, Object value) method : This method is called each time a field of the POJO is read. The first parameter is the instance of the concerned POJO, the second is the name of the accessed field and the third is the actual value of the POJO's field. If the field type is a primitive type, this method receives the boxed object. The returned object is the value the intercepted read process will return. It's the standard way to inject a value in the field : returning a specific object whatever the field really contains.

You need to implements some of the following methods to intercept methods accesses. When these methods are called, the first parameter is the POJO's instance on which the intercepted method is called and the second parameter contains the descriptor of the called method.

  • The void onEntry(Object pojo, Method method, Object[] args) method: This method is called before the execution of an intercepted method. The third parameter is the list of parameters with which the method have been called. The method is executed just after the execution of the onEntry() callback.
  • The void onExit(Object pojo, Method method, Object returnedObj) method: This method is called right after the successful execution of an intercepted method. The third parameter is the value returned by the method (or null if the method return type is void). This value must not be modified.
  • The void onError(Object pojo, Method method, Throwable throwable) method: This method is called right after the unexpected return of an intercepted method (i.e. when an uncaught exception occurred). The third parameter is the thrown object that caused the method termination.
  • The void onFinally(Object pojo, Method method) method: This method is called after the termination of an intercepted method (expected or not), after the call of the onExit() or onError() callback.

Warning : The InstanceManager has to know your handler wants to intercept fields or methods access, otherwise the implemented callbacks won't be called. Thus you need to register each field and method you want to intercept, so the InstanceManager will call the appropriated callbacks when the specified field or method is accessed :

Code Block


h3.  Interacting with the POJO 
One of the most interesting features of an handler is the ability to interact with the component's POJO. Indeed, you can intercept method calls and returns, inject values in the POJO's fields...

The {{getPojoMetadata()}} method of the PrimitiveHandler class lets you access the structure of the POJO (represented by the {{PojoMetadata}} type) without having to use (slow) reflection. It allows you to list all fields and methods of the POJO, and get informations about implemented interfaces and the super-class. The {{PojoMetadata}} class implements the following operations :

* The {{*getInterfaces()*}} method returns the list of implemented interfaces, while the {{*isInterfaceImplemented()*}} methods test if a given interface is implemented by the POJO.
* The {{*getSuperClass()*}} method returns the name of the class extended by the POJO (or {{null}} instead of {{java.lang.Object}}).
* The {{*getField()*}} methods lets you access the fields of the POJO. The returned object is a {{FieldMetadata}} that provides informations about a particular field inside the POJO.
* The {{*getMethod()*}} methods lets you access the methods of the POJO. The returned object is a {{MethodMetadata}} that provides informations about a particular method in the POJO.

Once you've retrieved informations about the POJO structure, you can interact with it, via the {{InstanceManager}}, accessible in your handler by the {{getInstanceManager()}} method. It allows you to register interceptors, that are called before and after POJO method calls or field accesses.

_Note :_ The InstanceManager manages the component instance attached to your handler instance. Thus, it can't be available in the {{initializeComponentFactory()}} because this method is run before the creation of any component instance.

You need to implement some of the following methods to intercept fields accesses :

* The {{void *onSet*(Object pojo, String fieldName, Object value)}} method: This method is called each time a field of the POJO is assigned. The first parameter is the instance of the concerned POJO, the second is the name of the accessed field and the third is the value assigned to the POJO's field. If the field type is a primitive type, this method receives the boxed object.
* The {{Object *onGet*(Object pojo, String fieldName, Object value)}} method : This method is called each time a field of the POJO is read. The first parameter is the instance of the concerned POJO, the second is the name of the accessed field and the third is the actual value of the POJO's field. If the field type is a primitive type, this method receives the boxed object. The returned object is the value the intercepted read process will return. It's the standard way to inject a value in the field : returning a specific object whatever the field really contains.

You need to implements some of the following methods to intercept methods accesses. When these methods are called, the first parameter is the POJO's instance on which the intercepted method is called and the second parameter contains the descriptor of the called method.

* The {{void *onEntry*(Object pojo, Method method, Object[] args)}} method: This method is called before the execution of an intercepted method. The third parameter is the list of parameters with which the method have been called. The method is executed just after the execution of the {{onEntry()}} callback.
* The {{void *onExit*(Object pojo, Method method, Object returnedObj)}} method: This method is called right after the successful execution of an intercepted method. The third parameter is the value returned by the method (or {{null}} if the method return type is {{void}}). This value must not be modified.
* The {{void *onError*(Object pojo, Method method, Throwable throwable)}} method: This method is called right after the unexpected return of an intercepted method (i.e. when an uncaught exception occurred). The third parameter is the thrown object that caused the method termination.
* The {{void *onFinally*(Object pojo, Method method)}} method: This method is called after the termination of an intercepted method (expected or not), after the call of the {{onExit()}} or {{onError()}} callback.

_Warning :_ The {{InstanceManager}} has to know your handler wants to intercept fields or methods access, otherwise the implemented callbacks won't be called. Thus you need to register each field and method you want to intercept, so the {{InstanceManager}} will call the appropriated callbacks when the specified field or method is accessed :

{code}
 getInstanceManager().register(anInterestingFieldMetadata, this);
 ...
 getInstanceManager().register(anInterestingMethodMetadata, this);
 ...

Note : The PrimitiveHandler abstract class implements the FieldInterceptor and MethodInterceptor interfaces, which declares the methods described just above. You can create your own interceptor class (implementing one or both of these interfaces) and give it to the InstanceManager register method instead of the handler object itself.

Using your handler

Once your handler has been declared, you can use it in iPOJO components. To do so, you first have to be bound to your handler's namespace (using standard XML namespace declaration). Then you can configure the handler in your components type description. An example of bundle's metadata.xml declaring components using the handler is shown hereafter :

Code Block
xml
xml
{code}

_Note :_ The {{PrimitiveHandler}} abstract class implements the {{FieldInterceptor}} and {{MethodInterceptor}} interfaces, which declares the methods described just above. You can create your own interceptor class (implementing one or both of these interfaces) and give it to the {{InstanceManager}} register method instead of the handler object itself.

h3.  Using your handler 
Once your handler has been declared, you can use it in iPOJO components. To do so, you first have to be bound to your handler's namespace (using standard XML namespace declaration). Then you can configure the handler in your components type description. An example of bundle's {{metadata.xml}} declaring components using the handler is shown hereafter :
{code:xml}
<ipojo xmlns:your-shortcut="the.namespace.of.your.handler">
    ...
    <component className="your.component.class">
        ...
        <your-shortcut:HandlerName param1="value1" ...>
            <!-- 
            Configuration of your handler for 
            this component type
             -->
        </your-shortcut{noformat}:HandlerName>
        ...
    </component>
    ...
</ipojo>
{code}

The

...

remainder

...

of

...

this

...

document

...

describes

...

two

...

examples

...

of

...

handlers:

...

  • A

...

  • log

...

  • handler

...

  • logging

...

  • messages

...

  • in

...

  • the

...

  • OSGi

...

  • Log

...

  • Service

...

  • A

...

  • properties

...

  • handler

...

  • reading

...

  • a

...

  • property

...

  • files

...

  • to

...

  • configure

...

  • POJO

...

  • field

...

Log

...

Handler

...

example

...

This

...

section

...

describes

...

how

...

to

...

create

...

a

...

simple

...

handler.

...

This

...

handler

...

logs

...

a

...

message

...

in

...

the

...

OSGi

...

Log

...

Service

...

(if

...

present)

...

when

...

the

...

component

...

instance

...

state

...

changes.

...

The

...

code

...

source

...

of

...

this

...

handler

...

is

...

downloadable

...

here

...

.

Handler metadata

The handler namespace is "org.apache.felix.ipojo.log.handler.LogHandler"

...

.

...

It

...

is

...

also

...

the

...

name

...

of

...

the

...

handler

...

implementation

...

class.

...

You

...

can

...

note

...

that

...

the

...

handler

...

has

...

an

...

optional

...

dependency

...

on

...

a

...

OSGi

...

log

...

service.

...

If

...

no

...

log

...

services

...

are

...

found,

...

a

...

default

...

implementation

...

is

...

used

...

instead.

Code Block
xml
xml

{code:xml}
<ipojo>
    <!-- Declare the handler -->
    <handler 
        classname="org.apache.felix.ipojo.handler.log.LogHandler"  
        name="log"  
        namespace="org.apache.felix.ipojo.log.handler.LogHandler" >
        <!-- The log service dependency -->
        <requires field="m_log"  optional="true"  
        default-implementation=
         "org.apache.felix.ipojo.handler.log.PrimitiveLogService"/>
    </handler>
</ipojo>
{code}

h3.  Handler implementation 
The handler needs to override following methods:
*  {{configure}} : to parse the metadata and load the properties file
*  {{stateChanged}} : to log messages when the instance state changes.

h4.  LogHandler class 
The handler is implemented inside the {{LogHandler}} class in the {{

Handler implementation

The handler needs to override following methods:

  • configure : to parse the metadata and load the properties file
  • stateChanged : to log messages when the instance state changes.

LogHandler class

The handler is implemented inside the LogHandler class in the org.apache.felix.ipojo.handler.log

...

package.

...

This

...

class

...

extends

...

the

...

org.apache.felix.ipojo.PrimitiveHandler

...

class.

...


The

...

handler

...

needs

...

to

...

be

...

notified

...

when

...

component

...

instances

...

becomes

...

valid

...

or

...

invalid,

...

thus

...

it

...

implements

...

the

...

InstanceStateListener

...

interface.

InitializeComponentFactory Method

This method parses and checks the component type metadata. The handler needs a log element from its namespace. According to the result, the configure method can throw an exception or parse the level attribute (to get the logging level).

Code Block


h4.  InitializeComponentFactory Method 
This method parses and checks the component type metadata. The handler needs a log element from its namespace. According to the result, the configure method can throw an exception or parse the level attribute (to get the logging level).
{code}
public void initializeComponentFactory(ComponentTypeDescription typeDesc,
                                       Element metadata)
                                      throws ConfigurationException { 
    
    // Get all Namespace:log element from the metadata 
    Element[] log_elements = metadata.getElements("log", NAMESPACE);  
    if (log elements.length == 1) { // There must be exactly one configuration element.
        Element log_element = log_elements[0]; // There must be a level attribute 
                                               // in the configuration element 
        if(log_element.containsAttribute("level")) { 
            String level = log_element.getAttribute("level"); // Check the value of the 
                                                              // level attribute
            if (level == null) {
                throw new ConfigurationException("No level attribute found in the configuration");
            }
            if (!level.equalsIgnoreCase("info") && 
                !level.equalsIgnoreCase("warning") && 
                !level.equalsIgnoreCase("error")) {
                throw new ConfigurationException("Bad log level specified, "
                + "accepted values are : info, warning, error");
            }
        }
    }
}
{code}

h4.  Configure Method 
This method reads the component description and configures the handler. Then, the handler registers itself to the instance manager to be informed of the component's validity changes.
{code}

Configure Method

This method reads the component description and configures the handler. Then, the handler registers itself to the instance manager to be informed of the component's validity changes.

Code Block
public void configure(Element metadata, Dictionary config)
                                throws ConfigurationException {
   
   // Get the configuration element
   Element log_element = metadata.getElements("log", NAMESPACE)[0];
   
   // Get the level attribute's value
   String level = log_element.getAttribute("level");
   
   // Extract the log level
   if(level.equalsIgnoreCase("info")) { 
       m_level = LogService.LOG_INFO;
    } else if (level.equalsIgnoreCase("warning")) { 
       m_level = LogService.LOG_WARNING;
    } else if (level.equalsIgnoreCase("error")) { 
       m_level = LogService.LOG_ERROR;
    }
    
    m_manager = getInstanceManager(); 
    m_context = m_manager.getContext();
}
{code}

h4

StateChanged Method

This method is called by the instance manager to notify that the component instance state changes. The handler needs to log a message containing the new state.

Code Block
.  StateChanged Method 
This method is called by the instance manager to notify that the component instance state changes. The handler needs to log a message containing the new state.
{code}
public void stateChanged(int state) {
    // Log the state changed events
    if (state == InstanceManager.VALID) { 
        m_log.log(m_level, "The component instance " 
            + m_manager.getInstanceName() + " becomes VALID"); 
    } else if (state == InstanceManager.INVALID) { 
        m_log.log(m_level, "The component instance " 
            + m_manager.getInstanceName() + " becomes INVALID"); 
    }
}
{code}

h3.  Handler packaging 
This handler needs to be packaged inside an iPOJO bundle. The bundle will import the {{

Handler packaging

This handler needs to be packaged inside an iPOJO bundle. The bundle will import the org.apache.felix.ipojo

...

,

...

org.osgi.framework

...

and

...

org.osgi.service.log

...

packages.

...

Handler usage

To use this handler, a component needs to declare an org.apache.felix.ipojo.log.handler.LogHandler:log

...

XML

...

element,

...

with

...

a

...

level

...

attribute.

...

This

...

level

...

attribute's

...

value

...

can

...

be

...

"error"

...

,

...

"warning"

...

or

...

"info"

...

.

...

Here

...

is

...

an

...

usage

...

example:

Code Block
xml
xml

{code:xml}
<ipojo xmlns:log="org.apache.felix.ipojo.log.handler.LogHandler">
     
    <!-- Declare a component using the LogHandler -->
    <component classname="...">
        ...
        <!-- Configuration of the LogHandler ?
        <log:log level="WARNING"/>
     </component>
     ...
</ipojo>
{code}

h3.  Download 
The LogHandler is available [here|http://people.apache.org/~clement/ipojo/tutorials/handler/loghandler.zip]. The archive file contains the handler implementation and a simple component using this handler.

h2.  Properties Handler example 
This section presents a second handler. This handler loads a property file containing field name and initial value. Then it injects and maintains these values inside POJO fields. In this example, only String values are managed.

You can find the sources of this example handler in the {{example/handler/property}} directory of the iPOJO sources.

This handler is always valid, so do not participate to the component instance lifecycle. Moreover, the handler does not need to be notified when the component instance state changed. But, it need to be notified when POJO fields need a value or change their value.

h3.  Handler implementation 
The handler needs to override following methods:

* {{configure}} : to parse the metadata and load the properties file 
* {{stop}} : to store the properties 
* {{onGet}} : to inject a values inside a field 
* {{onSet}} : to obtain the new field value 

h4.  PropertiesHandler class 
The handler is implemented by the {{PropertiesHandler}} class present in the {{

Download

The LogHandler is available here. The archive file contains the handler implementation and a simple component using this handler.

Properties Handler example

This section presents a second handler. This handler loads a property file containing field name and initial value. Then it injects and maintains these values inside POJO fields. In this example, only String values are managed.

You can find the sources of this example handler in the example/handler/property directory of the iPOJO sources.

This handler is always valid, so do not participate to the component instance lifecycle. Moreover, the handler does not need to be notified when the component instance state changed. But, it need to be notified when POJO fields need a value or change their value.

Handler implementation

The handler needs to override following methods:

  • configure : to parse the metadata and load the properties file
  • stop : to store the properties
  • onGet : to inject a values inside a field
  • onSet : to obtain the new field value

PropertiesHandler class

The handler is implemented by the PropertiesHandler class present in the org.apache.felix.ipojo.properties.handler

...

package.

...

The

...

class

...

has

...

several

...

fields:

...

  • The

...

  • properties

...

  • to

...

  • maintain

...

  • (

...

  • m_properties

...

  • )
  • The properties file name (m_file

...

  • )

Note:

...

the

...

file

...

name

...

is

...

the

...

absolute

...

path

...

on

...

the

...

local

...

machine

...

of

...

the

...

file.

Configure Method

This method begins by parsing the component type metadata. The handler needs a properties element from its namespace. According to the result, the configure method can return immediately or parse the file attribute (to get the properties file path). Then, it builds a field list (String array) to register to field notification. By registering with a field array, the handler will be a part of the component instance container and will be notified of field access.

Code Block


h4.  Configure Method 
This method begins by parsing the component type metadata. The handler needs a properties element from its namespace. According to the result, the configure method can return immediately or parse the file attribute (to get the properties file path). Then, it builds a field list (String array) to register to field notification. By registering with a field array, the handler will be a part of the component instance container and will be notified of field access.

{code}
public void configure(Element metadata, Dictionary configuration)
                                             throws ConfigurationException {
    // Parse metadata to get <properties file="$file"/>
    // Get all example.handler.properties:properties element
    Element[] elem = metadata.getElements("properties", NAMESPACE).   
    switch (elem.length) {
        case 0: // No matching element in metadata, throw a configuration error. 
            throw new ConfigurationException("No properties found");
        case 1: // One 'properties' found, get attributes
            m_file = elem[0].getAttribute("file"); 
            if (m_file == null) { // if file is null, throw a configuration error. 
                throw new ConfigurationException("Malformed properties " 
                   + " element : file attribute must be set");
             } 
             break;
         default: // To simplify we handle only one properties element. 
             throw new ConfigurationException("Only one properties element is supported"); 
     }  
     // Look if the instance configuration overrides file location : 
     String instanceFile = (String) configuration.get("properties.file"); 
     if (instanceFile != null) {
         m_file = instanceFile; 
      }
      // Load properties
      try{
          loadProperties();
      } catch(IOException e) {
          throw new ConfigurationException("Error when reading the " 
             +  m_file + " file : " + e.getMessage()); 
      }
      // Register fields 
      // By convention, properties file entries are field name, 
      // so looks for each property to get field list.
      //First get the Pojo Metadata metadata:
      PojoMetadata pojoMeta = getPojoMetadata();
      Enumeration e = m_properties.keys();
      while(e.hasMoreElements()) { 
          String field = (String) e.nextElement();
          FieldMetadata fm = pojoMeta.getField(field);
          if(fm==null){
              //The field does not exist
              throw new ConfigurationException("The field " + field + 
                   " is declared in the properties file but does not " +
                   " exist in the pojo");
          }
          // Then check that the field is a String field 
          if (!fm.getFieldType().equals(String.class.getName())) { 
              throw new ConfigurationException("The field " + field + 
                  " exists in the pojo, but is not a String"); 
          } 
          // All checks are ok, register the interceptor. 
          getInstanceManager().register(fm, this);
      }
  }
}
{code}

h4.  The start and stop methods 
The start method does nothing, but needs to be implemented

The start and stop methods

The start method does nothing, but needs to be implemented.

Code Block
.
{code}
public void start() {}
{code}

The

...

stop

...

method

...

stores

...

properties

...

inside

...

the

...

properties

...

file.

{
Code Block
}
public void stop() { 
    try { 
        saveProperties();
    } catch (IOException e) { 
        // Log an error message by using the iPOJO logger 
        error("Cannot read the file : " + m_file, e); 
    } 
    m_properties = null;
}
{code}

h4.  onGet and onSet methods 
The onGet method is called when the POJO need a field value. When called, the method needs to return the stored value.The onSet method is called when the POJO modifies a field value. If the new value if null, the handler will remove this properties from the property list.
{code}

onGet and onSet methods

The onGet method is called when the POJO need a field value. When called, the method needs to return the stored value.The onSet method is called when the POJO modifies a field value. If the new value if null, the handler will remove this properties from the property list.

Code Block
public Object onGet(Object pojo, String field, Object o) { 
    // When the pojo requires a value for a managed field,
    // this method is invoked. 
    // So, we have just to return the stored value. 
    return m_properties.get(field);
}

public void onSet(Object pojo, String field, Object newvalue) { 
    // When the pojo set a value to a managed field, 
    // this method is invoked. 
    // So, we update the stored value. 
    m_properties.put(field, newvalue);
}
{code}

h3.  Handler packaging 
This handler needs to be inside a bundle importing the {{

Handler packaging

This handler needs to be inside a bundle importing the org.apache.felix.ipojo

...

packages

...

and

...

exporting

...

the

...

org.apache.felix.ipojo.properties.handler

...

package.

...

Handler usage

To use this handler, a component need to declare an Properties XML element in the org.apache.felix.ipojo.properties.handler

...

namespace,

...

with

...

a

...

file

...

attribute

...

indicating

...

the

...

absolute

...

file

...

path

...

of

...

the

...

properties

...

file.

...

Note

...

:

...

you

...

need

...

to

...

escape

...

'\'

...

(anti-slash

...

characters)

...

in

...

the

...

file

...

path

...

name.

Download

The PropertiesHandler is available here. The archive file contains the handler implementation and a simple component using this handler.

Advanced topics

Handler reconfiguration

iPOJO has the ability to reconfigure component instances while they are running. When instances are reconfigured, their used handler need to update their configuration (if they support such an operation).
To do so, reconfigurable handlers must override the reconfigure() method, which notify the concerned handlers of the new instance configuration (represented as a Dictionary).

Describing your handler

Handlers have the possibility to describe their state, overriding the getDescription() method and the HandlerDescription class.
By default, only the handler's name and validity are displayed in component instance's description (informations displayed by the (arch -instance an.instance.name command). The standard way to add description to your handler is shown hereafter :

Code Block


h3.  Download 
The PropertiesHandler is available [here|http://people.apache.org/~clement/ipojo/tutorials/handler/property-handler.zip]. The archive file contains the handler implementation and a simple component using this handler.

h2.  Advanced topics 
h3.  Handler reconfiguration 
iPOJO has the ability to reconfigure component instances while they are running. When instances are reconfigured, their used handler need to update their configuration (if they support such an operation). 
To do so, reconfigurable handlers must override the {{reconfigure()}} method, which notify the concerned handlers of the new instance configuration (represented as a {{Dictionary}}).

h3.  Describing your handler 
Handlers have the possibility to describe their state, overriding the {{getDescription()}} method and the {{HandlerDescription}} class. 
By default, only the handler's name and validity are displayed in component instance's description (informations displayed by the ({{arch -instance an.instance.name}} command). The standard way to add description to your handler is shown hereafter :
{code}
public class YourHandler extends PrimitiveHandler {  
    ... 
    // Method returning the handler description. 
    public HandlerDescription getDescription() { 
        return new YourHandlerDescription(this);
    }
    
    ...
    
    private class YourHandlerDescription extends HandlerDescription {  
        public Description(PrimitiveHandler h) { super(h); }  
        
        // Method returning the custom description of this handler. 
        public Element getHandlerInfo() { 
             // Needed to get the root description element. 
             Element elem = super.getHandlerInfo();  
             // Add here attributes and sub-elements 
             // into the root description element. 
             // Example : elem.addAttribute(new Attribute("param", "value")); 
             Element subElement = new Element("subElement", ""); 
             subElement.addAttribute(new Attribute("subParam", "subValue")); 
             elem.addElement(subElement);
             ...
             return elem; 
       }
   }
}
{code}
{anchor:annotations} 
h2.  

Anchor
annotations
annotations

Handler's

...

annotations

...

Your

...

handle

...

can

...

also

...

provide

...

annotations.

...

Annotations

...

will

...

allows

...

users

...

to

...

configure

...

the

...

Handler

...

from

...

the

...

source

...

code

...

(avoiding

...

XML

...

edition).

...

iPOJO

...

supports

...

annotation

...

of

...

external

...

handlers.

...

Indeed,

...

it

...

detects

...

annotations

...

and

...

re-creates

...

the

...

Element-Attribute

...

structure.

...


So,

...

first,

...

external

...

Handler

...

annotations

...

MUST

...

follow

...

some

...

principles:

...

  • The

...

  • annotation

...

  • package

...

  • must

...

  • be

...

  • the

...

  • Handler

...

  • namespace

...

  • The

...

  • annotation

...

  • name

...

  • must

...

  • be

...

  • the

...

  • Handler

...

  • name

...

  • The

...

  • package

...

  • must

...

  • contain

...

  • either

...

  • the

...

  • 'ipojo'

...

  • or

...

  • the

...

  • 'handler'

...

  • word.

...

So,

...

when

...

iPOJO

...

detects

...

the

...

annotation,

...

an

...

Element

...

is

...

created

...

with

...

the

...

annotation

...

package

...

as

...

the

...

Element

...

namespace

...

and

...

the

...

annotation

...

name

...

as

...

the

...

Element

...

name

...

.

...


Then,

...

'scalar'

...

annotation

...

attributes

...

are

...

mapped

...

to

...

Attribute.

...

Sub-annotations

...

(annotation

...

attribute)

...

are

...

mapped

...

to

...

sub-elements.

...

For

...

example,

...

the

...

annotation

...

for

...

the

...

property

...

handler

...

is:

Code Block
java
java

{code:java}
package org.apache.felix.ipojo.properties.handler;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
public @interface Properties {
    
    String file();

}
{code}

This

...

annotations

...

is

...

put

...

on

...

the

...

class

...

element,

...

and

...

allows

...

setting

...

the

...

property

...

file:

Code Block
java
java

{code:java}
@Component
@Properties(file="/Users/clement/felix/properties/i1.properties")
public class Example {
    ...
}
{code}
 

However,

...

your

...

handler

...

can

...

also

...

provide

...

several

...

annotations

...

to

...

represent

...

Element

...

and

...

sub-elements.

...

Your

...

annotations

...

can

...

also

...

be

...

placed

...

on

...

different

...

code

...

elements

...

(Type,

...

Field,

...

Method).

...

In

...

this

...

case,

...

to

...

recreate

...

the

...

Element/Sub-Element

...

hierarchy,

...

iPOJO

...

processes

...

as

...

following:

...

  • The

...

  • first

...

  • annotation

...

  • of

...

  • a

...

  • package

...

  • P

...

  • is

...

  • processed

...

  • by

...

  • creating

...

  • the

...

  • root

...

  • Element

...

  • (component

...

  • sub-element).

...

  • All

...

  • others

...

  • annotations

...

  • of

...

  • the

...

  • package

...

  • P

...

  • are

...

  • processed

...

  • as

...

  • sub-element

...

  • of

...

  • the

...

  • previously

...

  • created

...

  • Element.

...

For

...

example,

...

the

...

following

...

code:

Code Block
java
java

{code:java}
import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.handlers.jmx.Config;
import org.apache.felix.ipojo.handlers.jmx.Method;
import org.apache.felix.ipojo.handlers.jmx.Property;

@Component
@Config(domain="my-domain", usesMOSGi=false) // External handler annotation
public class JMXSimple {

    @Property(name="prop", notification=true, rights="w") //External handler annotation
    String m_foo;
    
    @Method(description="set the foo prop") //External handler annotation
    public void setFoo(String mes) {
        System.out.println("Set foo to " + mes);
        m_foo = mes;
    }
    
    @Method(description="get the foo prop") //External handler annotation
    public String getFoo() {
        return m_foo;
    }
}
{code}

will

...

be

...

translated

...

to:

Code Block
shell
shell

{code:shell}
component { 
    $classname="org.apache.felix.ipojo.test.scenarios.component.jmx.JMXSimple"
    $public="true" $name="org.apache.felix.ipojo.test.scenarios.component.jmx.JMXSimple"
    org.apache.felix.ipojo.handlers.jmx:config { 
        $usesmosgi="false" $domain="my-domain" 
        org.apache.felix.ipojo.handlers.jmx:property { 
            $rights="w" $notification="true" $field="m_foo" $name="prop" }
        org.apache.felix.ipojo.handlers.jmx:method { 
            $description="set the foo prop" $method="setFoo" }
        org.apache.felix.ipojo.handlers.jmx:method { 
            $description="get the foo prop" $method="getFoo" }
    }
}
{code}

*

Note:

...

To

...

customize

...

this

...

hierarchy,

...

you

...

can

...

also

...

use

...

the

...

id/parent

...

annotation

...

attributse.

...

The

...

id

...

attribute

...

is

...

used

...

to

...

refer

...

to

...

an

...

Element.

...

An

...

annotation

...

with

...

a

...

parent

...

(targeting

...

an

...

id

...

)

...

attribute

...

will

...

be

...

processed

...

as

...

a

...

sub-element

...

of

...

the

...

Element

...

identified

...

by

...

the

...

given

...

id

...

.

...

Anchor

...

xsd
xsd

Handler's

...

XSD

...

Coming

...

soon...

...

Conclusion

In this document,

...

we

...

present

...

how-to

...

develop

...

handler

...

for

...

your

...

components.

...

We

...

describe

...

two

...

small

...

examples

...

:

...

a

...

log

...

handler

...

and

...

a

...

properties

...

handler.

...

These

...

handlers

...

are

...

plugged

...

on

...

(primitive)

...

instance.

...

However,

...

it

...

is

...

possible

...

to

...

extends

...

CompositeHandler

...

too to customize the composition model.

If you develop handler and you want to share it, feel free to contact us by sending a mail on the Felix mailing list.


Include Page
apache-felix-ipojo-footer
apache-felix-ipojo-footer

...