Wiring made easy using OSGi Blueprint and Apache Karaf

April 16th, 2012 by

The OSGi Blueprint Container specification allows us to use dependency injection in our OSGi environment, declarative import and export of OSGi services, registering lifecycle listeners and wiring dependencies into our services with a few lines of XML code.
In the following tutorial we’re first building an OSGi bundle classical style and afterwards take a trip into the advantages of the Blueprint specification.

Our OSGi container of choice here will be Apache Karaf a lightweight container with a lot of nice features and – of course – blueprint enabled…


 

Preface

As a first step we’re going to build a plain old OSGi bundle using Maven archetypes and afterwards we’re going to dig into the possibilities offered by the Blueprint specification.

If you’re not familiar with Apache Karaf or you need to install it, please skip to the chapter “Apache Karaf” first!

Creating a Plain Old OSGi Bundle

We’re going to create an OSGI bundle that exports some packages and registers an OSGi service the classical way first. I must resist the desire to call this Plain Old Osgi Bundle (just like POJOs) here ;)

For this purpose we’re creating a new maven project using the archetype org.codehaus.mojo.archetypes:osgi-archetype.

*Update* Please use version 1.4 of the archetype.

The archetype adds a dependency for the Felix OSGi implementation and the Maven Bundle Plugin that helps to create our bundles.

Creating a new maven project in Eclipse

Creating a new maven project in Eclipse

Now we’re adding a service interface and its implementation:

package com.hascode.tutorial.hascode_osgi_service.api;
 
public interface SampleService {
 String getGreeting(final String name);
}
package com.hascode.tutorial.hascode_osgi_service.api;
 
public class SampleServiceImpl implements SampleService {
 
 public String getGreeting(final String name) {
 return "hello, " + name;
 }
 
}

In the next step we’re adding a bundle activator to register our service

package com.hascode.tutorial.hascode_osgi_service;
 
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
 
import com.hascode.tutorial.hascode_osgi_service.api.SampleService;
import com.hascode.tutorial.hascode_osgi_service.api.SampleServiceImpl;
 
public class Activator implements BundleActivator {
 private ServiceRegistration serviceRegistration;
 
 public void start(final BundleContext context) throws Exception {
 serviceRegistration = context.registerService(
 SampleService.class.getName(), new SampleServiceImpl(), null);
 }
 
 public void stop(final BundleContext context) throws Exception {
 serviceRegistration.unregister();
 }
}

Last but not least we’re adding the OSGi declarations to our pom.xml – the Maven Bundle Plugin creates the META-INF/MANIFEST.MF from this information.

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.felix</groupId>
            <artifactId>maven-bundle-plugin</artifactId>
            <version>2.2.0</version>
            <extensions>true</extensions>
            <configuration>
                <instructions>
                    <Export-Package>com.hascode.tutorial.hascode_osgi_service.api</Export-Package>
                    <Private-Package>com.hascode.tutorial.hascode_osgi_service.*</Private-Package>
                    <Export-Service>com.hascode.tutorial.hascode_osgi_service.api.SampleService</Export-Service>
                    <Bundle-Activator>com.hascode.tutorial.hascode_osgi_service.Activator</Bundle-Activator>
                </instructions>
            </configuration>
        </plugin>
    </plugins>
</build>

Now build the bundle using mvn package and deploy it on your Apache Karaf instance:

karaf@root> install file:/path/osgi-blueprint-tutorial/hascode-osgi-service/target/hascode-osgi-service-0.0.1.jar
Bundle ID: 106
karaf@root> start 106
karaf@root> list
START LEVEL 100 , List Threshold: 50
 ID   State         Blueprint      Level  Name
[ 106] [Active     ] [            ] [   80] hascode-osgi-service OSGi Bundle (0.0.1)

Blueprint Bundles

Now that we’ve recapitulated the common method to create an OSGi bundle and register services we’re going to build some blueprint-enhanced examples to show the advantages of this specification..

Using Service Listeners

In our first example, we’re reusing the classic bundle that we’ve created in the first step and we’re adding a service listener to our new bundle using Blueprint.

