Wiring made easy using OSGi Blueprint and Apache Karaf
April 16th, 2012 by Micha KopsThe 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…
Contents
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.
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
Directory Structure
For the sake of completeness, this is what our project’s directory structure looks like by now:
blueprint-service-export ├── pom.xml └── src └── main ├── assembly │ └── felix.xml ├── java │ └── com │ └── hascode │ └── tutorial │ └── blueprint_service_export │ ├── Activator.java │ ├── SimpleTimeServiceImpl.java │ └── SimpleTimeService.java └── resources ├── com │ └── hascode │ └── tutorial │ └── blueprint_service_export └── OSGI-INF └── blueprint └── blueprint.xml blueprint-wiring-example ├── pom.xml └── src └── main ├── assembly │ └── felix.xml ├── java │ └── com │ └── hascode │ └── tutorial │ └── blueprint_wiring_example │ ├── Activator.java │ ├── TimeGreetServiceImpl.java │ └── TimeGreetService.java └── resources ├── com │ └── hascode │ └── tutorial │ └── blueprint_wiring_example └── OSGI-INF └── blueprint └── blueprint.xml hascode-blueprint-bundle ├── pom.xml └── src └── main ├── assembly │ └── felix.xml ├── java │ └── com │ └── hascode │ └── tutorial │ └── hascode_blueprint_bundle │ └── Activator.java └── resources ├── com │ └── hascode │ └── tutorial │ └── hascode_blueprint_bundle └── OSGI-INF └── blueprint └── blueprint.xml hascode-osgi-service ├── pom.xml └── src └── main ├── assembly │ └── felix.xml ├── java │ └── com │ └── hascode │ └── tutorial │ └── hascode_osgi_service │ ├── Activator.java │ └── api │ ├── SampleServiceImpl.java │ └── SampleService.java └── resources └── com └── hascode └── tutorial └── hascode_osgi_service
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
- Apache Karaf Website
- IBM.com: Building OSGi applications with the Blueprint Container specification
- OSGi Alliance: OSGi Service Platform Release 4 Version 4.2 Enterprise Specification
- Maven Bundle Plugin Website
- Springsource.com: Comparing SpringDM and Blueprint
Article Updates
- 2016-01-25: Project directory structure description added.
Tags: apache karaf, bind, blueprint, bundle, dependency injection, export, felix, listener, maven, osgi, service, springdm, tutorial, wiring
June 6th, 2012 at 12:50 pm
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.
June 6th, 2012 at 12:53 pm
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.
June 7th, 2012 at 8:47 am
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
November 19th, 2012 at 4:54 am
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?
November 19th, 2012 at 7:30 am
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
November 19th, 2012 at 8:31 am
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
November 20th, 2012 at 8:56 pm
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/
December 30th, 2015 at 11:30 am
Hi,
Did any body try last example?? its not work out for me. There is a wiring error while starting the bundle i am getting below error.
Error executing command: Error executing command on bundles:
Error starting bundle76: Unable to resolve com.hascode.tutorial.blueprint_wiring_example [76](R 76.8): missing requirement [com.hascode.tutorial.blueprint_wiring_example [76](R 76.8)] osgi.wiring.package; (osgi.wiring.package=com.hascode.tutorial.blueprint_service_export) Unresolved requirements: [[com.hascode.tutorial.blueprint_wiring_example [76](R 76.8)] osgi.wiring.package; (osgi.wiring.package=com.hascode.tutorial.blueprint_service_export)]
January 22nd, 2016 at 4:49 pm
Really wish you had shown us the folder structure on the side and you did not mention we have to go to each projects pom.xml and add the export statements there for the services to be shown under exports!
January 25th, 2016 at 5:56 am
Hi Ali,
is there anything missing in the sources linked in the article? Ntl, I’ve updated the article to show the exact project’s directory structure – hope it helps.
Cheers,
Micha
June 21st, 2016 at 11:28 am
Thank you, this was really helpful. =)
December 28th, 2017 at 4:27 pm
Hi,In first example.After deploying bundle ,how to display that “Hello”+message.I mean how to call that service method.
December 28th, 2017 at 8:43 pm
As the tutorial just demonstrates the wiring with OSGi blueprint, we haven’t added an external service like a rest-service or website.
If you’re interested how to achieve this within a few steps, please feel free to have a look at the following OSGi tutorial of mine: “How to create a simple OSGi Web Application using Maven“