Creating an OSGi service with eclipse Gemini blueprint

In the previous article I explained how to create and deploy an OSGi application. We will start where we left off in the last article. Next we should create some services to let the OSGi bundles talk with each other. We will create the services using the Eclipse Gemini Blueprint specification for OSGi.

Before we create a service we will have to create a few more bundles:

Client:
The osgi-client bundle will contain all the client side code to be able to connect to the server.
mvn archetype:generate -B -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.0 -DgroupId=javacodingspot -DartifactId=osgi-client -Dversion=1.0 -Dpackage=nl.javacodingspot.osgi.client 

Common:
The osgi-common bundle will contain all the code which is used by the server as wel as the client.
mvn archetype:generate -B -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.0 -DgroupId=javacodingspot -DartifactId=osgi-common -Dversion=1.0 -Dpackage=nl.javacodingspot.osgi.common 


Creating a service

We will now create a simple service in the server jar of our example application.

The interface


The service will have an interface that the client and server will use. We first create an interface for the service. We will do this in de osgi-common bundle. So that the server and client will be able to access it.

package nl.javacodingspot.common.services;

import java.util.List;

import nl.javacodingspot.common.domain.Person;

/**
 * Interface for the OSGi service to find people.
 * 
 */
public interface PeopleFinder {
 
 /**
  * The OSGi service that searches for people by surname.
  *  
  * @param surname
  * @return The list of persons with that surname
  */
 List<Person> findPeopleBySurname(String surname);
}

For the interface we need the Person class.

package nl.javacodingspot.common.domain;

/**
 * Domain class which represent a person.
 */
public class Person {

    private final String firstname;
    private final String surname;
    private final String phoneNumber;

    public Person(String firstname, String surname, String phoneNumber) {
        this.firstname = firstname;
        this.surname = surname;
        this.phoneNumber = phoneNumber;
    }

    public String getFirstname() {
        return firstname;
    }

    public String getSurname() {
        return surname;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }
}


Creating the OSGi service


After we created the interface we can make a simple implementation of the service in the server jar.

package nl.javacodingspot.server.services;

import nl.javacodingspot.common.domain.Person;
import nl.javacodingspot.common.services.PeopleFinder;
import nl.javacodingspot.server.dao.PeopleDao;

import java.util.List;

/**
 * Implementation of the OSGi service to find people.
 */
public class PeopleFinderImpl implements PeopleFinder {

    /* (non-Javadoc)
     * @see nl.javacodingspot.common.services.PeopleFinder#findPeopleBySurname(java.lang.String)
     */
    public List<Person> findPeopleBySurname(String surname) {
        return PeopleDao.findPersonBySurname(surname);
    }

}


Now the implementation is made, but it is still not a service. We have to define the service in the blueprint configuration file. Blueprint will register the service then as an OSGi service in the container. We will create the file in the "src/main/resources/OSGI-INF/blueprint" directory. Blueprint will check for configuration files in this directory in jar when it is installed on the OSGi server.

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0
        http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">

    <!-- Bean which references to the PeopleFinder interface implementation -->
    <bean id="peopleFinderImpl" class="nl.javacodingspot.server.services.PeopleFinderImpl"/>

    <!-- The Blueprint OSGi service -->
    <service id="peopleFinder" interface="nl.javacodingspot.common.services.PeopleFinder" ref="peopleFinderImpl">
    </service>

</blueprint>


Connecting to the OSGi service


We finally have to create the client implementation.
Again we tell the OSGi container that we want to is the service by adding it to the blueprint configuration file of the client. Blueprint will search for an implementation of the service.


<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0
        http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
    
    <!-- The reference to the Blueprint OSGi service for the PeopleFinder interface -->
    <reference id="peopleFinder" interface="nl.javacodingspot.common.services.PeopleFinder"/>

    <!-- The bean to start (using the init() method) when this OSGi bundle is started, injected with the service reference -->
    <bean id="app" class="nl.javacodingspot.client.App" init-method="init">
        <property name="peopleFinder" ref="peopleFinder"/>
    </bean>
</blueprint>


We finally have to implement some simple client code to really start using the service.

package nl.javacodingspot.client;

import nl.javacodingspot.common.domain.Person;
import nl.javacodingspot.common.services.PeopleFinder;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
 * Simple class which uses an OSGi client to retrieve persons for a service.
 */
public class App {
    private static Logger log = Logger.getLogger(App.class.getName());
    private PeopleFinder peopleFinder;
    final public void init() {
        List<Person> people = peopleFinder.findPeopleBySurname("FooBar");
        for (Person person : people) {
            log.log(Level.INFO, String.format("Person found: %s %s, with phonenumber: %s", person.getFirstname(), person.getSurname(), person.getPhoneNumber()));
        }
    }

    public void setPeopleFinder(PeopleFinder peopleFinder) {
        this.peopleFinder = peopleFinder;
    }
}


This code will retrieve persons from the OSGi server bundle with the last name "FooBar". When starting the Virgo server, the client will immediately call the service and log the results. The log can be found in the "<VIRGO_ROOT>/serviceability/logs/log.log" file.

Deploying

After running maven install (mvn install) in the root directory, each project is compiled and will have its own target directory. These will contain the jars, which are the OSGi bundles. 

Copy the client and server bundles to the "<VIRGO_ROOT>/pickup" directory
  • osgi/client/target/client-0.0.1-SNAPSHOT.jar
  • osgi/server/target/server-0.0.1-SNAPSHOT.jar
Copy the common bundle to the "<VIRGO_ROOT>/pickup" directory
  • osgi/common/target/common-0.0.1-SNAPSHOT.jar

Now start the Virgo server. (go to the "<VIRGO_ROOT>/bin" directory and run the startup script)

You should see something like this in the console:
..
 Installing bundle 'nl.javacodingspot.osgi.client' version '1.0.0'.
 Installed bundle 'nl.javacodingspot.osgi.client' version '1.0.0'.
 Starting bundle 'nl.javacodingspot.osgi.client' version '1.0.0'.
 Installing bundle 'nl.javacodingspot.osgi.server' version '1.0.0'.
 Installed bundle 'nl.javacodingspot.osgi.server' version '1.0.0'.
 Starting bundle 'nl.javacodingspot.osgi.server' version '1.0.0'.
 Started bundle 'nl.javacodingspot.osgi.server' version '1.0.0'.
 Started bundle 'nl.javacodingspot.osgi.client' version '1.0.0'.
..

You can now check the log file to see if any people are found.


In the next article we will convert the OSGi client to a more useful JavaFX application.

No comments:

Post a Comment