30/01/2012

OSGi Blueprint Services example

In this post we will give a fair example of the workings of the OSGi Blueprint Services.
We can use either one of the following frameworks:
and the Eclipse IDE (for Java). The code can be launched either inside Eclipse or from a standalone OSGi framework.
Interfaces, implementations and clients should all reside in their own package, meaning you should create a new Plug-in project, without Activator or template inside Eclipse (with the RCP Plug-in developer resources plugin installed) for each of them.
 
Suppose we have a service: HelloService, its interface: IHello and a client: HelloCunsomer who declares a Java Bean dependency on the service by pointing to its interfaces using the Blueprint method.
When a component declares it provides a service, the OSGi framework stores that information in its internal registry.
Download here the example's source code. You should be able to import everything inside Eclipse as-is. Inside you will find:
  • Interface – IHello.java
1:  package it.eng.test.blueprint.hello;  
2:    
3:  public interface IHello {  
4:    public void sayHello();  
5:  }  
  • Implementation – HelloService.java
1:  package it.eng.test.blueprint.helloservice;  
2:    
3:  import it.eng.test.blueprint.hello.IHello;  
4:    
5:  public class HelloService implements IHello{  
6:    public void sayHello(){  
7:      System.out.println( "Hello World!" );  
8:    }  
9:  }  

If you inspect it, you will see that the it offer a single, simple method which prints something on standard output.
  • Client – HelloConsumer.java

1:  package it.eng.test.blueprint.consumer;  
2:    
3:  import it.eng.test.blueprint.hello.IHello;  
4:    
5:  public class HelloConsumer {  
6:    private IHello service;  
7:      
8:    // Bean properties  
9:    public IHello getService() {  
10:      return service;  
11:    }  
12:    
13:    public void setService(IHello service) {  
14:      this.service = service;  
15:    }  
16:    
17:    public void init() {  
18:      System.out.println("OSGi client started.");  
19:      if (service != null) {  
20:        System.out.println("Calling sayHello()");  
21:        service.sayHello(); // Invoke the OSGi service!  
22:      }  
23:    }  
24:  }  



The consumer declares the bean dependency on the service by requiring its interface and has some methods of his own:
  • init: this method is called as soon as the requested bean is injected inside the application;
  • get*: this method is called this method is called to get the * property;
  • set*: this method is called this method is called to set the * property;
The framework knows that Consumer needs that particular service since he declared it in his XML configuration which must be put under the OSGI-INF/blueprint folder. As soon as the service becomes available it is injected as a bean inside the consumer:

1:  <?xml version="1.0" encoding="UTF-8"?>  
2:  <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">  
3:     
4:   <reference id="helloWorld"  
5:      interface="it.eng.test.blueprint.hello.IHello"/>  
6:     
7:   <bean id="client"  
8:      class="it.eng.test.blueprint.consumer.HelloConsumer"  
9:      init-method="init">  
10:    <property name="service" ref="helloWorld"/>  
11:   </bean>  
12:    
13:  </blueprint>  

Here the bundle is saying that the interface it.eng.test.blueprint.hello.IHello should be referenced as helloWorld and declares a bean for that reference to be injected on the property service of the it.eng.test.blueprint.consumer.HelloConsumer class with the init initialization method.

On its side, the service must declare its own XML configurations, listing the interface implementations offered through which beans.


1:  <?xml version="1.0" encoding="UTF-8"?>  
2:  <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">  
3:    
4:   <bean id="hello" class="it.eng.test.blueprint.helloservice.HelloService"/>  
5:     
6:   <service ref="hello" interface="it.eng.test.blueprint.hello.IHello"/>  
7:    
8:  </blueprint>  
When we export our projects as Deployable plug-ins and fragment and install all the resulting bundles inside the same OSGi framework and start them, we see that Consumer prints on the standard output the string "OSGi client started." right after it is starts and then, when the required bean becomes available and injected, "Calling sayHello()" then "Hello World!".

Update:

Vikram Kamath found a possible solution to the "ClassNotFound" exception you may get when trying to run the example.

The Manifest.MF file for it.eng.test.blueprint.hello Bundle contents are as follows: Manifest-Version: 1.0Bundle-ManifestVersion: 2Bundle-Name: HelloBundle-SymbolicName: it.eng.test.blueprint.helloBundle-Version: 1.0.0.qualifierBundle-ClassPath: it.eng.test.blueprint.helloExport-Package: it.eng.test.blueprint.helloBundle-RequiredExecutionEnvironment: JavaSE-1.6 Delete/Comment the Bundle-ClassPath property in all the 3 Manifest files and this is good to run!!

3 comments:

  1. Hello ...thanks for this simple example. I havent been able to run it in the Eclipse Gemini environment. Following is my configuration

    Eclipse 3.6.2 (Helios), gemini-blueprint-1.0.2, Spring framework 3.0.5.
    Osgi Framework validates bundles successfully. However, gemini fails to find the beans defined..throwing ClassNotFoundException for any/all beans in config.xml.


    Strange thing is...I was quickly able to modify your example and throw it in Spring DM 1.2 alongside Spring 3.0.5 and same Eclipse...it was running as expected. I can send you this example if you want to add it to this tutorial.

    Since you mentioned it worked for you in Eclipse Gemini environment can you send me more details about the Gemini release etc.

    On another note, I will log this as a bug with Eclipse Gemini

    Thanks again...hope to hear back from you soon. My email: vikikamath[at]gmail

    ReplyDelete
    Replies
    1. Apologize for my post above. I was able to figure out the problem. The Manifest.MF file for it.eng.test.blueprint.hello Bundle contents are as follows:

      Manifest-Version: 1.0
      Bundle-ManifestVersion: 2
      Bundle-Name: Hello
      Bundle-SymbolicName: it.eng.test.blueprint.hello
      Bundle-Version: 1.0.0.qualifier
      Bundle-ClassPath: it.eng.test.blueprint.hello
      Export-Package: it.eng.test.blueprint.hello
      Bundle-RequiredExecutionEnvironment: JavaSE-1.6

      Delete/Comment the Bundle-ClassPath property in all the 3 Manifest files and this is good to run!!

      Delete
    2. Cheers,

      nice to hear you sorted it out. I will add your suggestion to the post.

      Unfortunately, since I wrote this tutorial some time ago I do not remember exactly which Gemini version I used but I guess it doesn't matter anymore now.

      Have a nice day

      Delete

With great power comes great responsibility