Because I am lazy I’m using the OSGi archetype again and simply add the following Activator to the new project:

package com.hascode.tutorial.hascode_blueprint_bundle;
 
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
 
import com.hascode.tutorial.hascode_osgi_service.api.SampleService;
 
public class Activator implements BundleActivator {
 
 public void start(final BundleContext context) throws Exception {
 System.out.println("blueprint bundle activator called");
 }
 
 public void stop(final BundleContext context) throws Exception {
 }
 
 public void onBindService(final SampleService sampleService) {
 if (sampleService == null) {
 System.out.println("sample service is null");
 } else {
 System.out.println("greet: " + sampleService.getGreeting("bob"));
 }
 }
 
 public void onUnbindService(final SampleService sampleService) {
 System.out.println("service unbound");
 }
}

Don’t forget to add the dependency to the other OSGi bundle to your new project’s pom.xml

<dependency>
    <groupId>com.hascode.tutorial</groupId>
    <artifactId>hascode-osgi-service</artifactId>
    <version>0.0.1</version>
    <type>bundle</type>
    <scope>provided</scope>
</dependency>

Now the only thing missing is the Blueprint wiring stuff .. so we’re adding the following file named blueprint.xml in src/main/resources/OSGI-INF/blueprint

<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 id="bundle-activator" class="com.hascode.tutorial.hascode_blueprint_bundle.Activator"/>
<reference id="sampleService" availability="mandatory" activation="eager" interface="com.hascode.tutorial.hascode_osgi_service.api.SampleService">
    <reference-listener ref="bundle-activator" bind-method="onBindService" unbind-method="onUnbindService"/>
</reference>
</blueprint>

What are we doing here? First of all we’re creating a service reference to the SampleService from the first bundle. Second, we’re defining a service listener for that service and because we’re too lazy to create a second class, we’re using our activator again to handle the stuff.

Building the bundle using mvn package and installing/starting the bundle afterwards we will see the following output:

karaf@root> install file:/var/project/osgi-blueprint-tutorial/hascode-blueprint-bundle/target/hascode-blueprint-bundle-0.0.1.jar
Bundle ID: 107
karaf@root> start 107
blueprint bundle activator called
karaf@root> greet: hello, bob

In addition please notice that osgi:list displays you additional information about the blueprint activation from your bundle:

karaf@root> list
START LEVEL 100 , List Threshold: 50
 ID   State         Blueprint      Level  Name
[ 106] [Active     ] [            ] [   80] hascode-osgi-service OSGi Bundle (0.0.1)
[ 107] [Active     ] [Created     ] [   80] hascode-blueprint-bundle OSGi Bundle (0.0.1)

Registering Services

Now we’re declaring a service using Blueprint. You know the way .. Maven archetype, blueprint declaration in OSGI-INF/blueprint and of course a service and its interface:

package com.hascode.tutorial.blueprint_service_export;
 
public interface SimpleTimeService {
 String getTime();
}
package com.hascode.tutorial.blueprint_service_export;
 
import java.text.SimpleDateFormat;
import java.util.Date;
 
public class SimpleTimeServiceImpl implements SimpleTimeService {
 
 public String getTime() {
 return String.format("the time is: %s",
 new SimpleDateFormat("hh:mm:ss").format(new Date()));
 }
}

This is our blueprint.xml:

<blueprint xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
  <service id="simpleTimeService"
           interface="com.hascode.tutorial.blueprint_service_export.SimpleTimeService"
           ref="timeService"/>
  <bean id="timeService" class="com.hascode.tutorial.blueprint_service_export.SimpleTimeServiceImpl"/>
</blueprint>

Now build this bundle and deploy it to your Karaf instance – ensure that the service is correctly exported

karaf@root> install file:/path/osgi-blueprint-tutorial/blueprint-service-export/target/blueprint-service-export-0.0.1.jar
Bundle ID: 108
karaf@root> start 108
karaf@root> ls|grep hascode
hascode-osgi-service OSGi Bundle (106) provides:
com.hascode.tutorial.hascode_osgi_service.api.SampleService
hascode-blueprint-bundle OSGi Bundle (107) provides:
com.hascode.tutorial.blueprint_service_export.SimpleTimeService
karaf@root> exports|grep hascode
 107 com.hascode.tutorial.hascode_blueprint_bundle; version=0.0.0
 108 com.hascode.tutorial.blueprint_service_export; version=0.0.0

