Platinum Partner
java,osgi,annotation,extender,loader

OSGi Annotation Extender

We have finished one big OSGi project with many tools : JPA, GPRS, SMS, FTP, JNA, SPRING, Jetty, ext.js .... after about one year with heterogeneous team. Now, I have switched to another JEE projects as " an architect". But I still love OSGi.

I have blogged more about extender, and this post is not an exception. I will try to present a simple annotation extender to publish OSGi services.

For this reason we will create 3 projects :

1- osgi.annotations : annotation lib
2- annotations.extender : extender used to publish annotated class
3- client : service to be published as a test

osgi.annotations

We will create a RegisterService annotation type with RUNTIME Retention and ElementType.TYPE target as following
package com.jtunisie.osgi.annotation;

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

/**
 *
 * @author slim ouertani
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface RegisterService {

    String id();

    NamedInterface[] NamedInterfaces();

    Prop[] Props() default {};
}

  • id : used for debug
  • NamedInterface[] : list of interfaces (target is ANNOTATION_TYPE and as single value annotation)
  • Prop[] : optional additional service properties
package com.jtunisie.osgi.annotation;

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

/**
 *
 * @author slim ouertani
 */

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface NamedInterface {

    Class value();
}
package com.jtunisie.osgi.annotation;

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

/**
 *
 * @author slim ouertani
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Props {

    String key();

    String value();
}
It is very simple as a start up, but we can add other parameters as registration type(such as bundle context, extender context,...)

annotations.extender

This is a very basic extender to listen to all installed bundles (Activator implements BundleActivator, BundleListener ) and lists all class using :   Enumeration<?> entrs = b.findEntries("/", "*.class", true);

For each class we we call loadClass witch will scan if this class is a candidate to be published. We call the loadClass method using bundle classloader
  private void loadClass(Bundle b, String clazz) {
        try {
            Class loaded = b.loadClass(clazz);

            System.out.println("Loaded class [" + loaded + "]");
            boolean annotationPresent = loaded.isAnnotationPresent(RegisterService.class);
            System.out.println("" + annotationPresent);
            if (annotationPresent) {
                publishService(loaded, b);

            }

        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
if an annotation is present, we get data and we will publish service using it's context( we can do it using extender context but ;) )
 private void publishService(Class loaded, Bundle bundle) throws IllegalAccessException, InstantiationException {
        RegisterService annotation = loaded.getAnnotation(RegisterService.class);
        String id = annotation.id();
        NamedInterface[] interfaces = annotation.NamedInterfaces();
        Prop[] with = annotation.Props();
        Properties p = new Properties();
        for (Prop props : with) {
            p.put(props.key(), props.value());
        }
        Object newInstance = loaded.newInstance();
        for (NamedInterface object : interfaces) {
            //register service
            System.out.println("[ " + id + " ] registring service ..." + object.value().getName());
            bundle.getBundleContext().registerService(object.value().getName(), newInstance, p);
        }
    }
it's all for our first extender III- client. it's a simple pojo class and we annotate it using our annotation lib
import com.jtunisie.osgi.annotation.RegisterService;
import com.jtunisie.osgi.annotation.client.IService;
import com.jtunisie.osgi.annotation.NamedInterface;
import com.jtunisie.osgi.annotation.Prop;

/**
 *
 * @author slim ouertani
 */
@RegisterService(id = "tunisie",
NamedInterfaces = {@NamedInterface(IService.class)},
    Props = {
    @Prop(key = "url", value = "/jtunisie"),
    @Prop(key = "publish", value = "hessian")
})
public class ToPublish implements IService {

    public ToPublish() {
        System.out.println("service published..");
    }
}
and if we install the bundle service will be published

Console output :

osgi> up 48

osgi> New bundle: annotation.client-1.0-SNAPSHOT_1.0.0.SNAPSHOT [48]
file: /com/jtunisie/osgi/annotation/client/IService.classpath : /com/jtunisie/osgi/annotation/client/IService.class
c: com.jtunisie.osgi.annotation.client.IService
Loaded class [interface com.jtunisie.osgi.annotation.client.IService]
false
file: /com/jtunisie/osgi/annotation/client/impl/ToPublish.classpath : /com/jtunisie/osgi/annotation/client/impl/ToPublish.class
c: com.jtunisie.osgi.annotation.client.impl.ToPublish
Loaded class [class com.jtunisie.osgi.annotation.client.impl.ToPublish]
true
service published..
[ l ] registering service ...com.jtunisie.osgi.annotation.client.IService


osgi> b 48
annotation.client-1.0-SNAPSHOT_1.0.0.SNAPSHOT [48]
Id=48, Status=ACTIVE Data Root=/home/sst/NetBeansProjects/env/configuration/org.eclipse.osgi/bundles/48/data
Registered Services
{com.jtunisie.osgi.annotation.client.IService}={service.id=44}
No services in use.
Exported packages
com.jtunisie.osgi.annotation.client; version="0.0.0"[exported]
Imported packages
com.jtunisie.osgi.annotation; version="0.0.0"<annotation-0.0.1-SNAPSHOT_0.0.1.SNAPSHOT [47]>
No fragment bundles
Named class space
annotation.client-1.0-SNAPSHOT; bundle-version="1.0.0.SNAPSHOT"[provided]
No required bundles
 

We remark that our service is declared as Registered Services.

SCA extender :

We can use this annotation to be published in the SCA fashion like :
@RegisterService(id = "tunisie",
NamedInterfaces = {@NamedInterface(IService.class)},
    Props = {
    @Prop(key = "url", value = "/jtunisie"),
    @Prop(key = "publish", value = "hessian")
})
public class ToPublish implements IService {

    public ToPublish() {
        System.out.println("service published..");
    }
}

Next Step :

We have seen the power of the extender pattern in the OSGi world and how to use annotations. But this example isn't complete. We can do more, such as :
  • Publish by default all implemented interfaces
  • Check if a class really implements listed interfaces
  • Use adynamic proxy and register it ( not really instance) for many purpose ;)
  • Unregister service
  • @serviceReference as springOSGI
  • Add additional properties to MANIFEST to reduce scan file loop
  •  Service factory
  •  ...

The source code is under svn :svn checkout http://osgipublishannodation.googlecode.com/svn/trunk/ osgipublishannodation-read-only

Conclusion :

 Using annotations is a direction in publishing and consuming an OSGi service but I highly recommended DSL and if you have time take a look on scala with osgi. 
{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}