...
This document explains how developers can use iPOJO extensibility mechanism. This tutorial shows two small examples: a Log Handler logging messages inside the OSGiâ?¢ OSGiâ„¢ log service and a Property Handler injecting properties values inside fields.
This document is organized as follow. First, iPOJO concepts are briefly explained. The next section explains the first step to create a handler. The two last sections describe the implementation and the usage of two example handlers.
...
iPOJO is a service component model aiming to simplify OSGiâ?¢ 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.
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 owns a configuration (set of <key, value>).
...
- void configure(InstanceManager im, Element metadata, Dictionary configuration) : this method is mandatory. Your handler will receive the instance manager on which it need to registers, the component type metadata (to parse to find your handler metadata) and the component instance configuration.
- void start() : this method is called when the component instance management starts. Your handler needs to begin its management.
- void stop() : this method is called when the component instance management stops. Your handler needs to end its management, and release all used services.
- boolean isValid() : this method is called by the instance manager, when it needs to check the component instance state (VALID or INVALID). If your handler does not participate to the component instance lifecycle, you don't need to override this method. Else, you need to return if your handler is valid.
- void stateChanged(int state) : this method is called by the instance manager each time that the instance state changes. The parameter describe the new instance state (-1 : Disposed, 0 : Stopped, 1 : Invalid, 2 : Valid).
- void setterCallback (String fieldName, Object value) : this method is called by the instance manager, when a POJO field value changes. To be called, your handler needs to register to the field. The first argument of the method is the field name. The second argument is the new value of the field. If the field type is a primitive type, the method sends the boxing object.
- Object getterCallback (String fieldName, Object value) : this method is called by the instance manager, when a POJO field asks for a value. To be called, your handler needs to register to the field. The first argument of the method is the field name. The second argument is the actual value of the field. This method returns the value that your handler wants to inject in the field.
- void createInstance (Object inst) : this method is called by the instance manager, when a POJO object is created but before that anyone can use it. The argument is the created object.method is called by the instance manager, when a POJO object is created but before that anyone can use it. The argument is the created object.
- void reconfigure(Dictionary configuration) : this method is called when the instance manager recevie a new instance configuration. This configugration can be applied whithout restarting the instance. The parameter contains the new configuration. Each handler managing the dynamic reconfiguration can, by overriden this method, apply the new configuration.
- HandlerDescription getDescription(): this method allow an handler to participate to the instance introspection. When called, the handler needs to describe its state and returns a HandlerDescription object.
Log Handler example
This section describes how to create a simple handler. This handler logs a message in the OSGiâ?¢ 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).
...
- The bundle context (context)
- The instance manager (manage)
- The service reference of the actual used Log Service (ref)
- The used log service (log)
- The level of the log (level)
Note: Handler code is OSGiâ?¢ OSGiâ„¢ standard code. A handler cannot be a POJO.
...
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
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); } } |
...
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
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(); } } |
...
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
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.
...
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
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"); }
}
}
|
...
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
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(); }
}
}
|
...
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
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); } } |
...
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
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(); }
}
|
...
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
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; }
}
|
...