Bean Wiring

In the next example, we’re going to wire some services using blueprint. We’re injecting the SampleService from the first example (regular bundle) and the TimeService from the blueprint service export example into a service in our newly created bundle here.

First, we’re using the maven archetype again to create a new bundle and we’re adding the dependencies for the other bundles to our pom.xml

<dependencies>
    [..]
    <dependency>
        <groupId>com.hascode.tutorial</groupId>
        <artifactId>blueprint-service-export</artifactId>
        <version>0.0.1</version>
        <type>bundle</type>
    </dependency>
    <dependency>
        <groupId>com.hascode.tutorial</groupId>
        <artifactId>hascode-osgi-service</artifactId>
        <version>0.0.1</version>
        <type>bundle</type>
    </dependency>
</dependencies>

Afterwards we’re creating a new service interface: TimeGreetService

package com.hascode.tutorial.blueprint_wiring_example;
 
public interface TimeGreetService {
 String print();
}

And of course the implementation that makes use of two imported OSGi services: the SampleService and the TimeService: TimeGreetServiceImpl

package com.hascode.tutorial.blueprint_wiring_example;
 
import com.hascode.tutorial.blueprint_service_export.SimpleTimeService;
import com.hascode.tutorial.hascode_osgi_service.api.SampleService;
 
public class TimeGreetServiceImpl implements TimeGreetService {
 private final SimpleTimeService timeService;
 private final SampleService sampleService;
 
 public TimeGreetServiceImpl(final SimpleTimeService timeService,
 final SampleService sampleService) {
 this.timeService = timeService;
 this.sampleService = sampleService;
 }
 
 public String print() {
 return timeService.getTime() + ":" + sampleService.getGreeting("tim");
 }
 
}

In addition we’re adding two callback methods to our Activator class that shall be called when our TimeGreetService has been registered in the OSGi environment

package com.hascode.tutorial.blueprint_wiring_example;
 
import java.util.Map;
 
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
 
public class Activator implements BundleActivator {
 
 public void start(final BundleContext context) throws Exception {
 }
 
 public void stop(final BundleContext context) throws Exception {
 }
 
 public void onRegisterService(final TimeGreetService service,
 final Map properties) {
 System.out.println("TimeGreetService registered - output: "
 + service.print());
 }
 
 public void onUnregisterService(final TimeGreetService service,
 final Map properties) {
 
 }
}

Finally we’re doing some wiring in our src/main/resources/OSGI-INF/blueprint/blueprint.xml

<blueprint xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
  <bean id="timeGreetServiceImpl" class="com.hascode.tutorial.blueprint_wiring_example.TimeGreetServiceImpl">
      <argument ref="timeService"/>
      <argument ref="sampleService"/>
  </bean>
  <bean id="activator" class="com.hascode.tutorial.blueprint_wiring_example.Activator"/>
  <reference id="sampleService" activation="eager" availability="mandatory" interface="com.hascode.tutorial.hascode_osgi_service.api.SampleService">
  </reference>
  <reference id="timeService" activation="eager" availability="mandatory" interface="com.hascode.tutorial.blueprint_service_export.SimpleTimeService">
  </reference>
  <service id="timeGreetService" interface="com.hascode.tutorial.blueprint_wiring_example.TimeGreetService" ref="timeGreetServiceImpl">
      <registration-listener ref="activator" registration-method="onRegisterService" unregistration-method="onUnregisterService"/>
  </service>
</blueprint>

Now build, deploy, start and you should be able to see the following output (most likely a different date ;)

karaf@root> install file:/path/osgi-blueprint-tutorial/blueprint-wiring-example/target/blueprint-wiring-example-0.0.1.jar
Bundle ID: 109
karaf@root> start 109
karaf@root> TimeGreetService registered - output: the time is: 09:26:56:hello, tim

Apache Karaf

I am using version 2.2.6 throughout this tutorial and the installation is quite easy ..

