How to write your iPOJO HandlerThis document explains how developers can use iPOJO extensibility mechanism to extends the (primitive) component container. This tutorial shows two small examples: a Log Handler logging messages inside the OSGi log service and a Property Handler injecting properties values inside fields. iPOJO ConceptsiPOJO is a service 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 separate the POJO from the external world. Moreover, this container is flexible and extensible. 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 ... 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 will look for each handler needed by a component instance and plug an instance of each (needed) 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 management failed. An external handler is identified by a Namespace. This namespace matches with the class name (qualified name) implementing the handler behavior. 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 5 "core" handlers managing service providing, service dependencies, lifecycle callbacks, instance dynamic configuration and component instance architecture. Theses handlers follow the same rules than external handlers, except that they don't have a namespace. Handler development first stepsGo Go Go!To create a handler, you need to create a Bundle. To know how to create bundles see: http://cwiki.apache.org/confluence/display/FELIX/Bundle+Plugin+for+Maven+%28BND%29 Your bundle will contain a class extending the "org.apache.felix.ipojo.Handler" class. All handler need to extends this class. This class defines three abstracts methods: configure, start and stop. This method refers to the component instance lifecycle:
Before, implementing the handler, you need to answer to some questions:
According to theses answers, your handler class needs to parse the component type metadata, and select which method it needs to override from the Handler class. Your resulting bundle needs to export the package containing your handler class. Handlers class methodsThis section will explain the handler class. According to this description, you can select which method you want to override.
Log Handler exampleThis section describes how to create a simple handler. This handler logs a message in the OSGiâ„¢ log service when the component instance state changes. It participates to the component instance lifecycle (the handler in not valid when there is no log service available). Handler metadataThe handler namespace is "org.apache.felix.ipojo.log.handler.LogHandler". It is the handler implementation class too. A developer who wants to use the handler will configure its component type with the following metadata: <iPOJO xmlns:log= org.apache.felix.ipojo.log.handler.LogHandler> <component className="..."> <log:Log level="error"/><!- - error, warning or info accepted - -> </component> <instance component="..." name="LogHandlerTest-1"/> </iPOJO> Handler implementationThe handler needs to override following methods:
Log Handler classThe handler is implemented inside the "org.apache.felix.ipojo.log.handler.LogHandler" class. This class extends the "org.apache.felix.ipojo.Handler" class. This class has the final static string field "NAMESPACE", with the "org.apache.felix.ipojo.log.handler.LogHandler" value. The handler needs the Log Service. So it will implement the "org.osgi.framework.ServiceListener" interface too. The class has several other fields:
Note: Handler code is OSGiâ„¢ standard code. A handler cannot be a POJO. Configure MethodThis method begins by parsing the component type metadata. The handler needs a log element from its namespace. According to the result, the configure method can return immediately or parse the level attribute (to get the logging level). Then, the handler stores some value inside field as the instance manager and the bundle context (note how your handler can access to the bundle context). To finish, the handler must register itself to the instance manager to become a part of the instance container. LogHandler.java:Configure Method public void configure(InstanceManager im, Element metadata, Dictionary config) { // First parse the metadata to check if the log handler level // Get all Namespace:log element from the metadata Element[] log_elements = metadata.getElements("log", NAMESPACE); // if no element found, return if(log_elements.length == 0) { return; } else { // If an element match, parse the level attribute of the first element if(log_elements[0].containsAttribute("level")) { String l = log_elements[0].getAttribute("level"); if(l.equalsIgnoreCase("info")){level=LogService.LOG_INFO; } else if(l.equalsIgnoreCase("error")){level=LogService.LOG_ERROR; } else {level=LogService.LOG_WARNING;} } // Register on the instance manager, to be a part of the component // instance container manager = im; context = manager.getContext(); // Store the bundle context manager.register(this); } } The start and stop methodsThe start method needs to look for a Log Service, and register a service listener to track it. If a log service is available, it logs a message. LogHandler.java:Start Method public void start() { // When starting, look for an LogService ref = context.getServiceReference(LogService.class.getName()); if(ref != null) { log = (LogService) context.getService(ref); // Log a starting message log.log(level,"The component instance " + manager.getInstanceName() + " is starting"); } // Registered a service listenner try { context.addServiceListener(this,"(OBJECTCLASS="+LogService.class.getName()+")"); } catch (InvalidSyntaxException e) { e.printStackTrace(); } } The stop method logs a message if the Log Service is available. Then it releases the used Log Service. LogHandler.java:Stop Method public void stop() { // Log the event, unget the service if(log != null) { log.log(level, "The component instance " + manager.getInstanceName() + " is stopping"); } if(ref != null) { log = null; context.ungetService(ref); } } Note: do not forget to unget all used services and to release all service objects. The isValid and stateChanged methodsThe isValid method returns the handler validity. In our case, the handler is valid if a log service is available. public boolean isValid() { // The handler is valid <=> a log service is available return (ref != null); } The stateChanged method is called by the instance manager to notify that the component instance state changes. The handler needs to log a message (if the log service is available) containing the new state. LogHandler.java:stateChanged method public void stateChanged(int state) { // log the state changed events if(log != null) { if(state == InstanceManager.VALID) { log.log(level, "The component instance " + manager.getInstanceName() + " becomes valid"); } if(state == InstanceManager.INVALID) { log.log(level, "The component instance " + manager.getInstanceName() + " becomes invalid"); } } } Implementation of the Service Listener interfaceTo track the availability of the Log Service, the start method has registered the handler as a service listener. So, the handler class needs to implement the Service Listener interface. This interface contains a method called when a service appears, disappears or is modified. The start method has registered a service listener with a filter. So the handler will received only service events matching the filter. (In our case, only service events referring to a Log Service). LogHandler.java:serviceChanged method public void serviceChanged(ServiceEvent event) { // Check if the service event does not infers with the log service if(ref == null && event.getType() == ServiceEvent.REGISTERED) { ref = event.getServiceReference(); log = (LogService) context.getService(ref); // ask the component manager to check the component instance state manager.checkInstanceState(); } if(ref != null && event.getType() == ServiceEvent.UNREGISTERING && ref == event.getServiceReference()) { //The log service goes away log = null; context.ungetService(ref); ref = context.getServiceReference(LogService.class.getName()); if(ref != null) { log = (LogService) context.getService(ref); } else { manager.checkInstanceState(); } } } According to the event type, the method gets or releases a Log Service. If a Log Service appears, the method checks if the handler has already a Log Service. If not, it gets the service object, and asks the component instance to check the instance state because the handler state changes. If a Log Service disappears, the method checks if it used this Log Service. Then, it releases the service and look for another Log Service. If no other Log Service is available, the handler becomes invalid. It asks the instance manager to check the instance state. Handler packagingThis handler needs to be packaged inside a bundle exporting the package "org.apache.felix.ipojo.log.handler". The bundle will import "org.apache.felix.ipojo", "org.osgi.framework" and "org.osgi.service.log" packages. Handler usageTo 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 can be "error", "warning" or "info". DownloadThis handler is available here : http://cwiki.apache.org/confluence/download/attachments/35380/log_handler.zip Properties Handler exampleThis 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. Handler implementationThe handler needs to override following methods:
Properties Handler classThe handler is implemented inside the "org.apache.felix.ipojo. properties.handler.PropertiesHandler" class. This class extends the "org.apache.felix.ipojo.Handler" class.
Note: the file name is the absolute path on the local machine of the file. Configure MethodThis 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. PropertiesHandler.java:Configure Method public void configure(InstanceManager inst, Element meta, Dictionary conf) { manager = inst; Element[] prop_element = meta.getElements("Properties", NAMESPACE); if(prop_element.length == 0) { return; } else { if(prop_element[0].containsElement("file")) { file_name = prop_element[0].getAttribute("file"); } else { return; } // Load the file and register the field File file = new File(file_name); try { InputStream is = new FileInputStream(file); props.load(is); } catch (FileNotFoundException e) { e.printStackTrace(); return; } catch (IOException e) { e.printStackTrace(); return ; } // Register to field notification Enumeration e = props.propertyNames(); String[] fields = new String[props.size()]; for(int i=0; i < fields.length; i++) {fields[i]=(String)e.nextElement();} manager.register(this, fields); } } The start and stop methodsThe start method does nothing, but need to be implemented. PropertiesHandler.java:Start Method public void start() { }
The stop method stores properties inside the properties file. PropertiesHandler.java:Stop Method public void stop() { File file = new File(file_name); try { OutputStream os = new FileOutputStream(file); props.store(os, "written by " + NAMESPACE); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } GetterCallback and SetterCallback methodsThe getterCallback method is called when the POJO need a field value. When called, the method needs to return the stored value. PropertiesHandler.java: GetterCallback and SetterCallback methods public void setterCallback(String fieldName, Object value) { // Store the new value in props (if not null) else remove the property if(value != null) { props.put(fieldName, value); } else { props.remove(fieldName); } } public Object getterCallback(String fieldName, Object value) { if(props.containsKey(fieldName)) { return props.get(fieldName); } else { return value; } } Handler packagingThis handler needs to be packaged inside a bundle exporting the package "org.apache.felix.ipojo.properties.handler". The bundle will import the "org.apache.felix.ipojo" packages. Handler usageTo used this handler, a component need to declare an org.apache.felix.ipojo.properties.handler :Properties XML element, with a file attribute indicating the absolute file path. Note: the file path need to used two '\' instead of one '\' ConclusionIn 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 posible to extends Composite Handler too. See the handler proposition section of the iPOJO documentation to see available handlers or to propose your own handler. |
OverviewGetting StartedUser GuideToolsDeveloper GuideMisc & Contact
|

