Over a million developers have joined DZone.

OSGi Annotation Extender

· Java Zone

Discover how AppDynamics steps in to upgrade your performance game and prevent your enterprise from these top 10 Java performance problems, brought to you in partnership with AppDynamics.

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. 

The Java Zone is brought to you in partnership with AppDynamics. AppDynamics helps you gain the fundamentals behind application performance, and implement best practices so you can proactively analyze and act on performance problems as they arise, and more specifically with your Java applications. Start a Free Trial.

Topics:

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

{{ parent.tldr }}

{{ parent.urlSource.name }}