Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

Eclipse RCP Blog


Yet another blog for Eclipse RCP topics, like good Eclipse RCP architecture, software architecture control, ...

Declarative Annotations in Eclipse Neon

Motivation

Many Eclipse RCP applications are using OSGi Services make their application leightweight and flexible. The most convient method is to use OSGi Services in combination with Declarative Services. This allows to specifiy OSGi Services by XML files. Why this can be quite convient can be found here DS@Vogella. Before Eclipse Neon there were two possibilties to create or modify these XML files.

  1. By Hand - Disadvantage: Error prone.

  2. Integration editor - Disadvantages: Error prone on re-factoring, no consistent naming scheme.

No Eclipse Neon integrated a third solution: Generation of XML files by code annotations. In this blog I want to show more detailled how this can work.

Code Example

Expecting a simple HelloWorld OSGi application, with

  • a HelloWorldService and

  • a HelloWorldClient.

The HelloWorldService is exporting following simple interface.

package my.osgi.ds.example.helloservice.api;

public interface HelloWorldService {
    // Prints 'Hello + name' to the console
    public void sayHello(String name);
}

Whenever sayHello is called 'Hello + name' is printed on the console.

Here is the corresponding implementation of this OSGi Service. So the implementation is quite straight forward. Now let us come to the interesting part of this class - the @Component DS Annotations.

package my.osgi.ds.example.helloservice.impl;

import org.osgi.service.component.annotations.Component;
import my.osgi.ds.example.helloservice.api.HelloWorldService;

@Component
public class HelloWorldServiceImpl implements HelloWorldService {

	@Override
	public void sayHello(String name) {
		System.out.println(("Hello " + name + "!"));
	}

}

To register a class as OSGi-Service (Component is used here as synonym) just use @Component as class annotation. That’s the minimum to register a class as OSGi service. Whenever you save this class, automatically a new XML file my.osgi.ds.example.helloservice.impl.HelloWorldServiceImpl.xml will be generated OSGI-INF.

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="my.osgi.ds.example.helloservice.impl.HelloWorldServiceImpl">
   <service>
      <provide interface="my.osgi.ds.example.helloservice.api.HelloWorldService"/>
   </service>
   <implementation class="my.osgi.ds.example.helloservice.impl.HelloWorldServiceImpl"/>
</scr:component>

So in the most cases this should be enough. Look’s quite easy.

Let us have a look on the HelloWorldClientImpl. It’s the service that is using our HelloWorldService. At first it has to become also a OSGi service. So @Component`annotation is needed. Then we need the reference to the `HelloWorldService. This is done by a method, which is annotated by @Reference. But here its also a special parameter unbind needed, which also points to the unbind method. So the @Reference method is used inject the HelloWorldService to the HelloWorldClientImpl. In case you have multiple @Reference methods, you should use the @Activate method to start your component. Otherwise it’s not guaranteed, that all service references were already injected.

package my.osgi.ds.example.helloclient.impl;

import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;

import my.osgi.ds.example.helloservice.api.HelloWorldService;

@Component
public class HelloWorldClientImpl {
	private HelloWorldService helloWorldService;

	@Reference(unbind = "unbind")
	public void bind(HelloWorldService helloWorldService) {
		this.helloWorldService = helloWorldService;
	}

	@Activate
	public void start() {
		helloWorldService.sayHello("Mr. X");
	}

	public void unbind(HelloWorldService helloWorldService) {
		this.helloWorldService = null;
	}

	@Deactivate
	public void stop() {
		this.helloWorldService = null;
	}
}

Again a XML file my.osgi.ds.example.helloclient.impl.HelloWorldClientImpl.xml is generated in the folder OSGI-INF.

Content looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0"
	activate="start"
	deactivate="stop"
	name="my.osgi.ds.example.helloclient.impl.HelloWorldClientImpl">
    <reference
   	bind="bind"
        cardinality="1..1"
        interface="my.osgi.ds.example.helloservice.api.HelloWorldService"
        policy="static"
        unbind="unbind"/>
   <implementation class="my.osgi.ds.example.helloclient.impl.HelloWorldClientImpl"/>
</scr:component>

Advantages

  • Naming scheme of XML files are always the same.

  • Usability is trivial.

  • Annoations can be easily used for byte code generated traces (I will write another post for this topic!).

Try it out

A full example can be found here: ds.annotation.example@github.


Discussions

comments powered by Disqus