Installation

Just download the corresponding version for your operating system of choice from the Karaf website and unzip it somewhere.

Now switch to <installation_directory>/bin and run the karaf executable. The shell is quite comfortable – I have listed the most important commands for you here – for a full list just type TAB and ENTER or use the help command.

Important Commands

Install bundle from file
install file:/path/filename
List Bundles
list
List Services
ls
Start Bundle
start <BUNDLE_ID>
Display latest logs
log:tail
Display exported packages and versions
export

You’re also able to use grep here to filter for specific entries .. e.g.:

karaf@root> exports | grep hascode
 95 com.hascode.tutorial.hascode_osgi_service.api; version=0.0.0
 104 com.hascode.tutorial.hascode_blueprint_bundle; version=0.0.0
 105 com.hascode.tutorial.blueprint_service_export; version=0.0.0

Tutorial Sources

I have put the source from this tutorial on my Bitbucket repository – download it there or check it out using Mercurial:

hg clone https://bitbucket.org/hascode/osgi-blueprint-tutorial

Troubleshooting

  • org.osgi.framework.BundleException: Unresolved constraint in bundle somepackage.somebundle [10]: Unable to resolve 10.0: missing requirement [10.0] osgi.wiring.package; (&(osgi.wiring.package=org.osgi.service.blueprint)(version>=1.0.0)(!(version>=2.0.0)))” – Be sure to use an OSGi container that supports the Blueprint specification .. e.g. Apache Karaf or SpringDM Server.

Resources

Tags: , , , , , , , , , , , , ,

7 Responses to “Wiring made easy using OSGi Blueprint and Apache Karaf”

  1. dealbitte Says:

    hi,
    in the following configuration, attribute ‘class’ is missing

    hence when I deploy both the bundles, only the following message is printed and no greeting is printed.
    karaf@root> start 104
    blueprint bundle activator called

    When i change the configuration to

    then the greeting message is printed as well.

    karaf@root> update 104
    blueprint bundle activator called
    karaf@root> greet: hello, bob

    Thanks.

  2. dealbitte Says:

    hi,
    in the following configuration, attribute ‘class’ is missing
    *** bean id=”bundle-activator” ***

    hence when I deploy both the bundles, only the following message is printed and no greeting is printed.
    karaf@root> start 104
    blueprint bundle activator called

    When i change the configuration to
    *** bean id=”bundle-activator” class=”de.extended.Activator” ***
    then the greeting message is printed as well.

    karaf@root> update 104
    blueprint bundle activator called
    karaf@root> greet: hello, bob

    Thanks.

  3. micha kops Says:

    thanks a lot for mentioning!
    seems like my xmlprettyprinter has swallowed all class attributes .. I’ve fixed the article .. but at least my sources were right ;) https://bitbucket.org/hascode/osgi-blueprint-tutorial/src/868f351d6413/hascode-blueprint-bundle/src/main/resources/OSGI-INF/blueprint/blueprint.xml#cl-5

  4. Prasad GS Says:

    While creating a new Maven project, I don’t get the osgi-archetype (org.codehaus.mojo.archetypes).I get only the pre-installed spring archetype. Can somebody tell me how to install the osgi archetype provided by codehaus?

  5. micha kops Says:

    Hi, the archetype is available on the general maven repo -> http://mvnrepository.com/artifact/org.codehaus.mojo.archetypes/osgi-archetype
    Perhaps refreshing your local archetype catalog is an option here -> http://maven.apache.org/archetype/maven-archetype-plugin/plugin-info.html

  6. Prasad GS Says:

    Hi,I’m getting an “404 Not Found” page when I click on the archetype available on the general maven repo @ http://mvnrepository.com/artifact/org.codehaus.mojo.archetypes/osgi-archetype

  7. micha kops Says:

    Hi, you’re right! Simply use version 1.4 of the archetype – this one should be available -> http://repo1.maven.org/maven2/org/codehaus/mojo/archetypes/osgi-archetype/1.4/

Leave a Reply

Please leave these two fields as-is:

Protected by Invisible Defender. Showed 403 to 81,013 bad guys.

Search
Categories