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.
-
By Hand - Disadvantage: Error prone.
-
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.